I have had to use Timer0 with phase correct PWM and a prescaler of 1. This results in a 32khz PWM frequency and obviously impacts millis, delay and micros. (Timer1 and Timer 2 are also in use so changing to those is not an option)
millis() is trivial to work around - multiply by 32 as the TOV0 interrupt handler is called 32 times more frequently (subject to overflow of course)
micros() is somewhat trickier - as normally it is the Timer0 overflow count (as used in millis() though shifted in this case) plus the current value of TCNT0. That works great when TCNT0 is increasing only and resetting to 0 after MAX - but in phase correct PWM it could be going upwards or downwards.
I think what I need to do is find the direction the Timer is currently using - but pouring over the datasheet for the ATmega328P I can't seem to find a way to do so...
Is there a way of reading this, eg from a register I can interrogate for it?
Alternatively can anyone else think of a way to get microsecond (or close - 10 microsecond would be good enough) resolution when all timers are in phase correct mode?
I'm considering a fall-back of reading TCNT0 twice and deriving it from that - with a prescaler of 1I am assuming it is not possible to read it twice faster than it could change - however I'd prefer a neater solution if there is one.
2 Answers 2
I decided to go with the inspect method and modified wiring.c accordingly - may not fit everyone's use case but solves mine (along with a couple of tweaks changing the #define's to volatile variables and adding a setter for them.
micros updated to:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t1, t2;
cli();
m = timer0_overflow_count;
#if defined(TCNT0)
t1 = TCNT0;
t2 = TCNT0;
#elif defined(TCNT0L)
t1 = TCNT0L;
t2 = TCNT0L;
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t1 == timer0_phase_correct ? 0 : 255))
m++;
#else
if ((TIFR & _BV(TOV0)) && (t1 == timer0_phase_correct ? 0 : 255))
m++;
#endif
SREG = oldSREG;
if (timer0_phase_correct && t1 > t2){
t2 = 510 - t2;
}
return (m * us_per_timer0_overflow) + (clockCyclesToMicroseconds(timer0_prescale * t2));
}
-
Note: that won't work with just that change - but changing wiring.c is not a topic I want to go into discussion of and anyone capable of that can easily infer the other changes from what I've put above. Here be dragons.Simm– Simm06/07/2021 20:43:26Commented Jun 7, 2021 at 20:43
-
Where is
timer0_phase_correct
defined?user172650– user17265002/06/2023 10:27:33Commented Feb 6, 2023 at 10:27
This is my solution, based on Simm's. Tested and working on arduino nano with Timer0 set to phase correct PWM, f=31372.55 Hz
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t, t2;
cli();
m = timer0_overflow_count;
t = TCNT0;
t2 = TCNT0;
if (t > t2 || (t == t2 && t > 127)) {
//value read when decrement counter
t = 254 - ((t2 - 1) >> 1);
} else {
//value read when increment counter
if (TIFR0 & _BV(TOV0)){
m++;
}
t = t2 >> 1;
}
SREG = oldSREG;
return ((m << 5) - (m >> 3) + (t >> 3));
}
micros()
?OC0x
register. I don't have an atmega328p at hand now, but you can test it out. The relevant part in the datasheet:In non-inverting compare output mode, the output compare (OC0x) is cleared on the compare match between TCNT0 and OCR0x while upcounting, and set on the compare match while downcounting. In inverting output compare mode, the operation is inverted.
(header14.7.4 Phase Correct PWM Mode
)