//
//
// hello.button.45mod.c
//
// button hello-world
//    9600 baud FTDI interface
//
// Christian Teissl, modified from Neil Gershenfeld
// Nov. 2014
// Does ADC when pressing a button and sends serial to the computer when button is released
//



#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 102 // 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 input_port PORTB
#define input_direction DDRB
#define input_pin1 (1 << PB3) // switch (switch ON = VCC)
#define input_pin2 (1 << PB4) // analog voltage in
#define input_pins PINB
#define serial_port PORTB
#define serial_direction DDRB
#define serial_pin_out (1 << PB2)

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) {
   
   // declarations
   
   char adc_result;   
     
   
   //
   // main
   //
  
   //
   // initialize pins
   //
   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);
   
   set(input_port, input_pin1); // turn on pull-up   
   set(input_port, input_pin2); // turn on pull-up
   
   input(input_direction, input_pin1);
   input(input_direction, input_pin2);
   
   
   //
   // main loop
   //
   
   while (1) {
      
      //
      // wait for button down - triggers a single ADC conversion
      //
      
      
      while (0 == pin_test(input_pins,input_pin1))
         ;
         
        //
        // ***initialize ADC***
        //   
  
        // *ADMUX Register*
        //
        // SET 8 bits only (ADCH)! ADLAR = 1 
        // SET reference voltage 
        // voltage reference (rem: init. value of REFS[2:0]=0; 
        // REFS1=0, REFS2=0; VCC used as Voltage Reference
        // SET analog input channel (and differential gain if required)
        // SET PB4 as single analog input: MUX[3:0] = 0010 (0 as default)
          
        ADMUX =
            (1 << ADLAR) |             
            (0 << REFS1) | (0 << REFS0) |     
            (0 << MUX3)  | (0 << MUX2)  | (1 << MUX1)  | (0 << MUX0);       
            
        // *ADCSRA – Register*
        // ADC Control and Status Register A 
        //    
        // SET ADC enabled via ADEN
        // SET prescale 8000kHz/64=125kHz via ADP[2:0}]=(110)    
    
        ADCSRA = 
            (1 << ADEN)  |
            (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);      
            
            
        // SET ADC Start Conversion bit (ADSC) - start measurement    
        ADCSRA |= (1 << ADSC);         
        
        // When conversion is complete, the result is written to the ADC Data Registers, and ADIF is set
        // In single Conversion mode, ADSC is cleared simultaneously    
        
        while (ADCSRA & (1 << ADSC) );
        
        
        // conversation is completed
        // read result from the ADC Result Register (ADCH if 8bit only!).
           adc_result = ADCH;        
        
        // 
        // wait for button up 
        //
        while (0 != pin_test(input_pins,input_pin1))
        ;
                
        // set clock divider back to /1
        
        CLKPR = (1 << CLKPCE);
        CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
        
        put_char(&serial_port, serial_pin_out, 'r');
        put_char(&serial_port, serial_pin_out, adc_result);
        
      
      }
   }