You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
6.2KB

  1. // Copyright 2011 Olivier Gillet.
  2. //
  3. // Author: Olivier Gillet (ol.gillet@gmail.com)
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. #ifndef AVRLIBX_IO_DMA_H_
  16. #define AVRLIBX_IO_DMA_H_
  17. #include <avr/io.h>
  18. #include "avrlibx/avrlibx.h"
  19. namespace avrlibx {
  20. template<uint8_t index>
  21. struct DMAChannel { };
  22. #define WRAP_DMA_CHANNEL(channel_index) \
  23. template<> \
  24. struct DMAChannel<channel_index> { \
  25. static inline DMA_CH_t& channel() { \
  26. return DMA.CH ## channel_index; \
  27. } \
  28. };
  29. WRAP_DMA_CHANNEL(0)
  30. WRAP_DMA_CHANNEL(1)
  31. WRAP_DMA_CHANNEL(2)
  32. WRAP_DMA_CHANNEL(3)
  33. // Uses DMA to automatically store data from a peripheral to a ring buffer.
  34. template<
  35. typename Peripheral,
  36. uint8_t channel_index,
  37. uint16_t buffer_size>
  38. class DMARxBuffer {
  39. private:
  40. typedef DMAChannel<channel_index> Channel;
  41. typedef typename Peripheral::Value Value;
  42. public:
  43. DMARxBuffer() { }
  44. static inline void Init() {
  45. DMA_CTRL = 0x80;
  46. uint8_t address_control = 0;
  47. address_control |= DMA_CH_SRCRELOAD_BURST_gc;
  48. address_control |= DMA_CH_DESTRELOAD_BLOCK_gc;
  49. address_control |= DMA_CH_SRCDIR_INC_gc;
  50. address_control |= DMA_CH_DESTDIR_INC_gc;
  51. Channel::channel().ADDRCTRL = address_control;
  52. Channel::channel().REPCNT = 0;
  53. Channel::channel().TRFCNT = buffer_size * sizeof(Value);
  54. uint32_t source = (uint32_t)(Peripheral::dma_data());
  55. uint32_t destination = (uint32_t)(&buffer_);
  56. Channel::channel().SRCADDR0 = (source >> 0) & 0xff;
  57. Channel::channel().SRCADDR1 = (source >> 8) & 0xff;
  58. Channel::channel().SRCADDR2 = (source >> 16) & 0xff;
  59. Channel::channel().DESTADDR0 = (destination >> 0) & 0xff;
  60. Channel::channel().DESTADDR1 = (destination >> 8) & 0xff;
  61. Channel::channel().DESTADDR2 = (destination >> 16) & 0xff;
  62. Channel::channel().TRIGSRC = Peripheral::dma_rx_trigger();
  63. Channel::channel().CTRLA = DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm;
  64. if (sizeof(Value) == 2) {
  65. Channel::channel().CTRLA |= 0x01;
  66. } else if (sizeof(Value) == 4) {
  67. Channel::channel().CTRLA |= 0x02;
  68. } else if (sizeof(Value) == 8) {
  69. Channel::channel().CTRLA |= 0x03;
  70. }
  71. }
  72. static inline uint8_t readable() {
  73. uint8_t write_ptr = buffer_size - \
  74. Channel::channel().TRFCNTL / sizeof(Value);
  75. return (write_ptr - read_ptr_) & (buffer_size - 1);
  76. }
  77. static inline Value ImmediateRead() {
  78. Value result = buffer_[read_ptr_];
  79. read_ptr_ = (read_ptr_ + 1) & (buffer_size - 1);
  80. return result;
  81. }
  82. static inline Value Read() {
  83. while (!readable());
  84. return ImmediateRead();
  85. }
  86. static inline void Start() {
  87. Channel::channel().CTRLA |= DMA_CH_ENABLE_bm;
  88. }
  89. static inline void Stop() {
  90. Channel::channel().CTRLA &= ~DMA_CH_ENABLE_bm;
  91. }
  92. private:
  93. static uint8_t read_ptr_;
  94. static Value buffer_[buffer_size];
  95. DISALLOW_COPY_AND_ASSIGN(DMARxBuffer);
  96. };
  97. template<typename P, uint8_t c, uint16_t b> \
  98. uint8_t DMARxBuffer<P, c, b>::read_ptr_ = 0;
  99. template<typename P, uint8_t c, uint16_t b> \
  100. typename P::Value DMARxBuffer<P, c, b>::buffer_[b];
  101. // Uses DMA to automatically feed data to a peripheral.
  102. template<
  103. uint8_t channel_index,
  104. uint16_t buffer_size,
  105. typename Destination,
  106. typename Trigger,
  107. bool one_shot = false>
  108. class DMATxBuffer {
  109. private:
  110. typedef DMAChannel<channel_index> Channel;
  111. typedef typename Destination::Value Value;
  112. public:
  113. DMATxBuffer() { }
  114. static inline void Init() {
  115. DMA_CTRL = 0x80;
  116. uint8_t address_control = 0;
  117. address_control |= DMA_CH_SRCRELOAD_BLOCK_gc;
  118. address_control |= DMA_CH_DESTRELOAD_BURST_gc;
  119. address_control |= DMA_CH_SRCDIR_INC_gc;
  120. address_control |= DMA_CH_DESTDIR_INC_gc;
  121. Channel::channel().ADDRCTRL = address_control;
  122. Channel::channel().REPCNT = one_shot ? 1 : 0;
  123. Channel::channel().TRFCNT = buffer_size * sizeof(Value);
  124. uint32_t source = (uint32_t)(&buffer_);
  125. uint32_t destination = (uint32_t)(Destination::dma_data());
  126. Channel::channel().SRCADDR0 = (source >> 0) & 0xff;
  127. Channel::channel().SRCADDR1 = (source >> 8) & 0xff;
  128. Channel::channel().SRCADDR2 = (source >> 16) & 0xff;
  129. Channel::channel().DESTADDR0 = (destination >> 0) & 0xff;
  130. Channel::channel().DESTADDR1 = (destination >> 8) & 0xff;
  131. Channel::channel().DESTADDR2 = (destination >> 16) & 0xff;
  132. Channel::channel().TRIGSRC = Trigger::dma_tx_trigger();
  133. Channel::channel().CTRLA = DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm;
  134. if (sizeof(Value) == 2) {
  135. Channel::channel().CTRLA |= DMA_CH_BURSTLEN_2BYTE_gc;
  136. } else if (sizeof(Value) == 4) {
  137. Channel::channel().CTRLA |= DMA_CH_BURSTLEN_4BYTE_gc;
  138. } else if (sizeof(Value) == 8) {
  139. Channel::channel().CTRLA |= DMA_CH_BURSTLEN_8BYTE_gc;
  140. }
  141. }
  142. static inline uint8_t writable() {
  143. uint8_t read_ptr = buffer_size - Channel::channel().TRFCNTL / sizeof(Value);
  144. return (read_ptr - write_ptr_ - 1) & (buffer_size - 1);
  145. }
  146. static inline void Write(Value v) {
  147. while (!writable());
  148. Overwrite(v);
  149. }
  150. static inline void Overwrite(Value v) {
  151. uint8_t w = write_ptr_;
  152. buffer_[w] = v;
  153. write_ptr_ = (w + 1) & (buffer_size - 1);
  154. }
  155. static inline void Start() {
  156. Channel::channel().CTRLA |= DMA_CH_ENABLE_bm;
  157. }
  158. static inline void Stop() {
  159. Channel::channel().CTRLA &= ~DMA_CH_ENABLE_bm;
  160. }
  161. private:
  162. static uint8_t write_ptr_;
  163. static Value buffer_[buffer_size];
  164. DISALLOW_COPY_AND_ASSIGN(DMATxBuffer);
  165. };
  166. template<uint8_t c, uint16_t b, typename D, typename T, bool o> \
  167. uint8_t DMATxBuffer<c, b, D, T, o>::write_ptr_ = 0;
  168. template<uint8_t c, uint16_t b, typename D, typename T, bool o> \
  169. typename D::Value DMATxBuffer<c, b, D, T, o>::buffer_[b];
  170. } // namespace avrlibx
  171. #endif // AVRLIBX_IO_DMA_H_