// ****************************************
// ***        SET FREQUENCY CORRECTLY    **

#define F_CPU 1000000UL

// ****************************************


/*
PIN SETUP

pc5 - rs - data/command (0 - command, 1 - data)
pc4 - e  - strobe (start after data is set, at least 230 ns)
pc3 - d7 - data
pc2 - d6 - data
pc1 - d5 - data
pc0 - d4 - data

pd0 - r/w - write - 1, read -0+pullup
pd7 - test led anode - not really needed to onnect

bit values in PORT C

// 32  16   8  4  2  1
// rs  e   d7 d6 d5 d4 


*/


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



// 150 ns delay
static inline void Tick(void)
{

// 0mhz - 3mhz - 1 nop 
// 3mhz - 7mhz - 2 nops
// 7 - 11 - 3
// 11 - 15 - 4
// 15 - 19 - 5
// 19 ..23 - 6
// 23 .. - 8

#if F_CPU > 23000000UL
    asm volatile("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop"::);
#elif F_CPU > 19000000UL
    asm volatile("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop"::);
#elif F_CPU > 15000000UL
    asm volatile("nop\n\tnop\n\tnop\n\tnop\n\tnop"::);
#elif F_CPU > 11000000UL
    asm volatile("nop\n\tnop\n\tnop\n\tnop"::);
#elif F_CPU > 7000000UL
    asm volatile("nop\n\tnop\n\tnop"::);
#elif F_CPU > 3000000UL
    asm volatile("nop\n\tnop"::);
#else 
    asm volatile("nop\n"::);
#endif
}


void pulse_e() {
	// turn on e
	PORTC |= 0b00010000;
	// wait 1 microsecons (at least 250 ns)
	Tick();
	// turn off e
	PORTC &= ~0b00010000;
	// way 1 microsecons (at least 250 ns)
	Tick();
	
}



void sendcommand2(unsigned char b){
	unsigned char c=b;
	unsigned char state;

	DDRC =0b11110000; // set read for the 4 bits on data port
	PORTC|=0b00001111; // turn on pull ups on input lines
	PORTC&=0b11011111; // a0 (rs) to zero!
	
	PORTD|=0b00000001; // turn read mode on
	
	do {
		Tick(); // wait a bit
		PORTC |= 0b00010000;  // e is high
		Tick();
		state=PINC; // read BS bit and address
		PORTC &= 0b11101111; // e is low
		Tick();
		// repeat E cycle to read the next nibble
		PORTC |= 0b00010000;  // e is high
		Tick();
		PORTC &= 0b11101111; // e is low
		state=state & 0b00001000;
	} while (state);

	Tick();


	PORTD &= 0b11111110; // back to write mode

	PORTC=0; // turn off pullups
	DDRC =0xff; // back to write mode
	
	c=c>>4;
	c|=0b00000000;
	PORTC=c;
	pulse_e();
	c=b;
	c&=0b00001111;
	c|=0b00000000;
	PORTC=c;
	pulse_e(); 

}

void sendbyte2(unsigned char b){
	unsigned char c=b;
	unsigned char state;

	DDRC  =0b11110000; // set read for the 4 bits on data port
	PORTC|=0b00001111; // turn on pull ups on input lines
	PORTC&=0b11011111; // a0 (rs) to zero!
	
	PORTD|=0b00000001; // turn read mode on
	
	do {
		Tick(); // wait a bit
		PORTC |= 0b00010000;  // e is high
		Tick();
		state=PINC; // read BS bit and address
		PORTC &= 0b11101111; // e is low
		Tick();
		// repeat E cycle to read the next nibble
		PORTC |= 0b00010000;  // e is high
		Tick();
		PORTC &= 0b11101111; // e is low
		state=state & 0b00001000;

	} while (state);

	Tick();


	PORTD &= 0b11111110; // back to write mode

	PORTC=0; // turn off pullups
	DDRC =0xff; // back to write mode

	c=c>>4;
	c|=0b00100000;
	PORTC=c;
	pulse_e();
	c=b;
	c&=0b00001111;
	c|=0b00100000;
	PORTC=c;
	pulse_e(); 

}


void waitbs(){
	unsigned char state;

	DDRC  =0b11110000; // set read for the 4 bits on data port
	PORTC|=0b00001111; // turn on pull ups on input lines
	PORTC&=0b11011111; // a0 (rs) to zero!
	
	PORTD|=0b00000001; // turn read mode on
	
	do {
		Tick(); // wait a bit
		PORTC |= 0b00010000;  // e is high
		Tick();
		state=PINC; // read BS bit and address
		PORTC &= 0b11101111; // e is low
		Tick();
		// repeat E cycle to read the next nibble
		PORTC |= 0b00010000;  // e is high
		Tick();
		PORTC &= 0b11101111; // e is low
		state=state & 0b00001000;

	} while (state);

	Tick();


	PORTD &= 0b11111110; // back to write mode

	PORTC=0; // turn off pullups
	DDRC =0xff; // back to write mode

}

