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.

310 lines
6.8KB

  1. // Copyright 2009 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. //
  16. // -----------------------------------------------------------------------------
  17. //
  18. // Fast SPI communication (using the hardware implementation). This will take
  19. // ownership of the pins 11 (data output), 12 (data input) and 13 (clock), +
  20. // a user-definable pin for slave selection. Pin 10 should be kept as an output
  21. // pin, since the SPI master/slave mode is based upon the value of this pin.
  22. #ifndef AVRLIB_SPI_H_
  23. #define AVRLIB_SPI_H_
  24. #include "avrlib/avrlib.h"
  25. #include "avrlib/gpio.h"
  26. #include "avrlib/serial.h"
  27. namespace avrlib {
  28. IORegister(SPSR);
  29. typedef BitInRegister<SPSRRegister, SPI2X> DoubleSpeed;
  30. typedef BitInRegister<SPSRRegister, SPIF> TransferComplete;
  31. template<typename SlaveSelect,
  32. DataOrder order = MSB_FIRST,
  33. uint8_t speed = 4>
  34. class SpiMaster {
  35. public:
  36. enum {
  37. buffer_size = 0,
  38. data_size = 8
  39. };
  40. static void Init() {
  41. SpiSCK::set_mode(DIGITAL_OUTPUT);
  42. SpiMOSI::set_mode(DIGITAL_OUTPUT);
  43. SpiMISO::set_mode(DIGITAL_INPUT);
  44. SpiSS::set_mode(DIGITAL_OUTPUT); // I'm a master!
  45. SpiSS::High();
  46. SlaveSelect::set_mode(DIGITAL_OUTPUT);
  47. SlaveSelect::High();
  48. // SPI enabled, configured as master.
  49. uint8_t configuration = _BV(SPE) | _BV(MSTR);
  50. if (order == LSB_FIRST) {
  51. configuration |= _BV(DORD);
  52. }
  53. DoubleSpeed::clear();
  54. switch (speed) {
  55. case 2:
  56. DoubleSpeed::set();
  57. case 4:
  58. break;
  59. case 8:
  60. DoubleSpeed::set();
  61. case 16:
  62. configuration |= _BV(SPR0);
  63. break;
  64. case 32:
  65. DoubleSpeed::set();
  66. case 64:
  67. configuration |= _BV(SPR1);
  68. break;
  69. case 128:
  70. configuration |= _BV(SPR0);
  71. configuration |= _BV(SPR1);
  72. break;
  73. }
  74. SPCR = configuration;
  75. }
  76. static inline void PullUpMISO() {
  77. SpiMISO::High();
  78. }
  79. static inline void Begin() {
  80. SlaveSelect::Low();
  81. }
  82. static inline void End() {
  83. SlaveSelect::High();
  84. }
  85. static inline void Strobe() {
  86. SlaveSelect::High();
  87. SlaveSelect::Low();
  88. }
  89. static inline void Write(uint8_t v) {
  90. Begin();
  91. Send(v);
  92. End();
  93. }
  94. static inline uint8_t Read() {
  95. Begin();
  96. uint8_t result = Receive();
  97. End();
  98. return result;
  99. }
  100. static inline void Send(uint8_t v) {
  101. Overwrite(v);
  102. Wait();
  103. }
  104. static inline uint8_t Receive() {
  105. Send(0xff);
  106. return ImmediateRead();
  107. }
  108. static inline uint8_t ImmediateRead() {
  109. return SPDR;
  110. }
  111. static inline void Wait() {
  112. while (!TransferComplete::value());
  113. }
  114. static inline void OptimisticWait() {
  115. Wait();
  116. }
  117. static inline void Overwrite(uint8_t v) {
  118. SPDR = v;
  119. }
  120. static inline void WriteWord(uint8_t a, uint8_t b) {
  121. Begin();
  122. Send(a);
  123. Send(b);
  124. End();
  125. }
  126. };
  127. template<DataOrder order = MSB_FIRST,
  128. bool enable_interrupt = false>
  129. class SpiSlave {
  130. public:
  131. enum {
  132. buffer_size = 128,
  133. data_size = 8
  134. };
  135. static void Init() {
  136. SpiSCK::set_mode(DIGITAL_INPUT);
  137. SpiMOSI::set_mode(DIGITAL_INPUT);
  138. SpiMISO::set_mode(DIGITAL_OUTPUT);
  139. SpiSS::set_mode(DIGITAL_INPUT); // Ohhh mistress, ohhhh!
  140. // SPI enabled, configured as master.
  141. uint8_t configuration = _BV(SPE);
  142. if (order == LSB_FIRST) {
  143. configuration |= _BV(DORD);
  144. }
  145. if (enable_interrupt) {
  146. configuration |= _BV(SPIE);
  147. }
  148. SPCR = configuration;
  149. }
  150. static inline void Reply(uint8_t value) {
  151. SPDR = value;
  152. }
  153. static inline uint8_t readable() {
  154. return TransferComplete::value();
  155. }
  156. static inline uint8_t ImmediateRead() {
  157. return SPDR;
  158. }
  159. static inline uint8_t Read() {
  160. while (!readable());
  161. return ImmediateRead();
  162. }
  163. };
  164. template<typename XckPort,
  165. typename TxPort,
  166. typename RxPort,
  167. typename PrescalerRegister,
  168. typename ControlRegisterB,
  169. uint8_t BFlags,
  170. typename ControlRegisterC,
  171. uint8_t CFlags,
  172. typename TxReadyBit,
  173. typename DataRegister>
  174. struct UartSpiPort {
  175. static inline uint8_t tx_ready() { return TxReadyBit::value(); }
  176. static inline uint8_t data() { return *DataRegister::ptr(); }
  177. static inline void set_data(uint8_t value) { *DataRegister::ptr() = value; }
  178. static inline void Setup(uint16_t rate) {
  179. *PrescalerRegister::ptr() = 0;
  180. XckPort::set_mode(DIGITAL_OUTPUT);
  181. TxPort::set_mode(DIGITAL_OUTPUT);
  182. RxPort::set_mode(DIGITAL_INPUT);
  183. *ControlRegisterC::ptr() = CFlags;
  184. *ControlRegisterB::ptr() = BFlags;
  185. *PrescalerRegister::ptr() = rate;
  186. }
  187. };
  188. #ifdef HAS_USART0
  189. typedef UartSpiPort<
  190. UartSpi0XCK,
  191. UartSpi0TX,
  192. UartSpi0RX,
  193. UBRR0Register,
  194. UCSR0BRegister,
  195. _BV(RXEN0) | _BV(TXEN0),
  196. UCSR0CRegister,
  197. _BV(UMSEL01) | _BV(UMSEL00),
  198. BitInRegister<UCSR0ARegister, UDRE0>,
  199. UDR0Register> UartSpiPort0;
  200. #endif // HAS_USART0
  201. #ifdef HAS_USART1
  202. typedef UartSpiPort<
  203. UartSpi1XCK,
  204. UartSpi1TX,
  205. UartSpi1RX,
  206. UBRR1Register,
  207. UCSR1BRegister,
  208. _BV(RXEN1) | _BV(TXEN1),
  209. UCSR1CRegister,
  210. _BV(UMSEL11) | _BV(UMSEL10),
  211. BitInRegister<UCSR1ARegister, UDRE1>,
  212. UDR1Register> UartSpiPort1;
  213. #endif // HAS_USART1
  214. template<typename Port, typename SlaveSelect, uint8_t speed = 2>
  215. class UartSpiMaster {
  216. public:
  217. enum {
  218. buffer_size = 0,
  219. data_size = 8
  220. };
  221. static void Init() {
  222. SlaveSelect::set_mode(DIGITAL_OUTPUT);
  223. SlaveSelect::High();
  224. Port::Setup((speed / 2) - 1);
  225. }
  226. static inline void Begin() {
  227. SlaveSelect::Low();
  228. }
  229. static inline void End() {
  230. SlaveSelect::High();
  231. }
  232. static inline void Strobe() {
  233. SlaveSelect::High();
  234. SlaveSelect::Low();
  235. }
  236. static inline void Write(uint8_t v) {
  237. Begin();
  238. Send(v);
  239. End();
  240. }
  241. static inline void Send(uint8_t v) {
  242. Overwrite(v);
  243. Wait();
  244. }
  245. static inline void Wait() {
  246. while (!Port::tx_ready());
  247. }
  248. static inline void OptimisticWait() { }
  249. static inline void Overwrite(uint8_t v) {
  250. Port::set_data(v);
  251. }
  252. static inline void WriteWord(uint8_t a, uint8_t b) {
  253. Begin();
  254. Send(a);
  255. Send(b);
  256. End();
  257. }
  258. };
  259. #define SPI_RECEIVE ISR(SPI_STC_vect)
  260. } // namespace avrlib
  261. #endif AVRLIB_SPI_H_