px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
px_spi.c
1/* =============================================================================
2 ____ ___ ____ ___ _ _ ___ __ __ ___ __ __ TM
3 | _ \ |_ _| / ___| / _ \ | \ | | / _ \ | \/ | |_ _| \ \/ /
4 | |_) | | | | | | | | | | \| | | | | | | |\/| | | | \ /
5 | __/ | | | |___ | |_| | | |\ | | |_| | | | | | | | / \
6 |_| |___| \____| \___/ |_| \_| \___/ |_| |_| |___| /_/\_\
7
8 Copyright (c) 2018 Pieter Conradie <https://piconomix.com>
9
10 License: MIT
11 https://github.com/piconomix/px-fwlib/blob/master/LICENSE.md
12
13 Title: px_spi.h : SPI Peripheral Driver
14 Author(s): Pieter Conradie
15 Creation Date: 2018-03-01
16
17============================================================================= */
18
19/* _____STANDARD INCLUDES____________________________________________________ */
20
21/* _____PROJECT INCLUDES_____________________________________________________ */
22#include "px_spi.h"
23#include "px_board.h"
24#include "px_stm32cube.h"
25#include "px_log.h"
26
27/* _____LOCAL DEFINITIONS____________________________________________________ */
28PX_LOG_NAME("px_spi");
29
30/// Internal data for each SPI peripheral
31typedef struct px_spi_per_s
32{
33 SPI_TypeDef * spi_base_adr; ///< SPI peripheral base register address
34 DMA_Channel_TypeDef * dma_rx_base_adr; ///< DMA RX channel base register address
35 DMA_Channel_TypeDef * dma_tx_base_adr; ///< DMA TX channel base register address
36 px_spi_nr_t spi_nr; ///< Peripheral number
37 uint8_t open_counter; ///< Number of open handles referencing peripheral
39
40/* _____MACROS_______________________________________________________________ */
41
42/* _____GLOBAL VARIABLES_____________________________________________________ */
43
44/* _____LOCAL VARIABLES______________________________________________________ */
45/// Allocate data for each enabled SPI peripheral
46#if PX_SPI_CFG_SPI1_EN
47static px_spi_per_t px_spi_per_1;
48#endif
49#if PX_SPI_CFG_SPI2_EN
50static px_spi_per_t px_spi_per_2;
51#endif
52
53/* _____LOCAL FUNCTION DECLARATIONS__________________________________________ */
54
55/* _____LOCAL FUNCTIONS______________________________________________________ */
56static void px_spi_init_per(SPI_TypeDef * spi_base_adr,
57 px_spi_nr_t spi_nr,
58 uint32_t spi_cr1_val)
59{
60 // Enable peripheral clocks
61 switch(spi_nr)
62 {
63#if PX_SPI_CFG_SPI1_EN
64 case PX_SPI_NR_1:
65 // Enable peripheral clocks
66 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
67 LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
68 // Configure DMA channel 2 (SPI1_RX)
69 LL_DMA_SetPeriphAddress(DMA1, 2, (uint32_t)&SPI1->DR);
70 DMA1_Channel2->CCR = DMA_CCR_MINC;
71#if defined(STM32L0) || defined(STM32G0)
72 LL_DMA_SetPeriphRequest(DMA1, 2, LL_DMA_REQUEST_1);
73#endif
74 // Configure DMA channel 3 (SPI1_TX)
75 LL_DMA_SetPeriphAddress(DMA1, 3, (uint32_t)&SPI1->DR);
76 DMA1_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_DIR;
77#if defined(STM32L0) || defined(STM32G0)
78 LL_DMA_SetPeriphRequest(DMA1, 3, LL_DMA_REQUEST_1);
79#endif
80 break;
81#endif
82#if PX_SPI_CFG_SPI2_EN
83 case PX_SPI_NR_2:
84 // Enable peripheral clocks
85 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
86 LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
87 // Configure DMA channel 4 (SPI2_RX)
88 LL_DMA_SetPeriphAddress(DMA1, 4, (uint32_t)&SPI2->DR);
89 DMA1_Channel4->CCR = DMA_CCR_MINC;
90#if defined(STM32L0) || defined(STM32G0)
91 LL_DMA_SetPeriphRequest(DMA1, 4, LL_DMA_REQUEST_2);
92#endif
93 // Configure DMA channel 5 (SPI2_TX)
94 LL_DMA_SetPeriphAddress(DMA1, 5, (uint32_t)&SPI2->DR);
95 DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_DIR;
96#if defined(STM32L0) || defined(STM32G0)
97 LL_DMA_SetPeriphRequest(DMA1, 5, LL_DMA_REQUEST_2);
98#endif
99 break;
100#endif
101 default:
102 PX_LOG_E("Invalid peripheral");
103 return;
104 }
105
106 // Disable peripheral
107 LL_SPI_Disable(spi_base_adr);
108
109 // Set SPI Control Register 1:
110 // master, data order, clock divisor, mode (clock polarity & phase)
111 spi_base_adr->CR1 = spi_cr1_val;
112
113 // Enable peripheral
114 LL_SPI_Enable(spi_base_adr);
115}
116
117static void px_spi_update_cfg(SPI_TypeDef * spi_base_adr, uint32_t spi_cr1_val)
118{
119 // Must communication parameters change?
120 if((spi_base_adr->CR1 & (~SPI_CR1_SPE_Msk)) != spi_cr1_val)
121 {
122 // Disable peripheral
123 LL_SPI_Disable(spi_base_adr);
124 // Change communication parameters
125 spi_base_adr->CR1 = spi_cr1_val;
126 // Enable peripheral
127 LL_SPI_Enable(spi_base_adr);
128 }
129}
130
131static void px_spi_init_per_data(px_spi_nr_t spi_nr,
132 px_spi_per_t * spi_per)
133{
134 // Set peripheral number
135 spi_per->spi_nr = spi_nr;
136 // Set peripheral base address
137 switch(spi_nr)
138 {
139#if PX_SPI_CFG_SPI1_EN
140 case PX_SPI_NR_1:
141 spi_per->spi_base_adr = SPI1;
142 spi_per->dma_rx_base_adr = DMA1_Channel2;
143 spi_per->dma_tx_base_adr = DMA1_Channel3;
144 break;
145#endif
146#if PX_SPI_CFG_SPI2_EN
147 case PX_SPI_NR_2:
148 spi_per->spi_base_adr = SPI2;
149 spi_per->dma_rx_base_adr = DMA1_Channel4;
150 spi_per->dma_tx_base_adr = DMA1_Channel5;
151 break;
152#endif
153 default:
154 PX_LOG_E("Invalid peripheral");
155 return;
156 }
157 // Clear open counter
158 spi_per->open_counter = 0;
159}
160
161/* _____GLOBAL FUNCTIONS_____________________________________________________ */
162void px_spi_init(void)
163{
164 // Initialize peripheral data for each enabled peripheral
165#if PX_SPI_CFG_SPI1_EN
166 px_spi_init_per_data(PX_SPI_NR_1, &px_spi_per_1);
167#endif
168#if PX_SPI_CFG_SPI2_EN
169 px_spi_init_per_data(PX_SPI_NR_2, &px_spi_per_2);
170#endif
171}
172
174 px_spi_nr_t spi_nr,
175 uint8_t cs_id)
176{
177 px_spi_per_t * spi_per;
178 uint32_t spi_cr1_val = 0;
179
180#if PX_LOG
181 // Verify that pointer to handle is not NULL
182 if(handle == NULL)
183 {
184 PX_LOG_ASSERT(false);
185 return false;
186 }
187 if(handle->spi_per != NULL)
188 {
189 PX_LOG_W("Handle already open?");
190 }
191#endif
192
193 // Handle not initialised
194 handle->spi_per = NULL;
195 // Set pointer to peripheral data
196 switch(spi_nr)
197 {
198#if PX_SPI_CFG_SPI1_EN
199 case PX_SPI_NR_1:
200 spi_per = &px_spi_per_1;
201 break;
202#endif
203#if PX_SPI_CFG_SPI2_EN
204 case PX_SPI_NR_2:
205 spi_per = &px_spi_per_2;
206 break;
207#endif
208 default:
209 PX_LOG_E("Invalid peripheral specified");
210 return false;
211 }
212#if PX_LOG
213 // Check that px_spi_init() has been called
214 if(spi_per->spi_base_adr == NULL)
215 {
216 PX_LOG_ASSERT(false);
217 return false;
218 }
219#endif
220
221 // Set SPI data order (MSB or LSB first)
222 spi_cr1_val |= PX_SPI_CFG_DEFAULT_DATA_ORDER << SPI_CR1_LSBFIRST_Pos;
223 // Set SPI clock divisor
224 spi_cr1_val |= PX_SPI_CFG_DEFAULT_BAUD << SPI_CR1_BR_Pos;
225 // Set SPI mode (clock polarity and phase)
226 spi_cr1_val |= PX_SPI_CFG_DEFAULT_MODE << SPI_CR1_CPHA_Pos;
227 // Set master mode of operation (not slave)
228 spi_cr1_val |= SPI_CR1_MSTR;
229 // Enable software NSS management
230 spi_cr1_val |= SPI_CR1_SSM | SPI_CR1_SSI;
231 // Save value for SPI Control Register 1 (SPI_CR1)
232 handle->spi_cr1_val = spi_cr1_val;
233 // Save Chip Select GPIO ID
234 handle->cs_id = cs_id;
235 // Set default Master Out dummy byte
236 handle->mo_dummy_byte = 0x00;
237 // Peripheral already initialized?
238 if(spi_per->open_counter == 0)
239 {
240 // No. Initialise peripheral
241 px_spi_init_per(spi_per->spi_base_adr, spi_nr, spi_cr1_val);
242 }
243 // Point handle to peripheral
244 handle->spi_per = spi_per;
245
246 // Success
247 PX_LOG_ASSERT(spi_per->open_counter < 255);
248 spi_per->open_counter++;
249 return true;
250}
251
253 px_spi_nr_t spi_nr,
254 uint8_t cs_id,
255 px_spi_baud_t baud,
256 px_spi_mode_t mode,
257 px_spi_dord_t data_order,
258 uint8_t mo_dummy_byte)
259{
260 px_spi_per_t * spi_per;
261 uint32_t spi_cr1_val = 0;
262
263#if PX_LOG
264 // Verify that pointer to handle is not NULL
265 if(handle == NULL)
266 {
267 PX_LOG_ASSERT(false);
268 return false;
269 }
270 if(handle->spi_per != NULL)
271 {
272 PX_LOG_W("Handle already open?");
273 }
274#endif
275
276 // Handle not initialised
277 handle->spi_per = NULL;
278 // Set pointer to peripheral data
279 switch(spi_nr)
280 {
281#if PX_SPI_CFG_SPI1_EN
282 case PX_SPI_NR_1:
283 spi_per = &px_spi_per_1;
284 break;
285#endif
286#if PX_SPI_CFG_SPI2_EN
287 case PX_SPI_NR_2:
288 spi_per = &px_spi_per_2;
289 break;
290#endif
291 default:
292 PX_LOG_E("Invalid peripheral specified");
293 return false;
294 }
295#if PX_LOG
296 // Check that px_spi_init() has been called
297 if(spi_per->spi_base_adr == NULL)
298 {
299 PX_LOG_ASSERT(false);
300 return false;
301 }
302#endif
303
304 // Set SPI data order (MSB or LSB first)
305 if(data_order == PX_SPI_DATA_ORDER_LSB)
306 {
307 spi_cr1_val |= SPI_CR1_LSBFIRST;
308 }
309 // Set SPI clock divisor
310 spi_cr1_val |= baud << SPI_CR1_BR_Pos;
311 // Set SPI mode (clock polarity and phase)
312 spi_cr1_val |= mode << SPI_CR1_CPHA_Pos;
313 // Set master mode of operation (not slave)
314 spi_cr1_val |= SPI_CR1_MSTR;
315 // Enable software NSS management
316 spi_cr1_val |= SPI_CR1_SSM | SPI_CR1_SSI;
317 // Save value for SPI Control Register 1 (SPI_CR1)
318 handle->spi_cr1_val = spi_cr1_val;
319 // Save Chip Select GPIO ID
320 handle->cs_id = cs_id;
321 // Save Master Out dummy byte
322 handle->mo_dummy_byte = mo_dummy_byte;
323 // Peripheral already initialized?
324 if(spi_per->open_counter == 0)
325 {
326 // No. Initialise peripheral
327 px_spi_init_per(spi_per->spi_base_adr, spi_nr, spi_cr1_val);
328 }
329 // Point handle to peripheral
330 handle->spi_per = spi_per;
331
332 // Success
333 PX_LOG_ASSERT(spi_per->open_counter < 255);
334 spi_per->open_counter++;
335 return true;
336}
337
339{
340 px_spi_per_t * spi_per;
341 SPI_TypeDef * spi_base_adr;
342
343#if PX_LOG
344 // Check handle
345 if( (handle == NULL)
346 ||(handle->spi_per == NULL)
347 ||(handle->spi_per->spi_base_adr == NULL)
348 ||(handle->spi_per->open_counter == 0 ) )
349 {
350 PX_LOG_ASSERT(false);
351 return false;
352 }
353#endif
354
355 // Set pointer to peripheral
356 spi_per = handle->spi_per;
357 // Get SPI peripheral base register address
358 spi_base_adr = spi_per->spi_base_adr;
359 // Decrement open count
360 PX_LOG_ASSERT(spi_per->open_counter > 0);
361 spi_per->open_counter--;
362 // More open handles referencing peripheral?
363 if(spi_per->open_counter != 0)
364 {
365 // Yes. Only close handle
366 handle->spi_per = NULL;
367 // Success
368 return true;
369 }
370
371 // Disable peripheral
372 LL_SPI_Disable(spi_base_adr);
373 // Disable peripheral clock
374 switch(spi_per->spi_nr)
375 {
376#if PX_SPI_CFG_SPI1_EN
377 case PX_SPI_NR_1:
378 LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_SPI1);
379 break;
380#endif
381#if PX_SPI_CFG_SPI2_EN
382 case PX_SPI_NR_2:
383 LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_SPI2);
384 break;
385#endif
386 default:
387 break;
388 }
389 // Close handle
390 handle->spi_per = NULL;
391
392 // Success
393 return true;
394}
395
397 const void * data,
398 size_t nr_of_bytes,
399 uint8_t flags)
400{
401 px_spi_per_t * spi_per;
402 SPI_TypeDef * spi_base_adr;
403 const uint8_t * data_wr_u8 = (const uint8_t *)data;
404
405#if PX_LOG
406 // Check handle
407 if( (handle == NULL)
408 ||(handle->spi_per == NULL)
409 ||(handle->spi_per->spi_base_adr == NULL)
410 ||(handle->spi_per->open_counter == 0 ) )
411 {
412 PX_LOG_ASSERT(false);
413 return;
414 }
415#endif
416
417 // Set pointer to peripheral
418 spi_per = handle->spi_per;
419
420 // Assert Chip Select?
421 if(flags & PX_SPI_FLAG_START)
422 {
423 // Take Chip Select Low
424 PX_SPI_CFG_CS_LO(handle->cs_id);
425 }
426
427 // Write data
428 if(nr_of_bytes != 0)
429 {
430 // Get SPI peripheral base register address
431 spi_base_adr = spi_per->spi_base_adr;
432 PX_LOG_ASSERT(spi_base_adr != NULL);
433 // Update communication parameters (if different)
434 px_spi_update_cfg(spi_base_adr, handle->spi_cr1_val);
435 // Configure and enable DMA TX channel
436 spi_per->dma_tx_base_adr->CMAR = (uint32_t)data_wr_u8;
437 spi_per->dma_tx_base_adr->CNDTR = nr_of_bytes;
438 spi_per->dma_tx_base_adr->CCR |= DMA_CCR_EN;
439 // Enable DMA request for TX
440 LL_SPI_EnableDMAReq_TX(spi_base_adr);
441 // Block until TX DMA transfer is complete
442 switch(spi_per->spi_nr)
443 {
444 #if PX_SPI_CFG_SPI1_EN
445 case PX_SPI_NR_1:
446 while(!LL_DMA_IsActiveFlag_TC3(DMA1)) {;}
447 LL_DMA_ClearFlag_TC3(DMA1);
448 break;
449 #endif
450 #if PX_SPI_CFG_SPI2_EN
451 case PX_SPI_NR_2:
452 while(!LL_DMA_IsActiveFlag_TC5(DMA1)) {;}
453 LL_DMA_ClearFlag_TC5(DMA1);
454 break;
455 #endif
456 default:
457 PX_LOG_E("Invalid peripheral");
458 break;
459 }
460 // Wait until SPI transmit of last byte is complete
461 while(!LL_SPI_IsActiveFlag_TXE(spi_base_adr)) {;}
462 while(LL_SPI_IsActiveFlag_BSY(spi_base_adr)) {;}
463 // Clear data overrun
464 LL_SPI_ClearFlag_OVR(spi_base_adr);
465 // Disable DMA TX channel
466 spi_per->dma_tx_base_adr->CCR &= ~DMA_CCR_EN;
467 // Disable DMA request for TX
468 spi_base_adr->CR2 = 0;
469 }
470
471 // De-assert Chip Select?
472 if(flags & PX_SPI_FLAG_STOP)
473 {
474 // Take Chip Select High
475 PX_SPI_CFG_CS_HI(handle->cs_id);
476 }
477}
478
480 void * data,
481 size_t nr_of_bytes,
482 uint8_t flags)
483{
484 px_spi_per_t * spi_per;
485 SPI_TypeDef * spi_base_adr;
486 uint8_t * data_rd_u8 = (uint8_t *)data;
487
488#if PX_LOG
489 // Check handle
490 if( (handle == NULL)
491 ||(handle->spi_per == NULL)
492 ||(handle->spi_per->spi_base_adr == NULL)
493 ||(handle->spi_per->open_counter == 0 ) )
494 {
495 PX_LOG_ASSERT(false);
496 return;
497 }
498#endif
499
500 // Set pointer to peripheral
501 spi_per = handle->spi_per;
502
503 // Assert Chip Select?
504 if(flags & PX_SPI_FLAG_START)
505 {
506 // Take Chip Select Low
507 PX_SPI_CFG_CS_LO(handle->cs_id);
508 }
509
510 // Read data
511 if(nr_of_bytes != 0)
512 {
513 // Get SPI peripheral base register address
514 spi_base_adr = spi_per->spi_base_adr;
515 PX_LOG_ASSERT(spi_base_adr != NULL);
516 // Update communication parameters (if different)
517 px_spi_update_cfg(spi_base_adr, handle->spi_cr1_val);
518 // Enable DMA request for RX
519 LL_SPI_EnableDMAReq_RX(spi_base_adr);
520 // Configure and enable DMA RX channel
521 spi_per->dma_rx_base_adr->CMAR = (uint32_t)data_rd_u8;
522 spi_per->dma_rx_base_adr->CNDTR = nr_of_bytes;
523 spi_per->dma_rx_base_adr->CCR |= DMA_CCR_EN;
524 // Configure and enable DMA TX channel
525 spi_per->dma_tx_base_adr->CMAR = (uint32_t)&handle->mo_dummy_byte;
526 spi_per->dma_tx_base_adr->CNDTR = nr_of_bytes;
527 spi_per->dma_tx_base_adr->CCR &= ~DMA_CCR_MINC;
528 spi_per->dma_tx_base_adr->CCR |= DMA_CCR_EN;
529 // Enable DMA request for TX
530 LL_SPI_EnableDMAReq_TX(spi_base_adr);
531 // Block until RX DMA transfer is complete
532 switch(spi_per->spi_nr)
533 {
534 #if PX_SPI_CFG_SPI1_EN
535 case PX_SPI_NR_1:
536 while(!LL_DMA_IsActiveFlag_TC2(DMA1)) {;}
537 LL_DMA_ClearFlag_TC2(DMA1);
538 break;
539 #endif
540 #if PX_SPI_CFG_SPI2_EN
541 case PX_SPI_NR_2:
542 while(!LL_DMA_IsActiveFlag_TC4(DMA1)) {;}
543 LL_DMA_ClearFlag_TC4(DMA1);
544 break;
545 #endif
546 default:
547 PX_LOG_E("Invalid peripheral");
548 break;
549 }
550
551 // Disable DMA RX channel
552 spi_per->dma_rx_base_adr->CCR &= ~DMA_CCR_EN;
553 // Disable DMA TX channel
554 spi_per->dma_tx_base_adr->CCR &= ~DMA_CCR_EN;
555 spi_per->dma_tx_base_adr->CCR |= DMA_CCR_MINC;
556 // Disable DMA request for RX and TX
557 spi_base_adr->CR2 = 0;
558 }
559
560 // De-assert Chip Select?
561 if(flags & PX_SPI_FLAG_STOP)
562 {
563 // Take Chip Select High
564 PX_SPI_CFG_CS_HI(handle->cs_id);
565 }
566}
567
569 const void * data_wr,
570 void * data_rd,
571 size_t nr_of_bytes,
572 uint8_t flags)
573{
574 px_spi_per_t * spi_per;
575 SPI_TypeDef * spi_base_adr;
576 const uint8_t * data_wr_u8 = (const uint8_t *)data_wr;
577 uint8_t * data_rd_u8 = (uint8_t *)data_rd;
578
579#if PX_LOG
580 // Check handle
581 if( (handle == NULL)
582 ||(handle->spi_per == NULL)
583 ||(handle->spi_per->spi_base_adr == NULL)
584 ||(handle->spi_per->open_counter == 0 ) )
585 {
586 PX_LOG_ASSERT(false);
587 return;
588 }
589#endif
590
591 // Set pointer to peripheral
592 spi_per = handle->spi_per;
593
594 // Assert Chip Select?
595 if(flags & PX_SPI_FLAG_START)
596 {
597 // Take Chip Select Low
598 PX_SPI_CFG_CS_LO(handle->cs_id);
599 }
600
601 // Get SPI peripheral base register address
602 spi_base_adr = spi_per->spi_base_adr;
603 PX_LOG_ASSERT(spi_base_adr != NULL);
604 // Update communication parameters (if different)
605 px_spi_update_cfg(spi_base_adr, handle->spi_cr1_val);
606
607 // Exchange data
608 if(nr_of_bytes != 0)
609 {
610 // Enable DMA request for RX
611 LL_SPI_EnableDMAReq_RX(spi_base_adr);
612 // Configure and enable DMA RX channel
613 spi_per->dma_rx_base_adr->CMAR = (uint32_t)data_rd_u8;
614 spi_per->dma_rx_base_adr->CNDTR = nr_of_bytes;
615 spi_per->dma_rx_base_adr->CCR |= DMA_CCR_EN;
616 // Configure and enable DMA TX channel
617 spi_per->dma_tx_base_adr->CMAR = (uint32_t)data_wr_u8;
618 spi_per->dma_tx_base_adr->CNDTR = nr_of_bytes;
619 spi_per->dma_tx_base_adr->CCR |= DMA_CCR_EN;
620 // Enable DMA request for TX
621 LL_SPI_EnableDMAReq_TX(spi_base_adr);
622 // Block until RX DMA transfer is complete
623 switch(spi_per->spi_nr)
624 {
625 #if PX_SPI_CFG_SPI1_EN
626 case PX_SPI_NR_1:
627 while(!LL_DMA_IsActiveFlag_TC2(DMA1)) {;}
628 LL_DMA_ClearFlag_TC2(DMA1);
629 break;
630 #endif
631 #if PX_SPI_CFG_SPI2_EN
632 case PX_SPI_NR_2:
633 while(!LL_DMA_IsActiveFlag_TC4(DMA1)) {;}
634 LL_DMA_ClearFlag_TC4(DMA1);
635 break;
636 #endif
637 default:
638 PX_LOG_E("Invalid peripheral");
639 break;
640 }
641 // Disable DMA RX channel
642 spi_per->dma_rx_base_adr->CCR &= ~DMA_CCR_EN;
643 // Disable DMA TX channel
644 spi_per->dma_tx_base_adr->CCR &= ~DMA_CCR_EN;
645 // Disable DMA request for RX and TX
646 spi_base_adr->CR2 = 0;
647 }
648
649 // De-assert Chip Select?
650 if(flags & PX_SPI_FLAG_STOP)
651 {
652 // Take Chip Select High
653 PX_SPI_CFG_CS_HI(handle->cs_id);
654 }
655}
656
658 px_spi_baud_t baud)
659{
660 px_spi_per_t * spi_per;
661 SPI_TypeDef * spi_base_adr;
662 uint32_t spi_cr1_val;
663
664#if PX_LOG
665 // Check handle
666 if( (handle == NULL)
667 ||(handle->spi_per == NULL)
668 ||(handle->spi_per->spi_base_adr == NULL)
669 ||(handle->spi_per->open_counter == 0 ) )
670 {
671 PX_LOG_ASSERT(false);
672 return;
673 }
674#endif
675
676 // Set pointer to peripheral
677 spi_per = handle->spi_per;
678 // Get SPI peripheral base register address
679 spi_base_adr = spi_per->spi_base_adr;
680 PX_LOG_ASSERT(spi_base_adr != NULL);
681
682 // Disable peripheral
683 LL_SPI_Disable(spi_base_adr);
684
685 // Set new SPI clock divisor
686 spi_cr1_val = handle->spi_cr1_val;
687 spi_cr1_val &= ~SPI_CR1_BR_Msk;
688 spi_cr1_val |= baud << SPI_CR1_BR_Pos;
689 // Store new SPI clock divisor
690 handle->spi_cr1_val = spi_cr1_val;
691 // Set SPI Control Register 1 value
692 spi_base_adr->CR1 = spi_cr1_val;
693
694 // Enable peripheral
695 LL_SPI_Enable(spi_base_adr);
696}
697
699{
700 uint32_t actual_baud_hz;
701 px_spi_baud_t baud;
702
703 // Start with a divisor of 2
704 actual_baud_hz = PX_BOARD_PER_CLK_HZ >> 1;
706 // Closest baud equal or less than specified baud found?
707 while(actual_baud_hz > baud_hz)
708 {
709 // Divide by two
710 actual_baud_hz >>= 1;
711 // Stop if divisor reaches maximum
712 if(++baud == PX_SPI_BAUD_CLK_DIV_256)
713 {
714 break;
715 }
716 }
717 return baud;
718}
719
721{
722 return (((uint32_t)PX_BOARD_PER_CLK_HZ) >> (baud + 1));
723}
@ PX_SPI_DATA_ORDER_LSB
Data order is Least Significant Bit first (D0, D1, ..., D7)
Definition: px_spi.h:97
@ PX_SPI_BAUD_CLK_DIV_256
F_PCLK / 256.
Definition: px_spi.h:110
@ PX_SPI_BAUD_CLK_DIV_2
F_PCLK / 2.
Definition: px_spi.h:103
#define PX_BOARD_PER_CLK_HZ
Peripheral clock frequency in Hz.
Definition: px_board.h:49
#define NULL
NULL pointer.
Definition: px_defs.h:49
#define PX_LOG_NAME(name)
Macro to declare a log name string once for each file to reduce code size.
Definition: px_log.h:349
#define PX_LOG_W(format,...)
Macro to display a formatted WARNING message.
Definition: px_log.h:388
#define PX_LOG_E(format,...)
Macro to display a formatted ERROR message.
Definition: px_log.h:377
#define PX_LOG_ASSERT(expression)
Macro that will test an expression, and block indefinitely if false.
Definition: px_log.h:440
uint8_t cs_id
Chip Select GPIO ID.
Definition: px_spi.h:128
uint8_t mo_dummy_byte
Master Out dummy byte when data is read from Master In.
Definition: px_spi.h:130
uint32_t spi_cr1_val
SPI Control Register 1 value (SPI_CR1)
Definition: px_spi.h:129
struct px_spi_per_s * spi_per
SPI peripheral.
Definition: px_spi.h:127
bool px_spi_close(px_spi_handle_t *handle)
Close specified device.
Definition: px_spi.c:338
#define PX_SPI_CFG_CS_HI(cs_id)
Map PX_SPI_CFG_CS_HI() macro to px_board_spi_cs_hi() function.
px_spi_mode_t
Specify SPI Clock polarity / Clock phase.
Definition: px_spi.h:86
void px_spi_init(void)
Initialise SPI driver.
Definition: px_spi.c:162
bool px_spi_open2(px_spi_handle_t *handle, px_spi_nr_t spi_nr, uint8_t cs_id, px_spi_baud_t baud, px_spi_mode_t mode, px_spi_dord_t data_order, uint8_t mo_dummy_byte)
Open SPI peripheral using specified parameters.
Definition: px_spi.c:252
#define PX_SPI_CFG_DEFAULT_DATA_ORDER
Specify default SPI data order.
#define PX_SPI_CFG_DEFAULT_MODE
Specify default SPI mode (clock phase and polarity)
#define PX_SPI_CFG_CS_LO(cs_id)
Map PX_SPI_CFG_CS_LO() macro to px_board_spi_cs_lo() function.
px_spi_dord_t
Specify SPI Data order.
Definition: px_spi.h:95
#define PX_SPI_FLAG_START
Begin SPI transaction (take SPI slave's Chip Select line low)
Definition: px_spi.h:117
void px_spi_xc(px_spi_handle_t *handle, const void *data_wr, void *data_rd, size_t nr_of_bytes, uint8_t flags)
Perform an SPI exchange (write and read) transaction with an SPI slave.
Definition: px_spi.c:568
void px_spi_wr(px_spi_handle_t *handle, const void *data, size_t nr_of_bytes, uint8_t flags)
Perform an SPI write transaction with an SPI slave.
Definition: px_spi.c:396
void px_spi_rd(px_spi_handle_t *handle, void *data, size_t nr_of_bytes, uint8_t flags)
Perform an SPI read transaction with an SPI slave.
Definition: px_spi.c:479
px_spi_nr_t
Specify SPI peripheral number.
Definition: px_spi.h:79
void px_spi_change_baud(px_spi_handle_t *handle, px_spi_baud_t baud)
Change SPI peripheral baud.
Definition: px_spi.c:657
uint32_t px_spi_util_clk_div_to_baud_hz(px_spi_baud_t baud)
Calculate the actual BAUD (in Hz) from the specified division.
Definition: px_spi.c:720
#define PX_SPI_CFG_DEFAULT_BAUD
Specify default baud rate.
bool px_spi_open(px_spi_handle_t *handle, px_spi_nr_t spi_nr, uint8_t cs_id)
Open SPI peripheral using predefined (default) parameters.
Definition: px_spi.c:173
#define PX_SPI_FLAG_STOP
Finish SPI transaction (take SPI slave's Chip Select line high)
Definition: px_spi.h:119
px_spi_baud_t
Specify SPI baud rate as a ratio of the peripheral clock.
Definition: px_spi.h:102
px_spi_baud_t px_spi_util_baud_hz_to_clk_div(uint32_t baud_hz)
Calculate clock divisor that will yield closest frequency equal to or less than specified baud rate (...
Definition: px_spi.c:698
Define SPI handle.
Definition: px_spi.h:126
Internal data for each SPI peripheral.
Definition: px_spi.c:32
DMA_Channel_TypeDef * dma_rx_base_adr
DMA RX channel base register address.
Definition: px_spi.c:34
uint8_t open_counter
Number of open handles referencing peripheral.
Definition: px_spi.c:37
SPI_TypeDef * spi_base_adr
SPI peripheral base register address.
Definition: px_spi.c:33
DMA_Channel_TypeDef * dma_tx_base_adr
DMA TX channel base register address.
Definition: px_spi.c:35
px_spi_nr_t spi_nr
Peripheral number.
Definition: px_spi.c:36