\
uint8_t DMARxBuffer::read_ptr_ = 0;
template \
typename P::Value DMARxBuffer::buffer_[b];
// Uses DMA to automatically feed data to a peripheral.
template<
    uint8_t channel_index,
    uint16_t buffer_size,
    typename Destination,
    typename Trigger,
    bool one_shot = false>
class DMATxBuffer {
 private:
  typedef DMAChannel Channel;
  typedef typename Destination::Value Value;
 public:
  DMATxBuffer() { }
   
  static inline void Init() {
    DMA_CTRL = 0x80;
    uint8_t address_control = 0;
    address_control |= DMA_CH_SRCRELOAD_BLOCK_gc;
    address_control |= DMA_CH_DESTRELOAD_BURST_gc;
    address_control |= DMA_CH_SRCDIR_INC_gc;
    address_control |= DMA_CH_DESTDIR_INC_gc;
    
    Channel::channel().ADDRCTRL = address_control;
    Channel::channel().REPCNT = one_shot ? 1 : 0;
    Channel::channel().TRFCNT = buffer_size * sizeof(Value);
    
    uint32_t source = (uint32_t)(&buffer_);
    uint32_t destination = (uint32_t)(Destination::dma_data());
    Channel::channel().SRCADDR0 = (source >> 0) & 0xff;
    Channel::channel().SRCADDR1 = (source >> 8) & 0xff;
    Channel::channel().SRCADDR2 = (source >> 16) & 0xff;
    Channel::channel().DESTADDR0 = (destination >> 0) & 0xff;
    Channel::channel().DESTADDR1 = (destination >> 8) & 0xff;
    Channel::channel().DESTADDR2 = (destination >> 16) & 0xff;
    Channel::channel().TRIGSRC = Trigger::dma_tx_trigger();
    Channel::channel().CTRLA = DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm;
    if (sizeof(Value) == 2) {
      Channel::channel().CTRLA |= DMA_CH_BURSTLEN_2BYTE_gc;
    } else if (sizeof(Value) == 4) {
      Channel::channel().CTRLA |= DMA_CH_BURSTLEN_4BYTE_gc;
    } else if (sizeof(Value) == 8) {
      Channel::channel().CTRLA |= DMA_CH_BURSTLEN_8BYTE_gc;
    }
  }
  
  static inline uint8_t writable() {
    uint8_t read_ptr = buffer_size - Channel::channel().TRFCNTL / sizeof(Value);
    return (read_ptr - write_ptr_ - 1) & (buffer_size - 1);
  }
  
  static inline void Write(Value v) {
    while (!writable());
    Overwrite(v);
  }
  
  static inline void Overwrite(Value v) {
    uint8_t w = write_ptr_;
    buffer_[w] = v;
    write_ptr_ = (w + 1) & (buffer_size - 1);
  }
  
  static inline void Start() {
    Channel::channel().CTRLA |= DMA_CH_ENABLE_bm;
  }
  
  static inline void Stop() {
    Channel::channel().CTRLA &= ~DMA_CH_ENABLE_bm;
  }
  
 private:
  static uint8_t write_ptr_;
  static Value buffer_[buffer_size];
  
  DISALLOW_COPY_AND_ASSIGN(DMATxBuffer);
};
template \
uint8_t DMATxBuffer::write_ptr_ = 0;
template \
typename D::Value DMATxBuffer::buffer_[b];
}  // namespace avrlibx
#endif   // AVRLIBX_IO_DMA_H_