/*
 *      snowman.c
 *      
 *      Copyright 2010 Brian Starkey <stark3y[AT]gmail[DOT]com>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 *
 *      Bit-banged i2c write, displays Christmas animation on Seiko
 *      S-4548ACG LCD
 * 
 */

#define F_CPU 8000000

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

#define I2CPORT PORTD
#define SCL 0
#define SDA 1

#define SCK_LOW() I2CPORT &= ~(1 << SCL) 
#define SCK_HI() I2CPORT |= (1 << SCL)
#define SDA_LOW() I2CPORT &= ~(1 << SDA)
#define SDA_HI() I2CPORT |= (1 << SDA)
#define i2cWait() asm volatile("nop\n\t");
const char snow1[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char snow2[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char snow3[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
const char snow4[] PROGMEM = {
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char snow5[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00};

PGM_P snow[] PROGMEM = {
	snow1,
	snow2,
	snow3,
	snow4,
	snow5
};

const char snowman1[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char snowman2[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0xF9, 0x0D, 0x03, 0x01, 0x99, 0x19, 0x01, 0x41, 0x19, 0x99, 0x01, 0x03, 0x05, 0xF9, 0x00, 0x00, 0x00, 0x00};
const char snowman3[] PROGMEM = {
0x00, 0x00, 0xC0, 0x60, 0x21, 0x12, 0x34, 0x79, 0x71, 0x72, 0x72, 0x72, 0x72, 0x71, 0xF9, 0xFC, 0xD2, 0x21, 0x60, 0xC0, 0x80, 0x00};
const char snowman4[] PROGMEM = {
0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x00, 0x00, 0x03, 0x1F, 0x0F, 0x1F, 0x00, 0x00, 0x03, 0xFC};
const char snowman5[] PROGMEM = {
0x03, 0x0C, 0x10, 0x20, 0x40, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x80, 0x80, 0x80, 0x40, 0x60, 0x30, 0x0C, 0x03};

PGM_P snowman[] PROGMEM = {
	snowman1,
	snowman2,
	snowman3,
	snowman4,
	snowman5
};

const char sleigh1[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB0, 0x88, 0x88, 0x86, 0x87, 0x87, 0x88, 0x88, 0x90, 0xA0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char sleigh2[] PROGMEM = {
0x1C, 0x1C, 0x1C, 0x1C, 0x12, 0x12, 0x12, 0x0F, 0x0F, 0x07, 0x3F, 0xFF, 0x3F, 0x3F, 0x3F, 0xFF, 0xA1, 0x21, 0xAD, 0xED, 0xE1, 0x43, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char sleigh3[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0xFF, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x09, 0x09, 0x09, 0x0A, 0x88, 0x90, 0xA0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00};
const char sleigh4[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x1F, 0x10, 0x10, 0x10, 0x70, 0xF0, 0xF0, 0xF0, 0xF0, 0xDF, 0x98, 0x18, 0xF8, 0x08, 0xE8, 0x48, 0x34, 0x1A, 0x19, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x05, 0x0A, 0xF4, 0x1C, 0xF8};
const char sleigh5[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x20, 0x50, 0x50, 0x50, 0x50, 0x5F, 0x5F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x53, 0x57, 0x5F, 0x57, 0x63, 0x8C, 0x4F, 0x68, 0x5C, 0x58, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x30, 0x28, 0x14, 0x0B, 0x06, 0x01};

PGM_P sleigh[] PROGMEM = {
	sleigh1,
	sleigh2,
	sleigh3,
	sleigh4,
	sleigh5
};

const char blank[] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void i2cStart(void);
void i2cStop(void);
//void i2cWait(void);
void i2cBit0(void);
void i2cBit1(void);
void i2cWrite(uint8_t byte);
void writePage(char page, PGM_P data, char width, char col);
void drawSnowman(void);
void drawSleigh(void);
void clear(uint8_t pos, uint8_t width);

void i2cStart(void) {
	SCK_HI();
	i2cWait();
	SDA_LOW();
	i2cWait();
}

void i2cStop(void) {
	SCK_HI();
	i2cWait();
	SDA_HI();
	i2cWait();
}

//void i2cWait(void) {
//	_delay_us(1);
//}

void i2cBit1() {
	SCK_LOW();
	i2cWait();
	SDA_HI();
	i2cWait();
	SCK_HI();
	i2cWait();
}

void i2cBit0() {
	SCK_LOW();
	i2cWait();
	SDA_LOW();
	i2cWait();
	SCK_HI();
	i2cWait();
}	

void i2cWrite(uint8_t byte) {
	uint8_t i;
	for (i = 128; i > 0; i = i >> 1) {
		if ((i & byte) > 0) {
			i2cBit1();
		}
		else {
			i2cBit0();
		};
	}
};

void writePage(char page, PGM_P data, char width, char col) {
	i2cStart();
	i2cWrite(0x74);
	i2cBit0();
	uint8_t command = (page << 5) | 1;
	i2cWrite(command);
	i2cBit0();
	uint8_t xpos = col;
	if (col > 100) {
		col = 0;
	}
	i2cWrite(col);
	i2cBit0();
	uint8_t i = 0, in_ram;
	for (i = 0; i < width; i++) {
		xpos++;
		if (xpos < 101) {
			in_ram = pgm_read_byte(&data[i]);
			i2cWrite(in_ram);
			i2cBit0();	
		}
	}
	i2cStop();
};

void drawSnowman(void) {
	static uint8_t manpos = 20;
	static int mandir = 2;
	if ((manpos >= 2) && (manpos <= 77)) {
		manpos += mandir;
	}
	else {
		if (mandir == 2) {
			mandir = 254;
		}
		else (mandir = 2);
		manpos += mandir;
	}
	uint8_t i;
	PGM_P pagep;
	for (i = 0; i < 5; i++) {
		pagep = (PGM_P)pgm_read_word(&(snowman[i]));
		writePage(i, pagep, 22, manpos);
	}
};

void clear(uint8_t pos, uint8_t width) {
	uint8_t i;
	for (i = 0; i < 5; i++) {
		writePage(i, blank, width, pos);
	}
};

void drawSleigh() {
	uint8_t spos = 215;
	uint8_t sdir = 2;
	uint8_t i;
	PGM_P pagep;
	//static uint8_t frame = 1;
	while ((spos >= 215)) {
		//frame ^= 0x01;
		//if (frame == 0) {
			writePage(0, sleigh1, 41, spos);
		//}
		//else {
		//	writePage(sleigh1_2, spos, 48);
		//}
		for (i = 1; i < 5; i++) {
			pagep = (PGM_P)pgm_read_word(&(sleigh[i]));
			writePage(i, pagep, 41, spos);
		}
		_delay_ms(50);
		clear(spos, sdir);
		spos += sdir;
	}
	while ((spos <= 100)) {
		//frame ^= 0x01;
		//if (frame == 0) {
			writePage(0, sleigh1, 41, spos);
		//}
		//else {
		//	writePage(sleigh1_2, spos, 48);
		//}

		for (i = 1; i < 5; i++) {
			pagep = (PGM_P)pgm_read_word(&(sleigh[i]));
			writePage(i, pagep, 41, spos);
		}
		_delay_ms(50);
		clear(spos, sdir);
		spos += sdir;
		
	}
};

int main(void) {
		DDRD = 0x03;
		DDRB = 0xFF;
			
		_delay_ms(20);
		i2cStart();
		i2cWrite(0x74);
		i2cBit0();
		i2cWrite(0x05);
		i2cBit0();
		i2cStop();
		_delay_ms(1);
		
		TCCR1A = (0x0F << 4) | 0x01;
		TCCR1B |= (1 << WGM12) | (1 << CS11);
		TCCR0B |= (1 << CS02);
		TIMSK |= (1 << TOIE0);
		sei();
				
		uint8_t offset = 0, offset2, result, i;
		PGM_P pagep;
		for (;;) {
			for (i = 0; i < 30; i++) {
				offset = (offset+4)%5;
					for (offset2 = 0; offset2 < 5; offset2++) {
						result = ((offset + offset2) % 5);
						pagep = (PGM_P)pgm_read_word(&(snow[result]));
						writePage(offset2, pagep, 101, 0);
					}
					drawSnowman();
				_delay_ms(300);	
			}
			drawSleigh();
			_delay_ms(200);
		}
				
	return 0;
}

ISR(TIMER0_OVF_vect) {
	static uint8_t pwm = 50;
	static uint8_t dir = 1;
	OCR1A = pwm;
	OCR1B = (49-pwm);
	if ((pwm == 255) && (dir == 1)) {
		dir = 255;
	}
	else if ((pwm == 80) && (dir == 255)) {
		dir = 1;
	}
	pwm += dir;
	
}
