/*
 * File: varDutyT1.ino
 * Purpose: Varying the duty-cycle of the PWM on output pins 9 and 10 via the analog A0 input. Update rate: 15.625kHz, resolution: 10-bits.
 * 
 * Version: 1.0.0
 * 
 * By: M. Stokroos
 * 
 * Date: 02-12-2019
 *
 */

// Library to be included for fast digital I/O when observing the timing with an oscilloscope.
//#include <digitalWriteFast.h>  // library for high performance digital reads and writes by jrraines
                // see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0
                // and http://code.google.com/p/digitalwritefast/


#define LPERIOD 500000    // the main loop period time in us. In this case 500ms.
#define PIN_LED 13    // PLL locking status indicator LED
#define PIN_PWMA 9  // Timer1 OCR1A 10-bit PWM
#define PIN_PWMB 10 // Timer1 OCR1B 10-bit PWM
#define PIN_DEBUG 4 // test output pin

// global vars
volatile int adcVal;
unsigned long nextLoop;


// ******************************************************************
// Setup
// ******************************************************************
void setup(){
  cli(); // disable interrupts
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_PWMA, OUTPUT);
  pinMode(PIN_PWMB, OUTPUT);
  pinMode(PIN_DEBUG, OUTPUT);

  // initialize ADC for continuous sampling mode
  DIDR0 = 0x3F; // digital inputs disabled for ADC0D to ADC5D
  bitSet(ADMUX, REFS0); // Select Vcc=5V as the ADC reference voltage
  bitClear(ADMUX, REFS1);
  bitClear(ADMUX, MUX0); // selecting ADC CH# 0
  bitClear(ADMUX, MUX1);
  bitClear(ADMUX, MUX2);
  bitClear(ADMUX, MUX3);
  bitSet(ADCSRA, ADEN); // AD-converter enabled
  bitSet(ADCSRA, ADATE); // auto-trigger enabled
  bitSet(ADCSRA, ADIE); // ADC interrupt enabled
  bitSet(ADCSRA, ADPS0);  // ADC clock prescaler set to 128
  bitSet(ADCSRA, ADPS1);
  bitSet(ADCSRA, ADPS2);
  bitClear(ADCSRB, ACME); // Analog Comparator (ADC)Multiplexer enable OFF
  bitClear(ADCSRB, ADTS0); // triggered by Timer/Counter1 Overflow
  bitSet(ADCSRB, ADTS1);
  bitSet(ADCSRB, ADTS2);
  bitSet(ADCSRA, ADSC);    // start conversion

  /* configuring TIMER1 for PWM-mode 7 */
  // prescaler = 1:
  bitSet(TCCR1B, CS10);
  bitClear(TCCR1B, CS11);
  bitClear(TCCR1B, CS12);
  // mode 7:
  bitSet(TCCR1A, WGM10);
  bitSet(TCCR1A, WGM11);
  bitSet(TCCR1B, WGM12);
  bitClear(TCCR1B, WGM13);
  //set output compare
  bitClear(TCCR1A, COM1A0);  // Compare Match PWM 9
  bitSet(TCCR1A, COM1A1);
  bitSet(TCCR1A, COM1B0);  // Compare Match PWM 10 - inverted channel
  bitSet(TCCR1A, COM1B1);
  // enable TIMER1 compare interrupt
  bitSet(TIMSK1, TOIE1); // enable Timer1 Interrupt
  sei(); // global enable interrupts
  nextLoop = micros() + LPERIOD; // Set the loop timer variable for the next loop interval.
}




// ******************************************************************
// Main loop
// ******************************************************************
void loop(){
  // do nothing here except for flashing the LED...

  digitalWrite(PIN_LED, !digitalRead(PIN_LED));

  while(nextLoop > micros());  // wait until the end of the time interval
  nextLoop += LPERIOD;  // set next loop time at current time + LOOP_PERIOD
}




/* ******************************************************************
* ADC ISR. The ADC is triggered by Timer1.
* *******************************************************************/
ISR(ADC_vect){
  //digitalWriteFast(PIN_DEBUG, HIGH); //for checking the interrupt frequency
 
  // read the current ADC input channel
  adcVal=ADCL; // store low byte
  adcVal+=ADCH<<8; // store high byte

  // write out 10bit PWM output registers A&B (pin9 and pin10).
  OCR1AH = adcVal>>8; //MSB
  OCR1AL = adcVal; //LSB
  //(inverted) output:
  OCR1BH = OCR1AH; //MSB
  OCR1BL = OCR1AL; //LSB
 
  //digitalWriteFast(PIN_DEBUG, LOW);
}




/* ******************************************************************
*  Timer1 ISR
*********************************************************************/
ISR(TIMER1_OVF_vect) {
//empty ISR. Only used to trigger the ADC.
}
