Run an exact millis() timer...
WestfW opened this issue · 5 comments
Since the timer used for millis() (TIMERB3 on Uno WiFi2 and Every) is not shared with any functionality, couldn't it could be set up in "periodic" mode (instead of free-running with overflow interrupts), thereby producing an interrupt every one millisecond exactly, and removing the need for all the fancy logic in the ISR?
It sure can. I do just that. I also changed the TCA prescaler to /8. That way function micros gets a resolution of 1µs and all the PWMs are 8 times faster. 230 bytes of program memory is saved. Of course it loses compatibility with the Uno/Nano but at least for me that's a worthy sacrifice. While it works fine, I think I can also get rid of timer_overflow_count, but haven't worked that through yet.
Hi @tomalmy ,
would you try to describe that procedure for newbies like me?
I'm interested in optimising my code and learn.
Thank you
REVISION: A comment states that TCB3 is in the "default" periodic interrupt mode, however that is not the case, so it needs to be changed in the init function:
_timer->CTRLB = (TCB_CNTMODE_INT_gc); // Not really the default, so set it!
That it has worked before in 8-bit PWM mode is just a happy coincidence.
An interrupt occurs every TCB3.CCMP + 1 counter ticks. The count rate is set by the division factor in Timer/Counter A. The Arduino library sets this at divide by 64. While this can be changed (like I did) it does have repercussions in a number of places, so lets just consider leaving the division factor alone.
WIth a 16MHz clock, the /64 gives a 4µs count rate. So 250 counts give 1ms. Now find megaavr/1.8.6/variants/nona4809/timers.h. You will find the following 4 that configure the time tracker:
#define TIME_TRACKING_TIMER_PERIOD 0xFF
#define TIME_TRACKING_TICKS_PER_OVF (TIME_TRACKING_TIMER_PERIOD + 1) // Timer ticks per overflow of TCB3
#define TIME_TRACKING_TIMER_DIVIDER 64 // Clock divider for TCB0
#define TIME_TRACKING_CYCLES_PER_OVF (TIME_TRACKING_TICKS_PER_OVF * TIME_TRACKING_TIMER_DIVIDER)
Note that the second line and it's comment. From that we know that we want TIME_TRACKING_TICKS_PER_OVF to be 250. That means we have to change TIME_TRACKING_TIMER_PERIOD to be 249. Don't be concerned by the current definitions use of hexadecimal. That was just to show off that it would be an 8 bit value, which was important on the ATmega328P based Arduinos.
That's literally all you have to do. But the code for the timer/counter's interrupt service routine and that for the micros() function could now be simplified since the interrupt is exactly 1ms. See arduino-libraries/Servo#29 (comment) where I've also changed the division factor.
Would the code from MegaCoreX solve the problem?
MCUdude/MegaCoreX@298601b
Could it be "ported" to the megaavr core?
It looks like what I did, so I'd say yes. In my case I made it a conditional compilation, which can be selected from the Arduino IDE. This is perhaps needed in case maximum compatibility with existing code is desired. In the books I have published since the modification I always verify the example programs with the unmodified library as well as the modified one.