STM32F103C8 SPI DMA hangs at 1 byte remaining in RX channel
Hi there,
I'm using an STM32F103C8 and using the SPI1 interface with software CS together with DMA1 Ch2 and 3 (RX and TX, respectively) in master mode. Here is how I init my transactions:
DMA_ClearFlag(DMA1_FLAG_TC3 | DMA1_FLAG_TE3 | DMA1_FLAG_HT3 | DMA1_FLAG_TC2 | DMA1_FLAG_TE2 | DMA1_FLAG_HT2);
DMA_Channel_TypeDef *dma_channel_rx = DMA1_Channel2;
DMA_Channel_TypeDef *dma_channel_tx = DMA1_Channel3;
DMA_Init(dma_channel_rx, &(DMA_InitTypeDef){
.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR),
.DMA_MemoryBaseAddr = (uint32_t)(vdata),
.DMA_DIR = DMA_DIR_PeripheralSRC,
.DMA_BufferSize = length,
.DMA_PeripheralInc = DMA_PeripheralInc_Disable,
.DMA_MemoryInc = DMA_MemoryInc_Enable,
.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte,
.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte,
.DMA_Mode = DMA_Mode_Normal,
.DMA_Priority = DMA_Priority_Medium,
.DMA_M2M = DMA_M2M_Disable,
});
DMA_Init(dma_channel_tx, &(DMA_InitTypeDef){
.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR),
.DMA_MemoryBaseAddr = (uint32_t)(vdata),
.DMA_DIR = DMA_DIR_PeripheralDST,
.DMA_BufferSize = length,
.DMA_PeripheralInc = DMA_PeripheralInc_Disable,
.DMA_MemoryInc = DMA_MemoryInc_Enable,
.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte,
.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte,
.DMA_Mode = DMA_Mode_Normal,
.DMA_Priority = DMA_Priority_Medium,
.DMA_M2M = DMA_M2M_Disable,
});
DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
DMA_Cmd(dma_channel_tx, ENABLE);
DMA_Cmd(dma_channel_rx, ENABLE);
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);Then, I have two ISRs:
void DMA1_Channel2_Handler(void) {
DMA_Channel_TypeDef *dma_channel_rx = DMA1_Channel2;
if (DMA_GetITStatus(DMA1_IT_TC2)) {
DMA_ClearITPendingBit(DMA1_IT_TC2);
DMA_Cmd(dma_channel_rx, DISABLE);
w25qxx_cs_set_inactive();
}
}
void DMA1_Channel3_Handler(void) {
DMA_Channel_TypeDef *dma_channel_tx = DMA1_Channel3;
if (DMA_GetITStatus(DMA1_IT_TC3)) {
DMA_ClearITPendingBit(DMA1_IT_TC3);
DMA_Cmd(dma_channel_tx, DISABLE);
}
}This works *most* of the time. But at seemingly random intervals, the DMA just "hangs" with the TX interrupt fired and completed, but the RX interrupt never fired and 1 byte of read "hanging" in the queue.
I'm suspecting this happens when TX is complete (last byte has been transferred to the SPI data register, but not yet shifted out), and somehow the peripheral is then therefore stopped (causing the last byte of RX never to cocur). When I debug in the "hanged" state, these are some interesting register values:
DMA1 Interrupt Status (ISR): 00000550
DMA1_Channel2 (SPI1 RX): CCR 00001083 enabled 1 bytes remain
DMA1_Channel3 (SPI1 TX): CCR 00001092 disabled 0 bytes remainI've also noticed the DMA error flag gets set. However, neither is any access non-aligned (bytewise transfers) nor is the memory ever invalid. The reference manual does not give more indication as to what could lead to the error flag in this case.
What is the correct way to instrument the DMA for SPI as I want it without this race condition?
Thanks, Joe