unsigned char getaddr(){
	unsigned char s1;
	unsigned char s2;
	unsigned char addr=0xff;

	DDRC =0b11110000; // set read for the 4 bits on data port
	PORTC|=0b00001111; // turn on pull ups on input lines
	PORTC&=0b11011111; // a0 (rs) to zero!
	
	PORTD|=0b00000001; // turn read mode on
	
	Tick(); // wait a bit
	PORTC |= 0b00010000;  // e is high

	Tick();
	s1=PINC; // read address

	
	PORTC &= 0b11101111; // e is low
	
	Tick();
	PORTC |= 0b00010000;  // e is high
	Tick();

	s2=PINC; // read address

	PORTC &= 0b11101111; // e is low

	Tick();
	PORTD &= 0b11111110; // back to write mode

	PORTC=0; // turn off pullups
	DDRC =0xff; // back to write mode

	s1&=0b00000111;
	s2&=0b00001111;
	s1<<=4;
	addr=s1|s2;

	return addr;

}


unsigned char readdata(){
	unsigned char s1;
	unsigned char s2;
	unsigned char data=0xff;

	DDRC  =0b11110000; // set read for the 4 bits on data port
	PORTC|=0b00101111; // turn on pull ups on input lines, a0 (rs) is high for data read
		
	PORTD|=0b00000001; // turn read mode on
	
	Tick(); // wait a bit
	PORTC |= 0b00010000;  // e is high

	Tick();
	s1=PINC; // read address

	PORTC &= 0b11101111; // e is low
	
	Tick();
	PORTC |= 0b00010000;  // e is high
	Tick();

	s2=PINC; // read address

	PORTC &= 0b11101111; // e is low

	Tick();
	PORTD &= 0b11111110; // back to write mode

	PORTC=0; // turn off pullups
	DDRC =0xff; // back to write mode

	s1&=0b00001111;
	s2&=0b00001111;
	s1<<=4;
	data=s1|s2;

	return data;

}


void sendstring2(char* str){
	while (*str){
		sendbyte2((unsigned char)*str);
		str++;
	}
}


