+#include <avr/io.h>
+#include <util/delay.h>
+
+#define USE_SHIFTREG 1
+
+#define NOP() __asm__("nop")
+
+void lcd_init(void);
+void lcd_on(void);
+void lcd_clear(void);
+void lcd_goto(uint8_t);
+void lcd_write(uint8_t);
+
+void lcd_clock(void)
+{
+ _delay_us(1);
+ PORTB |= 0x02;
+ _delay_us(1);
+ PORTB &= ~0x02;
+}
+
+void lcd_set_data(uint8_t d)
+{
+#ifdef USE_SHIFTREG
+ uint8_t i;
+ for(i=0; i<8; ++i)
+ {
+ PORTB = (PORTB&~0x04)|((d&1)<<2);
+ PORTB |= 0x08;
+ NOP();
+ PORTB &= ~0x08;
+ d >>= 1;
+ }
+#else
+ PORTD = (PORTD&0x3F) | (d<<6);
+ PORTB = (PORTB&0xC0) | (d>>2);
+#endif
+}
+
+void lcd_command(uint8_t c)
+{
+ PORTB &= ~0x01;
+ lcd_set_data(c);
+ lcd_clock();
+}
+
+void lcd_init(void)
+{
+ DDRB |= 0x0F;
+ PORTB &= ~0x0F;
+ _delay_ms(20);
+ lcd_command(0x38);
+ _delay_us(50);
+}
+
+void lcd_on(void)
+{
+ lcd_command(0x0C);
+ _delay_us(50);
+}
+
+void lcd_clear(void)
+{
+ lcd_command(0x01);
+ _delay_us(1500);
+}
+
+void lcd_gotoxy(uint8_t x, uint8_t y)
+{
+ uint8_t a = x+10*y;
+ if(y&1)
+ a += 54;
+ lcd_command(a|0x80);
+ _delay_us(50);
+}
+
+void lcd_write(uint8_t c)
+{
+ PORTB |= 0x01;
+ lcd_set_data(c);
+ lcd_clock();
+ _delay_us(50);
+}