OSDN Git Service

First commitment for the BlackTank LPC1769.
[blacktank/blacktank.git] / kernel / pdic / spi / lpc17xx_spi.c
1 /**\r
2  * @file        : lpc17xx_spi.c\r
3  * @brief       : Contains all functions support for SPI firmware library on LPC17xx\r
4  * @version     : 1.0\r
5  * @date        : 3. April. 2009\r
6  * @author      : HieuNguyen\r
7  **************************************************************************\r
8  * Software that is described herein is for illustrative purposes only\r
9  * which provides customers with programming information regarding the\r
10  * products. This software is supplied "AS IS" without any warranties.\r
11  * NXP Semiconductors assumes no responsibility or liability for the\r
12  * use of the software, conveys no license or title under any patent,\r
13  * copyright, or mask work right to the product. NXP Semiconductors\r
14  * reserves the right to make changes in the software without\r
15  * notification. NXP Semiconductors also make no representation or\r
16  * warranty that such application will be suitable for the specified\r
17  * use without further testing or modification.\r
18  **********************************************************************/\r
19 \r
20 /* Peripheral group ----------------------------------------------------------- */\r
21 /** @addtogroup SPI\r
22  * @{\r
23  */\r
24 \r
25 /* Includes ------------------------------------------------------------------- */\r
26 #include "lpc17xx_spi.h"\r
27 #include "lpc17xx_clkpwr.h"\r
28 \r
29 /* If this source file built with example, the LPC17xx FW library configuration\r
30  * file in each example directory ("lpc17xx_libcfg.h") must be included,\r
31  * otherwise the default FW library configuration file must be included instead\r
32  */\r
33 #ifdef __BUILD_WITH_EXAMPLE__\r
34 #include "lpc17xx_libcfg.h"\r
35 #else\r
36 #include "lpc17xx_libcfg_default.h"\r
37 #endif /* __BUILD_WITH_EXAMPLE__ */\r
38 \r
39 #ifdef _SPI\r
40 \r
41 /* Private Types -------------------------------------------------------------- */\r
42 /** @defgroup SPI_Private_Types\r
43  * @{\r
44  */\r
45 \r
46 /** @brief SPI device configuration structure type */\r
47 typedef struct\r
48 {\r
49         int32_t         dataword;                               /* Current data word: 0 - 8 bit; 1 - 16 bit */\r
50         uint32_t    txrx_setup;                         /* Transmission setup */\r
51         void            (*inthandler)(void);    /* Transmission interrupt handler */\r
52 } SPI_CFG_T;\r
53 \r
54 /**\r
55  * @}\r
56  */\r
57 \r
58 \r
59 /* Private Variables ---------------------------------------------------------- */\r
60 /* SPI configuration data */\r
61 static SPI_CFG_T spidat;\r
62 \r
63 \r
64 /* Private Functions ---------------------------------------------------------- */\r
65 /** @defgroup SPI_Private_Functions\r
66  * @{\r
67  */\r
68 \r
69 /*********************************************************************//**\r
70  * @brief               Standard Private SPI Interrupt handler\r
71  * @param[in]   None\r
72  * @return              None\r
73  ***********************************************************************/\r
74 void SPI_IntHandler(void)\r
75 {\r
76         SPI_DATA_SETUP_Type *xf_setup;\r
77     uint16_t tmp;\r
78 \r
79     xf_setup = (SPI_DATA_SETUP_Type *)spidat.txrx_setup;\r
80 \r
81     /* Dummy read to clear SPI interrupt flag */\r
82     if (LPC_SPI->SPINT & SPI_SPINT_INTFLAG){\r
83         LPC_SPI->SPINT = SPI_SPINT_INTFLAG;\r
84     }\r
85 \r
86     // save status\r
87     tmp = LPC_SPI->SPSR;\r
88     xf_setup->status = tmp;\r
89     // Check for error\r
90     if (tmp & (SPI_SPSR_ABRT | SPI_SPSR_MODF | SPI_SPSR_ROVR | SPI_SPSR_WCOL)){\r
91         xf_setup->status |= SPI_STAT_ERROR;\r
92         // Disable Interrupt and call call-back\r
93         SPI_IntCmd(LPC_SPI, DISABLE);\r
94         if (xf_setup->callback != NULL){\r
95                 xf_setup->callback();\r
96         }\r
97         return;\r
98     }\r
99 \r
100     /* Check SPI complete flag */\r
101     if (tmp & SPI_SPSR_SPIF){\r
102            // Read data from SPI data\r
103                 tmp = SPI_ReceiveData(LPC_SPI);\r
104                 if (xf_setup->rx_data != NULL)\r
105                 {\r
106 //                      if (spidat.dataword == 0){\r
107 //                              *(uint8_t *)(xf_setup->rx_data + xf_setup->counter) = (uint8_t) tmp;\r
108 //                      } else {\r
109 //                              *(uint16_t *)(xf_setup->rx_data + xf_setup->counter) = (uint8_t) tmp;\r
110 //                      }\r
111                         if (spidat.dataword == 0){\r
112                                 *(uint8_t *)((uint8_t *)(xf_setup->rx_data) + xf_setup->counter) = (uint8_t) tmp;\r
113                         } else {\r
114                                 *(uint16_t *)((uint8_t *)(xf_setup->rx_data) + xf_setup->counter) = (uint8_t) tmp;\r
115                         }\r
116                 }\r
117                 // Increase counter\r
118                 if (spidat.dataword == 0){\r
119                         xf_setup->counter++;\r
120                 } else {\r
121                         xf_setup->counter += 2;\r
122                 }\r
123     }\r
124 \r
125     if (xf_setup->counter < xf_setup->length){\r
126                 // Write data to buffer\r
127                 if(xf_setup->tx_data == NULL){\r
128                         if (spidat.dataword == 0){\r
129                                 SPI_SendData(LPC_SPI, 0xFF);\r
130                         } else {\r
131                                 SPI_SendData(LPC_SPI, 0xFFFF);\r
132                         }\r
133                 } else {\r
134 //                      if (spidat.dataword == 0){\r
135 //                              SPI_SendData(SPI, (*(uint8_t *)(xf_setup->tx_data + xf_setup->counter)));\r
136 //                      } else {\r
137 //                              SPI_SendData(SPI, (*(uint16_t *)(xf_setup->tx_data + xf_setup->counter)));\r
138 //                      }\r
139                         if (spidat.dataword == 0){\r
140                                 SPI_SendData(LPC_SPI, (*(uint8_t *)((uint8_t *)(xf_setup->tx_data) + xf_setup->counter)));\r
141                         } else {\r
142                                 SPI_SendData(LPC_SPI, (*(uint16_t *)((uint8_t *)(xf_setup->tx_data) + xf_setup->counter)));\r
143                         }\r
144                 }\r
145     }\r
146     // No more data to send\r
147         else {\r
148         xf_setup->status |= SPI_STAT_DONE;\r
149         // Disable Interrupt and call call-back\r
150         SPI_IntCmd(LPC_SPI, DISABLE);\r
151         if (xf_setup->callback != NULL){\r
152                 xf_setup->callback();\r
153         }\r
154         }\r
155 }\r
156 \r
157 \r
158 /**\r
159  * @}\r
160  */\r
161 \r
162 /* Public Functions ----------------------------------------------------------- */\r
163 /** @addtogroup SPI_Public_Functions\r
164  * @{\r
165  */\r
166 \r
167 /*********************************************************************//**\r
168  * @brief               Setup clock rate for SPI device\r
169  * @param[in]   SPIx    SPI peripheral definition, should be SPI\r
170  * @param[in]   target_clock : clock of SPI (Hz)\r
171  * @return              None\r
172  ***********************************************************************/\r
173 void SPI_SetClock (LPC_SPI_TypeDef *SPIx, uint32_t target_clock)\r
174 {\r
175         uint32_t spi_pclk;\r
176         uint32_t prescale, temp;\r
177 \r
178         CHECK_PARAM(PARAM_SPIx(SPIx));\r
179 \r
180         if (SPIx == LPC_SPI){\r
181                 spi_pclk =  CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SPI);\r
182         } else {\r
183                 return;\r
184         }\r
185 \r
186         prescale = 8;\r
187         // Find closest clock to target clock\r
188         while (1){\r
189                 temp = target_clock * prescale;\r
190                 if (temp >= spi_pclk){\r
191                         break;\r
192                 }\r
193                 prescale += 2;\r
194                 if(prescale >= 254){\r
195                         break;\r
196                 }\r
197         }\r
198 \r
199         // Write to register\r
200         SPIx->SPCCR = SPI_SPCCR_COUNTER(prescale);\r
201 }\r
202 \r
203 \r
204 /*********************************************************************//**\r
205  * @brief               De-initializes the SPIx peripheral registers to their\r
206 *                  default reset values.\r
207  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
208  * @return              None\r
209  **********************************************************************/\r
210 void SPI_DeInit(LPC_SPI_TypeDef *SPIx)\r
211 {\r
212         CHECK_PARAM(PARAM_SPIx(SPIx));\r
213 \r
214         if (SPIx == LPC_SPI){\r
215                 /* Set up clock and power for SPI module */\r
216                 CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCSPI, DISABLE);\r
217         }\r
218 }\r
219 \r
220 \r
221 \r
222 /********************************************************************//**\r
223  * @brief               Initializes the SPIx peripheral according to the specified\r
224 *               parameters in the UART_ConfigStruct.\r
225  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
226  * @param[in]   SPI_ConfigStruct Pointer to a SPI_CFG_Type structure\r
227 *                    that contains the configuration information for the\r
228 *                    specified SPI peripheral.\r
229  * @return              None\r
230  *********************************************************************/\r
231 void SPI_Init(LPC_SPI_TypeDef *SPIx, SPI_CFG_Type *SPI_ConfigStruct)\r
232 {\r
233         uint32_t tmp;\r
234 \r
235         CHECK_PARAM(PARAM_SPIx(SPIx));\r
236 \r
237         if(SPIx == LPC_SPI){\r
238                 /* Set up clock and power for UART module */\r
239                 CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCSPI, ENABLE);\r
240         } else {\r
241                 return;\r
242         }\r
243 \r
244         // Configure SPI, interrupt is disable as default\r
245         tmp = ((SPI_ConfigStruct->CPHA) | (SPI_ConfigStruct->CPOL) \\r
246                 | (SPI_ConfigStruct->DataOrder) | (SPI_ConfigStruct->Databit) \\r
247                 | (SPI_ConfigStruct->Mode) | SPI_SPCR_BIT_EN) & SPI_SPCR_BITMASK;\r
248         // write back to SPI control register\r
249         SPIx->SPCR = tmp;\r
250 \r
251         if (SPI_ConfigStruct->Databit > SPI_DATABIT_8){\r
252                 spidat.dataword = 1;\r
253         } else {\r
254                 spidat.dataword = 0;\r
255         }\r
256 \r
257         // Set clock rate for SPI peripheral\r
258         SPI_SetClock(SPIx, SPI_ConfigStruct->ClockRate);\r
259 \r
260         // If interrupt flag is set, Write '1' to Clear interrupt flag\r
261         if (SPIx->SPINT & SPI_SPINT_INTFLAG){\r
262                 SPIx->SPINT = SPI_SPINT_INTFLAG;\r
263         }\r
264 }\r
265 \r
266 \r
267 \r
268 /*****************************************************************************//**\r
269 * @brief                Fills each SPI_InitStruct member with its default value:\r
270 *                               - CPHA = SPI_CPHA_FIRST\r
271 *                               - CPOL = SPI_CPOL_HI\r
272 *                               - ClockRate = 1000000\r
273 *                               - DataOrder = SPI_DATA_MSB_FIRST\r
274 *                               - Databit = SPI_DATABIT_8\r
275 *                               - Mode = SPI_MASTER_MODE\r
276 * @param[in]    SPI_InitStruct Pointer to a SPI_CFG_Type structure\r
277 *                    which will be initialized.\r
278 * @return               None\r
279 *******************************************************************************/\r
280 void SPI_ConfigStructInit(SPI_CFG_Type *SPI_InitStruct)\r
281 {\r
282         SPI_InitStruct->CPHA = SPI_CPHA_FIRST;\r
283         SPI_InitStruct->CPOL = SPI_CPOL_HI;\r
284         SPI_InitStruct->ClockRate = 1000000;\r
285         SPI_InitStruct->DataOrder = SPI_DATA_MSB_FIRST;\r
286         SPI_InitStruct->Databit = SPI_DATABIT_8;\r
287         SPI_InitStruct->Mode = SPI_MASTER_MODE;\r
288 }\r
289 \r
290 /*********************************************************************//**\r
291  * @brief               Transmit a single data through SPIx peripheral\r
292  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
293  * @param[in]   Data    Data to transmit (must be 16 or 8-bit long,\r
294  *                                              this depend on SPI data bit number configured)\r
295  * @return              none\r
296  **********************************************************************/\r
297 void SPI_SendData(LPC_SPI_TypeDef* SPIx, uint16_t Data)\r
298 {\r
299         CHECK_PARAM(PARAM_SPIx(SPIx));\r
300 \r
301         SPIx->SPDR = Data & SPI_SPDR_BITMASK;\r
302 }\r
303 \r
304 \r
305 \r
306 /*********************************************************************//**\r
307  * @brief               Receive a single data from SPIx peripheral\r
308  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
309  * @return              Data received (16-bit long)\r
310  **********************************************************************/\r
311 uint16_t SPI_ReceiveData(LPC_SPI_TypeDef* SPIx)\r
312 {\r
313         CHECK_PARAM(PARAM_SPIx(SPIx));\r
314 \r
315         return ((uint16_t) (SPIx->SPDR & SPI_SPDR_BITMASK));\r
316 }\r
317 \r
318 /*********************************************************************//**\r
319  * @brief               SPI     Read write data function\r
320  * @param[in]   SPIx    Pointer to SPI peripheral, should be SPI\r
321  * @param[in]   dataCfg Pointer to a SPI_DATA_SETUP_Type structure that\r
322  *                                              contains specified information about transmit\r
323  *                                              data configuration.\r
324  * @param[in]   xfType  Transfer type, should be:\r
325  *                                              - SPI_TRANSFER_POLLING: Polling mode\r
326  *                                              - SPI_TRANSFER_INTERRUPT: Interrupt mode\r
327  * @return              Actual Data length has been transferred in polling mode.\r
328  *                              In interrupt mode, always return (0)\r
329  *                              Return (-1) if error.\r
330  * Note: This function can be used in both master and slave mode.\r
331  ***********************************************************************/\r
332 int32_t SPI_ReadWrite (LPC_SPI_TypeDef *SPIx, SPI_DATA_SETUP_Type *dataCfg, \\r
333                                                 SPI_TRANSFER_Type xfType)\r
334 {\r
335         uint8_t *rdata8;\r
336     uint8_t *wdata8;\r
337         uint16_t *rdata16;\r
338     uint16_t *wdata16;\r
339     uint32_t stat;\r
340     uint32_t temp;\r
341 \r
342         //read for empty buffer\r
343         temp = SPIx->SPDR;\r
344         //dummy to clear status\r
345         temp = SPIx->SPSR;\r
346         dataCfg->counter = 0;\r
347         dataCfg->status = 0;\r
348 \r
349         if (xfType == SPI_TRANSFER_POLLING){\r
350 \r
351                 if (spidat.dataword == 0){\r
352                         rdata8 = (uint8_t *)dataCfg->rx_data;\r
353                         wdata8 = (uint8_t *)dataCfg->tx_data;\r
354                 } else {\r
355                         rdata16 = (uint16_t *)dataCfg->rx_data;\r
356                         wdata16 = (uint16_t *)dataCfg->tx_data;\r
357                 }\r
358 \r
359                 while(dataCfg->counter < dataCfg->length)\r
360                 {\r
361                         // Write data to buffer\r
362                         if(dataCfg->tx_data == NULL){\r
363                                 if (spidat.dataword == 0){\r
364                                         SPI_SendData(SPIx, 0xFF);\r
365                                 } else {\r
366                                         SPI_SendData(SPIx, 0xFFFF);\r
367                                 }\r
368                         } else {\r
369                                 if (spidat.dataword == 0){\r
370                                         SPI_SendData(SPIx, *wdata8);\r
371                                         wdata8++;\r
372                                 } else {\r
373                                         SPI_SendData(SPIx, *wdata16);\r
374                                         wdata16++;\r
375                                 }\r
376                         }\r
377                         // Wait for transfer complete\r
378                         while (!((stat = SPIx->SPSR) & SPI_SPSR_SPIF));\r
379                         // Check for error\r
380                         if (stat & (SPI_SPSR_ABRT | SPI_SPSR_MODF | SPI_SPSR_ROVR | SPI_SPSR_WCOL)){\r
381                                 // save status\r
382                                 dataCfg->status = stat | SPI_STAT_ERROR;\r
383                                 return (dataCfg->counter);\r
384                         }\r
385                         // Read data from SPI dat\r
386                         temp = (uint32_t) SPI_ReceiveData(SPIx);\r
387 \r
388                         // Store data to destination\r
389                         if (dataCfg->rx_data != NULL)\r
390                         {\r
391                                 if (spidat.dataword == 0){\r
392                                         *(rdata8) = (uint8_t) temp;\r
393                                         rdata8++;\r
394                                 } else {\r
395                                         *(rdata16) = (uint16_t) temp;\r
396                                         rdata16++;\r
397                                 }\r
398                         }\r
399                         // Increase counter\r
400                         if (spidat.dataword == 0){\r
401                                 dataCfg->counter++;\r
402                         } else {\r
403                                 dataCfg->counter += 2;\r
404                         }\r
405                 }\r
406 \r
407                 // Return length of actual data transferred\r
408                 // save status\r
409                 dataCfg->status = stat | SPI_STAT_DONE;\r
410                 return (dataCfg->counter);\r
411         }\r
412         // Interrupt mode\r
413         else {\r
414                 spidat.txrx_setup = (uint32_t)dataCfg;\r
415                 spidat.inthandler = SPI_IntHandler;\r
416 \r
417                 // Check if interrupt flag is already set\r
418                 if(SPIx->SPINT & SPI_SPINT_INTFLAG){\r
419                         SPIx->SPINT = SPI_SPINT_INTFLAG;\r
420                 }\r
421                 if (dataCfg->counter < dataCfg->length){\r
422                         // Write data to buffer\r
423                         if(dataCfg->tx_data == NULL){\r
424                                 if (spidat.dataword == 0){\r
425                                         SPI_SendData(SPIx, 0xFF);\r
426                                 } else {\r
427                                         SPI_SendData(SPIx, 0xFFFF);\r
428                                 }\r
429                         } else {\r
430                                 if (spidat.dataword == 0){\r
431                                         SPI_SendData(SPIx, (*(uint8_t *)dataCfg->tx_data));\r
432                                 } else {\r
433                                         SPI_SendData(SPIx, (*(uint16_t *)dataCfg->tx_data));\r
434                                 }\r
435                         }\r
436                         SPI_IntCmd(SPIx, ENABLE);\r
437                 } else {\r
438                         // Save status\r
439                         dataCfg->status = SPI_STAT_DONE;\r
440                 }\r
441                 return (0);\r
442         }\r
443         return (0);\r
444 }\r
445 \r
446 \r
447 /********************************************************************//**\r
448  * @brief               Enable or disable SPIx interrupt.\r
449  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
450  * @param[in]   NewState New state of specified UART interrupt type,\r
451  *                              should be:\r
452  *                              - ENALBE: Enable this SPI interrupt.\r
453 *                               - DISALBE: Disable this SPI interrupt.\r
454  * @return              None\r
455  *********************************************************************/\r
456 void SPI_IntCmd(LPC_SPI_TypeDef *SPIx, FunctionalState NewState)\r
457 {\r
458         CHECK_PARAM(PARAM_SPIx(SPIx));\r
459         CHECK_PARAM(PARAM_FUNCTIONALSTATE(NewState));\r
460 \r
461         if (NewState == ENABLE)\r
462         {\r
463                 SPIx->SPCR |= SPI_SPCR_SPIE;\r
464         }\r
465         else\r
466         {\r
467                 SPIx->SPCR &= (~SPI_SPCR_SPIE) & SPI_SPCR_BITMASK;\r
468         }\r
469 }\r
470 \r
471 \r
472 /********************************************************************//**\r
473  * @brief               Checks whether the SPI interrupt flag is set or not.\r
474  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
475  * @return              The new state of SPI Interrupt Flag (SET or RESET)\r
476  *********************************************************************/\r
477 IntStatus SPI_GetIntStatus (LPC_SPI_TypeDef *SPIx)\r
478 {\r
479         CHECK_PARAM(PARAM_SPIx(SPIx));\r
480 \r
481         return ((SPIx->SPINT & SPI_SPINT_INTFLAG) ? SET : RESET);\r
482 }\r
483 \r
484 \r
485 /********************************************************************//**\r
486  * @brief               Clear SPI interrupt flag.\r
487  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
488  * @return              None\r
489  *********************************************************************/\r
490 void SPI_ClearIntPending(LPC_SPI_TypeDef *SPIx)\r
491 {\r
492         CHECK_PARAM(PARAM_SPIx(SPIx));\r
493 \r
494         SPIx->SPINT = SPI_SPINT_INTFLAG;\r
495 }\r
496 \r
497 \r
498 /********************************************************************//**\r
499  * @brief               Get current value of SPI Status register in SPIx peripheral.\r
500  * @param[in]   SPIx    SPI peripheral selected, should be SPI\r
501  * @return              Current value of SPI Status register in SPI peripheral.\r
502  * Note:        The return value of this function must be used with\r
503  *                      SPI_CheckStatus() to determine current flag status\r
504  *                      corresponding to each SPI status type. Because some flags in\r
505  *                      SPI Status register will be cleared after reading, the next reading\r
506  *                      SPI Status register could not be correct. So this function used to\r
507  *                      read SPI status register in one time only, then the return value\r
508  *                      used to check all flags.\r
509  *********************************************************************/\r
510 uint32_t SPI_GetStatus(LPC_SPI_TypeDef* SPIx)\r
511 {\r
512         CHECK_PARAM(PARAM_SPIx(SPIx));\r
513 \r
514         return (SPIx->SPSR & SPI_SPSR_BITMASK);\r
515 }\r
516 \r
517 \r
518 \r
519 /********************************************************************//**\r
520  * @brief               Checks whether the specified SPI Status flag is set or not\r
521  *                              via inputSPIStatus parameter.\r
522  * @param[in]   inputSPIStatus Value to check status of each flag type.\r
523  *                              This value is the return value from SPI_GetStatus().\r
524  * @param[in]   SPIStatus       Specifies the SPI status flag to check,\r
525  *                              should be one of the following:\r
526                                 - SPI_STAT_ABRT: Slave abort.\r
527                                 - SPI_STAT_MODF: Mode fault.\r
528                                 - SPI_STAT_ROVR: Read overrun.\r
529                                 - SPI_STAT_WCOL: Write collision.\r
530                                 - SPI_STAT_SPIF: SPI transfer complete.\r
531  * @return              The new state of SPIStatus (SET or RESET)\r
532  *********************************************************************/\r
533 FlagStatus SPI_CheckStatus (uint32_t inputSPIStatus,  uint8_t SPIStatus)\r
534 {\r
535         CHECK_PARAM(PARAM_SPI_STAT(SPIStatus));\r
536 \r
537         return ((inputSPIStatus & SPIStatus) ? SET : RESET);\r
538 }\r
539 \r
540 /**\r
541  * @brief               Standard SPI Interrupt handler\r
542  * @param[in]   None\r
543  * @return              None\r
544  */\r
545 void SPI_StdIntHandler(void)\r
546 {\r
547         // Call relevant handler\r
548         spidat.inthandler();\r
549 }\r
550 \r
551 \r
552 /**\r
553  * @}\r
554  */\r
555 \r
556 #endif /* _SPI */\r
557 \r
558 /**\r
559  * @}\r
560  */\r
561 \r
562 /* --------------------------------- End Of File ------------------------------ */\r