7

I have an Arduino Nano with an 328P and need all 6 PWM pins.

Thus, I had to adjust the prescaler and WGM Mode of Timer0.

It is now in phase correct PWM mode with a prescaler of 1.

TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);
TCCR0B = _BV(CS00);

Now I need a working time calculation for other libraries, but since Timer0 had that duty everything is out of order now.

I tried adjusting the wiring.c

// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

to this

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(1 * 510))

But it's like I didn't change anything. (tested other settings that were changed so it was compiled anew )

Whole Code:

void setup() {

  // Set Timer 0, 1 and 2
  // Register A: Output A and B to non-inverted PWM and PWM mode to phase correct.
  // Register B: Pre Scaler to 1.
  TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);
  TCCR0B = _BV(CS00);

  TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
  TCCR1B = _BV(CS10);

  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(CS20);

  pinMode(8, OUTPUT);


}
void loop() {

  digitalWrite(8, LOW);
  delay(65000);
  digitalWrite(8, HIGH);
  delay(65000);

}
13
  • All of your settings are correct. My best guess if you maybe modified the wrong wiring.c. The best way to diagnose this is to go to File > Preferences and check the compilation checkbox under "Show verbose output during". Then click verify, copy the output into a text editor and search for wiring.c and see where it is pulling it from, then open the file and verify that your changes are there.
    – Jake C
    Commented Jul 17, 2015 at 6:52
  • When I clean the tmp folder and verify anew he shows: This. And that is also the wiring.c I corrected :(
    – Splitframe
    Commented Jul 17, 2015 at 7:04
  • I don't know what to say, by all means that should work. Another option would be to just scale the times yourself. You could wrap this in a macro a la #define SCALE_UP(x) x<<6 and then use it like such delay(SCALE_UP(1000))
    – Jake C
    Commented Jul 17, 2015 at 7:22
  • That is the weird thing, the real value seems to be really arbitrary. I tried two nanos and one Uno, both have the same results. And when I try to calculate the the value for delay I get 64000, but when I insert that it's only like 800ms instead of 1000ms. For testing purpose I already threw out all #includes it's just the Register change and digitalWrite and delay now.
    – Splitframe
    Commented Jul 17, 2015 at 7:29
  • How are you measuring? Do you have an oscilloscope by chance?
    – Jake C
    Commented Jul 17, 2015 at 7:40

4 Answers 4

4

Fixing the timekeeping functions with your PWM settings is not so simple. You should at least try to rewrite ISR(TIMER0_OVF_vect), micros(), and probably delay(). Here is why:

First, there is a rounding problem. Time is kept using two global variables:

volatile unsigned long timer0_millis;
static unsigned char timer0_fract;

The first one is what millis() returns. The second one keeps track of how much time has passed since the last full millisecond, and it does so in units of 8 µs. The two variables are incremented by ISR(TIMER0_OVF_vect) like this:

m += MILLIS_INC;  // temporary copy of timer0_millis
f += FRACT_INC;   // temporary copy of timer0_fract

On a normal Uno configuration, the ISR is called every 1024 µs. Then MILLIS_INC is 1 and FRACT_INC is 3. With your timer configuration, the ISR is called every 31.875 µs (510 cycles), then MILLIS_INC should be 0 and FRACT_INC should be 3.984375. But since we are dealing with integers, it will be rounded down to 3, and your millis() will tick about 25% too slow.

A simple fix would be to

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(1 * 512))

in order for FRACT_INC to be 4 and millis() to be 0.4% too fast. Or you could make timer0_fract a 16-bit variable and have it count clock cycles, just to avoid this error. Either option should fix millis(), but you still have a problem with micros().

micros() works by reading both timer0_overflow_count (incremented by 1 in the ISR) and the actual counter value. Since your counter is now going alternatively up and down, it will be harder to compute a microsecond count from these readings. Maybe you could take two consecutive readings of the counter, just to know whether it is going up or down...

