Timer Variation in State Machine

19 posts / 0 new
Author
Message

I'm writing a program that is intended to record data I'm capturing while performing an algorithm. I'd like to be able to maintain consistent write frequency between the different states of the high-level state machine but getting minor deviations in frequency.

Each state of course has a different number of instructions so I've created a timer in each state that I wait to expire before writing the data as the means to maintain the write frequency between states.

The simplified architecture is as follows:

State 1:

while(in state 1){

// Gather data

// Perform algorithm, if algorithm is completed this will exit the while loop in the state and return to the next state

// Use timer to maintain consistent write frequency - actual timer code below

while((TIFR & 0x04) == 0);            // wait here until timer expires (the timer was started before entering state 1)
TCNT0 = 0x00;                              // Reset counter, starting timer again immediately
TIFR |= (1<<OCF0B);                   // clear the flag

// write data

}

State 2:

// In this state I just wait for a second timer (timer1) to expire before moving to the next state, all while continuing to record data

// Start timer1

while(Timer1_Expired == false){

// gather data

// wait for timer0 to expire to maintain consistent write frequency - actual timer code below

while((TIFR & 0x04) == 0);            // wait here until timer expires
TCNT0 = 0x00;                              // Reset counter, starting timer again immediately
TIFR |= (1<<OCF0B);                   // clear the flag

// write data

}

return state3;

State 3:

// In this state I perform a few unrelated actions and then stay in a loop until the end of available memory, continuing to sample and record data

// gather data

// wait for timer0 to expire in order to maintain consistent write frequency - actual timer code below

while((TIFR & 0x04) == 0);            // wait here until timer expires
TCNT0 = 0x00;                              // Reset counter, starting timer again immediately
TIFR |= (1<<OCF0B);                   // clear the flag

// write data

}

There is no variation in each state, but there is variation between states.

The frequency in state 1 is 48.31kHz

The frequency in state 2 is 47.95kHz

The frequency in state 3 is 48.48kHz

Any thoughts or guidance would be greatly appreciated.

Thanks!

This topic has a solution.
Last Edited: Wed. Jun 22, 2022 - 11:18 PM

while((TIFR & 0x04) == 0);            // wait here until timer expires

Even a check like this will have some variation...how much are you talking about?

Instead maybe set up a timer IRQ tick, no polling.

What kind of freq counter are you using for these measurements & exactly how are you measuring?

You realize the difference between 48.31 & 48.48 KHz is only 73ns.

With an IRQ ticking at 48.000 KHz, and calling state handler, each will run at the exact same overall average rate.

timer IRQ handler: {

if thestate==0  ... dostate0()

else if  thestate==1  ...dostate1()

else if thestate==2 ...dostate2()

)

int8_t dostate0() {

blah

blah

thestate =2  ..state 0 might go to state 2 under certain conditions

return 2

blah

thestate =6   ..state 0 might go to state 6 under certain conditions

return 6

}

variables shared with IRQ (thestate) must be declared volatile

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Thu. Jun 16, 2022 - 07:05 PM
```State 1:
Do state 1 stuff
break
State 2:
Do state 2 stuff
break
State 3:
Do state 3 stuff
break

SLEEP

TIMER_ISR
Write stuff in here```

^^^this guarantees a clock cycle accurate start to the data writing routine.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

I think you need to tell us more about write_data(). Some of your variation might come from there.

For MCU clock cycle precision, you simply can't use interrupts. There is just too much variation. There is different latency just depending on the (clock cycle) variation of the instructions. The interrupt process will wait until the current instruction is finished. The amount of possible wait time depends on whether the current (machine) instruction takes one, two, or three clock cycles.

On top of that, if you have more than one interrupt service, each interrupt will wait until the current one is finished, unless you use one of the newer devices with priority interrupts.

This is not to say that the precision you want is impossible. It probably does mean that you cannot get it in the way you were thinking. For example, the event system and configurable logic (CCL) might be a way around this problem but this requires using one of the newer chips.

Jim

Until Black Lives Matter, we do not have "All Lives Matter"!

ka7ehk wrote:

For MCU clock cycle precision, you simply can't use interrupts. ....

Unless you use my technique in#3. The ISR response time to wake up from sleep is fixed, which means that  you can reliably hit the top of the ISR in a known repeatable time.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

This reply has been marked as the solution.

My habit for such things is to leave TCNT0 alone

and increment its compare match value.

That gets one a fairly precise average period,

but there can be some jitter.

Moderation in all things. -- ancient proverb

Depends on the OP needs of "frequency":

The frequency in state 2 is 47.95kHz

The frequency in state 3 is 48.48kHz

In 10 seconds that is a big difference (5300) in the number of writes.

If he simply sets an interrupt of 48.00 KHZ for writing (or maybe 96 KHz), the difference will be zero.  In that case the avg freq (writes/sec) match.

He still hasn't said how he is measuring these freqs.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

OP has not said a bunch of things, like:

(1) What is the minimum sample rate or, equivalently, the minimum number of samples per sine cycle? At close to 48KHz, you will have problems getting very many samples per cycle (with an MCU that is clocked at low MHz rates), which will lead to THD and frequency resolution problems.

