//
//
// hello.button.45.c
//
// button hello-world
//    9600 baud FTDI interface
//
// Neil Gershenfeld
// 10/31/10
//
// (c) Massachusetts Institute of Technology 2010
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//

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

#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
#define sample_delay() _delay_ms(500)

#define portA PORTA
#define portB PORTB
#define directionA DDRA
#define directionB DDRB
#define transmit_row1 (1 << PA5)
#define transmit_row2 (1 << PA6)
#define transmit_row3 (1 << PA7)
#define transmit_row4 (1 << PB2)
#define receive_col1 (1 << PA4)
#define receive_col2 (1 << PA3)
#define receive_col3 (1 << PA2)
#define receive_col4 (1 << PA1)
#define pinsA PINA
#define pinsB PINB

#define serial_port PORTA
#define serial_direction DDRA
#define serial_pin_out (1 << PA0)

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

int main(void) {
   static int row_sample;
   static int col_sample;

   //
   // main
   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

   //
   // initialize pins
   //
   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);

   set(portA, receive_col1); // turn on pull-up
   input(directionA, receive_col1);

   set(portA, receive_col2); 
   input(directionA, receive_col2);

   set(portA, receive_col3); 
   input(directionA, receive_col3);

   set(portA, receive_col4); 
   input(directionA, receive_col4);

   clear(portA, transmit_row1);
   output(directionA, transmit_row1);

   clear(portA, transmit_row2);
   output(directionA, transmit_row2);

   clear(portA, transmit_row3);
   output(directionA, transmit_row3);

   clear(portB, transmit_row4);
   output(directionB, transmit_row4);

   row_sample = 0;
   col_sample = 0;

   set(portA, transmit_row2);
   set(portA, transmit_row1);
   set(portA, transmit_row3);
   set(portB, transmit_row4);

   //
   // main loop
   //
   while (1) {

      set(portB, transmit_row4);
      clear(portA, transmit_row1);
      _delay_ms(10);
      if (pin_test(pinsA, receive_col1) == 0)
         put_char(&serial_port, serial_pin_out, '1');
      if (pin_test(pinsA, receive_col2) == 0)
         put_char(&serial_port, serial_pin_out, '2');
      if (pin_test(pinsA, receive_col3) == 0)
         put_char(&serial_port, serial_pin_out, '3');
      if (pin_test(pinsA, receive_col4) == 0)
         put_char(&serial_port, serial_pin_out, '+');

      set(portA, transmit_row1);
      clear(portA, transmit_row2);
      _delay_ms(10);
      if (pin_test(pinsA, receive_col1) == 0)
         put_char(&serial_port, serial_pin_out, '4');
      if (pin_test(pinsA, receive_col2) == 0)
         put_char(&serial_port, serial_pin_out, '5');
      if (pin_test(pinsA, receive_col3) == 0)
         put_char(&serial_port, serial_pin_out, '6');
      if (pin_test(pinsA, receive_col4) == 0)
         put_char(&serial_port, serial_pin_out, '-');

      set(portA, transmit_row2);
      clear(portA, transmit_row3);
      _delay_ms(10);
      if (pin_test(pinsA, receive_col1) == 0)
         put_char(&serial_port, serial_pin_out, '7');
      if (pin_test(pinsA, receive_col2) == 0)
         put_char(&serial_port, serial_pin_out, '8');
      if (pin_test(pinsA, receive_col3) == 0)
         put_char(&serial_port, serial_pin_out, '9');
      if (pin_test(pinsA, receive_col4) == 0)
         put_char(&serial_port, serial_pin_out, 'x');

      set(portA, transmit_row3);
      clear(portB, transmit_row4);
      _delay_ms(10);
      if (pin_test(pinsA, receive_col1) == 0)
         put_char(&serial_port, serial_pin_out, '.');
      if (pin_test(pinsA, receive_col2) == 0)
         put_char(&serial_port, serial_pin_out, '0');
      if (pin_test(pinsA, receive_col3) == 0)
         put_char(&serial_port, serial_pin_out, '=');
      if (pin_test(pinsA, receive_col4) == 0)
         put_char(&serial_port, serial_pin_out, '/');

      }
   }
