rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
hal_spi_lld.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2014-2015 Fabio Utzig
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file SPIv1/hal_spi_lld.c
19 * @brief KINETIS SPI subsystem low level driver source.
20 * @author andreika <prometheus.pcb@gmail.com>
21 *
22 * @addtogroup SPI
23 * @{
24 */
25
26#include "hal.h"
27
28//_spi_isr_code
29
30#if HAL_USE_SPI || defined(__DOXYGEN__)
31
32/*===========================================================================*/
33/* Driver local definitions. */
34/*===========================================================================*/
35
36/*===========================================================================*/
37/* Driver exported variables. */
38/*===========================================================================*/
39
40/** @brief SPI0 driver identifier.*/
41#if KINETIS_SPI_USE_SPI0 || defined(__DOXYGEN__)
42SPIDriver SPID1;
43#endif
44
45/** @brief SPI1 driver identifier.*/
46#if KINETIS_SPI_USE_SPI1 || defined(__DOXYGEN__)
47SPIDriver SPID2;
48#endif
49
50/*===========================================================================*/
51/* Driver local variables and types. */
52/*===========================================================================*/
53
54/*===========================================================================*/
55/* Driver local functions. */
56/*===========================================================================*/
57
58static int32_t spi_detectPCS(bool isMaster, ioportid_t ssport, uint16_t sspad, int *alt) {
59 // todo: check if PCS corresponds to SPI number
60 *alt = 3;
61 if (ssport == GPIOA) {
62 switch (sspad) {
63 case 6:
64 return isMaster ? kLPSPI_MasterPcs1 : kLPSPI_SlavePcs1;
65 case 15:
66 return isMaster ? kLPSPI_MasterPcs3 : kLPSPI_SlavePcs3;
67 case 16:
68 return isMaster ? kLPSPI_MasterPcs2 : kLPSPI_SlavePcs2;
69 }
70 } else if (ssport == GPIOB) {
71 switch (sspad) {
72 case 0:
73 return isMaster ? kLPSPI_MasterPcs0 : kLPSPI_SlavePcs0;
74 case 5:
75 return isMaster ? kLPSPI_MasterPcs1 : kLPSPI_SlavePcs1;
76 case 17:
77 return isMaster ? kLPSPI_MasterPcs3 : kLPSPI_SlavePcs3;
78 }
79 } else if (ssport == GPIOD && sspad == 3) {
80 return isMaster ? kLPSPI_MasterPcs0 : kLPSPI_SlavePcs0;
81 } else if (ssport == GPIOE && sspad == 6) {
82 *alt = 2;
83 return isMaster ? kLPSPI_MasterPcs2 : kLPSPI_SlavePcs2;
84 }
85 // wrong/unrecognized PCS!
86 *alt = 0;
87 return -1;
88}
89
90static int32_t spi_detectBaudRate(SPIDriver *spip) {
91 static const int baudRates[] = { 21000000, 10500000, 5250000, 2626000, 1312500, 656250, 328125, 164060 };
92 int flags = 0;
93 if (spip->config->cr1 & SPI_CR1_BR_0)
94 flags |= 1;
95 if (spip->config->cr1 & SPI_CR1_BR_1)
96 flags |= 2;
97 if (spip->config->cr1 & SPI_CR1_BR_2)
98 flags |= 4;
99 int br = baudRates[flags];
100 // SPI1 is faster on STM32 (42 MHz max) so we imitate this behavior
101 if (spip == &SPID1)
102 br *= 2;
103 return br;
104}
105
106/*===========================================================================*/
107/* Driver interrupt handlers. */
108/*===========================================================================*/
109
110void spi_lld_master_callback(LPSPI_Type *base, lpspi_master_handle_t *handle,
111 status_t status, void *userData) {
112 SPIDriver *spi = (SPIDriver *)userData;
113
114 // todo: check status?
115 _spi_isr_code(spi);
116}
117
118void spi_lld_slave_callback(LPSPI_Type *base, lpspi_slave_handle_t *handle,
119 status_t status, void *userData) {
120 SPIDriver *spi = (SPIDriver *)userData;
121
122 // todo: check status?
123 _spi_isr_code(spi);
124}
125
126/*===========================================================================*/
127/* Driver exported functions. */
128/*===========================================================================*/
129
130/**
131 * @brief Low level SPI driver initialization.
132 *
133 * @notapi
134 */
135void spi_lld_init(void) {
136#if KINETIS_SPI_USE_SPI0
137 spiObjectInit(&SPID1);
138#endif
139#if KINETIS_SPI_USE_SPI1
140 spiObjectInit(&SPID2);
141#endif
142}
143
144/**
145 * @brief Configures and activates the SPI peripheral.
146 *
147 * @param[in] spip pointer to the @p SPIDriver object
148 *
149 * @notapi
150 */
151void spi_lld_start(SPIDriver *spip) {
152
153 /* If in stopped state then enables the SPI and DMA clocks.*/
154 if (spip->state == SPI_STOP) {
155 clock_ip_name_t clockName;
156#if KINETIS_SPI_USE_SPI0
157 if (&SPID1 == spip) {
158 spip->spi = LPSPI0;
159 clockName = kCLOCK_Lpspi0;
160 }
161#endif
162
163#if KINETIS_SPI_USE_SPI1
164 if (&SPID2 == spip) {
165 spip->spi = LPSPI1;
166 clockName = kCLOCK_Lpspi1;
167 }
168#endif
169
170 spip->isMaster = (spip->config->cr1 & SPI_CR1_MSTR) != 0;
171 spip->flags = 0; // kLPSPI_MasterByteSwap;
172 int pcsAlt;
173 int pcsIdx = spi_detectPCS(spip->isMaster, spip->config->ssport, spip->config->sspad, &pcsAlt);
174 if (pcsIdx >= 0) {
175 spip->flags |= pcsIdx;
176 // enable corresponding alt.mode for hardware PCS control
177 palSetPadMode(spip->config->ssport, spip->config->sspad, PAL_MODE_ALTERNATE(pcsAlt));
178 } else {
179 // software PCS control for non-standard pins
180 palSetPadMode(spip->config->ssport, spip->config->sspad, PAL_MODE_OUTPUT_OPENDRAIN);
181 }
182
183 //CLOCK_SetIpSrc(clockName, kCLOCK_IpSrcSysPllAsync);
184
185 if (spip->isMaster) {
186 // Master mode
187 lpspi_master_config_t masterConfig;
188 LPSPI_MasterGetDefaultConfig(&masterConfig);
189
190 masterConfig.baudRate = spi_detectBaudRate(spip);
191 masterConfig.bitsPerFrame = (spip->config->cr1 & SPI_CR1_DFF) ? 16 : 8;
192 masterConfig.cpol = (spip->config->cr1 & SPI_CR1_CPOL) ? kLPSPI_ClockPolarityActiveLow : kLPSPI_ClockPolarityActiveHigh;
193 masterConfig.cpha = (spip->config->cr1 & SPI_CR1_CPHA) ? kLPSPI_ClockPhaseSecondEdge : kLPSPI_ClockPhaseFirstEdge;
194 masterConfig.direction = (spip->config->cr1 & SPI_CR1_LSBFIRST) ? kLPSPI_LsbFirst : kLPSPI_MsbFirst;
195
196 masterConfig.pcsToSckDelayInNanoSec = 1000000000 / masterConfig.baudRate * 2;
197 masterConfig.lastSckToPcsDelayInNanoSec = 1000000000 / masterConfig.baudRate * 2;
198 masterConfig.betweenTransferDelayInNanoSec = 1000000000 / masterConfig.baudRate * 2;
199
200 masterConfig.whichPcs = pcsIdx;
201
202 if (spip->config->circular)
203 spip->flags |= kLPSPI_MasterPcsContinuous;
204
205 uint32_t srcClock_Hz = CLOCK_GetIpFreq(clockName);
206 LPSPI_MasterInit(spip->spi, &masterConfig, srcClock_Hz);
207
208 LPSPI_MasterTransferCreateHandle(spip->spi, &spip->masterHandle, spi_lld_master_callback, spip);
209 } else {
210 // todo: add slave support
211 lpspi_slave_config_t slaveConfig;
212 LPSPI_SlaveGetDefaultConfig(&slaveConfig);
213 LPSPI_SlaveInit(spip->spi, &slaveConfig);
214
215 LPSPI_SlaveTransferCreateHandle(spip->spi, &spip->slaveHandle, spi_lld_slave_callback, spip);
216 }
217
218 // todo: add DMA support?
219 //nvicEnableVector(DMA0_IRQn, KINETIS_SPI0_RX_DMA_IRQ_PRIORITY);
220 }
221}
222
223/**
224 * @brief Deactivates the SPI peripheral.
225 *
226 * @param[in] spip pointer to the @p SPIDriver object
227 *
228 * @notapi
229 */
230void spi_lld_stop(SPIDriver *spip) {
231
232 /* If in ready state then disables the SPI clock.*/
233 if (spip->state == SPI_READY) {
234
235 //nvicDisableVector(DMA0_IRQn);
236
237#if KINETIS_SPI_USE_SPI0
238 if (&SPID1 == spip) {
239 LPSPI_Deinit(LPSPI0);
240 }
241#endif
242
243#if KINETIS_SPI_USE_SPI1
244 if (&SPID2 == spip) {
245 LPSPI_Deinit(LPSPI1);
246 }
247#endif
248 }
249}
250
251/**
252 * @brief Asserts the slave select signal and prepares for transfers.
253 *
254 * @param[in] spip pointer to the @p SPIDriver object
255 *
256 * @notapi
257 */
258void spi_lld_select(SPIDriver *spip) {
259 // software PCS control for non-standard pins
260 if (!(spip->flags & LPSPI_MASTER_PCS_MASK))
261 palClearPad(spip->config->ssport, spip->config->sspad);
262}
263
264/**
265 * @brief Deasserts the slave select signal.
266 * @details The previously selected peripheral is unselected.
267 *
268 * @param[in] spip pointer to the @p SPIDriver object
269 *
270 * @notapi
271 */
272void spi_lld_unselect(SPIDriver *spip) {
273 // software PCS control for non-standard pins
274 if (!(spip->flags & LPSPI_MASTER_PCS_MASK))
275 palSetPad(spip->config->ssport, spip->config->sspad);
276}
277
278/**
279 * @brief Ignores data on the SPI bus.
280 * @details This asynchronous function starts the transmission of a series of
281 * idle words on the SPI bus and ignores the received data.
282 * @post At the end of the operation the configured callback is invoked.
283 *
284 * @param[in] spip pointer to the @p SPIDriver object
285 * @param[in] n number of words to be ignored
286 *
287 * @notapi
288 */
289void spi_lld_ignore(SPIDriver *spip, size_t n) {
290/*
291 spip->count = n;
292 spip->rxbuf = NULL;
293 spip->txbuf = NULL;
294
295 spi_start_xfer(spip, false);
296*/
297}
298
299/**
300 * @brief Exchanges data on the SPI bus.
301 * @details This asynchronous function starts a simultaneous transmit/receive
302 * operation.
303 * @post At the end of the operation the configured callback is invoked.
304 * @note The buffers are organized as uint8_t arrays for data sizes below or
305 * equal to 8 bits else it is organized as uint16_t arrays.
306 *
307 * @param[in] spip pointer to the @p SPIDriver object
308 * @param[in] n number of words to be exchanged
309 * @param[in] txbuf the pointer to the transmit buffer
310 * @param[out] rxbuf the pointer to the receive buffer
311 *
312 * @notapi
313 */
314void spi_lld_exchange(SPIDriver *spip, size_t n,
315 const void *txbuf, void *rxbuf) {
316 spip->handleXfer.txData = (uint8_t *)txbuf;
317 spip->handleXfer.rxData = rxbuf;
318 spip->handleXfer.dataSize = n;
319 spip->handleXfer.configFlags = spip->flags;
320
321 if (spip->isMaster) {
322 LPSPI_MasterTransferNonBlocking(spip->spi, &spip->masterHandle, &spip->handleXfer);
323 } else {
324 LPSPI_SlaveTransferNonBlocking(spip->spi, &spip->slaveHandle, &spip->handleXfer);
325 }
326}
327
328/**
329 * @brief Sends data over the SPI bus.
330 * @details This asynchronous function starts a transmit operation.
331 * @post At the end of the operation the configured callback is invoked.
332 * @note The buffers are organized as uint8_t arrays for data sizes below or
333 * equal to 8 bits else it is organized as uint16_t arrays.
334 *
335 * @param[in] spip pointer to the @p SPIDriver object
336 * @param[in] n number of words to send
337 * @param[in] txbuf the pointer to the transmit buffer
338 *
339 * @notapi
340 */
341void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
342
343 spip->handleXfer.txData = (uint8_t *)txbuf;
344 spip->handleXfer.rxData = NULL;
345 spip->handleXfer.dataSize = n;
346 spip->handleXfer.configFlags = spip->flags;
347
348 if (spip->isMaster) {
349 LPSPI_MasterTransferNonBlocking(spip->spi, &spip->masterHandle, &spip->handleXfer);
350 } else {
351 LPSPI_SlaveTransferNonBlocking(spip->spi, &spip->slaveHandle, &spip->handleXfer);
352 }
353}
354
355/**
356 * @brief Receives data from the SPI bus.
357 * @details This asynchronous function starts a receive operation.
358 * @post At the end of the operation the configured callback is invoked.
359 * @note The buffers are organized as uint8_t arrays for data sizes below or
360 * equal to 8 bits else it is organized as uint16_t arrays.
361 *
362 * @param[in] spip pointer to the @p SPIDriver object
363 * @param[in] n number of words to receive
364 * @param[out] rxbuf the pointer to the receive buffer
365 *
366 * @notapi
367 */
368void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
369
370 spip->handleXfer.txData = NULL;
371 spip->handleXfer.rxData = (uint8_t *)rxbuf;
372 spip->handleXfer.dataSize = n;
373 spip->handleXfer.configFlags = spip->flags;
374
375 if (spip->isMaster) {
376 LPSPI_MasterTransferNonBlocking(spip->spi, &spip->masterHandle, &spip->handleXfer);
377 } else {
378 LPSPI_SlaveTransferNonBlocking(spip->spi, &spip->slaveHandle, &spip->handleXfer);
379 }
380}
381
382#if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__)
383/**
384 * @brief Aborts the ongoing SPI operation, if any.
385 *
386 * @param[in] spip pointer to the @p SPIDriver object
387 *
388 * @notapi
389 */
390void spi_lld_abort(SPIDriver *spip) {
391#if 0
392 //SPI_DisableDMA(base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable);
393 /* Stopping DMAs.*/
394 dmaStreamDisable(spip->dmatx);
395 dmaStreamDisable(spip->dmarx);
396#endif
397}
398#endif /* SPI_SUPPORTS_CIRCULAR == TRUE */
399
400/**
401 * @brief Exchanges one frame using a polled wait.
402 * @details This synchronous function exchanges one frame using a polled
403 * synchronization method. This function is useful when exchanging
404 * small amount of data on high speed channels, usually in this
405 * situation is much more efficient just wait for completion using
406 * polling than suspending the thread waiting for an interrupt.
407 *
408 * @param[in] spip pointer to the @p SPIDriver object
409 * @param[in] frame the data frame to send over the SPI bus
410 * @return The received data frame from the SPI bus.
411 */
412uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
413 // todo: use faster LPSPI_WriteData()
414 uint16_t rxFrame = 0;
415 spip->handleXfer.txData = (uint8_t *)&frame;
416 spip->handleXfer.rxData = (uint8_t *)&rxFrame;
417 spip->handleXfer.dataSize = sizeof(frame);
418 spip->handleXfer.configFlags = spip->flags;
419
420 if (spip->isMaster) {
421 LPSPI_MasterTransferBlocking(spip->spi, &spip->handleXfer);
422 } else {
423 // todo: slave support?
424 }
425 // todo: check status?
426 return rxFrame;
427}
428
429#endif /* HAL_USE_SPI */
430
431/** @} */
static const struct @5 baudRates[]
GPIO_TypeDef * ioportid_t
Port Identifier.
void spi_lld_ignore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
static int32_t spi_detectBaudRate(SPIDriver *spip)
Definition hal_spi_lld.c:90
SPIDriver SPID1
SPI0 driver identifier.
Definition hal_spi_lld.c:42
void spi_lld_slave_callback(LPSPI_Type *base, lpspi_slave_handle_t *handle, status_t status, void *userData)
void spi_lld_abort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
void spi_lld_select(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
void spi_lld_init(void)
Low level SPI driver initialization.
void spi_lld_exchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
void spi_lld_stop(SPIDriver *spip)
Deactivates the SPI peripheral.
void spi_lld_master_callback(LPSPI_Type *base, lpspi_master_handle_t *handle, status_t status, void *userData)
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
static int32_t spi_detectPCS(bool isMaster, ioportid_t ssport, uint16_t sspad, int *alt)
Definition hal_spi_lld.c:58
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame)
Exchanges one frame using a polled wait.
void spi_lld_start(SPIDriver *spip)
Configures and activates the SPI peripheral.
SPIDriver SPID2
SPI1 driver identifier.
Definition hal_spi_lld.c:47
void spi_lld_unselect(SPIDriver *spip)
Deasserts the slave select signal.
enum _clock_ip_name clock_ip_name_t
Peripheral clock name difinition used for clock gate, clock source and clock divider setting....
uint32_t CLOCK_GetIpFreq(clock_ip_name_t name)
Gets the clock frequency for a specific IP module.
Definition fsl_clock.c:250
@ kCLOCK_Lpspi1
Definition fsl_clock.h:280
@ kCLOCK_Lpspi0
Definition fsl_clock.h:279
int32_t status_t
Type used for all status and error return values.
Definition fsl_common.h:169
void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz)
Initializes the LPSPI master.
Definition fsl_lpspi.c:187
void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig)
LPSPI slave configuration.
Definition fsl_lpspi.c:279
status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer)
LPSPI slave transfer data using an interrupt method.
Definition fsl_lpspi.c:1403
void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig)
Sets the lpspi_slave_config_t structure to default values.
Definition fsl_lpspi.c:326
status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer)
LPSPI master transfer data using a polling method.
Definition fsl_lpspi.c:779
void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_slave_transfer_callback_t callback, void *userData)
Initializes the LPSPI slave handle.
Definition fsl_lpspi.c:1366
status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer)
LPSPI master transfer data using an interrupt method.
Definition fsl_lpspi.c:961
void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_master_transfer_callback_t callback, void *userData)
Initializes the LPSPI master handle.
Definition fsl_lpspi.c:686
void LPSPI_Deinit(LPSPI_Type *base)
De-initializes the LPSPI peripheral. Call this API to disable the LPSPI clock.
Definition fsl_lpspi.c:367
void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig)
Sets the lpspi_master_config_t structure to default values.
Definition fsl_lpspi.c:249
@ kLPSPI_MasterPcs1
Definition fsl_lpspi.h:199
@ kLPSPI_MasterPcs3
Definition fsl_lpspi.h:201
@ kLPSPI_MasterPcsContinuous
Definition fsl_lpspi.h:203
@ kLPSPI_MasterPcs2
Definition fsl_lpspi.h:200
@ kLPSPI_MasterPcs0
Definition fsl_lpspi.h:198
@ kLPSPI_MsbFirst
Definition fsl_lpspi.h:137
@ kLPSPI_LsbFirst
Definition fsl_lpspi.h:138
@ kLPSPI_ClockPolarityActiveHigh
Definition fsl_lpspi.h:121
@ kLPSPI_ClockPolarityActiveLow
Definition fsl_lpspi.h:122
@ kLPSPI_ClockPhaseFirstEdge
Definition fsl_lpspi.h:128
@ kLPSPI_ClockPhaseSecondEdge
Definition fsl_lpspi.h:130
@ kLPSPI_SlavePcs1
Definition fsl_lpspi.h:227
@ kLPSPI_SlavePcs3
Definition fsl_lpspi.h:229
@ kLPSPI_SlavePcs0
Definition fsl_lpspi.h:226
@ kLPSPI_SlavePcs2
Definition fsl_lpspi.h:228
LPSPI master configuration structure.
Definition fsl_lpspi.h:256
lpspi_shift_direction_t direction
Definition fsl_lpspi.h:261
lpspi_clock_phase_t cpha
Definition fsl_lpspi.h:260
uint32_t betweenTransferDelayInNanoSec
Definition fsl_lpspi.h:268
uint32_t lastSckToPcsDelayInNanoSec
Definition fsl_lpspi.h:265
lpspi_which_pcs_t whichPcs
Definition fsl_lpspi.h:271
uint32_t pcsToSckDelayInNanoSec
Definition fsl_lpspi.h:263
lpspi_clock_polarity_t cpol
Definition fsl_lpspi.h:259
LPSPI master transfer handle structure used for transactional API.
Definition fsl_lpspi.h:350
LPSPI slave configuration structure.
Definition fsl_lpspi.h:283
LPSPI slave transfer handle structure used for transactional API.
Definition fsl_lpspi.h:383