(2) What is the allowed THD?

(3) Are the frequencies fixed or do they have to be adjustable?

(4) Are the frequencies generated at the same time, or one at a time?

(5) Are the signals totally independent or are they combined into a single signal like DTMF is?

(6) How accurate do the frequencies have to be? For example, could 48.48KHz really be 48.475KHz? Note: there is no common divisor for the 3 given frequencies, so at least one will have an inherent error, maybe 2.

Jim

Until Black Lives Matter, we do not have "All Lives Matter"!

Last Edited: Sat. Jun 18, 2022 - 02:37 AM

What is the allowed THD?

I didn't read it as he wanted to generate any kind of signal, he just wants to record xx readings per second of several different things at the same rate.  At least that was my thought.

Not sure why they bother to ask if they show no interest, maybe delete their account if no reply.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

josdye19 wrote:

The frequency in state 1 is 48.31kHz

The frequency in state 2 is 47.95kHz

The frequency in state 3 is 48.48kHz

ka7ehk wrote:

(2) What is the allowed THD?

Ha - It just clicked into place. Not that the OP has told us, of course; but 48KHz is the sampling frequency used in professional audio.

Products in that workspace need jitter down in the picoseconds region.

You need a 32-bit Atmel SAM device not a simple 8-bitter..

N.Winterbottom wrote:
Ha - It just clicked into place. Not that the OP has told us, of course; but 48KHz is the sampling frequency used in professional audio.

Products in that workspace need jitter down in the picoseconds region.

Nanoseconds would be easier to believe.

48KHz corresponds to a 21 microsecond period.  A picosecond is a millionth of a microsecond.

Moderation in all things. -- ancient proverb

I can't find where I originally read this but the subject is fairly widespread.

A manufacturer's blog claiming 20ps is audible, so take this one with a small pinch of salt.

https://benchmarkmedia.com/blogs/application_notes/13124137-the-unique-evils-of-digital-audio-and-how-to-defeat-them

A more technical article with O'scope traces.

http://audio-probe.com/en/documentation/clock-jitter-and-audio-quality

By the way:
Line  TIFR |= (1<<OCF0B);  is bad because that also clear all other flags that are set ... you can write TIFR = TIFR and it will  do the same (with a very rare exception).

you can write TIFR = TIFR

That makes no sense, how would you choose whether to clear OCF0B versus OCF0A?

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Each flag is cleared by writing one on its bit location
TIFR = TIFR clears all flags that are set. For example if TIFR contains value 0b00000011. You read that value and write it back to TIFR - you are clearing lower two bits. In any case, it will end with cleared TIFR.
TIFR = 0xFF do the same, clear all flags and result is cleared TIFR (clearing flags that are not set has no meaning)
TIFR |= (1<<OCF0B) do the same as TIFR = TIFR, because if "OCF0B flag was set, it would be cleared also by any previous variants (TIFR = TIFR, or TIFR = 0xFF). If OCF0B flag was cleared, then clearing it has no meaning. Result is again, cleared whole TIFR.

TIFR = (1<<OCF0B) is the righ way how to clear only OCF0B

Sorry for the slow response, and I appreciate all the input! I've been burying myself in the lab trying to implement some of these ideas.

I believe I have the solution I was looking for to this problem as suggested here:

skeeve wrote:

My habit for such things is to leave TCNT0 alone

and increment its compare match value.

That gets one a fairly precise average period,

but there can be some jitter.

Using CTC mode on the timer and letting it continually run in the background keeps a consistent average period in all states. There does appear to be minor jitter sample-to-sample, but keeps the average frequency consistent which is what I was after.

I'm recording data for around 2 seconds, so the minor deviations do really add up.

I'm measuring frequency by setting pins high and low and monitoring on the scope, but also in post processing by verifying my recorded data matches the input data I gather on the scope. The post processing is what really gives me the best idea of average frequency.

The frequency requirement is not particularly well defined at this point, but analysis at this point suggests that > 45kHz is best for the algorithm to perform as needed. The priority requirement is ultimately the algorithm so a solution that drops be below that rate is less than ideal.

I've attempted to use the ISR but it seems to slow everything down too much as it takes about 23 instructions just to get in and out.

avrcandies wrote:
Not sure why they bother to ask if they show no interest, maybe delete their account if no reply.

Please don't delete me, I'm new here and this was very helpful.

Thanks again!

josdye19 wrote:
Using CTC mode on the timer and letting it continually run in the background keeps a consistent average period in all states. There does appear to be minor jitter sample-to-sample, but keeps the average frequency consistent which is what I was after.
If, as seems to be the case, the timer can be dedicated, CTC mode is certainly best.

Just reducing the amount of code is an improvement.

In my work, the timer was often not dedicated and normal mode was pretty much mandatory.

Edit: Jitter-free can be done.

One must read the timer to discover the required compensation.

Edit: see above

Moderation in all things. -- ancient proverb

Last Edited: Thu. Jun 23, 2022 - 06:39 PM