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.

529 line
15KB

  1. /*
  2. * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
  3. * SPI Master library for arduino.
  4. *
  5. * This file is free software; you can redistribute it and/or modify
  6. * it under the terms of either the GNU General Public License version 2
  7. * or the GNU Lesser General Public License version 2.1, both as
  8. * published by the Free Software Foundation.
  9. */
  10. #include "SPI.h"
  11. #include "pins_arduino.h"
  12. /**********************************************************/
  13. /* 8 bit AVR-based boards */
  14. /**********************************************************/
  15. #if defined(__AVR__)
  16. SPIClass SPI;
  17. uint8_t SPIClass::interruptMode = 0;
  18. uint8_t SPIClass::interruptMask = 0;
  19. uint8_t SPIClass::interruptSave = 0;
  20. #ifdef SPI_TRANSACTION_MISMATCH_LED
  21. uint8_t SPIClass::inTransactionFlag = 0;
  22. #endif
  23. void SPIClass::begin()
  24. {
  25. // Set SS to high so a connected chip will be "deselected" by default
  26. digitalWrite(SS, HIGH);
  27. // When the SS pin is set as OUTPUT, it can be used as
  28. // a general purpose output port (it doesn't influence
  29. // SPI operations).
  30. pinMode(SS, OUTPUT);
  31. // Warning: if the SS pin ever becomes a LOW INPUT then SPI
  32. // automatically switches to Slave, so the data direction of
  33. // the SS pin MUST be kept as OUTPUT.
  34. SPCR |= _BV(MSTR);
  35. SPCR |= _BV(SPE);
  36. // Set direction register for SCK and MOSI pin.
  37. // MISO pin automatically overrides to INPUT.
  38. // By doing this AFTER enabling SPI, we avoid accidentally
  39. // clocking in a single bit since the lines go directly
  40. // from "input" to SPI control.
  41. // http://code.google.com/p/arduino/issues/detail?id=888
  42. pinMode(SCK, OUTPUT);
  43. pinMode(MOSI, OUTPUT);
  44. }
  45. void SPIClass::end() {
  46. SPCR &= ~_BV(SPE);
  47. }
  48. // mapping of interrupt numbers to bits within SPI_AVR_EIMSK
  49. #if defined(__AVR_ATmega32U4__)
  50. #define SPI_INT0_MASK (1<<INT0)
  51. #define SPI_INT1_MASK (1<<INT1)
  52. #define SPI_INT2_MASK (1<<INT2)
  53. #define SPI_INT3_MASK (1<<INT3)
  54. #define SPI_INT4_MASK (1<<INT6)
  55. #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
  56. #define SPI_INT0_MASK (1<<INT0)
  57. #define SPI_INT1_MASK (1<<INT1)
  58. #define SPI_INT2_MASK (1<<INT2)
  59. #define SPI_INT3_MASK (1<<INT3)
  60. #define SPI_INT4_MASK (1<<INT4)
  61. #define SPI_INT5_MASK (1<<INT5)
  62. #define SPI_INT6_MASK (1<<INT6)
  63. #define SPI_INT7_MASK (1<<INT7)
  64. #elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
  65. #define SPI_INT0_MASK (1<<INT4)
  66. #define SPI_INT1_MASK (1<<INT5)
  67. #define SPI_INT2_MASK (1<<INT0)
  68. #define SPI_INT3_MASK (1<<INT1)
  69. #define SPI_INT4_MASK (1<<INT2)
  70. #define SPI_INT5_MASK (1<<INT3)
  71. #define SPI_INT6_MASK (1<<INT6)
  72. #define SPI_INT7_MASK (1<<INT7)
  73. #else
  74. #ifdef INT0
  75. #define SPI_INT0_MASK (1<<INT0)
  76. #endif
  77. #ifdef INT1
  78. #define SPI_INT1_MASK (1<<INT1)
  79. #endif
  80. #ifdef INT2
  81. #define SPI_INT2_MASK (1<<INT2)
  82. #endif
  83. #endif
  84. void SPIClass::usingInterrupt(uint8_t interruptNumber)
  85. {
  86. uint8_t mask;
  87. if (interruptMode > 1) return;
  88. noInterrupts();
  89. switch (interruptNumber) {
  90. #ifdef SPI_INT0_MASK
  91. case 0: mask = SPI_INT0_MASK; break;
  92. #endif
  93. #ifdef SPI_INT1_MASK
  94. case 1: mask = SPI_INT1_MASK; break;
  95. #endif
  96. #ifdef SPI_INT2_MASK
  97. case 2: mask = SPI_INT2_MASK; break;
  98. #endif
  99. #ifdef SPI_INT3_MASK
  100. case 3: mask = SPI_INT3_MASK; break;
  101. #endif
  102. #ifdef SPI_INT4_MASK
  103. case 4: mask = SPI_INT4_MASK; break;
  104. #endif
  105. #ifdef SPI_INT5_MASK
  106. case 5: mask = SPI_INT5_MASK; break;
  107. #endif
  108. #ifdef SPI_INT6_MASK
  109. case 6: mask = SPI_INT6_MASK; break;
  110. #endif
  111. #ifdef SPI_INT7_MASK
  112. case 7: mask = SPI_INT7_MASK; break;
  113. #endif
  114. default:
  115. interruptMode = 2;
  116. interrupts();
  117. return;
  118. }
  119. interruptMode = 1;
  120. interruptMask |= mask;
  121. interrupts();
  122. }
  123. /**********************************************************/
  124. /* 32 bit Teensy 3.0 and 3.1 */
  125. /**********************************************************/
  126. #elif defined(__arm__) && defined(TEENSYDUINO)
  127. SPIClass SPI;
  128. uint8_t SPIClass::interruptMasksUsed = 0;
  129. uint32_t SPIClass::interruptMask[(NVIC_NUM_INTERRUPTS+31)/32];
  130. uint32_t SPIClass::interruptSave[(NVIC_NUM_INTERRUPTS+31)/32];
  131. #ifdef SPI_TRANSACTION_MISMATCH_LED
  132. uint8_t SPIClass::inTransactionFlag = 0;
  133. #endif
  134. void SPIClass::begin()
  135. {
  136. SIM_SCGC6 |= SIM_SCGC6_SPI0;
  137. SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
  138. SPI0_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
  139. SPI0_CTAR1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
  140. SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
  141. SPCR.enable_pins(); // pins managed by SPCRemulation in avr_emulation.h
  142. }
  143. void SPIClass::end() {
  144. SPCR.disable_pins();
  145. SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
  146. }
  147. void SPIClass::usingInterrupt(IRQ_NUMBER_t interruptName)
  148. {
  149. uint32_t n = (uint32_t)interruptName;
  150. if (n >= NVIC_NUM_INTERRUPTS) return;
  151. //Serial.print("usingInterrupt ");
  152. //Serial.println(n);
  153. interruptMasksUsed |= (1 << (n >> 5));
  154. interruptMask[n >> 5] |= (1 << (n & 0x1F));
  155. //Serial.printf("interruptMasksUsed = %d\n", interruptMasksUsed);
  156. //Serial.printf("interruptMask[0] = %08X\n", interruptMask[0]);
  157. //Serial.printf("interruptMask[1] = %08X\n", interruptMask[1]);
  158. //Serial.printf("interruptMask[2] = %08X\n", interruptMask[2]);
  159. }
  160. void SPIClass::notUsingInterrupt(IRQ_NUMBER_t interruptName)
  161. {
  162. uint32_t n = (uint32_t)interruptName;
  163. if (n >= NVIC_NUM_INTERRUPTS) return;
  164. interruptMask[n >> 5] &= ~(1 << (n & 0x1F));
  165. if (interruptMask[n >> 5] == 0) {
  166. interruptMasksUsed &= ~(1 << (n >> 5));
  167. }
  168. }
  169. const uint16_t SPISettings::ctar_div_table[23] = {
  170. 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40,
  171. 56, 64, 96, 128, 192, 256, 384, 512, 640, 768
  172. };
  173. const uint32_t SPISettings::ctar_clock_table[23] = {
  174. SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0),
  175. SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0),
  176. SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0),
  177. SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0),
  178. SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0),
  179. SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1),
  180. SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0),
  181. SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1),
  182. SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2),
  183. SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0),
  184. SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2),
  185. SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3),
  186. SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2),
  187. SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2),
  188. SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4),
  189. SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4),
  190. SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5),
  191. SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5),
  192. SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6),
  193. SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6),
  194. SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7),
  195. SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6),
  196. SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7)
  197. };
  198. static void updateCTAR(uint32_t ctar)
  199. {
  200. if (SPI0_CTAR0 != ctar) {
  201. uint32_t mcr = SPI0_MCR;
  202. if (mcr & SPI_MCR_MDIS) {
  203. SPI0_CTAR0 = ctar;
  204. SPI0_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
  205. } else {
  206. SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
  207. SPI0_CTAR0 = ctar;
  208. SPI0_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
  209. SPI0_MCR = mcr;
  210. }
  211. }
  212. }
  213. void SPIClass::setBitOrder(uint8_t bitOrder)
  214. {
  215. SIM_SCGC6 |= SIM_SCGC6_SPI0;
  216. uint32_t ctar = SPI0_CTAR0;
  217. if (bitOrder == LSBFIRST) {
  218. ctar |= SPI_CTAR_LSBFE;
  219. } else {
  220. ctar &= ~SPI_CTAR_LSBFE;
  221. }
  222. updateCTAR(ctar);
  223. }
  224. void SPIClass::setDataMode(uint8_t dataMode)
  225. {
  226. SIM_SCGC6 |= SIM_SCGC6_SPI0;
  227. // TODO: implement with native code
  228. SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
  229. }
  230. void SPIClass::setClockDivider_noInline(uint32_t clk)
  231. {
  232. SIM_SCGC6 |= SIM_SCGC6_SPI0;
  233. uint32_t ctar = SPI0_CTAR0;
  234. ctar &= (SPI_CTAR_CPOL | SPI_CTAR_CPHA | SPI_CTAR_LSBFE);
  235. if (ctar & SPI_CTAR_CPHA) {
  236. clk = (clk & 0xFFFF0FFF) | ((clk & 0xF000) >> 4);
  237. }
  238. ctar |= clk;
  239. updateCTAR(ctar);
  240. }
  241. bool SPIClass::pinIsChipSelect(uint8_t pin)
  242. {
  243. if (pin == 10 || pin == 9 || pin == 6 || pin == 2 || pin == 15) return true;
  244. if (pin >= 20 && pin <= 23) return true;
  245. return false;
  246. }
  247. bool SPIClass::pinIsChipSelect(uint8_t pin1, uint8_t pin2)
  248. {
  249. if (!pinIsChipSelect(pin1) || !pinIsChipSelect(pin2)) return false;
  250. if ((pin1 == 2 && pin2 == 10) || (pin1 == 10 && pin2 == 2)) return false;
  251. if ((pin1 == 6 && pin2 == 9) || (pin1 == 9 && pin2 == 6)) return false;
  252. if ((pin1 == 20 && pin2 == 23) || (pin1 == 23 && pin2 == 20)) return false;
  253. if ((pin1 == 21 && pin2 == 22) || (pin1 == 22 && pin2 == 21)) return false;
  254. return true;
  255. }
  256. uint8_t SPIClass::setCS(uint8_t pin)
  257. {
  258. switch (pin) {
  259. case 10: CORE_PIN10_CONFIG = PORT_PCR_MUX(2); return 0x01; // PTC4
  260. case 2: CORE_PIN2_CONFIG = PORT_PCR_MUX(2); return 0x01; // PTD0
  261. case 9: CORE_PIN9_CONFIG = PORT_PCR_MUX(2); return 0x02; // PTC3
  262. case 6: CORE_PIN6_CONFIG = PORT_PCR_MUX(2); return 0x02; // PTD4
  263. case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); return 0x04; // PTD5
  264. case 23: CORE_PIN23_CONFIG = PORT_PCR_MUX(2); return 0x04; // PTC2
  265. case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTD6
  266. case 22: CORE_PIN22_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTC1
  267. case 15: CORE_PIN15_CONFIG = PORT_PCR_MUX(2); return 0x10; // PTC0
  268. }
  269. return 0;
  270. }
  271. /**********************************************************/
  272. /* 32 bit Arduino Due */
  273. /**********************************************************/
  274. #elif defined(__arm__) && defined(__SAM3X8E__)
  275. #include "SPI.h"
  276. SPIClass::SPIClass(Spi *_spi, uint32_t _id, void(*_initCb)(void)) :
  277. spi(_spi), id(_id), initCb(_initCb), initialized(false)
  278. {
  279. // Empty
  280. }
  281. void SPIClass::begin() {
  282. init();
  283. // NPCS control is left to the user
  284. // Default speed set to 4Mhz
  285. setClockDivider(BOARD_SPI_DEFAULT_SS, 21);
  286. setDataMode(BOARD_SPI_DEFAULT_SS, SPI_MODE0);
  287. setBitOrder(BOARD_SPI_DEFAULT_SS, MSBFIRST);
  288. }
  289. void SPIClass::begin(uint8_t _pin) {
  290. init();
  291. uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin);
  292. PIO_Configure(
  293. g_APinDescription[spiPin].pPort,
  294. g_APinDescription[spiPin].ulPinType,
  295. g_APinDescription[spiPin].ulPin,
  296. g_APinDescription[spiPin].ulPinConfiguration);
  297. // Default speed set to 4Mhz
  298. setClockDivider(_pin, 21);
  299. setDataMode(_pin, SPI_MODE0);
  300. setBitOrder(_pin, MSBFIRST);
  301. }
  302. void SPIClass::init() {
  303. if (initialized)
  304. return;
  305. interruptMode = 0;
  306. interruptMask = 0;
  307. interruptSave = 0;
  308. initCb();
  309. SPI_Configure(spi, id, SPI_MR_MSTR | SPI_MR_PS | SPI_MR_MODFDIS);
  310. SPI_Enable(spi);
  311. initialized = true;
  312. }
  313. #ifndef interruptsStatus
  314. #define interruptsStatus() __interruptsStatus()
  315. static inline unsigned char __interruptsStatus(void) __attribute__((always_inline, unused));
  316. static inline unsigned char __interruptsStatus(void) {
  317. unsigned int primask;
  318. asm volatile ("mrs %0, primask" : "=r" (primask));
  319. if (primask) return 0;
  320. return 1;
  321. }
  322. #endif
  323. void SPIClass::usingInterrupt(uint8_t interruptNumber)
  324. {
  325. uint8_t irestore;
  326. irestore = interruptsStatus();
  327. noInterrupts();
  328. if (interruptMode < 2) {
  329. if (interruptNumber > NUM_DIGITAL_PINS) {
  330. interruptMode = 2;
  331. } else {
  332. uint8_t imask = interruptMask;
  333. Pio *pio = g_APinDescription[interruptNumber].pPort;
  334. if (pio == PIOA) {
  335. imask |= 1;
  336. } else if (pio == PIOB) {
  337. imask |= 2;
  338. } else if (pio == PIOC) {
  339. imask |= 4;
  340. } else if (pio == PIOD) {
  341. imask |= 8;
  342. }
  343. interruptMask = imask;
  344. interruptMode = 1;
  345. }
  346. }
  347. if (irestore) interrupts();
  348. }
  349. void SPIClass::beginTransaction(uint8_t pin, SPISettings settings)
  350. {
  351. if (interruptMode > 0) {
  352. if (interruptMode == 1) {
  353. uint8_t imask = interruptMask;
  354. if (imask & 1) NVIC_DisableIRQ(PIOA_IRQn);
  355. if (imask & 2) NVIC_DisableIRQ(PIOB_IRQn);
  356. if (imask & 4) NVIC_DisableIRQ(PIOC_IRQn);
  357. if (imask & 8) NVIC_DisableIRQ(PIOD_IRQn);
  358. } else {
  359. interruptSave = interruptsStatus();
  360. noInterrupts();
  361. }
  362. }
  363. uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(pin);
  364. bitOrder[ch] = settings.border;
  365. SPI_ConfigureNPCS(spi, ch, settings.config);
  366. }
  367. void SPIClass::endTransaction(void)
  368. {
  369. if (interruptMode > 0) {
  370. if (interruptMode == 1) {
  371. uint8_t imask = interruptMask;
  372. if (imask & 1) NVIC_EnableIRQ(PIOA_IRQn);
  373. if (imask & 2) NVIC_EnableIRQ(PIOB_IRQn);
  374. if (imask & 4) NVIC_EnableIRQ(PIOC_IRQn);
  375. if (imask & 8) NVIC_EnableIRQ(PIOD_IRQn);
  376. } else {
  377. if (interruptSave) interrupts();
  378. }
  379. }
  380. }
  381. void SPIClass::end(uint8_t _pin) {
  382. uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin);
  383. // Setting the pin as INPUT will disconnect it from SPI peripheral
  384. pinMode(spiPin, INPUT);
  385. }
  386. void SPIClass::end() {
  387. SPI_Disable(spi);
  388. initialized = false;
  389. }
  390. void SPIClass::setBitOrder(uint8_t _pin, BitOrder _bitOrder) {
  391. uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin);
  392. bitOrder[ch] = _bitOrder;
  393. }
  394. void SPIClass::setDataMode(uint8_t _pin, uint8_t _mode) {
  395. uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin);
  396. mode[ch] = _mode | SPI_CSR_CSAAT;
  397. // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed
  398. // transfer. Some device needs that for working properly.
  399. SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1));
  400. }
  401. void SPIClass::setClockDivider(uint8_t _pin, uint8_t _divider) {
  402. uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin);
  403. divider[ch] = _divider;
  404. // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed
  405. // transfer. Some device needs that for working properly.
  406. SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1));
  407. }
  408. byte SPIClass::transfer(byte _pin, uint8_t _data, SPITransferMode _mode) {
  409. uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin);
  410. // Reverse bit order
  411. if (bitOrder[ch] == LSBFIRST)
  412. _data = __REV(__RBIT(_data));
  413. uint32_t d = _data | SPI_PCS(ch);
  414. if (_mode == SPI_LAST)
  415. d |= SPI_TDR_LASTXFER;
  416. // SPI_Write(spi, _channel, _data);
  417. while ((spi->SPI_SR & SPI_SR_TDRE) == 0)
  418. ;
  419. spi->SPI_TDR = d;
  420. // return SPI_Read(spi);
  421. while ((spi->SPI_SR & SPI_SR_RDRF) == 0)
  422. ;
  423. d = spi->SPI_RDR;
  424. // Reverse bit order
  425. if (bitOrder[ch] == LSBFIRST)
  426. d = __REV(__RBIT(d));
  427. return d & 0xFF;
  428. }
  429. void SPIClass::attachInterrupt(void) {
  430. // Should be enableInterrupt()
  431. }
  432. void SPIClass::detachInterrupt(void) {
  433. // Should be disableInterrupt()
  434. }
  435. #if SPI_INTERFACES_COUNT > 0
  436. static void SPI_0_Init(void) {
  437. PIO_Configure(
  438. g_APinDescription[PIN_SPI_MOSI].pPort,
  439. g_APinDescription[PIN_SPI_MOSI].ulPinType,
  440. g_APinDescription[PIN_SPI_MOSI].ulPin,
  441. g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
  442. PIO_Configure(
  443. g_APinDescription[PIN_SPI_MISO].pPort,
  444. g_APinDescription[PIN_SPI_MISO].ulPinType,
  445. g_APinDescription[PIN_SPI_MISO].ulPin,
  446. g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
  447. PIO_Configure(
  448. g_APinDescription[PIN_SPI_SCK].pPort,
  449. g_APinDescription[PIN_SPI_SCK].ulPinType,
  450. g_APinDescription[PIN_SPI_SCK].ulPin,
  451. g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
  452. }
  453. SPIClass SPI(SPI_INTERFACE, SPI_INTERFACE_ID, SPI_0_Init);
  454. #endif
  455. #endif