// Timer1Excerciser  -- Experiment with Timer1
// Code: https://wokwi.com/projects/328669448533705299
//
// Using TimeHelpers from http://www.gammon.com.au/timers
// Uno 328 DSatasheet http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
// Uno source https://store-usa.arduino.cc/products/arduino-uno-rev3
// 
// This is intended to excercise the different options of Timer1 CTC(OCR1A) 
// for https://github.com/wokwi/avr8js/issues/122
//
/*
Per https://github.com/wokwi/avr8js/issues/122#issuecomment-1492644222
I think the issue is that WGM Mode 4/CTC mode doesn't trigger interrupts or toggles
when OCR1A happens to be zero.

Per page 101 of https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
it should toggle at its fastest frequency when OCR1A == 0

*/


#include "TimerHelpers.h" // https://www.gammon.com.au/timers

/* ---------------------------------------------------------------
   Test sketch
   --------------------------------------------------------------- */

// pins
const byte OC1B_pin = 10;  // this is OC1B (timer 1 output compare B)
const byte OC1A_pin = 9;  // this is OC1A (timer 1 output compare A)
const byte OVF_LED = 8;  //
const byte MATCH_LED = 7;  //

// state vars
bool T1IntsOn = false;

volatile bool overflowed = false, matched = false;
volatile uint32_t overflow_time, match_time;


void setup() {
  Serial.begin(115200);
  pinMode (OC1A_pin, OUTPUT);
  pinMode (OC1B_pin, OUTPUT);
  digitalWrite (OC1B_pin, HIGH);
  pinMode(OVF_LED, OUTPUT);
  pinMode(MATCH_LED, OUTPUT);

  TCCR1A = 0;        // reset timer 1
  TCCR1B = 0;

  // set up Timer 1
  TCNT1 = 0;         // reset counter
  OCR1A =  65535;       // compare A register value
  OCR1B =  32767;       // compare A register value
  ICR1 = 65535;

  // Mode 4: CTC, top = OCR1A
  Timer1::setMode (4,
                   Timer1::PRESCALE_1024
                   ,Timer1::TOGGLE_A_ON_COMPARE | Timer1::TOGGLE_B_ON_COMPARE
                   );
  
  //TCCR1B = 0b00001001;  // From grbl
  TCCR1B |= 0b00001000;  // From grbl

  /*
    //
    Timer1::setMode (1,
                     Timer1::PRESCALE_256,
                     Timer1::SET_A_ON_COMPARE | Timer1::CLEAR_B_ON_COMPARE);

  */
  TIFR1 |= bit (OCF1A);    // clear interrupt flag
 // TIMSK1 = bit (OCIE1A) | bit(TOIE1);   // interrupt on Compare A Match & overflow
  T1IntsOn = true;

  OCR1A = 1;  // Works with 1
  OCR1A = 0; // Doesn't work

  Serial.print("TCCR0A=0b");Serial.print(TCCR0A,BIN);Serial.println(";");
  Serial.print("TCCR0B=0b");Serial.print(TCCR0B,BIN);Serial.println(";");
  Serial.print("TIMSK0=0b");Serial.print(TIMSK0,BIN);Serial.println(";\n");

  Serial.print("TCCR1A=0b");Serial.print(TCCR1A,BIN);Serial.println(";");
  Serial.print("TCCR1B=0b");Serial.print(TCCR1B,BIN);Serial.println(";");
  Serial.print("TIMSK1=0b");Serial.print(TIMSK1,BIN);Serial.println(";");
  Serial.print("OCR1A=");Serial.print(OCR1A);Serial.println(";");

  Serial.print("F_OC1A = fclk/(2*N*(1+OCR1A))=");
  Serial.print(16e6/(2*1024*(1+OCR1A)));
  Serial.println("Hz per page 101 of ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf");
  Serial.println("https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf");
  
}  // end of setup


