Skip to main content
deleted 140 characters in body
Source Link
 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

#ifdef TIFR0
    if ((TIFR0 & _BV(TOV0)) && (t1 < 255))      // if overflow flag is set -> increase m
        m++;
#else
    if ((TIFR & _BV(TOV0)) && (t1 < 255))
        m++;
#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 bework fine but somehow there is still a bug that I could not find out. If I leave the wiring.c delayhad issues to begin with (before editing this again) function untouched,. Due to the while loop runs into an overflow condititioncounter running much faster, because it seems thatevery 268 seconds the unsigned long datatype of micros() can also decreaseoverflows and starts from one interation to anotherzero again. This leads the second while clause while ( ms > 0 && (micros() - 1000) >= start) to overflow. I think this is dueled to microsan unwanted lockup of delay() not steadly increasing, it might fall back below its previous value for some interationespecially if long delay times, leading to a subtractionlike in my case delays of for example 984 - 1000 leading to the overflow conditionone second per loop iterations are used.

What Therefore I did is simply change the statementalso had to while ( ms > 0 && micros() >= (1000 + start)) which essentially is the same statement but much less proneadd an overflow detection to overflows. This did the trick for me and delay was running fine, now with the right timing() function in arduino wiring.c.

Here for your referenceIf the complete delay() function that togetheroverflow 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 revamped micors() gives more or less correct timings for phase correct PWM at 31kHzdelay 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) >= (1000start + startelapsed)) {         
            ms--;
            startelapsed += 1000;            
        }
        if( start > micros() ) {    // overflow detected
            start = micros();       // reset start
            elapsed = 0;            // reset elapsed
        }
    }
}
 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

#ifdef TIFR0
    if ((TIFR0 & _BV(TOV0)) && (t1 < 255))      // if overflow flag is set -> increase m
        m++;
#else
    if ((TIFR & _BV(TOV0)) && (t1 < 255))
        m++;
#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
    }       

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

HOWEVER - this seems to be fine but somehow there is still a bug that I could not find out. If I leave the wiring.c delay() function untouched, the while loop runs into an overflow conditition, because it seems that micros() can also decrease from one interation to another. This leads the second while clause while ( ms > 0 && (micros() - 1000) >= start) to overflow. I think this is due to micros() not steadly increasing, it might fall back below its previous value for some interation, leading to a subtraction of for example 984 - 1000 leading to the overflow condition.

What I did is simply change the statement to while ( ms > 0 && micros() >= (1000 + start)) which essentially is the same statement but much less prone to overflows. This did the trick for me and delay was running fine, now with the right timing.

Here for your reference the complete delay() function that together with the revamped micors() gives more or less correct timings for phase correct PWM at 31kHz.

void delay(unsigned long ms)
{
    uint32_t start = micros();
    
    while (ms > 0) {
        yield();
        while ( ms > 0 && micros() >= (1000 + start)) {         
            ms--;
            start += 1000;          
        }
    }
}
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
        }
    }
}
Source Link

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

#ifdef TIFR0
    if ((TIFR0 & _BV(TOV0)) && (t1 < 255))      // if overflow flag is set -> increase m
        m++;
#else
    if ((TIFR & _BV(TOV0)) && (t1 < 255))
        m++;
#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
    }       

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

HOWEVER - this seems to be fine but somehow there is still a bug that I could not find out. If I leave the wiring.c delay() function untouched, the while loop runs into an overflow conditition, because it seems that micros() can also decrease from one interation to another. This leads the second while clause while ( ms > 0 && (micros() - 1000) >= start) to overflow. I think this is due to micros() not steadly increasing, it might fall back below its previous value for some interation, leading to a subtraction of for example 984 - 1000 leading to the overflow condition.

What I did is simply change the statement to while ( ms > 0 && micros() >= (1000 + start)) which essentially is the same statement but much less prone to overflows. This did the trick for me and delay was running fine, now with the right timing.

Here for your reference the complete delay() function that together with the revamped micors() gives more or less correct timings for phase correct PWM at 31kHz.

void delay(unsigned long ms)
{
    uint32_t start = micros();
    
    while (ms > 0) {
        yield();
        while ( ms > 0 && micros() >= (1000 + start)) {         
            ms--;
            start += 1000;          
        }
    }
}