1+ /**
2+ * @file spi_ll.c
3+ * @brief SPI hardware implementation for STM32H563xx
4+ *
5+ * This module handles initialization and operation of the SPI peripheral of
6+ * the STM32H5 microcontroller. It configures the hardware and dispatches SPI
7+ * interrupts to the hardware-independent SPI implementation.
8+ *
9+ * Implementation Details:
10+ * - Supports single SPI instance (SPI1)
11+ * - Configured for 8-bit data size
12+ * - DMA-based transmission and reception
13+ *
14+ * @author Tejas Garg
15+ * @date 2024-10-09
16+ */
17+
18+ #include "stm32h5xx_hal.h"
19+
20+ #include "util/error.h"
21+
22+ #include "spi_ll.h"
23+
24+ enum { SPI_IRQ_PRIO = 4 }; // NVIC priority for SPI interrupts
25+
26+ /* SPI instance structure */
27+ typedef struct {
28+ SPI_HandleTypeDef * hspi ;
29+ bool initialized ;
30+ bool tx_in_progress ;
31+ bool rx_in_progress ;
32+ void (* tx_complete_callback )(void );
33+ void (* rx_complete_callback )(void );
34+ } SPIInstance ;
35+
36+ /* HAL SPI handle */
37+ static SPI_HandleTypeDef g_hspi1 = { nullptr };
38+ static SPI_HandleTypeDef g_hspi2 = { nullptr };
39+
40+ /* DMA handles */
41+ static DMA_HandleTypeDef g_hdma_spi1_tx = { nullptr };
42+ static DMA_HandleTypeDef g_hdma_spi1_rx = { nullptr };
43+ static DMA_HandleTypeDef g_hdma_spi2_tx = { nullptr };
44+ static DMA_HandleTypeDef g_hdma_spi2_rx = { nullptr };
45+
46+ /* SPI instance configuration */
47+ static SPIInstance g_spi_instances [2 ] = {
48+ [SPI_BUS_0 ] = {
49+ .hspi = & g_hspi1 ,
50+ },
51+ [SPI_BUS_1 ] = {
52+ .hspi = & g_hspi2 ,
53+ },
54+ };
55+
56+ void HAL_SPI_MspInit (SPI_HandleTypeDef * hspi )
57+ {
58+ GPIO_InitTypeDef gpio_init = { 0 };
59+
60+ if (hspi -> Instance == SPI1 ) {
61+ /* SPI1 clock enable */
62+ __HAL_RCC_SPI1_CLK_ENABLE ();
63+ __HAL_RCC_GPIOA_CLK_ENABLE ();
64+ __HAL_RCC_GPDMA1_CLK_ENABLE ();
65+
66+ /* SPI1 GPIO Configuration: PA5=SCK, PA6=MISO, PA7=MOSI */
67+ gpio_init .Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 ;
68+ gpio_init .Mode = GPIO_MODE_AF_PP ;
69+ gpio_init .Pull = GPIO_NOPULL ;
70+ gpio_init .Speed = GPIO_SPEED_FREQ_HIGH ;
71+ gpio_init .Alternate = GPIO_AF5_SPI1 ;
72+ HAL_GPIO_Init (GPIOA , & gpio_init );
73+
74+ /* Configure DMA for TX */
75+ g_hdma_spi1_tx .Instance = GPDMA1_Channel6 ;
76+ g_hdma_spi1_tx .Init .Request = GPDMA1_REQUEST_SPI1_TX ;
77+ g_hdma_spi1_tx .Init .BlkHWRequest = DMA_BREQ_SINGLE_BURST ;
78+ g_hdma_spi1_tx .Init .Direction = DMA_MEMORY_TO_PERIPH ;
79+ g_hdma_spi1_tx .Init .SrcInc = DMA_SINC_INCREMENTED ;
80+ g_hdma_spi1_tx .Init .DestInc = DMA_DINC_FIXED ;
81+ g_hdma_spi1_tx .Init .SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE ;
82+ g_hdma_spi1_tx .Init .DestDataWidth = DMA_DEST_DATAWIDTH_BYTE ;
83+ g_hdma_spi1_tx .Init .Priority = DMA_LOW_PRIORITY_LOW_WEIGHT ;
84+ g_hdma_spi1_tx .Init .SrcBurstLength = 1 ;
85+ g_hdma_spi1_tx .Init .DestBurstLength = 1 ;
86+ g_hdma_spi1_tx .Init .TransferAllocatedPort =
87+ (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0 );
88+ g_hdma_spi1_tx .Init .TransferEventMode = DMA_TCEM_BLOCK_TRANSFER ;
89+ g_hdma_spi1_tx .Init .Mode = DMA_NORMAL ;
90+ HAL_DMA_Init (& g_hdma_spi1_tx );
91+ __HAL_LINKDMA (hspi , hdmatx , g_hdma_spi1_tx );
92+
93+ /* Configure DMA for RX */
94+ g_hdma_spi1_rx .Instance = GPDMA1_Channel7 ;
95+ g_hdma_spi1_rx .Init .Request = GPDMA1_REQUEST_SPI1_RX ;
96+ g_hdma_spi1_rx .Init .BlkHWRequest = DMA_BREQ_SINGLE_BURST ;
97+ g_hdma_spi1_rx .Init .Direction = DMA_PERIPH_TO_MEMORY ;
98+ g_hdma_spi1_rx .Init .SrcInc = DMA_SINC_FIXED ;
99+ g_hdma_spi1_rx .Init .DestInc = DMA_DINC_INCREMENTED ;
100+ g_hdma_spi1_rx .Init .SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE ;
101+ g_hdma_spi1_rx .Init .DestDataWidth = DMA_DEST_DATAWIDTH_BYTE ;
102+ g_hdma_spi1_rx .Init .Priority = DMA_LOW_PRIORITY_LOW_WEIGHT ;
103+ g_hdma_spi1_rx .Init .SrcBurstLength = 1 ;
104+ g_hdma_spi1_rx .Init .DestBurstLength = 1 ;
105+ g_hdma_spi1_rx .Init .TransferAllocatedPort =
106+ (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0 );
107+ g_hdma_spi1_rx .Init .TransferEventMode = DMA_TCEM_BLOCK_TRANSFER ;
108+ g_hdma_spi1_rx .Init .Mode = DMA_NORMAL ;
109+ HAL_DMA_Init (& g_hdma_spi1_rx );
110+ __HAL_LINKDMA (hspi , hdmarx , g_hdma_spi1_rx );
111+
112+ /* SPI1 interrupt init */
113+ HAL_NVIC_SetPriority (SPI1_IRQn , SPI_IRQ_PRIO , 1 );
114+ HAL_NVIC_EnableIRQ (SPI1_IRQn );
115+
116+ /* DMA interrupt init */
117+ HAL_NVIC_SetPriority (GPDMA1_Channel6_IRQn , SPI_IRQ_PRIO , 1 );
118+ HAL_NVIC_EnableIRQ (GPDMA1_Channel6_IRQn );
119+ HAL_NVIC_SetPriority (GPDMA1_Channel7_IRQn , SPI_IRQ_PRIO , 1 );
120+ HAL_NVIC_EnableIRQ (GPDMA1_Channel7_IRQn );
121+
122+ } else if (hspi -> Instance == SPI2 ) {
123+ /* SPI2 GPIO Configuration: PB13=SCK, PB14=MISO, PB15=MOSI */
124+ gpio_init .Pin = GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 ;
125+ gpio_init .Mode = GPIO_MODE_AF_PP ;
126+ gpio_init .Pull = GPIO_NOPULL ;
127+ gpio_init .Speed = GPIO_SPEED_FREQ_HIGH ;
128+ gpio_init .Alternate = GPIO_AF5_SPI2 ;
129+ HAL_GPIO_Init (GPIOB , & gpio_init );
130+
131+ /* Configure DMA for TX */
132+ g_hdma_spi2_tx .Instance = GPDMA1_Channel6 ;
133+ g_hdma_spi2_tx .Init .Request = GPDMA1_REQUEST_SPI2_TX ;
134+ g_hdma_spi2_tx .Init .BlkHWRequest = DMA_BREQ_SINGLE_BURST ;
135+ g_hdma_spi2_tx .Init .Direction = DMA_MEMORY_TO_PERIPH ;
136+ g_hdma_spi2_tx .Init .SrcInc = DMA_SINC_INCREMENTED ;
137+ g_hdma_spi2_tx .Init .DestInc = DMA_DINC_FIXED ;
138+ g_hdma_spi2_tx .Init .SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE ;
139+ g_hdma_spi2_tx .Init .DestDataWidth = DMA_DEST_DATAWIDTH_BYTE ;
140+ g_hdma_spi2_tx .Init .Priority = DMA_LOW_PRIORITY_LOW_WEIGHT ;
141+ g_hdma_spi2_tx .Init .SrcBurstLength = 1 ;
142+ g_hdma_spi2_tx .Init .DestBurstLength = 1 ;
143+ g_hdma_spi2_tx .Init .TransferAllocatedPort =
144+ (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0 );
145+ g_hdma_spi2_tx .Init .TransferEventMode = DMA_TCEM_BLOCK_TRANSFER ;
146+ g_hdma_spi2_tx .Init .Mode = DMA_NORMAL ;
147+ HAL_DMA_Init (& g_hdma_spi2_tx );
148+ __HAL_LINKDMA (hspi , hdmatx , g_hdma_spi2_tx );
149+
150+ /* Configure DMA for RX */
151+ g_hdma_spi2_rx .Instance = GPDMA1_Channel7 ;
152+ g_hdma_spi2_rx .Init .Request = GPDMA1_REQUEST_SPI2_RX ;
153+ g_hdma_spi2_rx .Init .BlkHWRequest = DMA_BREQ_SINGLE_BURST ;
154+ g_hdma_spi2_rx .Init .Direction = DMA_PERIPH_TO_MEMORY ;
155+ g_hdma_spi2_rx .Init .SrcInc = DMA_SINC_FIXED ;
156+ g_hdma_spi2_rx .Init .DestInc = DMA_DINC_INCREMENTED ;
157+ g_hdma_spi2_rx .Init .SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE ;
158+ g_hdma_spi2_rx .Init .DestDataWidth = DMA_DEST_DATAWIDTH_BYTE ;
159+ g_hdma_spi2_rx .Init .Priority = DMA_LOW_PRIORITY_LOW_WEIGHT ;
160+ g_hdma_spi2_rx .Init .SrcBurstLength = 1 ;
161+ g_hdma_spi2_rx .Init .DestBurstLength = 1 ;
162+ g_hdma_spi2_rx .Init .TransferAllocatedPort =
163+ (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0 );
164+ g_hdma_spi2_rx .Init .TransferEventMode = DMA_TCEM_BLOCK_TRANSFER ;
165+ g_hdma_spi2_rx .Init .Mode = DMA_NORMAL ;
166+ HAL_DMA_Init (& g_hdma_spi2_rx );
167+ __HAL_LINKDMA (hspi , hdmarx , g_hdma_spi2_rx );
168+
169+ /* SPI2 interrupt init */
170+ HAL_NVIC_SetPriority (SPI2_IRQn , SPI_IRQ_PRIO , 1 );
171+ HAL_NVIC_EnableIRQ (SPI2_IRQn );
172+
173+ /* DMA interrupt init */
174+ HAL_NVIC_SetPriority (GPDMA1_Channel6_IRQn , SPI_IRQ_PRIO , 1 );
175+ HAL_NVIC_EnableIRQ (GPDMA1_Channel6_IRQn );
176+ HAL_NVIC_SetPriority (GPDMA1_Channel7_IRQn , SPI_IRQ_PRIO , 1 );
177+ HAL_NVIC_EnableIRQ (GPDMA1_Channel7_IRQn );
178+ }
179+ }
180+
181+ /**
182+ * @brief Initialize the SPI peripheral.
183+ *
184+ * @param bus SPI bus instance to initialize
185+ */
186+ void SPI_LL_init (SPI_Bus bus )
187+ {
188+ if (bus >= SPI_BUS_COUNT ) {
189+ THROW (ERROR_INVALID_ARGUMENT );
190+ }
191+
192+ SPIInstance * instance = & g_spi_instances [bus ];
193+ if (instance -> initialized ) {
194+ return ;
195+ }
196+ SPI_TypeDef * spi_instance [SPI_BUS_COUNT ] = {
197+ [SPI_BUS_0 ] = SPI1 ,
198+ [SPI_BUS_1 ] = SPI2 ,
199+ };
200+
201+ /* Initialize SPI1 */
202+ instance -> hspi -> Instance = spi_instance [bus ];
203+
204+ instance -> hspi -> Init .Mode = SPI_MODE_MASTER ;
205+ instance -> hspi -> Init .Direction = SPI_DIRECTION_2LINES ;
206+ instance -> hspi -> Init .DataSize = SPI_DATASIZE_8BIT ;
207+ instance -> hspi -> Init .CLKPolarity = SPI_POLARITY_LOW ;
208+ instance -> hspi -> Init .CLKPhase = SPI_PHASE_1EDGE ;
209+ instance -> hspi -> Init .NSS = SPI_NSS_SOFT ;
210+ instance -> hspi -> Init .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16 ;
211+ instance -> hspi -> Init .FirstBit = SPI_FIRSTBIT_MSB ;
212+ instance -> hspi -> Init .TIMode = SPI_TIMODE_DISABLE ;
213+ instance -> hspi -> Init .CRCCalculation = SPI_CRCCALCULATION_DISABLE ;
214+
215+ if (HAL_SPI_Init (instance -> hspi ) != HAL_OK ) {
216+ THROW (ERROR_HARDWARE_FAULT );
217+ }
218+
219+ /* Configure NVIC for SPI interrupts */
220+ HAL_NVIC_SetPriority (SPI1_IRQn , SPI_IRQ_PRIO , 0 );
221+ HAL_NVIC_EnableIRQ (SPI1_IRQn );
222+
223+ instance -> tx_in_progress = false;
224+ instance -> rx_in_progress = false;
225+ instance -> tx_complete_callback = nullptr ;
226+ instance -> rx_complete_callback = nullptr ;
227+ instance -> initialized = true;
228+ }
229+
230+ /**
231+ * @brief Deinitialize the SPI peripheral.
232+ *
233+ * @param bus SPI bus instance to deinitialize
234+ */
235+ void SPI_LL_deinit (SPI_Bus bus )
236+ {
237+ if (bus >= SPI_BUS_COUNT ) {
238+ THROW (ERROR_INVALID_ARGUMENT );
239+ }
240+
241+ SPIInstance * instance = & g_spi_instances [bus ];
242+ if (!instance -> initialized ) {
243+ return ;
244+ }
245+
246+ /* Disable interrupts */
247+ if (bus == SPI_BUS_0 ) {
248+ HAL_NVIC_DisableIRQ (SPI1_IRQn );
249+ } else if (bus == SPI_BUS_1 ) {
250+ HAL_NVIC_DisableIRQ (SPI2_IRQn );
251+ }
252+
253+ /* Deinitialize SPI */
254+ if (HAL_SPI_DeInit (instance -> hspi ) != HAL_OK ) {
255+ THROW (ERROR_HARDWARE_FAULT );
256+ }
257+
258+ instance -> tx_in_progress = false;
259+ instance -> rx_in_progress = false;
260+ instance -> tx_complete_callback = nullptr ;
261+ instance -> rx_complete_callback = nullptr ;
262+ instance -> initialized = false;
263+ }
264+
265+ /**
266+ * @brief Transmit data over SPI.
267+ *
268+ * @param bus SPI bus instance to use
269+ * @param data Pointer to the data buffer to transmit
270+ * @param size Size of the data buffer
271+ */
272+ void SPI_LL_transmit (SPI_Bus bus , uint8_t const * data , size_t size )
273+ {
274+ if (bus >= SPI_BUS_COUNT ) {
275+ THROW (ERROR_INVALID_ARGUMENT );
276+ }
277+
278+ SPIInstance * instance = & g_spi_instances [bus ];
279+ if (!instance -> initialized ) {
280+ THROW (ERROR_INVALID_ARGUMENT );
281+ }
282+
283+ // Start the transmission
284+ HAL_SPI_Transmit (instance -> hspi , (uint8_t * )data , size , HAL_MAX_DELAY );
285+ }
286+ /**
287+ * @brief Receive data over SPI.
288+ *
289+ * @param bus SPI bus instance to use
290+ * @param data Pointer to the buffer to store received data
291+ * @param size Size of the data buffer
292+ */
293+
294+ void SPI_LL_receive (SPI_Bus bus , uint8_t * data , size_t size )
295+ {
296+ if (bus >= SPI_BUS_COUNT ) {
297+ THROW (ERROR_INVALID_ARGUMENT );
298+ }
299+
300+ SPIInstance * instance = & g_spi_instances [bus ];
301+ if (!instance -> initialized ) {
302+ THROW (ERROR_INVALID_ARGUMENT );
303+ }
304+
305+ // Start the reception
306+ HAL_SPI_Receive (instance -> hspi , data , size , HAL_MAX_DELAY );
307+ }
0 commit comments