/*
  Routines om de LCD-module te bedienen

  Pros 2007
*/



#define LCD_DDR          DDRC
#define LCD_PORT         PORTC
#define LCD_RS           PC4
#define LCD_E            PC5
#define LCD_E_DDR        DDRC
#define LCD_RS_PORT      PORTC
#define LCD_E_PORT       PORTC


// instruction register bit positions
#define LCD_CLR             0                                   // DB0: clear display
#define LCD_HOME            1                                   // DB1: return to home position
#define LCD_ENTRY_MODE      2                                   // DB2: set entry mode
#define LCD_ENTRY_INC       1                                   // DB1: 1=increment, 0=decrement
#define LCD_ENTRY_SHIFT     0                                   // DB2: 1=display shift on
#define LCD_ON              3                                   // DB3: turn lcd/cursor on
#define LCD_ON_DISPLAY      2                                   // DB2: turn display on
#define LCD_ON_CURSOR       1                                   // DB1: turn cursor on
#define LCD_ON_BLINK        0                                   // DB0: blinking cursor ?
#define LCD_MOVE            4                                   // DB4: move cursor/display
#define LCD_MOVE_DISP       3                                   // DB3: move display (0-> cursor) ?
#define LCD_MOVE_RIGHT      2                                   // DB2: move right (0-> left) ?
#define LCD_FUNCTION        5                                   // DB5: function set
#define LCD_FUNCTION_8BIT   4                                   // DB4: set 8BIT mode (0->4BIT mode)
#define LCD_FUNCTION_2LINES 3                                   // DB3: two lines (0->one line)
#define LCD_FUNCTION_10DOTS 2                                   // DB2: 5x10 font (0->5x7 font)
#define LCD_CGRAM           6                                   // DB6: set CG RAM address
#define LCD_DDRAM           7                                   // DB7: set DD RAM address
#define LCD_BUSY            7                                   // DB7: LCD is busy

// set entry mode: display shift on/off, dec/inc cursor move direction
#define LCD_ENTRY_DEC            0x04                           // display shift off, dec cursor move dir
#define LCD_ENTRY_DEC_SHIFT      0x05                           // display shift on,  dec cursor move dir
#define LCD_ENTRY_INC_           0x06                           // display shift off, inc cursor move dir
#define LCD_ENTRY_INC_SHIFT      0x07                           // display shift on,  inc cursor move dir

// display on/off, cursor on/off, blinking char at cursor position
#define LCD_DISP_OFF             0x08                           // display off
#define LCD_DISP_ON              0x0C                           // display on, cursor off
#define LCD_DISP_ON_BLINK        0x0D                           // display on, cursor off, blink char
#define LCD_DISP_ON_CURSOR       0x0E                           // display on, cursor on
#define LCD_DISP_ON_CURSOR_BLINK 0x0F                           // display on, cursor on, blink char

// move cursor/shift display
#define LCD_MOVE_CURSOR_LEFT     0x10                           // move cursor left  (decrement)
#define LCD_MOVE_CURSOR_RIGHT    0x14                           // move cursor right (increment)
#define LCD_MOVE_DISP_LEFT       0x18                           // shift display left
#define LCD_MOVE_DISP_RIGHT      0x1C                           // shift display right

// function set: set interface data length and number of display lines
#define LCD_FUNCTION_4BIT_2LINES 0x28                           // 4-bit interface, dual line, 5x7 dots
#define LCD_FUNCTION_8BIT_1LINE  0x30                           // 8-bit interface, single line, 5x7 dots
#define LCD_MODE_DEFAULT     ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC))

// normally you do not change the following
#define LCD_LINES           2                                   // visible lines
#define LCD_LINE_LENGTH  0x40                                   // internal line length
#define LCD_START_LINE1  0x00                                   // DDRAM address of first char of line 1
#define LCD_START_LINE2  0x40                                   // DDRAM address of first char of line 2
#define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_2LINES

#define lcd_puts_P(__s) lcd_puts_p(P(__s))
#define lcd_cmd_mode()  cbi(LCD_RS_PORT, LCD_RS)                // RS=0  command mode
#define lcd_data_mode() sbi(LCD_RS_PORT, LCD_RS)                // RS=1  data mode
#define lcd_command(x)  lcd_write(x, 0)                         // Geef LCD-commando
#define lcd_putc(x)     lcd_write(x, 1)                         // Stuur karakter naar LCD