int main() {
	unsigned char i=0;


	DDRD  =0b10000001;	
	
	// blink led a bit to show that mcu is alive
	// blinking must at at 1 second between on/off. If not - freq is wrong!
	PORTD|=0b10000000;
	_delay_ms(500);_delay_ms(500);
	PORTD&=0b01111111;
	_delay_ms(500);_delay_ms(500);
	PORTD|=0b10000000;
	_delay_ms(500);_delay_ms(500);
	PORTD&=0b01111111;
	_delay_ms(500);_delay_ms(500);

	DDRC=0xFF; // all out

	PORTD&=0b11111110;


	// initial deay, it is not needed in THIS case, but usually you need it
	_delay_ms(40);

	// set 8 bit data
	PORTC=0b00000011;
	pulse_e();
	_delay_us(40);

	// set 8 bit data
	PORTC=0b00000011;
	pulse_e();
	_delay_us(40);

	// set 8 bit data
	PORTC=0b00000011;
	pulse_e();
	_delay_us(40);
	
	// set 4 bit data
	PORTC=0b00000010;
	pulse_e();
	
	_delay_ms(100);


	// set params
	PORTC=0b00000010;
	pulse_e(); _delay_ms(20);
	PORTC=0b00001010;
	pulse_e(); _delay_ms(20);

	// display off
	PORTC=0b00000000;
	pulse_e(); _delay_ms(20);
	PORTC=0b00001000;
	pulse_e(); _delay_ms(20);

	// display clear
	PORTC=0b00000000;
	pulse_e(); _delay_ms(20);
	PORTC=0b00000001;
	pulse_e(); _delay_ms(20);

	// entry mode
	PORTC=0b00000000;
	pulse_e(); _delay_ms(20);
	PORTC=0b00000110;
	pulse_e(); _delay_ms(20);


	// display on
	PORTC=0b00000000;
	pulse_e(); _delay_ms(20);
	PORTC=0b00001110;
	pulse_e(); _delay_ms(20);

	PORTD|=0b10000000;
	
	sendbyte2('X');
	sendbyte2('X');
	waitbs(); //getaddr does not wait for bs, but we need to give lcd some time to complete AC change
	i=getaddr()+0x30; // cursor position must be 2, without waitbs at high freq we get1 - that is why waitbs is needed
	sendbyte2(i);
	sendbyte2('X');
	sendbyte2('X');
	sendbyte2('X');
	sendbyte2('X');

	_delay_ms(500);_delay_ms(500);
	_delay_ms(500);_delay_ms(500);


	PORTD&=0b01111111;

	sendcommand2(0b00000001);
	sendstring2("copy");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b10000000);
	// now wait until address set is conlkete because there is no wait in the readdata
	waitbs();
	i=readdata();
	sendcommand2(0b10000100);
	sendbyte2(i);

	sendcommand2(0b10000001);
	waitbs();
	i=readdata();
	sendcommand2(0b10000101);
	sendbyte2(i);

	sendcommand2(0b10000010);
	waitbs();
	i=readdata();
	sendcommand2(0b10000110);
	sendbyte2(i);

	sendcommand2(0b10000011);
	waitbs();
	i=readdata();
	sendcommand2(0b10000111);
	sendbyte2(i);

	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00000001);
	sendstring2("move to0");
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b10000000);
	_delay_ms(500);_delay_ms(500);
	
	sendstring2("clear");
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00000001);
	_delay_ms(500);_delay_ms(500);

	sendstring2("go home");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00000010);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00000001);


	sendstring2("cur<-xx");
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00000100);

	sendstring2("123");
	_delay_ms(500);_delay_ms(500);
	_delay_ms(500);_delay_ms(500);


	sendcommand2(0b00000110);
	sendcommand2(0b00000001);
	
	sendstring2("shift1<");
	_delay_ms(500);_delay_ms(500);


	sendcommand2(0b10000100);
	

	// shift display 1
	sendcommand2(0b00000111);
	
	
	sendstring2("1");
	_delay_ms(500);

	sendstring2("2");
	_delay_ms(500);

	sendstring2("3");
	_delay_ms(500);

	sendstring2("4");
	_delay_ms(500);

	sendstring2("5");
	_delay_ms(500);

	sendstring2("6");
	_delay_ms(500);

	sendstring2("7");
	_delay_ms(500);

	sendstring2("8");
	_delay_ms(500);
	sendstring2("9");
	_delay_ms(500);_delay_ms(500);
	_delay_ms(500);_delay_ms(500);


	sendcommand2(0b00000100);
	sendcommand2(0b00000001);
	

	sendstring2("shift0>");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b10000100);
	
	// shift display 0
	sendcommand2(0b00000101);
	
	
	sendstring2("1");
	_delay_ms(500);

	sendstring2("2");
	_delay_ms(500);

	sendstring2("3");
	_delay_ms(500);

	sendstring2("4");
	_delay_ms(500);

	sendstring2("5");
	_delay_ms(500);

	sendstring2("6");
	_delay_ms(500);

	sendstring2("7");
	_delay_ms(500);

	sendstring2("8");
	_delay_ms(500);
	sendstring2("9");
	_delay_ms(500);_delay_ms(500);
	_delay_ms(500);_delay_ms(500);
	

	sendcommand2(0b00000100);
	sendcommand2(0b00000001);
	
	sendstring2("cursor");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001100);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001101);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001110);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001111);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00000100);
	sendcommand2(0b00000001);
	
	sendstring2("shiftcur");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00010000);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00010000);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00010000);
	_delay_ms(500);_delay_ms(500);


	sendcommand2(0b00000001);
	
	sendstring2("shiftdis");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00011000);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00011000);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00011100);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00011100);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00011100);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00011100);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00011000);
	_delay_ms(500);_delay_ms(500);
	sendcommand2(0b00011000);
	_delay_ms(500);_delay_ms(500);

	
	sendcommand2(0b00000001);
	
	sendstring2("off/on");
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001000);
	_delay_ms(500);_delay_ms(500);

	sendcommand2(0b00001100);
	_delay_ms(500);_delay_ms(500);


		
	sendcommand2(0b00000001);
	

	sendbyte2(0);
	sendbyte2('<');
	sendbyte2('-');


	sendcommand2(0b01000000);
	sendbyte2(1);
	sendbyte2(3);
	sendbyte2(7);
	sendbyte2(15);
	sendbyte2(31);
	sendbyte2(63);
	sendbyte2(127);
	sendbyte2(255);
	sendcommand2(0b10000101);
	sendbyte2('!');
	_delay_ms(500);_delay_ms(500);


	i=0;
	while(1){
		sendcommand2(0b01000000);
		for(int k=0;k<8;k++){
			sendbyte2(i+k);
		}
		_delay_ms(50);
		i++;
		
	}


	
}
