// Copyright 2011 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef AVRLIBX_IO_DMA_H_ #define AVRLIBX_IO_DMA_H_ #include #include "avrlibx/avrlibx.h" namespace avrlibx { template struct DMAChannel { }; #define WRAP_DMA_CHANNEL(channel_index) \ template<> \ struct DMAChannel { \ static inline DMA_CH_t& channel() { \ return DMA.CH ## channel_index; \ } \ }; WRAP_DMA_CHANNEL(0) WRAP_DMA_CHANNEL(1) WRAP_DMA_CHANNEL(2) WRAP_DMA_CHANNEL(3) // Uses DMA to automatically store data from a peripheral to a ring buffer. template< typename Peripheral, uint8_t channel_index, uint16_t buffer_size> class DMARxBuffer { private: typedef DMAChannel Channel; typedef typename Peripheral::Value Value; public: DMARxBuffer() { } static inline void Init() { DMA_CTRL = 0x80; uint8_t address_control = 0; address_control |= DMA_CH_SRCRELOAD_BURST_gc; address_control |= DMA_CH_DESTRELOAD_BLOCK_gc; address_control |= DMA_CH_SRCDIR_INC_gc; address_control |= DMA_CH_DESTDIR_INC_gc; Channel::channel().ADDRCTRL = address_control; Channel::channel().REPCNT = 0; Channel::channel().TRFCNT = buffer_size * sizeof(Value); uint32_t source = (uint32_t)(Peripheral::dma_data()); uint32_t destination = (uint32_t)(&buffer_); 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 = Peripheral::dma_rx_trigger(); Channel::channel().CTRLA = DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm; if (sizeof(Value) == 2) { Channel::channel().CTRLA |= 0x01; } else if (sizeof(Value) == 4) { Channel::channel().CTRLA |= 0x02; } else if (sizeof(Value) == 8) { Channel::channel().CTRLA |= 0x03; } } static inline uint8_t readable() { uint8_t write_ptr = buffer_size - \ Channel::channel().TRFCNTL / sizeof(Value); return (write_ptr - read_ptr_) & (buffer_size - 1); } static inline Value ImmediateRead() { Value result = buffer_[read_ptr_]; read_ptr_ = (read_ptr_ + 1) & (buffer_size - 1); return result; } static inline Value Read() { while (!readable()); return ImmediateRead(); } 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 read_ptr_; static Value buffer_[buffer_size]; DISALLOW_COPY_AND_ASSIGN(DMARxBuffer); }; template \ 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_