//
//
// hello.echo.44.c
//
// 115200 baud serial echo hello-world program
//
// Neil Gershenfeld
// CBA MIT 9/12/09
//
// (c) Massachusetts Institute of Technology 2009
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//
//

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

#define tx_pin PA6 // transmit pin
#define rx_pin PA7 // receive pin
#define led_pin PB2 // LED pin
#define bit_delay_time 8.7 // bit delay, 1/115200 in usec at 20 MHZ
#define led_delay_time 10 // LED delay time, in ms
#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 output_A(pin) (DDRA |= byte(pin)) // set PORTA pin for output
#define output_B(pin) (DDRB |= byte(pin)) // set PORTB pin for output
#define set_A(pin) (PORTA |= byte(pin)) // set pin in PORTA
#define clear_A(pin) (PORTA &= ~(byte(pin))) // clear pin in PORTA
#define set_B(pin) (PORTB |= byte(pin)) // set pin in PORTB
#define clear_B(pin) (PORTB &= ~(byte(pin))) // clear pin in PORTB
#define pin_test_A(bit) (PINA & (1 << bit)) // test for pin in PORTA
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define byte(bit) (1 << bit) // byte with bit set

char get_char() {
   //
   // read and return a character
   // assumes no line driver (doesn't invert bits)
   //
   unsigned char rxbyte = 0;
   while (! pin_test_A(rx_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_A(rx_pin)
      rxbyte |= (0 << 0);
   else
      rxbyte |= (1 << 0);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 1);
   else
      rxbyte |= (1 << 1);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 2);
   else
      rxbyte |= (1 << 2);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 3);
   else
      rxbyte |= (1 << 3);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 4);
   else
      rxbyte |= (1 << 4);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 5);
   else
      rxbyte |= (1 << 5);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 6);
   else
      rxbyte |= (1 << 6);
   bit_delay();
   if pin_test_A(rx_pin)
      rxbyte |= (0 << 7);
   else
      rxbyte |= (1 << 7);
   return rxbyte;
   }

void put_char(char txchar) {
   //
   // print the character in txchar
   // assumes no line driver (doesn't invert bits)
   //
   // start bit
   //
   set_A(tx_pin);
   bit_delay();
   //
   // unrolled loop to write data bits
   //
   if bit_test(txchar,0)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,1)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,2)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,3)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,4)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,5)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,6)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   if bit_test(txchar,7)
      clear_A(tx_pin);
   else
      set_A(tx_pin);
   bit_delay();
   //
   // stop bit
   //
   clear_A(tx_pin);
   bit_delay();
   }

void print_string(char *str) {
   //
   // print the null-terminated program memory string str
   //
   char chr;
   chr = pgm_read_byte(str++);
   while (chr != 0x00) {
      put_char(chr);
      chr = pgm_read_byte(str++);
      }
   }

void blink() {
   //
   // blink the LED
   //
   set_B(led_pin);
   _delay_ms(led_delay_time);
   clear_B(led_pin);
   }

char message[] PROGMEM = "received character: ";

int main(void) {
   unsigned char rxbyte;
   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
   //
   // initialize output pins
   //
   output_A(tx_pin);
   output_B(led_pin);
   clear_A(tx_pin);
   //
   // main loop
   //
   while (1) {
      rxbyte = get_char();
      print_string(message);
      put_char(rxbyte);
      put_char('\n');
      blink();
      }
   }
