wokwi / avr8js

Arduino (8-bit AVR) simulator, written in JavaScript and runs in the browser / Node.js
https://blog.wokwi.com/avr8js-simulate-arduino-in-javascript/
MIT License
481 stars 78 forks source link

16 bit TIMER1 doesn't work #29

Closed gfeun closed 4 years ago

gfeun commented 4 years ago

Took me a while to figure this out.

Interrupts are triggered here: https://github.com/wokwi/avr8js/blob/48b40797fe0158baa4f129f5f61d115f1f785237/src/peripherals/timer.ts#L206-L213

The this.WGM is implement here: https://github.com/wokwi/avr8js/blob/48b40797fe0158baa4f129f5f61d115f1f785237/src/peripherals/timer.ts#L192-L194

The timer1 is managed a bit differently than the 8 bit timers 0 and 2. The WGM is 4 bits with : WG11/WG10 in bits 1/0 of TCCR1A and WG13/WG12 in bits 4/3 of TCCR1B

See pages 140 and 142 of http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf

We should implement timer 0/2 and timer 1 differently

urish commented 4 years ago

What code / library did you use to see the effect of this?

gfeun commented 4 years ago

This should demonstrate: http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/ (see end of post for the code)

I adapted to toggle LED_BUILTIN so that you can copy paste on this playground: https://wokwi.com/playground/blink

void setup()
{
pinMode(LED_BUILTIN, OUTPUT);

// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B

// set compare match register to desired timer count:
OCR1A = 15624;
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
// Set CS10 and CS12 bits for 1024 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
// enable global interrupts:
sei();
}

void loop()
{
// do some crazy stuff while my LED keeps blinking
}

ISR(TIMER1_COMPA_vect)
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
gfeun commented 4 years ago

For reference, a working code using TIMER0 based on the same blog post:

void setup(){
  pinMode(LED_BUILTIN, OUTPUT);
  // initialize 8 bits Timer0
  // disable global interrupts
  cli();

  TCCR0A = 0;
  TCCR0B = 0;

  // set compare match register to desired timer count:
  OCR0A = 254;

  // turn on CTC mode:
  TCCR0A |= 1 << WGM01;
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR0B |= 0x05;
  // enable timer compare interrupt:
  TIMSK0 |= (1 << OCIE0A);

  // enable global interrupts:
  sei();
}

void loop(){}

ISR(TIMER0_COMPA_vect)
{
  static int nb = 0;
  nb++;
  if (nb > 60) {
    nb = 0;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}
urish commented 4 years ago

Thank Glenn! Actually, looking into the playground, I realized it doesn't even use Timer 1, seems like 0 and 2 were sufficient for all the code samples I've ran to date.

So I guess this also means I never tested it with Timer 1 :-)