STM32G431 SPI & DMA interrupt issue
- February 4, 2022
- 14 replies
- 4789 views
On a STM32G431K8 I'm using the DMA1 to handle SPI1 data transfer (channel 1 for data receiption and channel 2 for data transmission).
I'm using STM32CubeMX for code generation, resulting in following SPI1 and DMA1 initialization:
/**
* @brief SPI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_SPI1_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* SPI1_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_SPI1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_BYTE);
/* USER CODE BEGIN SPI1_Init 1 */
// Enable DMA transfer complete/error interrupts
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_EnableNSSPulseMgt(SPI1);
/* USER CODE BEGIN SPI1_Init 2 */
LL_SPI_SetRxFIFOThreshold(SPI1, LL_SPI_RX_FIFO_TH_QUARTER);
/* USER CODE END SPI1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* Init with LL driver */
/* DMA controller clock enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMAMUX1);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel2_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Channel2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(DMA1_Channel2_IRQn);
}According to the chapter "Communication using DMA" of the Reference Manual, following code represents the SPI communication handling.
// Prepare SPI request data
au8SpiReqBuffer[0] = 0x80;
au8SpiReqBuffer[1] = 0x3D;
au8SpiReqBuffer[2] = 0x40;
au8SpiReqBuffer[3] = 0x10;
// Configure the DMA functional parameters for SPI transmission
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2,
(uint32_t)au8SpiReqBuffer,
LL_SPI_DMA_GetRegAddr(SPI1),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 4);
// Configure the DMA functional parameters for SPI reception
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,
LL_SPI_DMA_GetRegAddr(SPI1),
(uint32_t)au8SpiRespBuffer,
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 4);
// Send SPI request
// Reset transfer state
tSpiTransferState = TRANSFER_WAIT;
// Enable DMA RX Buffer
LL_SPI_EnableDMAReq_RX(SPI1);
// Enable DMA Channels Tx and Rx
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
// Enable DMA TX Buffer
LL_SPI_EnableDMAReq_TX(SPI1);
// Pull SPI CLK line low while selecting SPI slave
LL_GPIO_ResetOutputPin(SPI1_SCK_GPIO_Port, SPI1_SCK_Pin);
// Select SPI slave (pull slave select line low)
LL_GPIO_ResetOutputPin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin);
LL_SPI_Enable(SPI1);
// Wait for SPI transmission completed
while (TRANSFER_WAIT == tSpiTransferState)
{
;
}
while (LL_SPI_TX_FIFO_EMPTY != LL_SPI_GetTxFIFOLevel(SPI1))
{
;
}
while (0 != LL_SPI_IsActiveFlag_BSY(SPI1))
{
;
}
// Pull SPI CLK line low while de-selecting SPI slave
LL_SPI_Disable(SPI1);
LL_GPIO_ResetOutputPin(SPI1_SCK_GPIO_Port, SPI1_SCK_Pin);
// De-select SPI slave (pull slave select line high)
LL_GPIO_SetOutputPin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin);
// Disable DMA TX Buffer
LL_SPI_DisableDMAReq_TX(SPI1);
// Disable DMA RX Buffer
LL_SPI_DisableDMAReq_RX(SPI1);For getting the SPI tansmission/receiption completed, I'm using the DMA channel interrupts.
/**
* @brief This function handles DMA1 channel1 global interrupt.
*/
void DMA1_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
if (0 != LL_DMA_IsActiveFlag_TC1(DMA1))
{
LL_DMA_ClearFlag_TC1(DMA1);
// Disable DMA1 Rx Channel
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
tSpiTransferState = TRANSFER_COMPLETE;
}
else if (0 != LL_DMA_IsActiveFlag_TE1(DMA1))
{
LL_DMA_ClearFlag_TE1(DMA1);
tSpiTransferState = TRANSFER_ERROR;
}
/* USER CODE END DMA1_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
/* USER CODE END DMA1_Channel1_IRQn 1 */
}
/**
* @brief This function handles DMA1 channel2 global interrupt.
*/
void DMA1_Channel2_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel2_IRQn 0 */
if (0 != LL_DMA_IsActiveFlag_TC2(DMA1))
{
LL_DMA_ClearFlag_TC2(DMA1);
// Disable DMA2 Tx Channel
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
}
else if (0 != LL_DMA_IsActiveFlag_TE2(DMA1))
{
LL_DMA_ClearFlag_TE2(DMA1);
tSpiTransferState = TRANSFER_ERROR;
}
/* USER CODE END DMA1_Channel2_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel2_IRQn 1 */
/* USER CODE END DMA1_Channel2_IRQn 1 */
}My problem is that the program hangs up at the wait for SPI transmission completed
// Wait for SPI transmission completed
while (TRANSFER_WAIT == tSpiTransferState)
{
;
}The DMA interrupt handlers (neither channel 1 nor channel 2) are not called...
Nevertheless, if I halt the MCU, everything seems to be done correctly.
DMA channels are configured correctly (including the transmission complete interrupts enabled)

And the transmission complete flags on the DMA channels are set.
Also the SPI status register looks good.
And even the response buffer shows the actually received data.
The only strange thing is that the DMA interrupt handlers are not triggered...
Anyone with an idea what I'm missing?
Best Regards
PS: Attached you could find the overall STM32CubeIDE project.