cancel
Showing results for 
Search instead for 
Did you mean: 

DMA+USART on STM32F407VG: TC Interrupt sometimes not triggered

niklas2
Associate II
Posted on July 11, 2017 at 17:53

Dear community,

I have run into a DMA-related issue while trying to implement an application for the STM32F407VG which receives data from a sensor via UART. The sensor sends a 162-byte data packet at 912600 baud every 10ms with pauses in between. Because the MCU is already quite busy, i want to use DMA. Since I have no way to stop/start the sensors' transmission, and the MCU may start up after the sensor and I want the whole system to be hot-pluggable, I have to find the beginning of each data packet. If I just enable the DMA for USART reception, configure it to 162 bytes and process the result in the transfer complete interrupt, I might end up receiving the end of a packet which is currently being transmitted and the beginning of the next packet as one data block.

To solve this, I use the USART's IDLE interrupt to recognize the pause between two packets and start the new DMA transfer. I use the DMA completion interrupt to determine that a packet has been completely received (and stored in memory). If the TC interrupt has not triggered before the next IDLE interrupt, I assume the data packet to be too short and discard it.

However, under certain conditions, sometimes (every few hundreds of packets), the DMA TC interrupt simply is not triggered. In the USART IDLE interrupt, I see that the DMA 'NDTR' register is zero (indicating a complete transfer), while 'LISR' is also zero (indicating that the TC interrupt is not pending). After resetting the DMA stream it works fine again for some time.

This behaviour seems to be influenced by the CPU load: I configured a timer to call an empty dummy ISR at a high frequency. The higher the frequency, the more TC interrupts go missing. This happens even though both the USART and the DMA interrupt have a higher priority than the timer interrupt.

Executing the code from RAM instead of flash results in many more missed interrupts too.

My suspicion is that this has something to do with a high (RAM) bus load, which would affect performance but should not lead to missing interrupts.

I have tried many variations but could not find a working constellation. I have uploaded an example code demonstrating the problem on

https://github.com/Erlkoenig90/INSReceive

. The interesting part is in the Src/main.c file (shortened):

static uint8_t rxBuffer [176] __attribute__ ((aligned (16)));
static DMA_Stream_TypeDef* const dmaStream = DMA1_Stream1;
static unsigned int state = 0;
static unsigned int printCounter = 0;
void USART3_IRQHandler (void) {
if (USART3->SR & USART_SR_IDLE) {
// Clear Interrupt via dummy read
(void) USART3->DR;
switch (state) {
case 0:
// First IDLE detected. Do nothing special.
break;
case 1:
// IDLE has been detected without a DMA interrupt. This should not happen.
printf ('Reception failed: NDTR = %lu, LISR = 0x%lx\n', dmaStream->NDTR, DMA1->LISR);
printCounter = 0;
break;
case 2:
// DMA Completion and IDLE has happened. A packet has been properly received.
if (rxBuffer [0] == 0xFA && rxBuffer [160] == 0x27 && rxBuffer [161] == 0x10) {
if (printCounter == 99) {
puts ('Received 100 packets OK');
printCounter = 0;
} else {
++printCounter;
}
} else
puts ('Packet received, but is invalid');
break;
}
state = 1;
// Disable DMA stream properly
dmaStream->CR = 0;
while ((dmaStream->CR & DMA_SxCR_EN) != 0);
// Clear Interrupt flags
DMA1->LIFCR = DMA_LIFCR_CTCIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1 | DMA_LIFCR_CFEIF1;
// Make sure buffer is correctly aligned
uint32_t mptr = (uint32_t) rxBuffer;
assert_param (mptr % 16 == 0);
// (Re-)Initialize DMA
dmaStream->PAR = (uint32_t) (&USART3->DR);
dmaStream->M0AR = mptr;
dmaStream->NDTR = 162;
dmaStream->FCR = DMA_SxFCR_DMDIS;
dmaStream->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_PL_0 | DMA_SxCR_MSIZE_1 | DMA_SxCR_MINC | DMA_SxCR_EN | DMA_SxCR_TCIE;
USART3->CR3 = USART_CR3_DMAR;
}
}
void DMA1_Stream1_IRQHandler (void) {
if (DMA1->LISR & DMA_LISR_TCIF1)
state = 2;
}
// Dummy Timer ISR to simulate high workload
void TIM8_UP_TIM13_IRQHandler () {
if (TIM13->SR & TIM_SR_UIF) {
TIM13->SR = ~TIM_SR_UIF;
__NOP ();
}
}
int main(void) {
// ... The usual initialization ...
puts ('Application startup');
// Configure interrupts
HAL_NVIC_SetPriority (TIM8_UP_TIM13_IRQn, 1, 1);
HAL_NVIC_EnableIRQ (TIM8_UP_TIM13_IRQn);
HAL_NVIC_SetPriority (USART3_IRQn, 0, 1);
HAL_NVIC_EnableIRQ (USART3_IRQn);
HAL_NVIC_SetPriority (DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ (DMA1_Stream1_IRQn);
// Enable peripheral clocks
RCC->APB1ENR |= RCC_APB1ENR_TIM13EN;
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
// Initialize TIM13 to call the interrupt at 50kHz, which simulates some dummy load
TIM13->PSC = 83;
TIM13->DIER = TIM_DIER_UIE;
TIM13->CR1 = 0;
TIM13->SR = ~TIM_SR_UIF;
TIM13->ARR = 19;
TIM13->CR1 = TIM_CR1_URS;
TIM13->EGR = TIM_EGR_UG;
TIM13->CR1 = TIM_CR1_CEN;
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM13_STOP;
// Initialize UsART3 for reception
USART3->BRR = 46;// 921600 Baud.
USART3->CR1 = USART_CR1_UE | USART_CR1_RE | USART_CR1_IDLEIE; // Only enable IDLE interrupt
while (1) {
__WFI ();
}
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

An example output is:

Application startup

Received 100 packets OK Received 100 packets OK Received 100 packets OK Reception failed: NDTR = 0, LISR = 0x0 Received 100 packets OK Received 100 packets OK Received 100 packets OK Reception failed: NDTR = 0, LISR = 0x0 Reception failed: NDTR = 0, LISR = 0x0 Received 100 packets OK Received 100 packets OK

The output is different each time the code is run. The problem also occurs when i remove the (slow) printf statements. The whole thing seems to be quite erratic and elusive...

Does anyone have an idea as to what I am doing wrong or maybe a workaround that still allows robust operation when the sensor and MCU are randomy hotplugged?

Thank you very much in advance!

#interrupt #issue #stm32f4 #dma #usart
13 REPLIES 13
Posted on July 22, 2017 at 21:22

Hello!!

the whole situation 'smells' frame error.

Did you check for this?

At speed 921,6k  when you have fPCLK8 MHZ  the actual speed is 888.88k

Add also the 10 ppm from ordinary crystals plus the jitter of an hi speeded main PLL.

Take a look at  RM  page 984, ....

Posted on July 22, 2017 at 21:54

The APB clock in question is 42 MHz, so the serial clock error isn't nearly that bad

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on July 24, 2017 at 11:38

... and it really does not matter for DMA...

JW

(PS in my test, as it was a loopback within the same UASRT, the actual baudrate is not the least relevant thus FE can't occur)

Posted on July 25, 2017 at 10:33

Wow, thanks for the detailed analysis! Sorry for the late answer, i was busy with race cars 😉 I had lost my patience debugging this much earlier... So next time I should read the manual closer...