#include "header.h"

// 14.3.1 : alternate functions for led2 PB1
// - OC1A (Timer/Counter1 Output Compare Match A Output)
//   - 16.11.1 controlled by COM1A1:0 in TCCR1A
//   - Table 16-2 : Compare Output Mode, Fast PWM
//     (1 << COM1A1) | (0 << COM1A0) -> non-inverting mode
//   - Table 16-4 : set timer 1 in fast PWM mode, with top as ICR1 or OCR1A
//   - ICR1 : Input Capture Register ((1 << WGM13) | (1 << WGM12) | (1 << WGM11) | (0 << WGM10))
//   - OCR1A : Output Compare Register ((1 << WGM13) | (1 << WGM12) | (1 << WGM11) | (1 << WGM10))
//     -> if OCR1A is top, it's also duty cycle, so changing duty cycle will also change frequency
//     -> if ICR1 is top, OCR1A is duty cycle, so it can be changed independantly
// - PCINT1 (Pin Change Interrupt 1) : to interrupt when pin change, in input mode only

// calulations
// - frequency is 16Mhz -> it means that the clock runs 16 000 000 times per seconds
// - and the timer1 can store 16 bits value of cycle -> which is 2^16 = 65536
// - so it will only last 65536/16000000 = 0,004096 second
// - if i want my cycle to be 0.001 second, prescaler 1 is enough
// - if i want my cycle to be 0.01 second, which prescaler ?
//   - 8 * 0,004096 = 0.032768 -> it is enough
//   - 64 * 0,004096 = 0.262144 -> it is largely enough
// - after how many "ticks" do i need to reset my clock, for a duration of 0.01s, for every prescalers :
//   - 1    : (16000000 / 1)    * 0.01 = 16000000 * 0.01 = 160000 "ticks"
//   - 8    : (16000000 / 8)    * 0.01 = 2000000  * 0.01 = 20000 "ticks"
//   - 64   : (16000000 / 64)   * 0.01 = 250000   * 0.01 = 2500 "ticks"
//   - 256  : (16000000 / 256)  * 0.01 = 62500    * 0.01 = 625 "ticks"
//   - 1024 : (16000000 / 1024) * 0.01 = 15625    * 0.01 = 156.25 "ticks"
// - after how many "ticks" do i need to reset my clock, for a duration of 0.005s (*200/s), for every prescalers :
//   - 1    : (16000000 / 1)    * 0.005 = 16000000 * 0.005 = 80000 "ticks"
//   - 8    : (16000000 / 8)    * 0.005 = 2000000  * 0.005 = 10000 "ticks"
//   - 64   : (16000000 / 64)   * 0.005 = 250000   * 0.005 = 1250 "ticks"
//   - 256  : (16000000 / 256)  * 0.005 = 62500    * 0.005 = 312.5 "ticks"
//   - 1024 : (16000000 / 1024) * 0.005 = 15625    * 0.005 = 78.125 "ticks"

// alternatively, can use the default values :
// - Fast PWM, 8-bit, TOP 0x00FF (255)
// - Fast PWM, 9-bit, TOP 0x01FF (511)
// - Fast PWM, 10-bit, TOP 0x03FF (1023)

// no need to set a second for the "blinking" with PWM and duty cycle :
// only need to use a top value convenient, like a multiple of 100
// if choose top value of 100 :
// - at prescaler 1    : 100 / 16000000 = 0.00000625s (it takes this time to do a cycle)
// - at prescaler 8    : (100 * 8) / 16000000 = 0.00005s
// - at prescaler 64   : (100 * 64) / 16000000 = 0.0004s
// - at prescaler 256  : (100 * 256) / 16000000 = 0.0016s
// - at prescaler 1024 : (100 * 1024) / 16000000 = 0.0064s
// if choose top value of 1000 :
// - at prescaler 1    : 1000 / 16000000 = 0.0000625s (it takes this time to do a cycle)
// - at prescaler 8    : (1000 * 8) / 16000000 = 0.0005s
// - at prescaler 64   : (1000 * 64) / 16000000 = 0.004s
// - at prescaler 256  : (1000 * 256) / 16000000 = 0.016s
// - at prescaler 1024 : (1000 * 1024) / 16000000 = 0.064s

