/******************************************************************************************
* button_brd_1.c
*
* based on hello.ftdi.c by Neil Gershenfeld
*      and button44 by Rob Hart
* set lfuse to 0x5E for 20 MHz xtal
*
* Debounces button inputs and sends the count over serial
* Flag as 1 does the 1-8 inputs
*      as 0 does the 9,*,0,# and extras
*
* Brian Plancher
* 12/7/16
*
********************************************************************************************/

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

// helper defs
#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input
#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() _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

// serial port
#define serial_port PORTB
#define serial_direction DDRB
#define serial_pins PINB
#define serial_pin_out (1 << PB2)

// input devices
// Buttons  PA0 - PA7
#define input_port PORTA
#define input_direction DDRA
#define input_pins PINA
#define button_0 (1 << PA0)
#define button_1 (1 << PA1)
#define button_2 (1 << PA2)
#define button_3 (1 << PA3)
#define button_4 (1 << PA4)
#define button_5 (1 << PA5)
#define button_6 (1 << PA6)
#define button_7 (1 << PA7)

// for debounce
#define XTAL		      20000000L    // Crystal frequency in Hz
#define OUTER_LOOPS     2000
#define INNER_LOOPS     (XTAL / OUTER_LOOPS / 750)
#define MAX_COUNT       32767
#define MAX_COUNT_SIZE  15

// define the buttons
#define BOARD_FLAG 0
#if BOARD_FLAG
   #define BUTTON_0_CHAR '1'
   #define BUTTON_1_CHAR '2'
   #define BUTTON_2_CHAR '3'
   #define BUTTON_3_CHAR '4'
   #define BUTTON_4_CHAR '5'
   #define BUTTON_5_CHAR '6'
   #define BUTTON_6_CHAR '7'
   #define BUTTON_7_CHAR '8'
#else
   #define BUTTON_0_CHAR '9'
   #define BUTTON_1_CHAR '*'
   #define BUTTON_2_CHAR '0'
   #define BUTTON_3_CHAR '#'
   #define BUTTON_4_CHAR 'B' // backspace
   #define BUTTON_5_CHAR 'M' // menu
   #define BUTTON_6_CHAR 'D' // down arrow
   #define BUTTON_7_CHAR 'E' // enter
#endif

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();
   }

   
int main(void) {
   // set clock divider to /1
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
   
   // initialize output pins
   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);

   // initialize buttons
   set(input_port, button_0|button_1|button_2|button_3|button_4|button_5|button_6|button_7); // turn on pull-up for the buttons
   input(input_direction, button_0|button_1|button_2|button_3|button_4|button_5|button_6|button_7); // make button input
   volatile uint16_t inner_loop_cntr = 0;
   volatile uint16_t outer_loop_cntr = 0;
   volatile uint8_t btn_state = 0;
   volatile uint8_t btn_hold = 0;
   
   // main loop
   while (1) {
      // debounce the button inputs
      volatile uint8_t btn_flag_0 = !pin_test(input_pins,button_0); // invert b/c pullup
      volatile uint8_t btn_flag_1 = !pin_test(input_pins,button_1); // invert b/c pullup
      volatile uint8_t btn_flag_2 = !pin_test(input_pins,button_2); // invert b/c pullup
      volatile uint8_t btn_flag_3 = !pin_test(input_pins,button_3); // invert b/c pullup
      volatile uint8_t btn_flag_4 = !pin_test(input_pins,button_4); // invert b/c pullup
      volatile uint8_t btn_flag_5 = !pin_test(input_pins,button_5); // invert b/c pullup
      volatile uint8_t btn_flag_6 = !pin_test(input_pins,button_6); // invert b/c pullup
      volatile uint8_t btn_flag_7 = !pin_test(input_pins,button_7); // invert b/c pullup
      volatile uint8_t btn_flag = btn_flag_0 | btn_flag_1 | btn_flag_2 | btn_flag_3 | btn_flag_4 | btn_flag_5 | btn_flag_6 | btn_flag_7;
      if(!btn_flag){
         btn_hold = 0;
      }
      if(!btn_hold){
         if (btn_state != btn_flag){
             btn_state = btn_flag;
             inner_loop_cntr = 0;
             outer_loop_cntr = 0;
         }      
         if (outer_loop_cntr == OUTER_LOOPS){
             outer_loop_cntr = 0;
             // see if majority of count was a bttn press
             if (btn_state){
                btn_hold = 1;
               // notify over serial the appropriate char
               if(btn_flag_0){
                  put_char(&serial_port, serial_pin_out, BUTTON_0_CHAR);
               }
               else if(btn_flag_1){
                  put_char(&serial_port, serial_pin_out, BUTTON_1_CHAR);
               }
               else if(btn_flag_2){
                  put_char(&serial_port, serial_pin_out, BUTTON_2_CHAR);
               }
               else if(btn_flag_3){
                  put_char(&serial_port, serial_pin_out, BUTTON_3_CHAR);
               }
               else if(btn_flag_4){
                  put_char(&serial_port, serial_pin_out, BUTTON_4_CHAR);
               }
               else if(btn_flag_5){
                  put_char(&serial_port, serial_pin_out, BUTTON_5_CHAR);
               }
               else if(btn_flag_6){
                  put_char(&serial_port, serial_pin_out, BUTTON_6_CHAR);
               }
               else {
                  put_char(&serial_port, serial_pin_out, BUTTON_7_CHAR);
               }
             }
         }
         else{
            if (inner_loop_cntr == INNER_LOOPS){
                inner_loop_cntr = 0;
                outer_loop_cntr += 1;
            }
            else{
                inner_loop_cntr++;
            }
         }
      }  
   }
}