2

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.

Simm
  • 51
  • 5

2 Answers2

1

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)); }

Simm
  • 51
  • 5
0

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)); }

rwrqwe
  • 1