cancel
Showing results for 
Search instead for 
Did you mean: 

Tricky Timer Input Capture -> DMA problem

LCE
Principal

Hello,

I'm using an STM32F767 to measure input frequencies, using 2 timers.

It's a bit tricky because these measurements must be synced to the SAI's audio sampling rate, AND input frequencies can be higher than the audio sampling rate.

It took me a while to figure out how to set up the timers to get there:

TIM3:

  • input capture Ch1 = signal to measure
  • clock source: internal clock (96MHz)
  • trigger: external
    • ETRF = audio sampling rate (e.g. 200 kHz (yes, that's unusual...))
    • slave mode reset: resets the CNT
    • triggers DMA to get the input capture register CCR1 to SRAM buffer

TIM4:

  • external clock mode 2
  • clock source ETRF = signal to measure
  • trigger: external
    • Ch1 = audio sampling rate
    • slave mode trigger
    • DMA triggered by input capture

From TIM3 I get the time of the latest input event (CCR1) within an audio sample (overcapture flag is willingly ignored in case of input freq > sampling rate),

from TIM4 I get the number of input events (CNT) from an audio sample.

So far so good, it works, that way I can measure input freq. with the timers' resolution, synced to the audio - most of the time...

BUT there's a problem in TIM3 with the IC / CCR1 to DMA:

Sometimes at input freq. > sampling rate when an input event happens very shortly after the audio sampling rate's counter reset and the DMA trigger, it seems that the DMA access to the CCR1 register comes too late, so that not the latest event of the last sample is captured, but already the first event of the new sample after the CNT reset.

Example:

  • timer counter max at 96 MHz and 200 kHz sampling rate = 480 (okay, 0..479)
  • input: 1 MHz -> 5 events within one 200kHz audio sample
  • CCR1 values from latest event within sample should be around 479 - (480 / 5) = 383 up to 479
  • but sometimes the CCR1 result in DMA is something between 0..3, so I think when the 1 MHz input signal's 1st event occurs very shortly after DMA trigger / CNT reset, that CCR1 is overwritten with that value

So I have checked the manuals & datasheets about any DMA timer specs and did not find anything.

Any explanations or ideas how to prevent that?

PS: I'm checking the STM32F7 to replace an existing FPGA design, and this is the first real problem...

10 REPLIES 10

I'm not sure your interpretation what you see is entriely correct. I would like to see this in sequence - a long sequence of captured times with a regular but non-synchronous input signal.

Given in your example the input signal is cca 1MHz, at cca 200MHz system clock it's cca 200 cycles for the DMA to store the captured value. That may seem sufficient - half-to-full-dozen or so should suffice - but we don't know about the rest of your system. What we know is that you have SAI running at relatively high clock.

Also, there are different paths from input to capture and from input to reset. This is deliberate so that ST can pull out the "PWM capture" trick where both reset and capture come from the same source: reset must be delayed otherwise the already-reset counter would be captured, i.e. period information would be lost. This delay is in my experience 2 timer clocks and is undocumented. But I am not sure how would this affect your case.

If this is the root of the problem, remedy would be to capture both audio sampling clock and the input signal on two channels - these, assuming same filter settings, hopefully have the same path/delays, without using the reset. The "fast" capture can be moved out using DMA as it is now; the "slow" from audio might probably be moved using interrupts.

But if the root of the problem is indeed DMA exhaustion, then this would exacerbate the problem further by CPU and DMA potentially contesting for access to the same TIM in some critical moment. In that case, you might want to decrease the pressure on DMA by combination of:

  • using TIMs on different APB bus, thus different DMA; but only if the other APB bus is not heavily used
  • storing captured values from TIM-DMA into RAM which is *not* used by other parts of the system (SRAM is partitioned for a reason)
  • shuffling the DMA priorities around so that the critical TIM has the highest
  • trying to optimize other processes potentially clogging up the system, e.g. making sure DMA serving SAI does have FIFO switched on but not using bursts, etc.

JW

LCE
Principal

Hello Jan,

thanks for your reply and taking a look at this!

> I would like to see this in sequence - a long sequence of captured times with a regular but non-synchronous input signal.

The timer signals are stored in 2 300 word buffers (using DMA's DBM), so with 600 timer values I can get a pretty good idea what's happening.

I check these buffers via UART, to prevent printing while the buffers are written by DMA they are copied to dedicated uart output buffers.

That's what I tried concerning the input signal:

a) test signal from PWM created from other timer - the lazy way. Depending on when which timer starts, the problem occurs in fixed and plausible intervals, due to being based on the same internal STM32 clock

b) same as a) but with "non-synced" test frequency, for example 558.140 kHz (TIM11 PWM: 192 MHz / PSC / ARR, with PSC = 7, ARR = 42), the problem is harder to find because of the slight drift between generator and STM clock, but it was there, I just had to check the complete buffer

c) test signal from generator: the problem is even harder to find than in b)

The buffer signal :

  • bits 31..16: counter value of events (rising edge of input signal)
    • counter value from TIM4 (see description in 1st post)
  • bits 15..0: input capture of last event in sample, at least that's what it should be...
    • counter value from TIM3 (see description in 1st post)