And then there is delay(), which relies on micros(). If you fix micros(), delay() should work fine. If not, You could rewrite delay() to use millis() instead, which should be easy but you will loose some accuracy.

7
  • Thanks for the sophisticated answer! At this point also thanks to @jakec for discussing the problem with me!
    – Splitframe
    Commented Jul 17, 2015 at 8:51
  • Since your counter is now going alternatively up and down Why is the TCNT0 going up or down? It's going up and every time it overflow the ISR is called. Am I missing something? I tried to solve this by increasing the timer0_overflow_count once every 64 times only, and on the micros() function return the time as return ((m << 8) + (t % 64)) * (64 / clockCyclesPerMicrosecond()); So I do a mod on the TCNT0 counter as my Timer is ticking 64 times faster. But although the micros() seem to be ok delay() is still running faster and I'm not understanding why...
    – Lefteris
    Commented Nov 20, 2017 at 8:07
  • @Lefteris: The counter is going up and down because the OP configured it in “phase correct PWM” mode. Commented Nov 20, 2017 at 8:23
  • Correction on the above, modulo was wrong, I'm dividing by 64 on the return function now: return ((m << 8) + (t/64) ) * (64 / clockCyclesPerMicrosecond()); Still the problem remains with delays function which I don't understand
    – Lefteris
    Commented Nov 20, 2017 at 8:25
  • @EdgarBonet If I have understood correct, the TCTN0 register increases every CLK/64 tick. So it is only going up. It is reset every time it overflows and TIMER0_OVF_vect ISR is called. So still not understanding what phase correct PWM has to do with this.
    – Lefteris
    Commented Nov 20, 2017 at 8:29
0

You have set MICROSECONDS_PER_TIMER0_OVERFLOW to a proper value, however, this is only ever used by MILLIS_INC, which in turn is only ever used by millis(). This means that the other timing functions, such as micros(), delay(), delayMicroseconds() break when timer0 is changed. This is kind of a bug, and it may be fixed in a future version, but for now, the Arduino libraries are expecting you to leave timer0 alone. The best workaround is to only use millis() for your timing critical functions.

0

THIS IS AN INCOMPLETE ANSWER - Edgar knows what he's talking about, listen to him

I am currently working with this same thing but in an ATMega2560. This website helped me understand the millis() function better. The timer overflows every 510 counts (see datasheet, pg 123 for the atmega2560). I believe it is 510 and not 512 because it counts up and down, but does not repeat a count at the top or bottom - for example if you count starting from 1 up to 10 and then back down (without repeating 10) you will have counted 19 times, not 20. This counter starts at 0, counts to 255, and then back to 0, triggering an overflow before hitting 0 again. This is is 256 (count up including 0 and 255) + 254 (count down, not including 255 or 0) = 510. I wrote a python script to visualize the effect of these changes and trying to account for them. This counts to 10 million, so it takes time to run, but by the end, even with scaling, the error if simply adjusting millis() after the fact could be ~7 seconds.

#! /usr/bin/env python
import numpy as np

realT = np.zeros(10000000)
timer0_millis = np.zeros(10000000)
timer0_fract = np.zeros(10000000)
i=0

for i in range (1,10000000):
    timer0_millis[i] = timer0_millis[i-1] + 1
    timer0_fract[i] = timer0_fract[i-1] + 3
    realT[i] = i*31875
    if timer0_fract[i-1] >= 125:
        timer0_fract[i] = timer0_fract[i-1] - 125
        timer0_millis[i] = timer0_millis[i-1] + 1

adjusted = timer0_millis*510/(256*64)

