// 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
//

#include "TimerHelpers.h"

/* ---------------------------------------------------------------
   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_256,
                   Timer1::TOGGLE_A_ON_COMPARE | Timer1::TOGGLE_B_ON_COMPARE);

  /*
    //
    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;

}  // 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 Timer1 Interrupts ");
    Serial.print(T1IntsOn ? "on" : "off");
    Serial.print(", TCCR1A = 0b");
    Serial.print(TCCR1A,BIN);
    Serial.print(", TCCR1B = 0b");
    Serial.println(TCCR1B,BIN);
  }
}


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 == 0 ) {
      OCR1A = 0xFFFF;
    } else {
      OCR1A = OCR1A >> 1;

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


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

  if (matched) {
    noInterrupts();
    matched = false;
    interrupts();
    digitalWrite(MATCH_LED, !digitalRead(MATCH_LED));
    Serial.print(match_time);
    Serial.print("us Matched TNCT1 == OCR1A == 0x");
    Serial.println(OCR1A,HEX);
  }

  toggleTimerInterrupt();
  doOcr1aChanges();
}  // end of loop