example buffer from b):

000: D004013B D007015F D00A0183 D00D01A7
004: D01001CB D0120143 D0150167 D018018B
008: D01B01AF D01E01D3 D020014B D023016F
012: D0260193 D02901B7 D02C01DB D02E0153
016: D0310177 D034019B D03701BF D0390003   <- 4th word "xxxx0003"
020: D03C015B D03F017F D04201A3 D04501C7
024: D047013F D04A0163 D04D0187 D05001AB
028: D05301CF D0550147 D058016B D05B018F
032: D05E01B3 D06101D7 D063014F D0660173
036: D0690197 D06C01BB D06E01DF D0710157
040: D074017B D077019F D07A01C3 D07C013B
044: D07F015F D0820183 D08501A7 D08801CB
048: D08A0143 D08D0167 D090018B D09301AF
052: D09601D3 D098014B D09B016F D09E0193
056: D0A101B7 D0A401DB D0A60153 D0A90177
060: D0AC019B D0AF01BF D0B10003 D0B4015B   <- 3rd word "xxxx0003"
064: D0B7017F D0BA01A3 D0BD01C7 D0BF013F

In lines 5 and 16 the error can be seen.

As there are at least 2 events per sample (558 kHz with 200 kHz sample rate), the event capture is the error,

because "0x0003" cannot be the last event if counter goes from 0..479

example calculation:

with values from line 5 before error: 0xD031 0177 and 0xD034 019B

0xD031: event counter value

0xD034: next event counter value

-> difference: 3 events

0x0177: capture value 1

0x019B: capture value 2

-> tick between 3 (4) events: 0x01E0 (480 ticks per sample) + cv2 -cv1 = 516

ticks / events -> 516 / 3 = 172 ticks per period

-> 96 MHz / 172 = 558.140 kHz -> exactly how it should be (see above)

But not with the 0x0003 capture values...

I have checked the DMA settings:

  • TIM3 and TIM4 use DMA 1, and right now these are the only peripherals using this DMA, SAI1A (only 1 for now) is using DMA2
    • might that still be relevant?
  • both TIM3/4 DMA streams:
    • highest priorities
    • highest interrupt priorities
    • use FIFOs

I just see that TIM4's DMA stream has higher priority due to having lower stream number, I'll check that...

LCE
Principal

DMA priority doesn't seem to have an influence on this problem.

LCE
Principal

What I already tried, but found that it took a little too much of CPU time, was close to what you mentioned:

  • an EXTI interrupt on the output of the the SAI sampling rate (Frame Sync here? I usually know it as "LRCK" from audio stereo ADCs)
  • get CCR value, reset counter
  • get event counter value
  • counting samples to set flag when buffer is full and can be copied for transmission, then switch buffer

That worked only with highest interrupt priority, and was called on every sample - using CPU time which I'd like to reserve for later use, so DMA preferred...

Aaaaah now it downed to me - in TIM3, you trigger DMA from source asynchronous to the capture!

Short answer is: don't. Find another way to achieve what you need.

Long explanation is, that DMA is not an instantaneous magic, it takes time until the transfer happens, and this time is jittery, highly dependent on many little details, most of them undocumented or practically unpredictible (e.g. bus collision with other busmasters).

Note, that in the "manual" version you have:

> get CCR value, reset counter

in this order. With DMA, you do it in the other order: first reset counter, then, after some time, you read CCR value. There might be another capture meantime - and that's exactly what you see.

As I've said, I'd recommend you to forget about what you've already done and start afresh. I don't know what's your intention so can't really help more at this point.

JW

LCE
Principal

Hello @Community member​ ,

thanks so far.

> Long explanation is, that DMA is not an instantaneous magic

I didn't expect magic, but I thought that maybe the DMA trigger would cause some smart logic to save the DMA periph. source register - here CCR - in some shadow register until DMA would fetch it.

Seemingly not, and looking at it again, this makes sense.

My goal is getting a frequency measurement synced to the audio sampling rate (so asynchronous to the STM32, although the STM32 is creating the audio sampling rate (great PLLs, by the way)), so for each audio sample I also get a "frequency measurement" sample,

with the info of :

  • number of events within last audio sampling period (that's TIM4->CNT, the MSBs of the above words)
  • time of latest event within last audio sampling period (that's TIM3->CCR, the LSBs of the above words)

So I was very close, but seemingly I have to get back to the time-consuming interrupt method, or find something else.

Pity, would have been so nice getting it done with DMA.

LCE
Principal

... and it's really sad that there are no DMA timing specs.

Need to check some other ARMs.

LCE
Principal

I tried the interrupt solution again, that doesn't work reliably either.

Here the problems seem to be that:

  • it takes a few cycles to get into the interrupt handler
  • 2 timer registers can't be read at the same time

Looks like I have to give up one of the 4 SAI blocks and connect an FPGA (already got that one with I2S)

for the frequency measurement.

Would have been so nice to get rid of that... and to have 8, not 6, audio channels.

Unless maybe there's another idea coming...

I would simply trigger DMA from capture itself, and then post-process the captured array to find the last-edge-within-audio-sample.

JW