ISR(TIMER1_OVF_vect)
{
  overflowed = true;
  overflow_time = micros();
  //  digitalWrite(OVF_LED, !digitalRead(OVF_LED));
  // TCCR1B = 0; // kill the timer

}  // end of TIMER1_COMPA_vect

ISR(TIMER1_COMPA_vect)
{
  matched = true;
  match_time = micros();
  //digitalWrite(MATCH_LED, !digitalRead(MATCH_LED));
}  // end of TIMER1_COMPA_vect



void toggleTimerInterrupt(void) {
  const unsigned long interval = 5000000UL;
  static unsigned long last = -interval;
  unsigned long now = micros();
  if (now - last >= interval) {
    last += interval;
    T1IntsOn = !T1IntsOn;
    if (T1IntsOn) {
      TIFR1 |= bit (OCF1A);    // clear interrupt flag
      TIMSK1 = bit (OCIE1A) | bit(TOIE1);   // interrupt on Compare A Match & overflow
    } else { // off
      TIFR1 |= bit (OCF1A);    // clear interrupt flag
      TIMSK1 &= ~(bit (OCIE1A) | bit(TOIE1));   // clear interrupt on Compare A Match & overflow
    }
    Serial.print(now);
    Serial.print("us Setting Timer1 Interrupts ");
    Serial.print(T1IntsOn ? "on" : "off");
    Serial.print(":  TCCR1A = 0b");
    Serial.print(TCCR1A,BIN);
    Serial.print("; TCCR1B = 0b");
    Serial.print(TCCR1B,BIN);
    Serial.println(";");
  }
}


void doOcr1aChanges(void) {
  static uint16_t OCR1Astate = 0;
  const unsigned long interval = 10000000UL;
  static unsigned long last = 0;
  unsigned long now = micros();
  if (now - last >= interval) {
    last += interval;
    if (OCR1A == 1 ) {
      OCR1A = 0;
    } else {
      OCR1A = 1;

    }
    Serial.print("\n");
    Serial.print(now);
    Serial.print("us Setting OCR1A = 0x");
    Serial.println(OCR1A,HEX);


  Serial.print("TCCR1A=0b");Serial.print(TCCR1A,BIN);Serial.println(";");
  Serial.print("TCCR1B=0b");Serial.print(TCCR1B,BIN);Serial.println(";");
  Serial.print("TIMSK1=0b");Serial.print(TIMSK1,BIN);Serial.println(";");
  Serial.print("OCR1A=");Serial.print(OCR1A);Serial.println(";");

  Serial.print("F_OC1A = fclk/(2*N*(1+OCR1A))=");
  Serial.print(16e6/(2*1024*(1+OCR1A)));
  Serial.println("Hz per page 101 of ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf");
  Serial.println("https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf");

  }
}


void loop() {
  if (overflowed) {
    noInterrupts();
    overflowed = false;
    interrupts();
    digitalWrite(OVF_LED, !digitalRead(OVF_LED));
    Serial.print(overflow_time);
    Serial.print("us ISR(TIMER1_OVF_vect): Overflow TCNT1 = 0xFFFF\n");
  }

  if (matched) {
    noInterrupts();
    matched = false;
    interrupts();
    digitalWrite(MATCH_LED, !digitalRead(MATCH_LED));
    Serial.print(match_time);
    Serial.print("us ISR(TIMER1_COMPA_vect): Matched TNCT1 == OCR1A == 0x");
    Serial.println(OCR1A,HEX);
  }
  if(Serial.available()){
    Serial.read();
    TCNT1 =2; // try some one shots per https://wp.josh.com/2015/03/12/avr-timer-based-one-shot-explained/
  }
  //toggleTimerInterrupt();
  doOcr1aChanges();
  report();

}  // end of loop
void report(){
  const unsigned long interval = 500000;
  static unsigned long last = -interval;
  if(micros() - last < interval) return;
  last += random(interval);
  Serial.print(TCNT1);
  Serial.print(' ');
}
Loading chip...chip-scope