//
//
// hello.ftdi.44.echo.interrupt.c
//
// 115200 baud FTDI character echo, interrupt version
//
// set lfuse to 0x5E for 20 MHz xtal
//
// Neil Gershenfeld
// 12/8/10
//
// (c) Massachusetts Institute of Technology 2010
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all 
// liability.
//


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h> //for converting integer to string

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 8.5 // bit delay for 115200 with overhead
// #define bit_delay_time 104.17 // bit delay for 9600 (1s/9600 in us) with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define char_delay() _delay_ms(10) // char delay

#define serial_port PORTA
#define serial_direction DDRA
#define serial_pins PINA
#define serial_pin_in (1 << PA0)
#define serial_pin_out (1 << PA1)
#define serial_interrupt (1 << PCIE0)
#define serial_interrupt_pin (1 << PCINT0 )
#define max_buffer 25

volatile uint8_t button_trigger;
volatile uint8_t led_level = 0;

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte) {
   //
   // read character into rxbyte on pins pin
   //    assumes line driver (inverts bits)
   //
   *rxbyte = 0;
   while (pin_test(*pins,pin))
      //
      // wait for start bit
      //
      ;
   //
   // delay to middle of first data bit
   //
   half_bit_delay();
   bit_delay();
   //
   // unrolled loop to read data bits
   //
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 0);
   else
      *rxbyte |= (0 << 0);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 1);
   else
      *rxbyte |= (0 << 1);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 2);
   else
      *rxbyte |= (0 << 2);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 3);
   else
      *rxbyte |= (0 << 3);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 4);
   else
      *rxbyte |= (0 << 4);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 5);
   else
      *rxbyte |= (0 << 5);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 6);
   else
      *rxbyte |= (0 << 6);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 7);
   else
      *rxbyte |= (0 << 7);
   //
   // wait for stop bit
   //
   bit_delay();
   half_bit_delay();
   }

void put_char(volatile unsigned char *port, unsigned char pin, char txchar) {
   //
   // send character in txchar on port pin
   //    assumes line driver (inverts bits)
   //
   // start bit
   //
   clear(*port,pin);
   bit_delay();
   //
   // unrolled loop to write data bits
   //
   if bit_test(txchar,0)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,1)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,2)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,3)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,4)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,5)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,6)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,7)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   //
   // stop bit
   //
   set(*port,pin);
   bit_delay();
   //
   // char delay
   //
   bit_delay();
   }

void put_string(volatile unsigned char *port, unsigned char pin, char *str) {
   //
   // print a null-terminated string
   //
   static int index;
   index = 0;
   do {
      put_char(port, pin, str[index]);
      ++index;
      } while (str[index] != 0);
   }

   void ButtonPressDebounced(void)
   {  
      // button_state = 0;
   	  static uint8_t count = 0;
      uint8_t button_pin_state = (PINA & (1<<2));//!(PINA & (1<<PA2)); 
      //check if the button pin has been pulled low
      //if low, this value will be 1
      uint8_t button_hold_state = !(button_pin_state); // if button pin is pulled low, i.e. 0, the button press state is 1

      if ((button_hold_state == 1)&&(button_trigger!=1)) // the second condition make sure that the counter does not continue counting after the trigger is set
      {
         count++;
      }

      if (count >=255) //debouncing period and delay to account for fast clock rate; 8 bit can only counter 255
      {

         button_trigger = 1;
         count = 0;         
      }
   }

 ISR(TIM0_COMPB_vect) //argument = vector name + "_vect"
   {
      			ButtonPressDebounced(); //this cannot be activated in while loop, only in here
      			//  char CNT[10]; // PA7 port status buffer, to convert it to string
     			 // sprintf(CNT, "Count: %d \n", count); 
     			 // put_string(&serial_port, serial_pin_out, CNT);
                if(button_trigger)
                	{	
                		// _delay_ms(50);
	                	if(led_level<=2)
		                    {
		                        led_level++;
		                        button_trigger = 0;
		                    }
		                if(led_level>2)
			                {
			                	led_level=0;
			                	button_trigger = 0;  //remember to reset trigger after each level change, otherwise it will loop forever, and does noe give much visual perceptive difference as compared to all high(seemingly no brigtness change)
			                }
                	}
			         char lvlbf[10]; // PA7 port status buffer, to convert it to string
			         sprintf(lvlbf, " %d \n", led_level); 
			         put_string(&serial_port, serial_pin_out, lvlbf);

                switch(led_level)
                    {
                        case 0: 
                        {
                        	OCR0B = 1; 
                        	break;
                        }
                        case 1: 
                        {
                        	OCR0B = 32;  
                        	break;
                        }
                        case 2: 
                	    {
                            OCR0B = 255;
                            break;
                        }
                        default: break;
                    }
      // put_string(&serial_port, serial_pin_out, "test");
      // char flagbf[10]; // PA7 port status buffer, to convert it to string
      // // uint8_t count = TCNT0;
      // sprintf(flagbf, "f, n, p: %d, " "%d, " "%d \n", flag, tot_overflown, tot_overflowp); 
      // put_string(&serial_port, serial_pin_out, flagbf);
    
   }

int main(void)    {

   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

   DDRA |= (1<<7); 
   DDRA &= ~(1<<2);
   PORTA |= (1<<2);

   TCCR0A = (2<<COM0B0) | (3<<WGM00); //setting TCCR0B, activate OC0B in non-inverting fast PWM
   TCCR0B = (1<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);  //64 prescaler for counter clock rate


   // SREG |= (1<<7);  //dimming does not work?
   TIMSK0 |= (1<<OCIE0B);  //with this compare match interrupt activated, at the frequency close to prescaled system clock(20MHz), basically anything in the while loop will not be able to run, so we should use the ISR for OCIE0B to handle the function instead
   OCR0A =255; //OCR0A is specifically for setting top, i.e. the counting frequency(as 255 equals to one sys cycle), but also the duty cycle resolution, higher the OCR0A, finer we can tune the duty cycle; If not set, will be 255?
   OCR0B = 1; //with top set, OCR0B is for seting the toggle point, hence the duty cycle for fast pwm mode

   //to sum up, using PA7 (OC0B) in fast pwm requires setting up two output compare registers, 
   //OCR0A for setting the counter TOP(counting frequency and duty cycle resolution) 
   //and OCR0B for the duty cycle

   TCNT0 = 0;

   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);
   // clear(serial_port, serial_pin_in);
   // output(serial_direction, serial_pin_in);

   // set(GIMSK, serial_interrupt);
   // set (PCMSK0, serial_interrupt_pin);

   sei();
   //
   // main loop
   //
   



   while (1) {
   			

      		}

   }