XMC Spi C
XMC Spi C
2 * @file xmc_spi.c
3 * @date 2020-12-05
4 *
5 * @cond
6 *****************************************************************************
7 * XMClib v2.2.0 - XMC Peripheral Driver Library
8 *
9 * Copyright (c) 2015-2020, Infineon Technologies AG
10 * All rights reserved.
11 *
12 * Boost Software License - Version 1.0 - August 17th, 2003
13 *
14 * Permission is hereby granted, free of charge, to any person or organization
15 * obtaining a copy of the software and accompanying documentation covered by
16 * this license (the "Software") to use, reproduce, display, distribute,
17 * execute, and transmit the Software, and to prepare derivative works of the
18 * Software, and to permit third-parties to whom the Software is furnished to
19 * do so, all subject to the following:
20 *
21 * The copyright notices in the Software and this entire statement, including
22 * the above license grant, this restriction and the following disclaimer,
23 * must be included in all copies of the Software, in whole or in part, and
24 * all derivative works of the Software, unless such copies or derivative
25 * works are solely in the form of machine-executable object code generated by
26 * a source language processor.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
31 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
32 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
33 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34 * DEALINGS IN THE SOFTWARE.
35 *
36 * To improve the quality of the software, users are encouraged to share
37 * modifications, enhancements or bug fixes with Infineon Technologies AG
38 * at [email protected].
39 *****************************************************************************
40 *
41 * Change History
42 * --------------
43 *
44 * 2015-02-20:
45 * - Initial <br>
46 *
47 * 2015-05-20:
48 * - Modified XMC_SPI_CH_Stop() API for not setting to IDLE the channel if it is
busy
49 * - Modified XMC_SPI_CH_SetInterwordDelay() implementation in order to gain
accuracy <br>
50 *
51 * 2015-06-20:
52 * - Removed GetDriverVersion API <br>
53 *
54 * 2015-09-01:
55 * - Modified XMC_SPI_CH_EnableEvent() and XMC_SPI_CH_DisableEvent() for
supporting multiple events configuration <br>
56 *
57 * 2015-11-04:
58 * - Modified the check of XMC_USIC_CH_GetTransmitBufferStatus() in the
XMC_SPI_CH_Transmit() flag <br>
59 *
60 * 2019-05-07:
61 * - Added XMC_SPI_CH_SetBaudrateEx() which allows to select between baudrate
generator normal divider and fractional divider mode
62 *
63 * 2019-12-16:
64 * - Fix including files following the convention: angle brackets are used for
standard includes and double quotes for everything else.
65 *
66 * 2020-12-05:
67 * - Added XMC_SPI_CH_InitEx that allows user select if automatic baudrate
configuration should be done or not
68 *
69 * @endcond
70 *
71 */
72 /**
73 *
74 * @brief SPI driver for XMC microcontroller family
75 *
76 */
77 /*************************************************************************************
********************************
78 * HEADER FILES
79
*************************************************************************************
*******************************/
80
81 #include "xmc_scu.h"
82 #include "xmc_spi.h"
83
84 /*************************************************************************************
********************************
85 * MACROS
86
*************************************************************************************
*******************************/
87 #define XMC_SPI_CH_OVERSAMPLING (2UL)
88
89 /*************************************************************************************
********************************
90 * API IMPLEMENTATION
91
*************************************************************************************
*******************************/
92
93 /* Initializes the selected SPI channel with the config structure. */
94 void XMC_SPI_CH_InitEx(XMC_USIC_CH_t *const channel, const XMC_SPI_CH_CONFIG_t *const
config, bool init_brg)
95 {
96 XMC_USIC_CH_Enable(channel);
97
98 if ((config->bus_mode == XMC_SPI_CH_BUS_MODE_MASTER) && init_brg)
99 {
100 /* Configure baud rate */
101 if (config->normal_divider_mode)
102 {
103 /* Normal divider mode */
104 (void)XMC_USIC_CH_SetBaudrateEx(channel, config->baudrate,
XMC_SPI_CH_OVERSAMPLING);
105 }
106 else
107 {
108 /* Fractional divider mode */
109 (void)XMC_USIC_CH_SetBaudrate(channel, config->baudrate, XMC_SPI_CH_OVERSAMPLING
);
110 }
111 }
112
113 /* Configuration of USIC Shift Control */
114 /* Transmission Mode (TRM) = 1 */
115 /* Passive Data Level (PDL) = 1 */
116 channel->SCTR = USIC_CH_SCTR_PDL_Msk |
117 (0x1UL << USIC_CH_SCTR_TRM_Pos) |
118 (0x3fUL << USIC_CH_SCTR_FLE_Pos) |
119 (0x7UL << USIC_CH_SCTR_WLE_Pos);
120
121 /* Configuration of USIC Transmit Control/Status Register */
122 /* TBUF Data Enable (TDEN) = 1 */
123 /* TBUF Data Single Shot Mode (TDSSM) = 1 */
124 channel->TCSR = (uint32_t)(USIC_CH_TCSR_HPCMD_Msk |
125 (0x01UL << USIC_CH_TCSR_TDEN_Pos) |
126 USIC_CH_TCSR_TDSSM_Msk);
127
128 if (config->bus_mode == XMC_SPI_CH_BUS_MODE_MASTER)
129 {
130 /* Configuration of Protocol Control Register */
131 channel->PCR_SSCMode = (uint32_t)(USIC_CH_PCR_SSCMode_MSLSEN_Msk |
132 USIC_CH_PCR_SSCMode_SELCTR_Msk |
133 (uint32_t)config->selo_inversion |
134 USIC_CH_PCR_SSCMode_FEM_Msk);
135 }
136
137 /* Clear protocol status */
138 channel->PSCR = 0xFFFFFFFFUL;
139
140 /* Set parity settings */
141 channel->CCR = (uint32_t)config->parity_mode;
142 }
143
144 XMC_SPI_CH_STATUS_t XMC_SPI_CH_SetBaudrate(XMC_USIC_CH_t *const channel, const
uint32_t rate)
145 {
146 XMC_SPI_CH_STATUS_t status;
147
148 status = XMC_SPI_CH_STATUS_ERROR;
149
150 if (rate <= (XMC_SCU_CLOCK_GetPeripheralClockFrequency() >> 1U))
151 {
152 if (XMC_USIC_CH_SetBaudrate(channel, rate, XMC_SPI_CH_OVERSAMPLING) ==
XMC_USIC_CH_STATUS_OK)
153 {
154 status = XMC_SPI_CH_STATUS_OK;
155 }
156 }
157 return status;
158 }
159
160 XMC_SPI_CH_STATUS_t XMC_SPI_CH_SetBaudrateEx(XMC_USIC_CH_t *const channel, const
uint32_t rate, bool normal_divider_mode)
161 {
162 XMC_USIC_CH_STATUS_t status;
163
164 if (rate <= (XMC_SCU_CLOCK_GetPeripheralClockFrequency() >> 1U))
165 {
166 if (normal_divider_mode)
167 {
168 /* Normal divider mode */
169 status = XMC_USIC_CH_SetBaudrateEx(channel, rate, XMC_SPI_CH_OVERSAMPLING);
170 }
171 else
172 {
173 /* Fractional divider mode */
174 status = XMC_USIC_CH_SetBaudrate(channel, rate, XMC_SPI_CH_OVERSAMPLING);
175 }
176 }
177 else
178 {
179 status = XMC_USIC_CH_STATUS_ERROR;
180 }
181
182 return (XMC_SPI_CH_STATUS_t)status;
183 }
184
185 /* Enable the selected slave signal by setting (SELO) bits in PCR register. */
186 void XMC_SPI_CH_EnableSlaveSelect(XMC_USIC_CH_t *const channel, const
XMC_SPI_CH_SLAVE_SELECT_t slave)
187 {
188 /* Configuration of Protocol Control Register */
189 channel->PCR_SSCMode &= (uint32_t)~USIC_CH_PCR_SSCMode_SELO_Msk;
190 channel->PCR_SSCMode |= (uint32_t)slave;
191 }
192
193 /* Disable the slave signals by clearing (SELO) bits in PCR register. */
194 void XMC_SPI_CH_DisableSlaveSelect(XMC_USIC_CH_t *const channel)
195 {
196 XMC_SPI_CH_ClearStatusFlag(channel, (uint32_t)XMC_SPI_CH_STATUS_FLAG_MSLS);
197
198 /* Configuration of Protocol Control Register */
199 channel->PCR_SSCMode &= (uint32_t)~USIC_CH_PCR_SSCMode_SELO_Msk;
200 }
201
202 /* Puts the data into FIFO if FIFO mode is enabled or else into standard buffers, by
setting the proper mode. */
203 void XMC_SPI_CH_Transmit(XMC_USIC_CH_t *const channel, const uint16_t data, const
XMC_SPI_CH_MODE_t mode)
204 {
205
206 channel->CCR = (channel->CCR & (uint32_t)(~USIC_CH_CCR_HPCEN_Msk)) |
207 (((uint32_t) mode << USIC_CH_CCR_HPCEN_Pos) & (uint32_t)
USIC_CH_CCR_HPCEN_Msk);
208
209
210 /* Check FIFO size */
211 if ((channel->TBCTR & USIC_CH_TBCTR_SIZE_Msk) == 0U)
212 {
213 while (XMC_USIC_CH_GetTransmitBufferStatus(channel) ==
XMC_USIC_CH_TBUF_STATUS_BUSY)
214 {
215 }
216
217 XMC_SPI_CH_ClearStatusFlag(channel, (uint32_t)
XMC_SPI_CH_STATUS_FLAG_TRANSMIT_BUFFER_INDICATION);
218
219 channel->TBUF[mode] = data;
220 }
221 else
222 {
223 channel->IN[mode] = data;
224 }
225 }
226
227 /* Reads the data from the buffers based on the FIFO mode selection. */
228 uint16_t XMC_SPI_CH_GetReceivedData(XMC_USIC_CH_t *const channel)
229 {
230 uint16_t retval;
231
232 /* Check FIFO size */
233 if ((channel->RBCTR & USIC_CH_RBCTR_SIZE_Msk) == 0U)
234 {
235 retval = (uint16_t)channel->RBUF;
236 }
237 else
238 {
239 retval = (uint16_t)channel->OUTR;
240 }
241
242 return retval;
243 }
244
245 /* Configures the inter word delay by setting PCR.PCTQ1, PCR.DCTQ1 bit fields. */
246 void XMC_SPI_CH_SetInterwordDelay(XMC_USIC_CH_t *const channel, uint32_t
tinterword_delay_us)
247 {
248 uint32_t peripheral_clock;
249 uint32_t pdiv;
250 uint32_t step;
251 uint32_t fFD;
252 uint32_t fpdiv;
253 uint32_t divider_factor1 = 0U;
254 uint32_t divider_factor2 = 32U;
255 uint32_t divider_factor1_int = 0U;
256 uint32_t divider_factor1_int_min = 4U;
257 uint32_t divider_factor1_frac_min = 100U;
258 uint32_t divider_factor1_frac = 0U;
259 uint32_t divider_factor2_temp = 0U;
260 peripheral_clock = XMC_SCU_CLOCK_GetPeripheralClockFrequency();
261 pdiv = (uint32_t)(channel->BRG & USIC_CH_BRG_PDIV_Msk) >> USIC_CH_BRG_PDIV_Pos;
262 step = (uint32_t)(channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos;
263 fFD = (uint32_t)((peripheral_clock >> 10U) * step);
264 fpdiv = fFD / (1U + pdiv);
265
266 if (tinterword_delay_us < (128000000 / fpdiv))
267 {
268 for (divider_factor2_temp = 32U; divider_factor2_temp > 0U; --divider_factor2_temp
)
269 {
270
271 divider_factor1 = (tinterword_delay_us * fpdiv) / (divider_factor2_temp * 10000
);
272 divider_factor1_frac = divider_factor1 % 100U;
273
274 if (divider_factor1_frac > 50)
275 {
276 divider_factor1_int = (divider_factor1 / 100U) + 1;
277 divider_factor1_frac = (divider_factor1_int * 100) - divider_factor1;
278 }
279 else
280 {
281 divider_factor1_int = (divider_factor1 / 100U);
282 }
283
284 if ((divider_factor1_int < 5U) && (divider_factor1_int > 0) && (
divider_factor1_frac < divider_factor1_frac_min))
285 {
286 divider_factor1_frac_min = divider_factor1_frac;
287 divider_factor1_int_min = divider_factor1_int;
288 divider_factor2 = divider_factor2_temp;
289 }
290 }
291 }
292
293 channel->PCR_SSCMode = (uint32_t)((channel->PCR_SSCMode) & (~(
USIC_CH_PCR_SSCMode_DCTQ1_Msk |
294 USIC_CH_PCR_SSCMode_PCTQ1_Msk |
295 USIC_CH_PCR_SSCMode_CTQSEL1_Msk))) |
296 (((divider_factor1_int_min - 1) <<
USIC_CH_PCR_SSCMode_PCTQ1_Pos) & (uint32_t)
USIC_CH_PCR_SSCMode_PCTQ1_Msk) |
297 (((divider_factor2 - 1 ) << USIC_CH_PCR_SSCMode_DCTQ1_Pos) &
(uint32_t)USIC_CH_PCR_SSCMode_DCTQ1_Msk);
298 }
299
300 XMC_SPI_CH_STATUS_t XMC_SPI_CH_Stop(XMC_USIC_CH_t *const channel)
301 {
302 XMC_SPI_CH_STATUS_t status = XMC_SPI_CH_STATUS_OK;
303
304 if (XMC_USIC_CH_GetTransmitBufferStatus(channel) == XMC_USIC_CH_TBUF_STATUS_BUSY)
305 {
306 status = XMC_SPI_CH_STATUS_BUSY;
307 }
308 else
309 {
310
311 /* USIC channel in IDLE mode */
312 XMC_USIC_CH_SetMode(channel, XMC_USIC_CH_OPERATING_MODE_IDLE);
313 }
314
315 return status;
316 }
317
318 void XMC_SPI_CH_EnableEvent(XMC_USIC_CH_t *const channel, const uint32_t event)
319 {
320 channel->CCR |= (event & 0x1fc00U);
321 channel->PCR_SSCMode |= ((event << 13U) & 0xe000U);
322 }
323
324 void XMC_SPI_CH_DisableEvent(XMC_USIC_CH_t *const channel, const uint32_t event)
325 {
326 channel->CCR &= (uint32_t)~(event & 0x1fc00U);
327 channel->PCR_SSCMode &= (uint32_t)~((event << 13U) & 0xe000U);
328 }
329