template < typename LCDdata >
class LCD
{
    public:
        LCD ( ) ;
        void print( u08 text );
        void print( const char* text );
        void printHex( u08 num ) { printUInt(num,16); };
        void printBin( u08 num );
        void printDec( u08 num ) { printUInt(num,10); };
        void printHex( s08 num ) { printSInt(num,16); };
        void printBin( s08 num ) { printSInt(num, 2); };
        void printDec( s08 num ) { printSInt(num,10); };
        void home()              { sendcmd ( 2 ); };
        void goToLine( u08 line ) { if ( line ) sendcmd( 0x40 + 0x80 ); else sendcmd( 0x80 );};
        void clear() { sendcmd(1); };
    private:
        void send( LCDdata data );
        void wait();
        void sendcmd ( u08 data );
        void delay() { _delay_loop_2(6000);};
        void shortdelay() { _delay_loop_1(100);};
        inline void printUInt( u08 num, u08 radix );
        inline void printSInt( s08 num, u08 radix );
};

template <typename LCDdata>
LCD<LCDdata>::LCD( )
{

    _delay_loop_2(20000);

    LCDdata t;
    t.rs=0;
    // t.rw=0;
    t.e =0;
    t.db=3;

    send(t);
    delay();
    send(t);
    delay();
    send(t);
    delay();

    t.db=2;
    send(t);

    // function set
    if ( LCDdata::lines == 1 )
        sendcmd( 32 );
    else
        sendcmd( 32+8 );

    // display on
    sendcmd ( 4 + 8 );

    // clear display
    sendcmd ( 1 );

    // entry mode set
    sendcmd ( 2 + 4 );

    // cursor display shift
    sendcmd ( 4 + 16 );

    home();
}


template <typename LCDdata>
void LCD<LCDdata>::print( u08 text )
{
    wait();
    LCDdata data;
    data.db = text >> 4;
    data.rs=1;
    // data.rw=0;
    send(data);
    data.db = text & 0xf;
    send(data);
}


template <typename LCDdata>
void LCD<LCDdata>::printSInt( s08 num, u08 radix )
{
    char buf[10];
    print ( itoa( num, buf, radix ) );
}

template <typename LCDdata>
void LCD<LCDdata>::printUInt( u08 num, u08 radix )
{
    char buf[10];
    print ( utoa( num, buf, radix ) );
}

template <typename LCDdata>
void LCD<LCDdata>::printBin( u08 num )
{
    for ( char i = 0; i < 8; i++ )
        if( num & ( 1 << ( 7-i)))
            print('1');
        else
            print('0');
}


template <typename LCDdata>
void LCD<LCDdata>::print( const char* text )
{
    while ( *text )
        print( *(text++));
}


template <typename LCDdata>
void LCD<LCDdata>::sendcmd( u08 data )
{
    wait();
    LCDdata d;
    d.db = data >> 4;
    d.rs=0;
    // d.rw=0;
    send(d);
    d.db = data & 0xf;
    send(d);
}

template <typename LCDdata>
void LCD<LCDdata>::send( LCDdata data )
{
    data.e = 1;
    LCDdata::set( data );
    _delay_loop_1(100);
    data.e = 0;
    LCDdata::set( data );
    _delay_loop_1(100);
}


template <typename LCDdata>
void LCD<LCDdata>::wait()
{
    delay();
}