Improve accuracy in micros() for nearly all clocks
My work on MicroCore begins with my typical timing accuracy improvements 😋 I touched most of the clocks and improved the multiplication. I also changed the comments so you can see how much my multiplication is more precise than before. I must admit that some very difficult frequencies need a bunch of additions and bitshifts. But since the ATTiny13 doesn't support multiplication in assembly, and therefore not in a single instruction, this should not matter since the attiny13 however supports bitshift and addition in a single instruction.
With this commit I also changed naming of the variables to adapt them to the default arduino naming.
EDIT: I did some size comparison tests and we might even safe a lot of space with this more accurate solution! I just added micros() to the loop() and compiled it (LTO disabled):
| Before commit | After commit | |
|---|---|---|
| 4.8MHz internal | 496 bytes | 418 bytes |
| 9.6MHz internal | 496 bytes | 372 bytes |
I've repeated it twice, since I couldn't believe this result at first 😄 Either I'm doing something wrong here (which I belive, since 100 bytes by just bitshifting?!) or this is hilarious.
I'm sorry, I totally forgot about this PR..
Here's a simple test sketch I've used for testing:
uint32_t previousMillis = 0;
const uint16_t interval = 1000;
void setup()
{
DDRB |= _BV(PB2);
}
void loop()
{
while(1)
{
uint32_t currentMillis = micros();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
PINB = _BV(PB2); // Toggle PB2
}
}
}
Let's focus on the 9.6 MHz clock for now. Here's some results from the sketch above. These measurements was very time consuming because of all the jitter (probably due to interrupts). Anyways here's the results!
| micros() (some jitter on higher numbers) | On time with LTO |
|---|---|
| 0 | 1.560 |
| 1 | 16.26 to 22.48 |
| 2 | 16.88 to 23.12 |
| 3 | 16.88 to 23.12 |
| 4 | 16.88 to 23.12 |
| 5 | 16.88 to 23.12 |
| 10 | 16.88 to 23.12 |
| 20 | 16.85 to 32.75 |
| 50 | 48.60 to 64.40 |
| 100 | 96.10 to 111.9 |
| 200 | 197.4 to 213.0 |
| 500 | 504.5 to 520.5 |
| 1000 | 1001 to 1017 |
Wow, looks like on 9.6MHz the code easily eats away 16 micros. From 50micros onwards it seems okay I guess. I will think about a solution for 0-50 micros and test my results. The jitter is kind of strange since the only interrupt that should happen is our timer0 overflow that we use for counting. Maybe it's also the inprecision of the internal oscillator. I will look into that :)
I did not use the internal 9.6 MHz oscillator for this test, because this will add irregularities. I used my trusty old signal generator running at 9.6 MHz instead.
Okay, alright. Then it is even more strange :smile: Will look into a solution.
Bear in mind that it's OK to trade of some accuracy in favor for code size. The new micros code occupied about 50 bytes more than the old one. A little more is expected of course.
IIRC I did not disable millis(), so this may be the the reason why I'm seeing all this jitter. This would not be a problem if millis where derived from micros.
I can do the same test with millis disabled this afternoon.
Sorry for forgetting this PR completely 😞 I did a new test with millis disabled. I'm still seeing the same jitter as before. And the lowest duty cycle I'm able to achieve is still 16.26 to 22.48.
The code below gives a very steady 5.00us pulse width. I doubt we can get it any lower than this by improving micros(). First I tried without addling cli(), but then I got some periodic jitter (just like before). I'm not seeing this jitter when interrupts are disabled. This tells me it's timer0 that's causing all this. I'm not sure it's possible to solve this jitter issue completely because of this. I'm sure an Arduino UNO will have the same jitter as well..
uint32_t previousMillis = 0;
uint32_t currentMillis = 0;
const uint16_t interval = 1;
void setup()
{
DDRB |= _BV(PB2);
cli();
}
void loop()
{
while(1)
{
++currentMillis;
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
PINB = _BV(PB2); // Toggle PB2
}
}
}