static void lcd_out_low(unsigned char d)
{                                                               // output low nibble
    sbi(LCD_E_PORTLCD_E);                                     // E hoog
    LCD_PORT &= 0xF0;                                           // Bit0 ... Bit3 = 0
    d &= 0x0F;
    LCD_PORT |= d;
    delay(4);
    cbi(LCD_E_PORTLCD_E);                                     // E laag
}


static void lcd_out_high(unsigned char d)
{                                                               // output high nibble
    sbi(LCD_E_PORTLCD_E);                                     // E hoog
    LCD_PORT &= 0xF0;                                           // Bit0 ... Bit3 = 0
    d = d >> 4;                                                 // D7 ... D4 -> D3 ... D0
    d &= 0x0F;
    LCD_PORT |= d;
    delay(4);
    cbi(LCD_E_PORTLCD_E);                                     // E laag
}


static void lcd_write(unsigned char dataunsigned char rs)
{
    if (rs == TXT) {
        if (data == 0xB5) {                                     // µ?
            data = 0xE4;                                        // Daar gebruikt de LCD-module een ander teken voor
        }
        lcd_data_mode();                                        // RS=1: write data
    } else {
        lcd_cmd_mode();                                         // RS=0: write instruction    
    }
    lcd_out_high(data);                                         // output high nibble first
    delay(4);
    lcd_out_low(data);                                          // output low nibble
    _delay_us(40);
}


void lcd_clrscr(void)
{
    lcd_command(1 << LCD_CLR);                                  // clear lcd and set cursor to home position
    delay_ms(2);
}


void lcd_home(void)
{
    lcd_command(1 << LCD_HOME);                                 // set cursor to home position
    delay_ms(2);
}


void lcd_puts(const char *s)
{
    while (*s) {                                                // print string on lcd
        lcd_putc(*s);
        s++;
    }
}


void lcd_puts_p(const prog_char * progmem_s)
{
    register char c;

    while ((c = PRG_RDB(progmem_s++))) {                        // print string from program memory on lcd
        lcd_putc(c);
    }
}


// Ga naar regel y (1 ... 4), karakter x (1 ... 20)
void lcd_gotoyx(unsigned char yunsigned char x)
{
    x--;
    x += linestart[y];
    lcd_command((1 << LCD_DDRAM) + x);                          // goto position (x,y)
}


void lcd_clearline(unsigned char line)
{
    lcd_command((1 << LCD_DDRAM) + linestart[line]);
    for (line = 0line < 20line++) {
        lcd_putc(' ');
    }
}


// Print een string op regel y, plaats x
void lcd_puts_at(unsigned char yunsigned char xconst char *s)
{
    x--;
    x += linestart[y];
    lcd_command((1 << LCD_DDRAM) + x);                          // goto position (x,y)

    while (*s) {                                                // print string on lcd
        lcd_putc(*s);
        s++;
    }
}


// initialize display and select type of cursor
// dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR, LCD_DISP_CURSOR_BLINK
void lcd_init(unsigned char dispAttr)
{
    LCD_DDR |= 0x3F;                                            // PB7 ... PB4 = lcd_data
    sbi(LCD_E_DDRLCD_E);
    sbi(LCD_E_DDRLCD_RS);
    sbi(LCD_E_PORTLCD_E);                                     // E hoog
    lcd_cmd_mode();
    delay_ms(24);                                               // wait 16ms or more after power-on
    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);                      // initial write to lcd is 8bit
    delay_ms(6);                                                // delay, busy flag can't be checked here
    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
    _delay_us(110);                                             // delay, busy flag can't be checked here
    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
    _delay_us(64);                                              // delay, busy flag can't be checked here
    lcd_out_high(LCD_FUNCTION_4BIT_2LINES);                     // set IO mode to 4bit

    // from now the lcd only accepts 4 bit I/O, we can use lcd_command()
    lcd_command(LCD_FUNCTION_DEFAULT);                          // function set: display lines
    lcd_command(LCD_DISP_OFF);                                  // display off
    lcd_clrscr();                                               // display clear
    lcd_command(LCD_MODE_DEFAULT);                              // set entry mode
    lcd_command(dispAttr);                                      // display/cursor control
}


void display_dat_item(unsigned char item)
{
    unsigned char tientalleneenheden;

    tientallen = item / 10;
    eenheden = item - (tientallen * 10);
    lcd_write(tientallen + '0'TXT);                           // Tientallen op scherm plaatsen
    lcd_write(eenheden + '0'TXT);                             // Eenheden op scherm plaatsen
}


