/** * ---------------------------------------------------------------+ * @desc HD44780 LCD Driver * ---------------------------------------------------------------+ * Copyright (C) 2020 Marian Hrinko. * Written by Marian Hrinko (mato.hrinko@gmail.com) * * @author Marian Hrinko * @datum 18.11.2020 * @file hd44780.c * @tested AVR Atmega16a * * @depend hd44780.h * ---------------------------------------------------------------+ * @usage default set 16x2 LCD * 4-bit with 3 control wires (RW, RS, E) */ // include libraries #include //#include //#include #include "define.h" #include "BoardCfg.h" #include "hd44780.h" #include "timer.h" // +---------------------------+ // | Power on | // | Wait for more than 15 ms | // 15 ms wait // | after VCC rises to 4.5 V | // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // | 0 0 0 0 1 1 | // Initial sequence 0x30 // | Wait for more than 4.1 ms | // 4.1 ms us writing DATA into DDRAM or CGRAM // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // | 0 0 0 0 1 1 | // Initial sequence 0x30 // | Wait for more than 0.1 ms | // 100 us writing DATA into DDRAM or CGRAM // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // Initial sequence 0x30 // | 0 0 0 0 1 1 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared // | Wait for more than 45 us | // 37 us + 4 us = 41 us * (270/250) = 45us // +---------------------------+ // | // +---------------------------+ // 4bit mode 0x20 !!! MUST BE SET TIME, BF CHECK DOESN'T WORK CORRECTLY !!! // | RS R/W DB7 DB6 DB5 DB4 | // // | 0 0 0 0 1 0 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared // | Wait for more than 45 us | // !!! MUST BE SET DELAY TIME, BUSY FLAG CHECK DOESN'T WORK CORRECTLY !!! // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // Display off 0x08 // | 0 0 0 0 1 0 | // // | 0 0 1 0 0 0 | // // | Wait for BF Cleared | // Wait for BF Cleared // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // Display clear 0x01 // | 0 0 0 0 0 0 | // // | 0 0 0 0 0 1 | // // | Wait for BF Cleared | // Wait for BF Cleared // +---------------------------+ // | // +---------------------------+ // | RS R/W DB7 DB6 DB5 DB4 | // Entry mode set 0x06 // | 0 0 0 0 0 0 | // // | 0 0 0 1 1 0 | // shift cursor to the left, without text shifting // | Wait for BF Cleared | // Wait for BF Cleared // +---------------------------+ /** * @desc LCD display clear * * @param void * * @return void */ void HD44780_DisplayClear (void) { // Diplay clear HD44780_SendInstruction(HD44780_DISP_CLEAR); } /** * @desc LCD display on * * @param void * * @return void */ void HD44780_DisplayOn (void) { // send instruction - display on HD44780_SendInstruction(HD44780_DISP_ON); } /** * @desc LCD cursor on, display on * * @param void * * @return void */ void HD44780_CursorOn (void) { // send instruction - cursor on HD44780_SendInstruction(HD44780_CURSOR_ON); } /** * @desc LCD cursor off * * @param void * * @return void */ void HD44780_CursorOff (void) { // send instruction - cursor on HD44780_SendInstruction(HD44780_CURSOR_OFF); } /** * @desc LCD cursor blink, cursor on, display on * * @param void * * @return void */ void HD44780_CursorBlink (void) { // send instruction - Cursor blink HD44780_SendInstruction(HD44780_CURSOR_BLINK); } /** * @desc LCD draw char * * @param char * * @return void */ void HD44780_DrawChar (char character) { // Diplay clear HD44780_SendData(character); } /** * @desc LCD draw string * * @param char * * * @return void */ void HD44780_DrawString (char *str) { unsigned char i = 0; // loop through 5 bytes while (str[i] != '\0') { //read characters and increment index HD44780_SendData(str[i++]); } } /** * @desc Got to position x,y * * @param char * @param char * * @return char */ char HD44780_PositionXY (char x, char y) { if (x > HD44780_COLS || y > HD44780_ROWS) { // error return ERROR; } // check which row if (y == 0) { // send instruction 1st row HD44780_SendInstruction(HD44780_POSITION | (HD44780_ROW1_START + x)); } else if (y == 1) { // send instruction 2nd row HD44780_SendInstruction(HD44780_POSITION | (HD44780_ROW2_START + x)); } // success return 0; } /** * @desc Shift cursor / display to left / right * * @param char item {HD44780_CURSOR; HD44780_DISPLAY} * @param char direction {HD44780_RIGHT; HD44780_LEFT} * * @return char */ char HD44780_Shift (char item, char direction) { // check if item is cursor or display or direction is left or right if ((item != HD44780_DISPLAY) && (item != HD44780_CURSOR)) { // error return ERROR; } // check if direction is left or right if ((direction != HD44780_RIGHT) && (direction != HD44780_LEFT)) { // error return ERROR; } // cursor shift if (item == HD44780_CURSOR) { // right shift if (direction == HD44780_RIGHT) { // shit cursor / display to right / left HD44780_SendInstruction(HD44780_SHIFT | HD44780_CURSOR | HD44780_RIGHT); } else { // shit cursor / display to right / left HD44780_SendInstruction(HD44780_SHIFT | HD44780_CURSOR | HD44780_LEFT); } // display shift } else { // right shift if (direction == HD44780_RIGHT) { // shit cursor / display to right / left HD44780_SendInstruction(HD44780_SHIFT | HD44780_DISPLAY | HD44780_RIGHT); } else { // shit cursor / display to right / left HD44780_SendInstruction(HD44780_SHIFT | HD44780_DISPLAY | HD44780_LEFT); } } // success return 0; } /** * @desc LCD init - initialisation routine * * @param void * * @return void */ void HD44780_Init (void) { // set E as output //SETBIT(HD44780_DDR_E, HD44780_E); // set RS as output //SETBIT(HD44780_DDR_RS, HD44780_RS); // set RW as output //SETBIT(HD44780_DDR_RW, HD44780_RW); // set DB7-DB4 as output // HD44780_SetDDR_DATA4to7(); // clear RS // CLRBIT(HD44780_PORT_RS, HD44780_RS); LCD_RS_PIN = 0; // clear RW // CLRBIT(HD44780_PORT_RW, HD44780_RW); LCD_RW_PIN = 0; // clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // delay > 15ms _delay_ms(16); // Busy Flag (BF) cannot be checked in these instructions // --------------------------------------------------------------------- // Initial sequence 0x30 - send 4 bits in 4 bit mode // HD44780_SendInstruction(HD44780_INIT_SEQ);JFM HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ); // delay > 4.1ms _delay_ms(5); // pulse E //HD44780_PulseE(); // delay > 100us _delay_us(110); HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ); // pulse E // HD44780_PulseE(); // delay > 45us (=37+4 * 270/250) _delay_us(50); HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ); // 4 bit mode 0x20 - send 4 bits in 4 bit mode HD44780_Send4bitsIn4bitMode(HD44780_4BIT_MODE); // pulse E //HD44780_PulseE(); // delay > 45us (=37+4 * 270/250) _delay_us(50); // ---------------------------------------------------------------------- // 4-bit & 2-lines & 5x8-dots 0x28 - send 8 bits in 4 bit mode HD44780_SendInstruction(HD44780_4BIT_MODE | HD44780_2_ROWS | HD44780_FONT_5x8); // display off 0x08 - send 8 bits in 4 bit mode HD44780_SendInstruction(HD44780_DISP_OFF); // display clear 0x01 - send 8 bits in 4 bit mode HD44780_SendInstruction(HD44780_DISP_CLEAR); // entry mode set 0x06 - send 8 bits in 4 bit mode HD44780_SendInstruction(HD44780_ENTRY_MODE); } /** * @desc Check Busy Flag (BF) in 4 bit mode * * @param void * * @return void */ void HD44780_CheckBFin4bitMode (void) { unsigned char input = 0; Sleep(5); return; // clear DB7-DB4 as input HD44780_ClearDDR_DATA4to7(); // set pull-up resistors for DB7-DB4 // HD44780_SetPORT_DATA4to7(); // clear RS // CLRBIT(HD44780_PORT_RS, HD44780_RS); LCD_RS_PIN = 0; // set RW - read instruction // SETBIT(HD44780_PORT_RW, HD44780_RW); LCD_RW_PIN = 1; // test HIGH level on PIN DB7 // after clear PIN DB7 should continue // ------------------------------------- // us: 0.5|0.5|0.5 // ___ ___ // E: ___/ \___/ \__ // ___ ___ // DB7: \___/ \___/ \__ // while (1) { // Read upper nibble // -------------------------------- // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // PWeh > 0.5us _delay_us(0.5); // read upper nibble (tDDR > 360ns) input = HD44780_PIN_DATA; // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); // Read lower nibble // -------------------------------- // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // PWeh > 0.5us _delay_us(0.5); // read lower nibble (tDDR > 360ns) //input |= (unsigned char)(HD44780_PIN_DATA >> 4); char IsBusy = (HD44780_DATA7 == 1); // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); // // check if DB7 is cleared // if (!(input & (1 << HD44780_DATA7))) if (IsBusy == 0) { // if BF cleared -> end loop break; } } // clear RW // CLRBIT(HD44780_PORT_RW, HD44780_RW); LCD_RW_PIN = 0; // set DB7-DB4 as output HD44780_SetDDR_DATA4to7(); } /** * @desc Check Busy Flag (BF) in 8 bit mode * * @param void * @return void */ void HD44780_CheckBFin8bitMode (void) { } /** * @desc LCD send instruction * * @param unsigned short int * * @return void */ void HD44780_SendInstruction (unsigned short int data) { // Clear RS // HD44780_PORT_RS &= ~(1 << HD44780_RS); LCD_RS_PIN = 0; // 4bit mode // ------------------------------------------ if (HD44780_MODE == HD44780_4BIT_MODE) { // send required data in required mode HD44780_Send8bitsIn4bitMode(data); // check busy flag HD44780_CheckBFin4bitMode(); // 8 bit mode // ------------------------------------------ } else if (HD44780_MODE == HD44780_8BIT_MODE) { // send required data in required mode HD44780_Send8bitsIn8bitMode(data); // check busy flag HD44780_CheckBFin8bitMode(); } } /** * @desc LCD send data * * @param unsigned short int * * @return void */ void HD44780_SendData (unsigned short int data) { // Set RS // SETBIT(HD44780_PORT_RS, HD44780_RS); LCD_RS_PIN = 1; // 4bit mode // ------------------------------------------ if (HD44780_MODE == HD44780_4BIT_MODE) { // send required data in required mode HD44780_Send8bitsIn4bitMode(data); // check busy flag HD44780_CheckBFin4bitMode(); // 8 bit mode // ------------------------------------------ } else if (HD44780_MODE == HD44780_8BIT_MODE) { // send required data in required mode HD44780_Send8bitsIn8bitMode(data); // check busy flag HD44780_CheckBFin8bitMode(); } // Clear RS // CLRBIT(HD44780_PORT_RS, HD44780_RS); LCD_RS_PIN = 0; } /** * @desc LCD send 4bits instruction in 4 bit mode * * @param unsigned short int * * @return void */ void HD44780_Send4bitsIn4bitMode (unsigned short int data) { // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // send data to LCD HD44780_SetUppNibble(data); // PWeh delay time > 450ns _delay_us(0.5); // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); } /** * @desc LCD send 8bits instruction in 4 bit mode * * @param unsigned short int * * @return void */ void HD44780_Send8bitsIn4bitMode (unsigned short int data) { // Send upper nibble // ---------------------------------- // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // send data to LCD HD44780_SetUppNibble(data); // PWeh delay time > 450ns _delay_us(0.5); // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); // Send lower nibble // ---------------------------------- // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // send data to LCD HD44780_SetUppNibble(data << 4); // PWeh delay time > 450ns _delay_us(0.5); // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); } /** * @desc LCD send 8bits instruction in 8 bit mode * * @param unsigned short int * * @return void */ void HD44780_Send8bitsIn8bitMode (unsigned short int data) { // Set E SETBIT(HD44780_PORT_E, HD44780_E); // send data to LCD HD44780_SetUppNibble(data); // send data to LCD HD44780_SetLowNibble(data); // PWeh delay time > 450ns _delay_us(0.5); // Clear E CLRBIT(HD44780_PORT_E, HD44780_E); // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); } /** * @desc LCD send upper nibble * * @param unsigned short int * * @return void */ void HD44780_SetUppNibble (unsigned short int data) { // LCD_DB4_PIN = 0; // LCD_DB5_PIN = 0; // LCD_DB6_PIN = 0; // LCD_DB7_PIN = 0; unsigned short test = data; if (data & 0x80) { LCD_DB7_PIN = 1; } else { LCD_DB7_PIN = 0; } if (data & 0x40) { LCD_DB6_PIN = 1; } else { LCD_DB6_PIN = 0; } if (data & 0x20) { LCD_DB5_PIN = 1; } else { LCD_DB5_PIN = 0; } if (data & 0x10) { LCD_DB4_PIN = 1; } else { LCD_DB4_PIN = 0; } // clear bits DB7-DB4 // CLRBIT(HD44780_PORT_DATA, HD44780_DATA7); // CLRBIT(HD44780_PORT_DATA, HD44780_DATA6); // CLRBIT(HD44780_PORT_DATA, HD44780_DATA5); // CLRBIT(HD44780_PORT_DATA, HD44780_DATA4); // // set DB7-DB4 if corresponding bit is set // if (data & 0x80) { SETBIT(HD44780_PORT_DATA, HD44780_DATA7); } // if (data & 0x40) { SETBIT(HD44780_PORT_DATA, HD44780_DATA6); } // if (data & 0x20) { SETBIT(HD44780_PORT_DATA, HD44780_DATA5); } // if (data & 0x10) { SETBIT(HD44780_PORT_DATA, HD44780_DATA4); } } /** * @desc LCD send lower nibble * * @param unsigned short int * * @return void */ void HD44780_SetLowNibble (unsigned short int data) { // clear bits DB7-DB4 CLRBIT(HD44780_PORT_DATA, HD44780_DATA3); CLRBIT(HD44780_PORT_DATA, HD44780_DATA2); CLRBIT(HD44780_PORT_DATA, HD44780_DATA1); CLRBIT(HD44780_PORT_DATA, HD44780_DATA0); // set DB7-DB4 if corresponding bit is set if (data & 0x08) { SETBIT(HD44780_PORT_DATA, HD44780_DATA3); } if (data & 0x04) { SETBIT(HD44780_PORT_DATA, HD44780_DATA2); } if (data & 0x02) { SETBIT(HD44780_PORT_DATA, HD44780_DATA1); } if (data & 0x01) { SETBIT(HD44780_PORT_DATA, HD44780_DATA0); } } /** * @desc LCD pulse E * * @param void * * @return void */ void HD44780_PulseE (void) { // Set E // SETBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 1; // PWeh delay time > 450ns _delay_us(0.5); // Clear E // CLRBIT(HD44780_PORT_E, HD44780_E); LCD_E_PIN = 0; // TcycE > 1000ns -> delay depends on PWeh delay time // delay = TcycE - PWeh = 1000 - 500 = 500ns _delay_us(0.5); } /** * @desc Set PORT DB4 to DB7 * * @param void * * @return void */ void HD44780_SetPORT_DATA4to7 (void) { // set DB4-DB7 LCD_DB4_PIN = 1; LCD_DB5_PIN = 1; LCD_DB6_PIN = 1; LCD_DB7_PIN = 1; // SETBIT(HD44780_PORT_DATA, HD44780_DATA4); // SETBIT(HD44780_PORT_DATA, HD44780_DATA5); // SETBIT(HD44780_PORT_DATA, HD44780_DATA6); // SETBIT(HD44780_PORT_DATA, HD44780_DATA7); } /** * @desc Clear DDR DB4 to DB7 * * @param void * * @return void */ void HD44780_SetDDR_DATA4to7 (void) { // set DB4-DB7 LCD_DB4_PIN_DIR = PIN_OUTPUT; LCD_DB5_PIN_DIR = PIN_OUTPUT; LCD_DB6_PIN_DIR = PIN_OUTPUT; LCD_DB7_PIN_DIR = PIN_OUTPUT; // CLRBIT(HD44780_DDR_DATA, HD44780_DATA4); // CLRBIT(HD44780_DDR_DATA, HD44780_DATA5); // CLRBIT(HD44780_DDR_DATA, HD44780_DATA6); // CLRBIT(HD44780_DDR_DATA, HD44780_DATA7); } /** * @desc Set DDR DB4 to DB7 * * @param void * * @return void */ void HD44780_ClearDDR_DATA4to7 (void) { LCD_DB4_PIN_DIR = PIN_INPUT; LCD_DB5_PIN_DIR = PIN_INPUT; LCD_DB6_PIN_DIR = PIN_INPUT; LCD_DB7_PIN_DIR = PIN_INPUT; // SETBIT(HD44780_DDR_DATA, HD44780_DATA4); // SETBIT(HD44780_DDR_DATA, HD44780_DATA5); // SETBIT(HD44780_DDR_DATA, HD44780_DATA6); // SETBIT(HD44780_DDR_DATA, HD44780_DATA7); }