// Matt Edwards
// RGB LED color test (for MDE RGB board v1)
// Based on hello.array.44.c, "Charlieplex LED array hello-world", by Neil Gershenfeld

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.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

static const uint8_t rowPins[16] = {
  1 << PD2, 
  1 << PD1,
  1 << PD0,
  1 << PC5,
  1 << PC4,
  1 << PC3,
  1 << PC2,
  1 << PC1,
  1 << PC0,
  1 << PB5,
  1 << PB4,
  1 << PB2,
  1 << PB1,
  1 << PB0,
  1 << PD7,
  1 << PD6,
};

static const uint8_t map[4][4][4] = {
  {
    {0xff,  0,  0, 0xff},
    {0xff,0xff,0xff, 0xff},
    {0xff,  0,  0, 0xff},
    {0xff,  0,  0, 0xff},
  },
  {
    {0xff,0xff,0xff,0xff},
    {  0,0xff,0xff,  0},
    {  0,0xff,0xff,  0},
    {0xff,0xff,0xff,0xff},
  },
  {
    {0xff,0xff,0xff,0xff},
    {  0,0xff,0xff,  0},
    {  0,0xff,0xff,  0},
    {  0,0xff,0xff,  0},
  },
  {
    {0xff,0xff,0xff,0xff},
    {  0,0xff,0xff,  0},
    {  0,0xff,0xff,  0},
    {  0,0xff,0xff,  0},
  },
};

static volatile uint8_t* rowPorts[16];

static volatile uint8_t* rowDirections[16];

void rgbflash(uint8_t from, uint8_t to1, uint8_t to2, uint8_t to3, 
	      uint8_t Rpct, uint8_t Gpct, uint8_t Bpct, uint8_t cycles) {
  set(*rowPorts[from], rowPins[from]);
  clear(*rowPorts[to1], rowPins[to1]);
  clear(*rowPorts[to2], rowPins[to2]);
  clear(*rowPorts[to3], rowPins[to3]);
  output(*rowDirections[from], rowPins[from]);

  for (uint8_t j = 0; j < cycles; ++j) {
    output(*rowDirections[to1], rowPins[to1]);
    output(*rowDirections[to2], rowPins[to2]);
    output(*rowDirections[to3], rowPins[to3]);

    for (uint8_t i = 0; i < 100; ++i) {
      if (i >= Rpct) {
	clear(*rowDirections[to1], rowPins[to1]);
      }
      if (i >= Gpct) {
	clear(*rowDirections[to2], rowPins[to2]);
      }
      if (i >= Bpct) {
	clear(*rowDirections[to3], rowPins[to3]);
      }
      _delay_us(1);
    }
  }

  input(*rowDirections[from], rowPins[from]);
  input(*rowDirections[to1], rowPins[to1]);
  input(*rowDirections[to2], rowPins[to2]);
  input(*rowDirections[to3], rowPins[to3]);
}

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

  rowPorts[0] = &PORTD;
  rowPorts[1] = &PORTD;
  rowPorts[2] = &PORTD;
  rowPorts[3] = &PORTC;
  rowPorts[4] = &PORTC;
  rowPorts[5] = &PORTC;
  rowPorts[6] = &PORTC;
  rowPorts[7] = &PORTC;
  rowPorts[8] = &PORTC;
  rowPorts[9] = &PORTB;
  rowPorts[10] = &PORTB;
  rowPorts[11] = &PORTB;
  rowPorts[12] = &PORTB;
  rowPorts[13] = &PORTB;
  rowPorts[14] = &PORTD;
  rowPorts[15] = &PORTD;

  rowDirections[0] = &DDRD;
  rowDirections[1] = &DDRD;
  rowDirections[2] = &DDRD;
  rowDirections[3] = &DDRC;
  rowDirections[4] = &DDRC;
  rowDirections[5] = &DDRC;
  rowDirections[6] = &DDRC;
  rowDirections[7] = &DDRC;
  rowDirections[8] = &DDRC;
  rowDirections[9] = &DDRB;
  rowDirections[10] = &DDRB;
  rowDirections[11] = &DDRB;
  rowDirections[12] = &DDRB;
  rowDirections[13] = &DDRB;
  rowDirections[14] = &DDRD;
  rowDirections[15] = &DDRD;

  uint8_t t = 1, mult = 0;
  uint8_t r = 0, g = 0, b = 100;
  uint16_t count = 0;

  // green pin of top left LED is not soldered correctly (bottom right pin).

  while (1) {
    for (uint8_t base = 0; base < 16; base += 4) {
      mult = map[(count >> 7) & 3][base/4][0];
      rgbflash(0 + base, 1 + base, 2 + base, 3 + base, r&mult, g&mult, b&mult, t);
      mult = map[(count >> 7) & 3][base/4][1];
      rgbflash(3 + base, 2 + base, 1 + base, 0 + base, r&mult, g&mult, b&mult, t);
      mult = map[(count >> 7) & 3][base/4][2];
      rgbflash(1 + base, 0 + base, 3 + base, 2 + base, r&mult, g&mult, b&mult, t);
      mult = map[(count >> 7) & 3][base/4][3];
      rgbflash(2 + base, 3 + base, 0 + base, 1 + base, r&mult, g&mult, b&mult, t);

      if ((count & 0xf) == 0) {
	r += (rand() % 5) - 2;
	g += (rand() % 5) - 2;
	b += (rand() % 5) - 2;

	r %= 100;
	g %= 100;
	b %= 100;
      }
      count += 1;
    }

    continue;
    rgbflash(4, 5, 6, 7, r, g, b, t);
    rgbflash(5, 6, 4, 7, r, g, b, t);
    rgbflash(6, 4, 5, 7, r, g, b, t);
    rgbflash(7, 4, 5, 6, r, g, b, t);

    rgbflash(8, 9, 10, 11, r, g, b, t);
    rgbflash(9, 10, 8, 11, r, g, b, t);
    rgbflash(10, 8, 9, 11, r, g, b, t);
    rgbflash(11, 8, 9, 10, r, g, b, t);

    rgbflash(12, 13, 14, 15, r, g, b, t);
    rgbflash(13, 14, 12, 15, r, g, b, t);
    rgbflash(14, 12, 13, 15, r, g, b, t);
    rgbflash(15, 12, 13, 14, r, g, b, t);

    // rgbflash(0, 1, 2, 3, 0, 0, 0, 100);
  }
}
