// feet.44
// sam calisch
//
//
// adapted from hello.txrx.45.c
//
// step response transmit-receive hello-world
//    9600 baud FTDI interface
//
// Neil Gershenfeld
// 11/6/11
//
// (c) Massachusetts Institute of Technology 2011
// 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 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 100 // bit delay for 9600 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 settle_delay() _delay_us(100) // settle delay
#define char_delay() _delay_ms(10) // char delay
#define nloop 100 // loops to accumulate

#define serial_port PORTB
#define serial_direction DDRB
#define serial_pin_out (1 << PB2)
#define transmit_port PORTB
#define transmit_direction DDRB
#define transmit_pin_0 (1 << PA4)
#define transmit_pin_1 (1 << PA5)
#define transmit_pin_2 (1 << PA6)
#define transmit_pin_3 (1 << PA7)

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) {
   //
   // main
   //
   static unsigned char count, txpin, rxpin;
//   static uint16_t up,down;
   //
   // 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);
   clear(transmit_port, transmit_pin_0);
   output(transmit_direction, transmit_pin_0);
   clear(transmit_port, transmit_pin_1);
   output(transmit_direction, transmit_pin_1);
   clear(transmit_port, transmit_pin_2);
   output(transmit_direction, transmit_pin_2);
   clear(transmit_port, transmit_pin_3);
   output(transmit_direction, transmit_pin_3);
   //
   // main loop
   //
   while (1) {
      //
      // accumulate
      //
      static int up[4][4];
      static uint16_t down[4][4];

      for(rxpin = 0; txpin < 4; ++txpin){
         //
         // init A/D
         //
         switch (rxpin)
         {
            case 0:
               ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
                  | (0 << ADLAR) // right adjust
                  | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // PA0
            case 1:
               ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
                  | (0 << ADLAR) // right adjust
                  | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (1 << MUX0); // PA1
            case 2:
               ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
                  | (0 << ADLAR) // right adjust
                  | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (0 << MUX0); // PA2
            case 3:
               ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
                  | (0 << ADLAR) // right adjust
                  | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (1 << MUX0); // PA3
         }

         ADCSRA = (1 << ADEN) // enable
            | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128

         for(txpin = 0; rxpin < 4; ++rxpin){
            for (count = 0; count < nloop; ++count) { 
               //
               // settle, charge
               //
               settle_delay();
               switch (txpin)
               {
               case 0:
                  set(transmit_port, transmit_pin_0);
               case 1:
                  set(transmit_port, transmit_pin_1);
               case 2:
                  set(transmit_port, transmit_pin_2);
               case 3:
                  set(transmit_port, transmit_pin_3);
               }
               //
               // initiate conversion
               //
               ADCSRA |= (1 << ADSC);
               //
               // wait for completion
               //
               while (ADCSRA & (1 << ADSC))
                  ;
               //
               // save result
               //
               up[rxpin][txpin] += ADC;
               //
               // settle, discharge
               //
               settle_delay();
               switch (txpin)
               {
               case 0:
                  clear(transmit_port, transmit_pin_0);
               case 1:
                  clear(transmit_port, transmit_pin_1);
               case 2:
                  clear(transmit_port, transmit_pin_2);
               case 3:
                  clear(transmit_port, transmit_pin_3);
               }
               //
               // initiate conversion
               //
               ADCSRA |= (1 << ADSC);
               //
               // wait for completion
               //
               while (ADCSRA & (1 << ADSC))
                  ;
               //
               // save result
               //
               down[rxpin][txpin] += ADC;
            }
         }
      }
      
      //
      // send framing
      //
      put_char(&serial_port, serial_pin_out, 1);
      char_delay();
      put_char(&serial_port, serial_pin_out, 2);
      char_delay();
      put_char(&serial_port, serial_pin_out, 3);
      char_delay();
      put_char(&serial_port, serial_pin_out, 4);
      //
      // send results
      //
      for(rxpin = 0; rxpin < 4; ++rxpin){
         for (txpin = 0; txpin < 4; ++txpin) { 
            put_char(&serial_port, serial_pin_out, (up[rxpin][txpin] & 255));
            char_delay();
            put_char(&serial_port, serial_pin_out, ((up[rxpin][txpin] >> 8) & 255));
            char_delay();
            put_char(&serial_port, serial_pin_out, (down[rxpin][txpin] & 255));
            char_delay();
            put_char(&serial_port, serial_pin_out, ((down[rxpin][txpin] >> 8) & 255));
            char_delay();
            }
         }
      }
   }
