Electronic – Why does DMA send the first byte as soon as it is enabled

dmastm32video

I'm using DMA to send data to GPIOF which is configured as a parallel bus. DMA is being driven by update events on TIM8. TIM8 is configured in slave mode and starts on an output compare event from TIM1. And TIM1 runs in input mode, being reset by a falling edge on pin A9, and capturing the width and duty cycle of the pulse on A9.

TIM1 also raises an interrupt on input capture events. Then I check the width and duty cycle of the last pulse to determine whether I am currently in active video or not, and re-enable DMA for the next scanline if I am.

For context, this is for a video graphics application. GPIOF outputs pixels driven by a pixel clock (TIM8), in time with sync pulses from an external composite video source (pin A9).

Mostly this all works like clockwork. But I noticed that when I enable DMA in the interrupt, a single byte gets sent to GPIOF by DMA immediately. The rest of the bytes in the scanline buffer are sent at the expected time, when TIM8 starts running.

Here is a logic analyzer session showing what happens. CSYNC is the pulse going to pin A9. PCLK is a pin being toggled by TIM8. PIXEL0 is pin 0 on GPIOF. The first black band is (roughly) the time when the interrupt occurs and DMA is enabled. The second black band is when TIM8 is gated on by an output compare on TIM1.

Logic Analyzer Session

What is the reason that PIXEL0 turns on shortly after DMA is enabled, causing a very wide pulse which continues until PCLK starts?

Best Answer

I found the answer to this some time ago, posting it here in the hope it will help others.

The reason 1 byte is sent immediately on enabling DMA, is because a DMA request (DRQ) is still active from the last time that TIM8 ran.

TIM8 keeps running and raising DRQ signals, even after all the bytes in the buffer have been written and DMA stops. TIM8 is gated by TIM1, and only stops when TIM1 is reset.

Before enabling DMA, the previously-activated DRQ needs to be reset. There is no way to directly do this. Resetting TIM8 doesn't clear the DRQ. But you can clear it by clearing and then re-setting the appropriate bit in the DIER register.

The code to do so is below.

// Disable the DMA request line, then re-enable it to clear any pending request.
TIM8->DIER &= ~ TIM_DIER_UDE;
TIM8->DIER |= TIM_DIER_UDE;