1 /**
2  * @file xmc_spi.c
3  * @date 2019-05-07
4  *
5  * @cond
6  *********************************************************************************************************************
7  * XMClib v2.1.24 - XMC Peripheral Driver Library
8  *
9  * Copyright (c) 2015-2019, Infineon Technologies AG
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the
13  * following conditions are met:
14  *
15  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
16  * disclaimer.
17  *
18  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
19  * disclaimer in the documentation and/or other materials provided with the distribution.
20  *
21  * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with
33  * Infineon Technologies AG dave@infineon.com).
34  *********************************************************************************************************************
35  *
36  * Change History
37  * --------------
38  *
39  * 2015-02-20:
40  *     - Initial <br>
41  *
42  * 2015-05-20:
43  *     - Modified XMC_SPI_CH_Stop() API for not setting to IDLE the channel if it is busy
44  *     - Modified XMC_SPI_CH_SetInterwordDelay() implementation in order to gain accuracy <br>
45  *
46  * 2015-06-20:
47  *     - Removed GetDriverVersion API <br>
48  *
49  * 2015-09-01:
50  *     - Modified XMC_SPI_CH_EnableEvent() and XMC_SPI_CH_DisableEvent() for supporting multiple events configuration <br>
51  *
52  * 2015-11-04:
53  *     - Modified the check of XMC_USIC_CH_GetTransmitBufferStatus() in the XMC_SPI_CH_Transmit() flag <br>
54  *
55  * 2019-05-07:
56  *     - Added XMC_SPI_CH_SetBaudrateEx() which allows to select between baudrate generator normal divider and fractional divider mode
57  *
58  * @endcond
59  *
60  */
61 /**
62  *
63  * @brief SPI driver for XMC microcontroller family
64  *
65  */
66 /*********************************************************************************************************************
67  * HEADER FILES
68  ********************************************************************************************************************/
69 
70 #include <xmc_scu.h>
71 #include <xmc_spi.h>
72 
73 /*********************************************************************************************************************
74  * MACROS
75  ********************************************************************************************************************/
76 #define XMC_SPI_CH_OVERSAMPLING (2UL)
77 
78 /*********************************************************************************************************************
79  * API IMPLEMENTATION
80  ********************************************************************************************************************/
81 
82 /* Initializes the selected SPI channel with the config structure. */
XMC_SPI_CH_Init(XMC_USIC_CH_t * const channel,const XMC_SPI_CH_CONFIG_t * const config)83 void XMC_SPI_CH_Init(XMC_USIC_CH_t *const channel, const XMC_SPI_CH_CONFIG_t *const config)
84 {
85   XMC_USIC_CH_Enable(channel);
86 
87   if(config->bus_mode == XMC_SPI_CH_BUS_MODE_MASTER)
88   {
89     /* Configure baud rate */
90     if (config->normal_divider_mode)
91     {
92       /* Normal divider mode */
93       (void)XMC_USIC_CH_SetBaudrateEx(channel, config->baudrate, XMC_SPI_CH_OVERSAMPLING);
94     }
95     else
96     {
97       /* Fractional divider mode */
98       (void)XMC_USIC_CH_SetBaudrate(channel, config->baudrate, XMC_SPI_CH_OVERSAMPLING);
99     }
100   }
101 
102   /* Configuration of USIC Shift Control */
103   /* Transmission Mode (TRM) = 1  */
104   /* Passive Data Level (PDL) = 1 */
105   channel->SCTR = USIC_CH_SCTR_PDL_Msk |
106                   (0x1UL << USIC_CH_SCTR_TRM_Pos) |
107                   (0x3fUL << USIC_CH_SCTR_FLE_Pos)|
108                   (0x7UL << USIC_CH_SCTR_WLE_Pos);
109 
110   /* Configuration of USIC Transmit Control/Status Register */
111   /* TBUF Data Enable (TDEN) = 1 */
112   /* TBUF Data Single Shot Mode (TDSSM) = 1 */
113   channel->TCSR = (uint32_t)(USIC_CH_TCSR_HPCMD_Msk |
114                   (0x01UL  << USIC_CH_TCSR_TDEN_Pos) |
115                   USIC_CH_TCSR_TDSSM_Msk);
116 
117   if(config->bus_mode == XMC_SPI_CH_BUS_MODE_MASTER)
118   {
119     /* Configuration of Protocol Control Register */
120     channel->PCR_SSCMode = (uint32_t)(USIC_CH_PCR_SSCMode_MSLSEN_Msk |
121                            USIC_CH_PCR_SSCMode_SELCTR_Msk |
122                            (uint32_t)config->selo_inversion |
123                            USIC_CH_PCR_SSCMode_FEM_Msk);
124   }
125 
126   /* Clear protocol status */
127   channel->PSCR = 0xFFFFFFFFUL;
128 
129   /* Set parity settings */
130   channel->CCR = (uint32_t)config->parity_mode;
131 }
132 
XMC_SPI_CH_SetBaudrate(XMC_USIC_CH_t * const channel,const uint32_t rate)133 XMC_SPI_CH_STATUS_t XMC_SPI_CH_SetBaudrate(XMC_USIC_CH_t *const channel, const uint32_t rate)
134 {
135   XMC_SPI_CH_STATUS_t status;
136 
137   status = XMC_SPI_CH_STATUS_ERROR;
138 
139   if (rate <= (XMC_SCU_CLOCK_GetPeripheralClockFrequency() >> 1U))
140   {
141     if (XMC_USIC_CH_SetBaudrate(channel, rate, XMC_SPI_CH_OVERSAMPLING) == XMC_USIC_CH_STATUS_OK)
142     {
143       status = XMC_SPI_CH_STATUS_OK;
144     }
145   }
146   return status;
147 }
148 
XMC_SPI_CH_SetBaudrateEx(XMC_USIC_CH_t * const channel,const uint32_t rate,bool normal_divider_mode)149 XMC_SPI_CH_STATUS_t XMC_SPI_CH_SetBaudrateEx(XMC_USIC_CH_t *const channel, const uint32_t rate, bool normal_divider_mode)
150 {
151   XMC_USIC_CH_STATUS_t status;
152 
153   if (rate <= (XMC_SCU_CLOCK_GetPeripheralClockFrequency() >> 1U))
154   {
155     if (normal_divider_mode)
156     {
157       /* Normal divider mode */
158       status = XMC_USIC_CH_SetBaudrateEx(channel, rate, XMC_SPI_CH_OVERSAMPLING);
159     }
160     else
161     {
162       /* Fractional divider mode */
163       status = XMC_USIC_CH_SetBaudrate(channel, rate, XMC_SPI_CH_OVERSAMPLING);
164     }
165   }
166   else
167   {
168     status = XMC_USIC_CH_STATUS_ERROR;
169   }
170 
171   return (XMC_SPI_CH_STATUS_t)status;
172 }
173 
174 /* Enable the selected slave signal by setting (SELO) bits in PCR register. */
XMC_SPI_CH_EnableSlaveSelect(XMC_USIC_CH_t * const channel,const XMC_SPI_CH_SLAVE_SELECT_t slave)175 void XMC_SPI_CH_EnableSlaveSelect(XMC_USIC_CH_t *const channel, const XMC_SPI_CH_SLAVE_SELECT_t slave)
176 {
177   /* Configuration of Protocol Control Register */
178   channel->PCR_SSCMode &= (uint32_t)~USIC_CH_PCR_SSCMode_SELO_Msk;
179   channel->PCR_SSCMode |= (uint32_t)slave;
180 }
181 
182 /* Disable the slave signals by clearing (SELO) bits in PCR register. */
XMC_SPI_CH_DisableSlaveSelect(XMC_USIC_CH_t * const channel)183 void XMC_SPI_CH_DisableSlaveSelect(XMC_USIC_CH_t *const channel)
184 {
185   XMC_SPI_CH_ClearStatusFlag(channel, (uint32_t)XMC_SPI_CH_STATUS_FLAG_MSLS);
186 
187   /* Configuration of Protocol Control Register */
188   channel->PCR_SSCMode &= (uint32_t)~USIC_CH_PCR_SSCMode_SELO_Msk;
189 }
190 
191 /* Puts the data into FIFO if FIFO mode is enabled or else into standard buffers, by setting the proper mode. */
XMC_SPI_CH_Transmit(XMC_USIC_CH_t * const channel,const uint16_t data,const XMC_SPI_CH_MODE_t mode)192 void XMC_SPI_CH_Transmit(XMC_USIC_CH_t *const channel, const uint16_t data, const XMC_SPI_CH_MODE_t mode)
193 {
194 
195   channel->CCR = (channel->CCR & (uint32_t)(~USIC_CH_CCR_HPCEN_Msk)) |
196                  (((uint32_t) mode << USIC_CH_CCR_HPCEN_Pos) & (uint32_t)USIC_CH_CCR_HPCEN_Msk);
197 
198 
199   /* Check FIFO size */
200   if ((channel->TBCTR & USIC_CH_TBCTR_SIZE_Msk) == 0U)
201   {
202     while((uint32_t)XMC_USIC_CH_GetTransmitBufferStatus(channel) == (uint32_t)XMC_USIC_CH_TBUF_STATUS_BUSY)
203     {
204     }
205 
206     XMC_SPI_CH_ClearStatusFlag(channel, (uint32_t)XMC_SPI_CH_STATUS_FLAG_TRANSMIT_BUFFER_INDICATION);
207 
208     channel->TBUF[mode] = data;
209   }
210   else
211   {
212     channel->IN[mode] = data;
213   }
214 }
215 
216 /* Reads the data from the buffers based on the FIFO mode selection. */
XMC_SPI_CH_GetReceivedData(XMC_USIC_CH_t * const channel)217 uint16_t XMC_SPI_CH_GetReceivedData(XMC_USIC_CH_t *const channel)
218 {
219   uint16_t retval;
220 
221   /* Check FIFO size */
222   if ((channel->RBCTR & USIC_CH_RBCTR_SIZE_Msk) == 0U)
223   {
224     retval = (uint16_t)channel->RBUF;
225   }
226   else
227   {
228     retval = (uint16_t)channel->OUTR;
229   }
230 
231   return retval;
232 }
233 
234 /* Configures the inter word delay by setting PCR.PCTQ1, PCR.DCTQ1 bit fields. */
XMC_SPI_CH_SetInterwordDelay(XMC_USIC_CH_t * const channel,uint32_t tinterword_delay_us)235 void XMC_SPI_CH_SetInterwordDelay(XMC_USIC_CH_t *const channel,uint32_t tinterword_delay_us)
236 {
237   uint32_t peripheral_clock;
238   uint32_t pdiv;
239   uint32_t step;
240   uint32_t fFD;
241   uint32_t fpdiv;
242   uint32_t divider_factor1 = 0U;
243   uint32_t divider_factor2 = 32U;
244   uint32_t divider_factor1_int = 0U;
245   uint32_t divider_factor1_int_min = 4U;
246   uint32_t divider_factor1_frac_min =100U;
247   uint32_t divider_factor1_frac = 0U;
248   uint32_t divider_factor2_temp = 0U;
249   peripheral_clock = XMC_SCU_CLOCK_GetPeripheralClockFrequency();
250   pdiv = (uint32_t)(channel->BRG & USIC_CH_BRG_PDIV_Msk) >> USIC_CH_BRG_PDIV_Pos;
251   step = (uint32_t)(channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos;
252   fFD = (uint32_t)((peripheral_clock >> 10U) * step);
253   fpdiv= fFD/(1U+pdiv);
254 
255   if(tinterword_delay_us < (128000000/fpdiv))
256   {
257     for(divider_factor2_temp = 32U; divider_factor2_temp > 0U; --divider_factor2_temp)
258     {
259 
260       divider_factor1 = (tinterword_delay_us*fpdiv)/(divider_factor2_temp*10000);
261       divider_factor1_frac = divider_factor1%100U;
262 
263       if(divider_factor1_frac > 50)
264       {
265         divider_factor1_int =  (divider_factor1/100U)+1;
266         divider_factor1_frac = (divider_factor1_int*100)-divider_factor1;
267       }
268       else
269       {
270         divider_factor1_int =  (divider_factor1/100U);
271       }
272 
273       if ((divider_factor1_int < 5U) && (divider_factor1_int > 0) && (divider_factor1_frac < divider_factor1_frac_min))
274       {
275         divider_factor1_frac_min = divider_factor1_frac;
276         divider_factor1_int_min = divider_factor1_int;
277         divider_factor2= divider_factor2_temp;
278       }
279     }
280   }
281 
282   channel->PCR_SSCMode = (uint32_t)((channel->PCR_SSCMode) & (~(USIC_CH_PCR_SSCMode_DCTQ1_Msk |
283                                                                 USIC_CH_PCR_SSCMode_PCTQ1_Msk |
284                                                                 USIC_CH_PCR_SSCMode_CTQSEL1_Msk))) |
285                          (((divider_factor1_int_min - 1) << USIC_CH_PCR_SSCMode_PCTQ1_Pos) & (uint32_t)USIC_CH_PCR_SSCMode_PCTQ1_Msk) |
286                          (((divider_factor2 - 1 ) << USIC_CH_PCR_SSCMode_DCTQ1_Pos) & (uint32_t)USIC_CH_PCR_SSCMode_DCTQ1_Msk);
287 }
288 
XMC_SPI_CH_Stop(XMC_USIC_CH_t * const channel)289 XMC_SPI_CH_STATUS_t XMC_SPI_CH_Stop(XMC_USIC_CH_t *const channel)
290 {
291   XMC_SPI_CH_STATUS_t status = XMC_SPI_CH_STATUS_OK;
292 
293   if (((uint32_t)XMC_USIC_CH_GetTransmitBufferStatus(channel) & (uint32_t)XMC_USIC_CH_TBUF_STATUS_BUSY) != 0U)
294   {
295     status = XMC_SPI_CH_STATUS_BUSY;
296   }
297   else
298   {
299 
300     /* USIC channel in IDLE mode */
301     XMC_USIC_CH_SetMode(channel, XMC_USIC_CH_OPERATING_MODE_IDLE);
302   }
303 
304   return status;
305 }
306 
XMC_SPI_CH_EnableEvent(XMC_USIC_CH_t * const channel,const uint32_t event)307 void XMC_SPI_CH_EnableEvent(XMC_USIC_CH_t *const channel, const uint32_t event)
308 {
309   channel->CCR |= (event&0x1fc00U);
310   channel->PCR_SSCMode |= ((event << 13U) & 0xe000U);
311 }
312 
XMC_SPI_CH_DisableEvent(XMC_USIC_CH_t * const channel,const uint32_t event)313 void XMC_SPI_CH_DisableEvent(XMC_USIC_CH_t *const channel, const uint32_t event)
314 {
315   channel->CCR &= (uint32_t)~(event&0x1fc00U);
316   channel->PCR_SSCMode &= (uint32_t)~((event << 13U) & 0xe000U);
317 }
318