sppif0.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. * Copyright (C) 2007 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. *
  32. */
  33. /*!
  34. * \file arch/avr/dev/sppif0.c
  35. * \brief Legacy AVR polling mode SPI support.
  36. *
  37. * \verbatim
  38. * $Id: sppif0.c 4706 2012-10-06 17:42:01Z haraldkipp $
  39. * \endverbatim
  40. */
  41. #include <cfg/arch/avr.h>
  42. #include <sys/timer.h>
  43. #include <dev/sppif0.h>
  44. /* SPI control shadow registers. */
  45. static uint8_t sppi0_spcr[SPPI0_MAX_DEVICES];
  46. /* SPI status shadow registers. Used to configure the double speed bit. */
  47. #ifdef SPI2X
  48. static uint8_t sppi0_spsr[SPPI0_MAX_DEVICES];
  49. #endif
  50. /*!
  51. * \brief Set mode for a given device at SPI0.
  52. *
  53. * This is typically the first call in order to use the device. It will
  54. * not directly enable the SPI hardware. This is done when selecting
  55. * the device.
  56. *
  57. * The transfer rate is set to the lowest possible value and can be
  58. * adjusted by calling Sppi0SetSpeed().
  59. *
  60. * \param ix The device index, starting at 0.
  61. * \param mode The mode to set.
  62. * - 0: Leading edge is rising, data sampled on rising edge.
  63. * - 1: Leading edge is rising, data sampled on falling edge.
  64. * - 2: Leading edge is falling, data sampled on falling edge.
  65. * - 3: Leading edge is falling, data sampled on rising edge.
  66. *
  67. * \return 0, if the device index and the mode are both valid. Otherwise
  68. * the return value will be -1.
  69. */
  70. int Sppi0SetMode(uint8_t ix, uint8_t mode)
  71. {
  72. if (ix >= SPPI0_MAX_DEVICES || mode > 3) {
  73. return -1;
  74. }
  75. /* A bit obfuscated, but compact. A simple shift
  76. correctly sets CPHA and CPOL. */
  77. sppi0_spcr[ix] = _BV(SPE) | _BV(MSTR) | (mode << 2) | _BV(SPR1) | _BV(SPR0);
  78. #if defined(SPI2X)
  79. sppi0_spsr[ix] = 0;
  80. #endif
  81. return 0;
  82. }
  83. /*!
  84. * \brief Set transfer rate for a given device at SPI0.
  85. *
  86. * \param ix The device index, starting at 0.
  87. * \param rate Transfer rate in bits per second.
  88. *
  89. */
  90. void Sppi0SetSpeed(uint8_t ix, uint32_t rate)
  91. {
  92. uint32_t fosc;
  93. uint8_t i;
  94. fosc = NutGetCpuClock();
  95. sppi0_spcr[ix] &= ~(_BV(SPR1) | _BV(SPR0));
  96. /* Find the frequency that is below or equal the requested
  97. one, using the double speed bit if available. */
  98. #if defined(SPI2X)
  99. for (i = 0; i < 7; i++) {
  100. fosc >>= 1;
  101. if (fosc <= rate) {
  102. break;
  103. }
  104. }
  105. sppi0_spcr[ix] |= (i >> 1);
  106. if (i < 6) {
  107. sppi0_spsr[ix] = ~i & 1;
  108. }
  109. #else
  110. for (i = 0; i < 3; i++) {
  111. fosc >>= 2;
  112. if (fosc <= rate) {
  113. break;
  114. }
  115. }
  116. sppi0_spcr[ix] |= i;
  117. #endif
  118. }
  119. /*!
  120. * \brief Enable the serial peripheral interface 0.
  121. *
  122. * Enables SPI with the parameters previously set by Sppi0SetMode() and
  123. * Sppi0SetSpeed().
  124. *
  125. * \param ix The device index, starting at 0. The routine will not check
  126. * if this is valid.
  127. */
  128. void Sppi0Enable(uint8_t ix)
  129. {
  130. /*
  131. * When configured as SPI master, MOSI (PB2) and SCK (PB1)
  132. * lines are not automatically switched to output.
  133. */
  134. if (sppi0_spcr[ix] & _BV(CPOL)) {
  135. cbi(PORTB, 1);
  136. } else {
  137. sbi(PORTB, 1);
  138. }
  139. sbi(DDRB, 1);
  140. cbi(PORTB, 2);
  141. sbi(DDRB, 2);
  142. /* Enable MISO pull-up to avoid floating. */
  143. sbi(PORTB, 3);
  144. /*
  145. * When SS (PB0) is configured as input, we will be forced
  146. * into slave mode if this pin goes low. Enable the pull-up.
  147. */
  148. if (bit_is_clear(DDRB, 0)) {
  149. sbi(PORTB, 0);
  150. }
  151. /* Set SPI mode and optionally the double speed bit. */
  152. outb(SPCR, sppi0_spcr[ix]);
  153. #if defined(SPI2X)
  154. outb(SPSR, sppi0_spsr[ix]);
  155. #endif
  156. /* Clean-up the status. */
  157. ix = inb(SPSR);
  158. ix = inb(SPDR);
  159. }
  160. /*!
  161. * \brief Set or clear a configured reset line for a given device.
  162. *
  163. * Reset lines must be configured when building the system libraries.
  164. * This routine silently ignores them, if not configured.
  165. *
  166. * \param ix The device index, starting at 0.
  167. * \param hi If 0, the reset line is driven low. Otherwise the
  168. * line is driven high.
  169. */
  170. void Sppi0ChipReset(uint8_t ix, uint8_t hi)
  171. {
  172. #if defined(SPPI0_RST0_BIT)
  173. if (ix == 0) {
  174. if (hi) {
  175. SPPI0_RST0_SET();
  176. } else {
  177. SPPI0_RST0_CLR();
  178. }
  179. SPPI0_RST0_ENA();
  180. }
  181. #endif
  182. #if defined(SPPI0_RST1_BIT)
  183. if (ix == 1) {
  184. if (hi) {
  185. SPPI0_RST1_SET();
  186. } else {
  187. SPPI0_RST1_CLR();
  188. }
  189. SPPI0_RST1_ENA();
  190. }
  191. #endif
  192. #if defined(SPPI0_RST2_BIT)
  193. if (ix == 2) {
  194. if (hi) {
  195. SPPI0_RST2_SET();
  196. } else {
  197. SPPI0_RST2_CLR();
  198. }
  199. SPPI0_RST2_ENA();
  200. }
  201. #endif
  202. #if defined(SPPI0_RST3_BIT)
  203. if (ix == 3) {
  204. if (hi) {
  205. SPPI0_RST3_SET();
  206. } else {
  207. SPPI0_RST3_CLR();
  208. }
  209. SPPI0_RST3_ENA();
  210. }
  211. #endif
  212. }
  213. /*!
  214. * \brief Set or clear a configured chip select for a given device.
  215. *
  216. * Chip selects must be configured when building the system libraries.
  217. * This routine silently ignores selects, if they are not configured.
  218. *
  219. * \param ix The device index, starting at 0. Same as the chip select
  220. * number.
  221. * \param hi If 0, the chip select is driven low. Otherwise the
  222. * line is driven high.
  223. */
  224. void Sppi0ChipSelect(uint8_t ix, uint8_t hi)
  225. {
  226. #if defined(SPPI0_CS0_BIT)
  227. if (ix == 0) {
  228. if (hi) {
  229. SPPI0_CS0_SET();
  230. } else {
  231. SPPI0_CS0_CLR();
  232. }
  233. SPPI0_CS0_ENA();
  234. }
  235. #endif
  236. #if defined(SPPI0_CS1_BIT)
  237. if (ix == 1) {
  238. if (hi) {
  239. SPPI0_CS1_SET();
  240. } else {
  241. SPPI0_CS1_CLR();
  242. }
  243. SPPI0_CS1_ENA();
  244. }
  245. #endif
  246. #if defined(SPPI0_CS2_BIT)
  247. if (ix == 2) {
  248. if (hi) {
  249. SPPI0_CS2_SET();
  250. } else {
  251. SPPI0_CS2_CLR();
  252. }
  253. SPPI0_CS2_ENA();
  254. }
  255. #endif
  256. #if defined(SPPI0_CS3_BIT)
  257. if (ix == 3) {
  258. if (hi) {
  259. SPPI0_CS3_SET();
  260. } else {
  261. SPPI0_CS3_CLR();
  262. }
  263. SPPI0_CS3_ENA();
  264. }
  265. #endif
  266. }
  267. /*!
  268. * \brief Select the device at a given chip select.
  269. *
  270. * Enables the serial peripheral interface with the parameters
  271. * previously set for the given device by Sppi0SetMode() and
  272. * Sppi0SetSpeed(). Then the configured chip select line is
  273. * driven high.
  274. *
  275. * \param ix The device index, starting at 0. The routine will not
  276. * check if this is a valid number.
  277. */
  278. void Sppi0SelectDevice(uint8_t ix)
  279. {
  280. Sppi0Enable(ix);
  281. Sppi0ChipSelect(ix, 1);
  282. }
  283. /*!
  284. * \brief Deselect the device at a given chip select.
  285. *
  286. * The configured chip select line will be driven low.
  287. *
  288. * \param ix The device index, starting at 0. The routine will not
  289. * check if this is a valid number.
  290. */
  291. void Sppi0DeselectDevice(uint8_t ix)
  292. {
  293. Sppi0ChipSelect(ix, 0);
  294. }
  295. /*!
  296. * \brief Select the device at a given negated chip select.
  297. *
  298. * Enables the serial peripheral interface with the parameters
  299. * previously set for the given device by Sppi0SetMode() and
  300. * Sppi0SetSpeed(). Then the configured chip select line is
  301. * driven low.
  302. *
  303. * \param ix The device index, starting at 0. The routine will not
  304. * check if this is a valid number.
  305. */
  306. void Sppi0NegSelectDevice(uint8_t ix)
  307. {
  308. Sppi0Enable(ix);
  309. Sppi0ChipSelect(ix, 0);
  310. }
  311. /*!
  312. * \brief Deselect the device at a given negated chip select.
  313. *
  314. * The configured chip select line will be driven high.
  315. *
  316. * \param ix The device index, starting at 0. The routine will not
  317. * check if this is a valid number.
  318. */
  319. void Sppi0NegDeselectDevice(uint8_t ix)
  320. {
  321. Sppi0ChipSelect(ix, 1);
  322. }
  323. /*!
  324. * \brief Exchange a byte with the currently selected SPI device.
  325. *
  326. * \param data Byte to transmit.
  327. *
  328. * \return Received byte.
  329. */
  330. uint8_t Sppi0Byte(uint8_t data)
  331. {
  332. outb(SPDR, data);
  333. loop_until_bit_is_set(SPSR, SPIF);
  334. return inb(SPDR);
  335. }
  336. /*!
  337. * \brief Exchange a specified number of bytes with the currently selected SPI device.
  338. *
  339. * It is assumed, that the serial peripheral interface has been properly
  340. * initialized by calling Sppi0SetMode() and optionally Sppi0SetSpeed().
  341. *
  342. * Further it is assumed, that the chip select (if there is one) had
  343. * been enabled by a previous call to Sppi0SelectDevice() or
  344. * Sppi0NegSelectDevice().
  345. *
  346. * \param wdata Pointer to the data to transmit.
  347. * \param rdata Pointer to a buffer that receives the data from the device.
  348. * Can be set to NULL for transmit only. May also point to the
  349. * tranmit buffer, in which case the transmitted bytes are
  350. * replaced by the bytes received.
  351. */
  352. void Sppi0Transact(const void *wdata, void *rdata, size_t len)
  353. {
  354. const uint8_t *wp = (const uint8_t *)wdata;
  355. if (rdata) {
  356. uint8_t *rp = (uint8_t *)rdata;
  357. while(len--) {
  358. *rp++ = Sppi0Byte(*wp);
  359. wp++;
  360. }
  361. } else {
  362. while(len--) {
  363. Sppi0Byte(*wp);
  364. wp++;
  365. }
  366. }
  367. }