// 16.9.3 : timer1 fast PWM

// TIMER 1
#define T1_PWM_PRESCALER		256		// can be 1, 8, 64, 256, 1024
#define T1_DUTY_CYCLE_PERCENT	9
// #define FREQUENCY_MS			1
#define T1_TOP_VALUE			1000
// TIMER 0
#define T0_PWM_PRESCALER		1024	// can be 1, 8, 64, 256, 1024
#define T0_FREQUENCY_MS			5

typedef enum {
    DOWN,
    UP
} Slope;

volatile uint8_t duty_cycle = T1_DUTY_CYCLE_PERCENT;

// using TIMER 1
void blink_led_1() {
	TCCR1A = (1 << WGM11);									// Table 16-4 : set timer1 in fast PWM mode 14, with TOP ICR1, so we can use OCR1A for duty cycle and keep ICR1 for frequency
	TCCR1B = (1 << WGM13) | (1 << WGM12);					//   mode 15 with OCR1A TOP prevent using duty cycle trigger on OC1A (OCR1B is free but triggers OC1B -> PB2)
															//   other fast PWM modes 5, 6, and 7 uses predefined TOP, preventing to define a multiple of 100 for percent accuracy

	TCCR1A |= (1 << COM1A1);								// Table 16-2 : set non-inverting mode to trigger OC1A on fast PWM (14.3.1 : OC1A is alternate function of PB1)

	TCCR1B |= T1_PRESCALE_SET(T1_PWM_PRESCALER);			// Table 16-5 : set the appropriate prescale values for timer 1

	// ICR1 = TIME_MS(FREQUENCY_MS, T1_PWM_PRESCALER);		// top value for Xms
	ICR1 = T1_TOP_VALUE;
	OCR1A = PERCENT(duty_cycle, ICR1);						// 16.9.3 : duty cycle
}

// using TIMER 0
void vary_duty() {
	TCCR0A = (1 << WGM01);									// Table 15-8 : set timer0 in CTC mode, with TOP OCRA (15.9.1 : WGM01 in register TCCR0A)

	OCR0A = TIME_MS(T0_FREQUENCY_MS, T0_PWM_PRESCALER);		// 15.9.4 : compare value to trigger an Output Compare interrupt on register A

	TCCR0B |= T0_PRESCALE_SET(T0_PWM_PRESCALER);			// 15.9.2 : set the appropriate prescale values for timer 0

	TIMSK0 = (1 << OCIE0A);									// 15.9.6 : enable interrupt in timer 0
}

// use timers interruptions to varie led1 intensity using duty cycle, from 0% to 100% to 0% in 1 second
// timer1 sets the led duty cycle, timer0 change the duty cycle percentage
int main() {
	MODE_OUTPUT(LED1);
	CLEAR_ELEM(LED1);
	MODE_OUTPUT(LED2);
	CLEAR_ELEM(LED2);
	MODE_OUTPUT(LED3);
	CLEAR_ELEM(LED3);
	MODE_OUTPUT(LED4);
	CLEAR_ELEM(LED4);

	SREG |= ENABLE_GLOBAL_INTERRUPT;

	blink_led_1();
	vary_duty();

    while (1);
}

ISR(TIMER0_COMPA_vect) {
	volatile static Slope slope = UP;
	if (slope == UP) {
		if (duty_cycle >= 100) {
			duty_cycle = 100;
			slope = DOWN;
		} else {
			duty_cycle += 1;
		}
	} else if (slope == DOWN) {
		if (duty_cycle <= 0) {
			duty_cycle = 0;
			slope = UP;
		} else {
			duty_cycle -= 1;
		}
	}
	OCR1A = PERCENT(duty_cycle, ICR1);
}