print "after 100 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[99], realT[99]/1000000, adjusted[99])
print "after 1000 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[999], realT[999]/1000000, adjusted[999])
print "after 10000 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[9999], realT[9999]/1000000, adjusted[9999])
print "after 100000 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[99999], realT[99999]/1000000, adjusted[99999])
print "after 1000000 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[999999], realT[999999]/1000000, adjusted[999999])
print "after 10000000 timer interrupts millis() will read %d, actual millis is %.6f, adjusted millis is %.6f" % (timer0_millis[9999999], realT[9999999]/1000000, adjusted[9999999])
8
  • You wrote: “I believe in phase correct mode the timer overflows every 510 counts, not 512 as indicated by Edgar”. Please, read my answer again, and do not misquote me. Commented Oct 12, 2016 at 14:27
  • Sorry, I was refering to this line "#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(1 * 512))" - I have updated my comment, correct me if I am wrong here, I didn't intend to misquote
    – Nerbsie
    Commented Oct 13, 2016 at 12:13
  • There is a very good reason for having 512 instead of 510 in this line. Read my answer and you will understand. Commented Oct 13, 2016 at 12:28
  • Okay, so you are adding two timer counts, each being 62.5ns, to account for the error caused by the forced round-down?
    – Nerbsie
    Commented Oct 13, 2016 at 12:38
  • 1
    Edgar, if you would like to continue helping me I decided that instead of answering a question with questions of my own and potential misinformation I would ask a question
    – Nerbsie
    Commented Oct 13, 2016 at 14:48
0

Thanks to Edgar's answer and Nebsie's explaination on how the counter exactly works I could come up with my own implementation of how to rectify delay and micros() that - so far - seems to work fine on my Arduino Uno.

I'm not saying that this is the most precise implementation, especially I have my doubts on micros() if for example an timer overflow occours between reading t1 and t2, but it is one that so far for me works well.

First thing - according to Edgar's recommendation I defined:

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(1 * 512)) 

However this doesn't affect timing of micros() or delay().

Function micros() I changed to read two times from TCNT0 - to determine whether it is counting upwards or downwards. This is done with the "if (t1 > t2)" clause. The overflow count "m" is multiplied by 510, because the counter elapses after 510 steps. Then the calculated counter value "t" is added to this, divided by the number of clocks per Microsecond. (Note: Prescaler = 1, therefore no further multplication).

unsigned long micros() {
    unsigned long m;
    uint8_t oldSREG = SREG, t1, t2;
    uint16_t t;

    cli();
    m = timer0_overflow_count;
#if defined(TCNT0)
    t1 = TCNT0;
#elif defined(TCNT0L)
    t1 = TCNT0L;
#else
    #error TIMER 0 not defined
#endif

#if defined(TCNT0)
    t2 = TCNT0;
#elif defined(TCNT0L)
    t2 = TCNT0L;
#else
    #error TIMER 0 not defined
#endif

    if (t1 >= t2) {
        t = 510 - t2;       // counter running backwards
    } else {
        t = t2;             // counter running upwards
    }       

#ifdef TIFR0
    if ((TIFR0 & _BV(TOV0)) && (t2 > 1))        // if overflow flag is set -> increase m
        m++;
#else
    if ((TIFR & _BV(TOV0)) && (t2 > 1))
        m++;
#endif

    SREG = oldSREG;

    return ((m * 510) + t)  / clockCyclesPerMicrosecond();          
}

HOWEVER - this seems to work fine but I had issues to begin with (before editing this again). Due to the counter running much faster, every 268 seconds the unsigned long datatype of micros() overflows and starts from zero again. This led to an unwanted lockup of delay(), especially if long delay times, like in my case delays of one second per loop iterations are used. Therefore I also had to add an overflow detection to the delay() function in arduino wiring.c.

If the overflow occurs, timing might not be very precise. But since this is once overy 268 seconds it may be acceptable. So far with this change the delay function works fine again on my side.

void delay(unsigned long ms)
{
    uint32_t start = micros();
    uint32_t elapsed = 0;   

    while (ms > 0) {
        yield();
        while ( ms > 0 && (micros() - 1000) >= (start + elapsed)) {         
            ms--;
            elapsed += 1000;            
        }
        if( start > micros() ) {    // overflow detected
            start = micros();       // reset start
            elapsed = 0;            // reset elapsed
        }
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.