//
//
// hello.array.44.c
//
// Charlieplex LED array hello-world
//
// Neil Gershenfeld
// 11/13/10
//
// (c) Massachusetts Institute of Technology 2010
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all 
// liability.
//

#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 led_delay() _delay_ms(1) // LED delay

#define flash_delay 1

#define led_port PORTB
#define led_direction DDRB

#define C (1 << PB0) // row 1
#define B (1 << PB1) // row 2
#define A (1 << PB2) // row 3

#define serial_port PORTB
#define serial_direction DDRB
#define serial_pins PINB
#define serial_pin_in (1 << PB3)

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte) {
   //
   // read character into rxbyte on pins pin
   //    assumes line driver (inverts bits)
   //
   *rxbyte = 0;
   while (pin_test(*pins,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(*pins,pin)
      *rxbyte |= (1 << 0);
   else
      *rxbyte |= (0 << 0);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 1);
   else
      *rxbyte |= (0 << 1);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 2);
   else
      *rxbyte |= (0 << 2);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 3);
   else
      *rxbyte |= (0 << 3);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 4);
   else
      *rxbyte |= (0 << 4);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 5);
   else
      *rxbyte |= (0 << 5);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 6);
   else
      *rxbyte |= (0 << 6);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 7);
   else
      *rxbyte |= (0 << 7);
   //
   // wait for stop bit
   //
   bit_delay();
   half_bit_delay();
   }



void flash(uint8_t from, uint8_t to, uint8_t delay) {
   //
   // source from, sink to, flash
   //
   static uint8_t i;
   set(led_port,from);
   clear(led_port,to);
   output(led_direction,from);
   output(led_direction,to);
   for (i = 0; i < delay; ++i)
       led_delay();
   input(led_direction,from);
   input(led_direction,to);
   }

void led_cycle_CCW(uint8_t number, uint8_t delay) {
   //
   // cycle through LEDs
   //
   uint8_t i;
   for (i = 0; i < number; ++i) {
      flash(A,B,delay);
      flash(A,C,delay);
      flash(B,A,delay);
      flash(B,C,delay);
      flash(C,A,delay);
      flash(C,B,delay);
      }
   }
void led_cycle_CW(uint8_t number, uint8_t delay) {
   //
   // cycle through LEDs
   //
   uint8_t i;
   for (i = 0; i < number; ++i) {
      flash(C,B,delay);
      flash(B,C,delay);
      flash(B,A,delay);
      flash(C,A,delay);
      flash(A,C,delay);
      flash(A,B,delay);
      }
   }

void flash_all(uint8_t number)
{
   for (int i = 0; i < number; ++i)
   {
      flash(A, B, flash_delay);
      flash(A, C, flash_delay);
      flash(B, A, flash_delay);
      flash(B, C, flash_delay);
      flash(C, A, flash_delay);
      flash(C, B, flash_delay);
   }
}
void dance(void)
{
   led_cycle_CCW(10, 50);
   flash_all(100);
   led_cycle_CW(10, 50);
   flash_all(100);
}

void flash_pattern(uint8_t pattern, uint8_t number)
{
   for (int i = 0; i < number; ++i)
   {
      if((pattern >> 1) & (1))
         flash(A, B, flash_delay);
      if((pattern >> 2) & (1))
         flash(A, C, flash_delay);

      if((pattern >> 3) & (1))
         flash(B, A, flash_delay);
      if((pattern >> 4) & (1))
         flash(B, C, flash_delay);

      if((pattern >> 5) & (1))
         flash(C, A, flash_delay);
      if((pattern >> 6) & (1))
         flash(C, B, flash_delay);
   }
}

static char chr;

void setup()
{
   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
  
}

void loop()
{
      dance();

      // comment above line and uncomment below lines for demo 2
      //get_char(&serial_pins,serial_pin_in,&chr);
      //flash_pattern(chr, 2000);
}