// Verstuur een unsigned int als ascii-string naar de LCD-module
// dp bepaalt hoeveel decimale tekens er na de dp moeten komen (0 = geen dp)
void dec2LCD(unsigned int getalchar dp)
{
    unsigned int tmpdeeltal = 10000;
    char cntzeroflag = 0;

    for (cnt = 5cnt > 0cnt--) {
        if (dp == cnt) {                                        // dp plaatsen?
            if (zeroflag == 0) {                                // reeds cijfers verzonden?
                lcd_write('0'TXT);                            // nee, dan eerst een 0
            }
            lcd_write('.'TXT);
            zeroflag = 1;                                       // na dp alle nullen afdrukken
        }
        tmp = 0;
        while (deeltal > getal) {
            getal -= deeltal;
            tmp++;
        }
        if (tmp > 0) {                                          // als dat geen 0 is
            zeroflag = 1;                                       // worden alle volgende decaden afgedrukt
            lcd_write(tmp + '0'TXT);                          // ascii-teken verzenden
        } else if (zeroflag == 1) {                             // Is dit de eerste 0?
            lcd_write('0'TXT);                                // nee; '0' verzenden
        }
        deeltal /= 10;                                          // deeltal aanpassen voor volgende decade
    }
}

// Plaats een unsigned long op het LCD
void UL2LCD(unsigned long getal)
{
    unsigned long deeltal = 1000000000UL;
    char tmpcntzeroflag = 0;

    for (cnt = 10cnt > 1cnt--) {
        tmp = 0;
        while (getal >= deeltal) {
            tmp++;
            getal -= deeltal;                                   // decade berekenen
        }
        if (tmp > 0) {                                          // als dat geen 0 is
            zeroflag = 1;                                       // worden alle volgende decaden afgedrukt
            lcd_putc(tmp + '0');                                // ascii-teken verzenden
        } else if (zeroflag == 1) {                             // Is dit de eerste 0?
            lcd_putc('0');                                      // nee; '0' verzenden
        }
        deeltal /= 10UL;                                        // deeltal aanpassen voor volgende decade
    }
    lcd_putc((unsigned chargetal + '0');
}


// Verstuur de cijfers na de komma (duizendsten) op het LCD
void DS2LCD(unsigned long getal)
{
    char tmp;

    if (getal > 999) {
        return;
    }
    tmp = 0;
    while (getal >= 100) {
        tmp++;
        getal -= 100;                                           // decade berekenen
    }
    lcd_putc(tmp + '0');                                        // ascii-teken verzenden
    tmp = 0;
    while (getal >= 10) {
        tmp++;
        getal -= 10;                                            // decade berekenen
    }
    lcd_putc(tmp + '0');                                        // ascii-teken verzenden
    lcd_putc((unsigned chargetal + '0');
}


void float2LCD(double getal)
{
    unsigned long eenheden;
    unsigned long duizendsten;

    eenheden = (unsigned longgetal;
    getal -= (doubleeenheden;
    duizendsten = (unsigned long) (getal *= 1000.0);
    UL2LCD(eenheden);
    lcd_putc('.');
    DS2LCD((unsigned longduizendsten);
}


// Toon alle gegevens op het display

//      F: 12345678.90Hz
//      P: 50%
//      DDS: 15000000Hz
//      Mode: frequency
//      Mode: duty-cycle
void display_ALL(void)
{
    lcd_clrscr();
    lcd_puts_P("Fr: ");
    float2LCD(OutputFrequency);
    lcd_puts_P("Hz");
    lcd_puts_at(21"Pb: ");
    float2LCD(pulsbreedte);
    lcd_putc('%');
    switch (KB_mode) {
    case FREQ:
        lcd_puts_at(31"Frequentie:");
        break;
    case PULS:
        lcd_puts_at(31"Pulsbreedte:");
        break;
    case SW_START:
        lcd_puts_at(31"Sweep-frequentie 1:");
        break;
    case SW_END:
        lcd_puts_at(31"Sweep-frequentie 2:");
        break;
    case SW_DELAY:
        lcd_puts_at(31"Sweep-delay in µS:");
        break;
    case SW_STEP:
        lcd_puts_at(31"Sweep-step in Hz:");
        break;
    case SWEEP:
        lcd_puts_at(31"Start sweep now?");
        break;
    default:
        break;
    }
    lcd_puts_at(41" ");
}