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.

6
  • 1
    I'm afraid that this not simple, or possible at all. Since every code to check for the direction takes some clocks, you need to count them and take them into account. Additionally, if you use interrupts, you would need to disable them in this critical part. -- Do you really need micros()? Jun 7 at 6:54
  • Your fallback solution is the best one, and your assumption is correct: with the prescaler at one, you can't read the counter twice faster than it changes. Jun 7 at 7:11
  • Maybe you can use the 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. (header 14.7.4 Phase Correct PWM Mode)
    – Swedgin
    Jun 7 at 8:08
  • Use a flag to keep tabs on it
    – Swedgin
    Jun 7 at 8:09
  • You could sidestep the problem and use _delay_us instead. The only downside is that _delay_us doesn't account for time spend inside ISRs, as it just does a bunch of NOP instructions.
    – Gerben
    Jun 7 at 15:17
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));
}
1
  • 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
    Jun 7 at 20:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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