2022-05-02 10:25 AM
To build a metronome, I plan on using on using a freeRTOS task running a while(1) with RTC timestamps to trigger clock ticks.
The freeRTOS task will run high priority but yield to other tasks which will do other work like toggle leds or other pins. Using the timestamps, I can track and compensate for 'drift' between beats. Do I need the RTC clock for this approach? I don't want the metronome beats to lag/drift over long duration.
Should I use a hardware timer with interrupt instead? I'm trying to determine the best approach before I proceed.
Update: I'm now thinking of using a hardware timer to interrupt every 2ms and increment global variable ms_elapased and checking that variable in my freeRTOS Task's while(1) to play each 'beat'.
Thanks for any ideas!
2022-05-02 04:10 PM
What wasn't clear from this topic?
On F407 you can even take a 32-bit timer (TIM2 or TIM5) and generate frequencies starting from 42 MHz down to some ridiculously low ones - a period of 39 days with APB1 bus still running at 42 MHz. You can use a timer output compare channel to output a pulse by hardware and have an absolute accuracy. With this approach your 76 BPM example can be set to 76,00000054 BPM, which is by far more accurate than even a very expensive stabilized oscillator, not even talking about a simple crystal oscillator. And trying it out takes less than 10 lines of code!
2022-05-03 05:11 AM
I was able to get a PWM below 1Hz with the help form your last reply.
I need to dynamically adjust the value of the timer presaller but it is not linear. Setting the prescaller from 50,000 to 65535 has little effect on the output BPM, but setting the prescaller from 1 to 200 has a drastic effect on output BPM. I have the timer's autoreload avlue set to 6000.
I was thinking of using a hardware timer to increment a tick every 2ms, and then have a freeRTOS Task polling the timestamp to see when to send out the next metronome tick. It is easy to set the BPM since it can be set in MS and not as an exponential prescale value. I could track the 'drift' and compensate on the next beat, but not sure if drift would be a concern when using interrupt to service the metronome tick.
I was also trying to determine the amount of drift in the clock pulse over long duration, for example several months.
2022-05-03 07:05 AM
Looks like you're looking for a serious hardware timer.
2022-05-03 07:33 AM
maybe its a bit overkill for a metronome
2022-05-03 08:01 AM
Nice atomic clock :) I had looked at that part a couple of weeks ago as an oscillator but had forgotten about it... it may be overkill but keeping it in mind now.
I suspect some of the C code I've been looking at is meant to run on a PC where usleep may not be accurate for each beat so the code needs to compensate for drift over long duration. If I use hardware timers ( even RTC ) than i suspect I may not get much drift. I would like to set it to let say '54BPM' and know it will keep that beat for long duration. I can't run it for months to test. I could use the RTC crystal as I'm sure that is precise enough for my requirements.
2022-05-03 01:31 PM
Just... what the hell? You are inventing a problem, which just doesn't exist!
In one of the other topics you said you need a range or 2-1000 BPM. The worst case resolution is at the higher rate. At 84 MHz the 1000 BPM period is 5'040'000 cycles, which means a 0,2 ppm resolution/accuracy. And even without prescaling a 32-bit timer will be able to go as low as 1,17 BPM with a 1/2^32 = 0,2 ppb resolution/accuracy.
TIM2->ARR = (TIM_FREQ * 60ul + BPM / 2) / BPM - 1;
That is all you need! I even included the correct rounding. Just take a look that the multiplication doesn't overflow a 32-bit range. With a maximum BPM of 1000, the timer frequency must not exceed 71'582'779 Hz. For example, use a prescaler /2 to get a frequency of 42 MHz. The ppm/ppb resolution values I wrote above will double, but those are insane numbers anyway!
2022-05-03 04:01 PM
Ok great, thank you for the additional info. I changed my timer from a 16 bit to 32bit ARR and am now able to get BPM as per your function.
2022-05-03 04:39 PM
I edited the original code - changed the constant 60 to 60ul, which changes it form an int to an unsigned long type and forces the whole expression to be calculated as a 32-bit unsigned type.
2022-05-03 05:33 PM
Ok, got it.
The only last remaining question is how much drift should I expect from the hardware timer if I run it for a couple of months? I'm more curious since what I have now is sufficiently precise. If I can easily reduce drift by using the RTC instead than I would use it. Still need to look into RTC. I'm guessing the long-term accuracy of the hardware timers is dependent on the crystal...
Thanks again for all the great info.!