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.

258 lines
5.7KB

  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_SPI_H_
  16. #define AVRLIBX_IO_SPI_H_
  17. #include <avr/io.h>
  18. #include "avrlibx/avrlibx.h"
  19. #include "avrlibx/io/gpio.h"
  20. namespace avrlibx {
  21. enum SPIPrescaler {
  22. SPI_PRESCALER_CLK_2 = 2,
  23. SPI_PRESCALER_CLK_4 = 4,
  24. SPI_PRESCALER_CLK_8 = 8,
  25. SPI_PRESCALER_CLK_16 = 16,
  26. SPI_PRESCALER_CLK_32 = 32,
  27. SPI_PRESCALER_CLK_64 = 64,
  28. SPI_PRESCALER_CLK_128 = 128,
  29. };
  30. template<typename Port> struct SPIWrapper { };
  31. #define WRAP_SPI(port) \
  32. template<> \
  33. struct SPIWrapper<Port ## port> { \
  34. static inline SPI_t& spi() { \
  35. return SPI ## port; \
  36. } \
  37. static volatile inline uint8_t data() { \
  38. return SPI ## port ## _DATA; \
  39. } \
  40. static inline void set_data(uint16_t value) { \
  41. SPI ## port ## _DATA = value; \
  42. } \
  43. static inline uint8_t readable() { \
  44. return SPI ## port ## _STATUS & SPI_IF_bm; \
  45. } \
  46. static inline uint8_t writable() { \
  47. return SPI ## port ## _STATUS & SPI_IF_bm; \
  48. } \
  49. static inline uint8_t dma_rx_trigger() { \
  50. return DMA_CH_TRIGSRC_SPI ## port ## _gc; \
  51. } \
  52. static inline uint8_t dma_tx_trigger() { \
  53. return DMA_CH_TRIGSRC_SPI ## port ## _gc; \
  54. } \
  55. static inline volatile void* dma_data() { \
  56. return &(SPI ## port ## _DATA); \
  57. } \
  58. };
  59. WRAP_SPI(C)
  60. WRAP_SPI(D)
  61. #ifdef SPIE
  62. WRAP_SPI(E)
  63. #endif
  64. #ifdef SPIF
  65. WRAP_SPI(F)
  66. #endif
  67. template<
  68. typename Port,
  69. typename SlaveSelect,
  70. DataOrder order = MSB_FIRST,
  71. SPIPrescaler speed = SPI_PRESCALER_CLK_4,
  72. bool pull_up_miso=false>
  73. struct SPIMaster {
  74. typedef SPIWrapper<Port> SPI;
  75. typedef Gpio<Port, 4> SS;
  76. typedef Gpio<Port, 5> MOSI;
  77. typedef Gpio<Port, 6> MISO;
  78. typedef Gpio<Port, 7> SCK;
  79. static inline void Init() {
  80. SCK::set_direction(OUTPUT);
  81. MOSI::set_direction(OUTPUT);
  82. MISO::set_direction(INPUT);
  83. if (pull_up_miso) {
  84. PullUpMISO();
  85. }
  86. SS::set_direction(OUTPUT);
  87. SS::High();
  88. SlaveSelect::set_direction(OUTPUT);
  89. SlaveSelect::High();
  90. uint8_t control = SPI_ENABLE_bm | SPI_MASTER_bm;
  91. if (order == LSB_FIRST) {
  92. control |= SPI_DORD_bm;
  93. }
  94. if (speed == SPI_PRESCALER_CLK_2) {
  95. control |= SPI_CLK2X_bm;
  96. } else if (speed == SPI_PRESCALER_CLK_4) {
  97. } else if (speed == SPI_PRESCALER_CLK_8) {
  98. control |= SPI_CLK2X_bm | 0x1;
  99. } else if (speed == SPI_PRESCALER_CLK_16) {
  100. control |= 0x1;
  101. } else if (speed == SPI_PRESCALER_CLK_32) {
  102. control |= SPI_CLK2X_bm | 0x2;
  103. } else if (speed == SPI_PRESCALER_CLK_64) {
  104. control |= 0x2;
  105. } else if (speed == SPI_PRESCALER_CLK_128) {
  106. control |= 0x3;
  107. }
  108. SPI::spi().CTRL = control;
  109. }
  110. static inline void PullUpMISO() {
  111. MISO::set_mode(PORT_MODE_PULL_UP);
  112. }
  113. static inline void Begin() {
  114. SlaveSelect::Low();
  115. }
  116. static inline void End() {
  117. SlaveSelect::High();
  118. }
  119. static inline void Strobe() {
  120. SlaveSelect::High();
  121. SlaveSelect::Low();
  122. }
  123. static inline void Write(uint8_t v) {
  124. Begin();
  125. Send(v);
  126. End();
  127. }
  128. static inline uint8_t Read() {
  129. Begin();
  130. uint8_t result = Receive();
  131. End();
  132. return result;
  133. }
  134. static inline void Send(uint8_t v) {
  135. Overwrite(v);
  136. Wait();
  137. }
  138. static inline uint8_t Receive() {
  139. Send(0xff);
  140. return ImmediateRead();
  141. }
  142. static inline uint8_t ImmediateRead() {
  143. return SPI::data();
  144. }
  145. static inline void Wait() {
  146. while (!SPI::writable());
  147. }
  148. static inline void OptimisticWait() {
  149. Wait();
  150. }
  151. static inline void Overwrite(uint8_t v) {
  152. SPI::set_data(v);
  153. }
  154. static inline void WriteWord(uint8_t a, uint8_t b) {
  155. Begin();
  156. Send(a);
  157. Send(b);
  158. End();
  159. }
  160. static inline uint8_t dma_rx_trigger() {
  161. return SPI::dma_rx_trigger();
  162. }
  163. static inline uint8_t dma_tx_trigger() {
  164. return SPI::dma_rx_trigger();
  165. }
  166. static inline volatile void* dma_data() {
  167. return SPI::dma_data();
  168. }
  169. typedef uint8_t Value;
  170. };
  171. template<typename Port, DataOrder order = MSB_FIRST>
  172. struct SPISlave {
  173. typedef SPIWrapper<Port> SPI;
  174. typedef Gpio<Port, 4> SS;
  175. typedef Gpio<Port, 5> MOSI;
  176. typedef Gpio<Port, 6> MISO;
  177. typedef Gpio<Port, 7> SCK;
  178. static inline void Init() {
  179. SCK::set_direction(INPUT);
  180. MOSI::set_direction(INPUT);
  181. MISO::set_direction(OUTPUT);
  182. SS::set_direction(INPUT);
  183. SS::High();
  184. uint8_t control = SPI_ENABLE_bm;
  185. if (order == LSB_FIRST) {
  186. control |= SPI_DORD_bm;
  187. }
  188. SPI::spi().CTRL = control;
  189. }
  190. static inline void Reply(uint8_t value) {
  191. SPI::set_data(value);
  192. }
  193. static inline uint8_t readable() {
  194. return SPI::readable();
  195. }
  196. static inline uint8_t ImmediateRead() {
  197. return SPI::data();
  198. }
  199. static inline uint8_t Read() {
  200. while (!readable());
  201. return ImmediateRead();
  202. }
  203. static inline uint8_t dma_rx_trigger() {
  204. return SPI::dma_rx_trigger();
  205. }
  206. static inline uint8_t dma_tx_trigger() {
  207. return SPI::dma_rx_trigger();
  208. }
  209. static inline volatile void* dma_data() {
  210. return SPI::dma_data();
  211. }
  212. typedef uint8_t Value;
  213. };
  214. } // namespace avrlibx
  215. #endif // AVRLIBX_IO_SPI_H_