2019-08-22 08:45 AM
I have an STM32F405, with circular i2s DMA set up and working fine. Interrupts fire perfectly, on every half and full completion (which is every 30ms, in my case).
I want to keep the interrupts firing every 30 ms, but shift when that happens, in order for the interrupts to roughly coincide (±100usec) with some external event.
NDTR is the counter which increments DMA memory, so I figured I could do a quick calculation of the needed shift, directly write that calculated value to NDTR, and the 30ms interrupts would continue, except shifted. Writing NDTR required disabling the stream first (from datasheet).
Unfortunately, writing to NDTR means that NDTR will forever henceforth be reloaded with that value (I suspect when the stream is re-enabled, the silicon latches NDTR into some inaccessible register. The datasheet says NDTR is "reloaded automatically with the previously programmed value"). So writing my calculated value to NDTR means the 30ms is not 30ms anymore.
So the question: How can I directly write to the DMA memory position counter?
2019-08-22 10:38 AM
Since the purpose of the DMA unit is to operate independent of the core I don't think you can accomplish what you want to do. That's why you can't directly access DMA while it's running.
In any case, to meet your timing no other interrupts or DMA transfers can occur, otherwise you'll see jitter from DMA bus contention plus latency injected from higher priority interrupts delaying the DMA HT event. It appears a timer triggering the start of DMA transfers would work with far greater reliability. Inserting the delays in your I2S DMA stream between HT events won't help your audio quality either.
Jack Peacock
2019-08-22 11:02 AM
Jack - thanks for the answer. I did as you suggested - I have a separate 1ms recurring timer interrupt whose handler figgers out if DMA adjustment is needed, and if so does HAL_I2S_DMAStop(..) and HAL_I2SEx_TransmitReceive_DMA(..). So I can twiddle DMA timing to within a ms, and it works.