cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F429ZI aborting DMA transfer on USART3 prematurely

TDK
Guru

On an STM32F429ZI, I'm using USART3_RX to receive information via DMA into a circular buffer. The buffer is 4096 bytes long.

If I send 1 or 2 bytes at a time (i.e. with a delay of ~100ms+ between transfers), things work correctly. By "work correctly" I mean the NDTR register is updated and the values I send appear in the circular buffer.

If I send 3+ bytes at once, the transfer gets aborted:

  • Only the first 3 bytes show up in the buffer, and NDTR is decremented by 3
  • The stream EN bit is changed from 1 (enabled) to 0 (disabled).
  • The DMA1->LISR:TCIF1 bit is set to 1.

In other words, it's as if the microcontroller thinks the transfer is complete. However, it's clearly not complete as NDTR != 0. And it only appears when I send 3+ consecutive bytes with no delay between them.

The values in the DMA register appear to be correctly configured, and do not change apart from EN and NDTR as noted above:

DMA1 stream index 1 detailed description:
- CR: SxCR_CHSEL=100 SxCR_PL=01 SxCR_MINC=1 SxCR_CIRC=1, SxEN=1
- NDTR: SxNDT=0x0FFD
- PAR: SxPAR_PA=0x40004804
- M0AR: SxM0AR_M0A=0x20008C58
- FCR: SxFCR_FS=100

This behavior is only affecting USART3. I can change everything over to USART2 or USART1 and it works as expected.

Any ideas? I'm running out of ideas. The behavior is the same with interrupts disabled.

I don't see anything in the errata sheet to explain this but I feel like I'm running into a bug in the silicon.

I've debugged this to the point where I am only monitoring the NDTR register in a loop, so it's very unlikely I'm touching the EN bit somewhere unknowingly.

 auto & ndtr = DMA1_Stream1->NDTR;
 uint32_t last_ndtr = ndtr;
 while (1) {
  uint32_t this_ndtr = ndtr;
  if (this_ndtr != last_ndtr) {
   last_ndtr = this_ndtr;
   LOG("\nNDTR=", last_ndtr);
  }
 }

If you feel a post has answered your question, please click "Accept as Solution".
1 ACCEPTED SOLUTION

Accepted Solutions

I don't know if this is your issue, and it looks like you might not be using HAL, but... *IF* you are using HAL, there is an option when generating the code to have UART errors halt DMA (i.e. framing error, noise, etc.). This may be enabled by default.

View solution in original post

4 REPLIES 4
TDK
Guru

Hmm, I boiled it down even more to just toggle an LED when the value changes. PB4 is tied to an LED.

  __disable_irq();
  auto & ndtr = stream->NDTR;
  uint32_t last_ndtr = ndtr;
  while (1) {
    uint32_t this_ndtr = ndtr;
    if (this_ndtr != last_ndtr) {
      if (this_ndtr % 2) {
        GPIOC->BSRR = GPIO_PIN_4;
      } else {
        GPIOC->BSRR = GPIO_PIN_4 << 16;
      }
      last_ndtr = this_ndtr;
    }
  }

With this loop, the behavior is as expected. However, if I don't disable interrupts, the unwanted behavior returns. That means something is happening in an interrupt handler somewhere to disable the stream. Still haven't tracked it down, but it gives me hope that it's just a software bug somewhere.

If you feel a post has answered your question, please click "Accept as Solution".

I don't know if this is your issue, and it looks like you might not be using HAL, but... *IF* you are using HAL, there is an option when generating the code to have UART errors halt DMA (i.e. framing error, noise, etc.). This may be enabled by default.

Bob, I really hope that is the explanation. I am using parts of HAL. That fits what I'm seeing. Thanks for the reply.

I had initially ruled out framing errors since (1) none of the flags are set and (2) the signal looks fine on the scope. But if the interrupt is silently clearing the flag, then of course I'm not going to see it change in the main thread.

If you feel a post has answered your question, please click "Accept as Solution".

Just to close this out, the issue was indeed caused by a framing error, which was cleared by the interrupt. The interrupt also stops the DMA transfer, which is not at all what I was expecting. In the future I'll add an error callback that will output an error message when a UART error is encountered.

The port was configured as no parity instead of even parity. I guess sending 1 or 2 bytes didn't trigger an error, but sending 3 at once did.

If you feel a post has answered your question, please click "Accept as Solution".