#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>


#include "utils.h"
#include "bitmanip.h"
#include "timer.h"
#include "usart.h"
#include "interrupt.h"

// TIMER
#define PERIOD							2000
#define PRESCALE_VALUE					1024		// can be 1, 8, 64, 256, 1024
// USART
#define USART_BAUDRATE					115200		// Table 20-1 : Baud Rate Calculation

// END MACROS

void uart_init() {
	UBRR0H = (unsigned char) (BAUD_PRESCALER(USART_BAUDRATE) >> 8);				// 20.11.5 : UBRRnL and UBRRnH – USART Baud Rate Registers
	UBRR0L = (unsigned char) BAUD_PRESCALER(USART_BAUDRATE);

	UCSR0C |= ASYNCHRONOUS | PARITY_DISABLED | STOP_ONE_BIT | DATA_EIGHT_BIT;	// 20.11.4 : set Frame Format
	
	UCSR0B |= RECEIVER_DISABLED | TRANSMITTER_ENABLED;							// 20.11.3 : enable Receiver and/or Transmitter
}

void uart_tx(char c) {
	while (TEST(UCSR0A, UDRE0) == 0);											// 20.11.2 : do nothing until UDR emission buffer is empty, if UDREn flag is 1, UCSRnA register is empty
	UDR0 = (unsigned char) c;													// 20.11.1 : Put data into buffer, UDRn – USART I/O Data Register (read and write)
}

void uart_printstr(const char* str) {
	while (*str) {
		uart_tx(*str);
		str++;
	}
}

// print "Hello World!\n", on serial port, every 2 seconds, with empty infinite loop
// `screen /dev/ttyUSB0 115200`
int main() {
	uart_init();

	TCCR1A |= CTC_TOP_OCR1A_IN_TCCR1A;				// Table 16-4 : set timer in CTC (Clear Time on Compare) mode
	TCCR1B |= CTC_TOP_OCR1A_IN_TCCR1B;

	SREG |= ENABLE_GLOBAL_INTERRUPT;				// 7.3.1 : Status Register, bit 7 : I – Global Interrupt Enable

	TIMSK1 |= INTERRUPT_ENABLE_CHANNEL_A;			// 16.11.8 : enables the Timer1 Compare Match A interrupt, this makes the MCU react when OCR1A == TCNT1 by calling TIMER1_COMPA_vect

	OCR1A = TIME_MS(PERIOD, PRESCALE_VALUE);		// Table 16-4 : set CTC compare value on channel A, the counter is cleared to zero when the counter value (TCNT1) matches the OCR1A register

	TCCR1B |= (PRESCALE_SET(PRESCALE_VALUE));		// 16.4 : set timer according to prescale value, in register TCCR1B, table 16-5 : prescale sets

	while(1);
}

// ISR interrupt macro : https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
ISR(TIMER1_COMPA_vect) {							// Table 12-7 : we select the code for timer 1 on channel A
	uart_printstr("Hello World!\r\n");				// \n and \r : https://stackoverflow.com/questions/7812142/how-to-toggle-cr-lf-in-gnu-screen
}
