/* lcdpcf8574 lib 0x03 copyright (c) Davide Gironi, 2013 Released under GPLv3. Please refer to LICENSE file for licensing information. */ #include #include #include #include "pcf8574.h" #include "lcdpcf8574.h" #define lcd_e_delay() __asm__ __volatile__( "rjmp 1f\n 1:" ); #define lcd_e_toggle() toggle_e() #if LCD_LINES==1 #define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE #else #define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES #endif volatile uint8_t dataport = 0; /* ** function prototypes */ static void toggle_e(void); /* ** local functions */ /************************************************************************* delay loop for small accurate delays: 16-bit counter, 4 cycles/loop *************************************************************************/ static inline void _delayFourCycles(unsigned int __count) { if ( __count == 0 ) __asm__ __volatile__( "rjmp 1f\n 1:" ); // 2 cycles else __asm__ __volatile__ ( "1: sbiw %0,1" "\n\t" "brne 1b" // 4 cycles/loop : "=w" (__count) : "0" (__count) ); } /************************************************************************* delay for a minimum of microseconds the number of loops is calculated at compile-time from MCU clock frequency *************************************************************************/ #define delay(us) _delayFourCycles( ( ( 1*(F_CPU/4000) )*us)/1000 ) /* toggle Enable Pin to initiate write */ static void toggle_e(void) { pcf8574_setoutputpinhigh(LCD_PCF8574_DEVICEID, LCD_E_PIN); lcd_e_delay(); pcf8574_setoutputpinlow(LCD_PCF8574_DEVICEID, LCD_E_PIN); } /************************************************************************* Low-level function to write byte to LCD controller Input: data byte to write to LCD rs 1: write data 0: write instruction Returns: none *************************************************************************/ static void lcd_write(uint8_t data,uint8_t rs) { if (rs) /* write data (RS=1, RW=0) */ dataport |= _BV(LCD_RS_PIN); else /* write instruction (RS=0, RW=0) */ dataport &= ~_BV(LCD_RS_PIN); dataport &= ~_BV(LCD_RW_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); /* output high nibble first */ dataport &= ~_BV(LCD_DATA3_PIN); dataport &= ~_BV(LCD_DATA2_PIN); dataport &= ~_BV(LCD_DATA1_PIN); dataport &= ~_BV(LCD_DATA0_PIN); if(data & 0x80) dataport |= _BV(LCD_DATA3_PIN); if(data & 0x40) dataport |= _BV(LCD_DATA2_PIN); if(data & 0x20) dataport |= _BV(LCD_DATA1_PIN); if(data & 0x10) dataport |= _BV(LCD_DATA0_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); lcd_e_toggle(); /* output low nibble */ dataport &= ~_BV(LCD_DATA3_PIN); dataport &= ~_BV(LCD_DATA2_PIN); dataport &= ~_BV(LCD_DATA1_PIN); dataport &= ~_BV(LCD_DATA0_PIN); if(data & 0x08) dataport |= _BV(LCD_DATA3_PIN); if(data & 0x04) dataport |= _BV(LCD_DATA2_PIN); if(data & 0x02) dataport |= _BV(LCD_DATA1_PIN); if(data & 0x01) dataport |= _BV(LCD_DATA0_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); lcd_e_toggle(); /* all data pins high (inactive) */ dataport |= _BV(LCD_DATA0_PIN); dataport |= _BV(LCD_DATA1_PIN); dataport |= _BV(LCD_DATA2_PIN); dataport |= _BV(LCD_DATA3_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); } /************************************************************************* Low-level function to read byte from LCD controller Input: rs 1: read data 0: read busy flag / address counter Returns: byte read from LCD controller *************************************************************************/ static uint8_t lcd_read(uint8_t rs) { uint8_t data; if (rs) /* write data (RS=1, RW=0) */ dataport |= _BV(LCD_RS_PIN); else /* write instruction (RS=0, RW=0) */ dataport &= ~_BV(LCD_RS_PIN); dataport |= _BV(LCD_RW_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); /* read high nibble first */ pcf8574_setoutputpinhigh(LCD_PCF8574_DEVICEID, LCD_E_PIN); lcd_e_delay(); data = 0; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA0_PIN)) data |= 0x10; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA1_PIN)) data |= 0x20; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA2_PIN)) data |= 0x40; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA3_PIN)) data |= 0x80; pcf8574_setoutputpinlow(LCD_PCF8574_DEVICEID, LCD_E_PIN); /* Enable 500ns low */ lcd_e_delay(); /* read low nibble */ pcf8574_setoutputpinhigh(LCD_PCF8574_DEVICEID, LCD_E_PIN); lcd_e_delay(); if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA0_PIN)) data |= 0x01; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA1_PIN)) data |= 0x02; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA2_PIN)) data |= 0x04; if(!pcf8574_getinputpin(LCD_PCF8574_DEVICEID, LCD_DATA3_PIN)) data |= 0x08; pcf8574_setoutputpinlow(LCD_PCF8574_DEVICEID, LCD_E_PIN); return data; } /************************************************************************* loops while lcd is busy, returns address counter *************************************************************************/ static uint8_t lcd_waitbusy(void) { register uint8_t c; /* wait until busy flag is cleared */ while ( (c=lcd_read(0)) & (1<= LCD_START_LINE2) && (pos < LCD_START_LINE4) ) addressCounter = LCD_START_LINE3; else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2) ) addressCounter = LCD_START_LINE4; else addressCounter = LCD_START_LINE1; #endif lcd_command((1< 7) { return 1; } lcd_command((1 << LCD_CGRAM) + charnum * 8); // set CGRAM address charnum * 8 byte for (; j < 8; j++) { lcd_putc (*(p + j)); // write 8 byte data (one character) to CGRAM } return (0); } /************************************************************************* Set cursor to specified position Input: x horizontal position (0: left most position) y vertical position (0: first line) Returns: none *************************************************************************/ void lcd_gotoxy(uint8_t x, uint8_t y) { #if LCD_LINES==1 lcd_command((1<>4; dataport |= _BV(LCD_DATA0_PIN); // _BV(LCD_FUNCTION_8BIT)>>4; pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); lcd_e_toggle(); delay(4992); /* delay, busy flag can't be checked here */ /* repeat last command */ lcd_e_toggle(); delay(64); /* delay, busy flag can't be checked here */ /* repeat last command a third time */ lcd_e_toggle(); delay(64); /* delay, busy flag can't be checked here */ /* now configure for 4bit mode */ dataport &= ~_BV(LCD_DATA0_PIN); pcf8574_setoutput(LCD_PCF8574_DEVICEID, dataport); lcd_e_toggle(); delay(64); /* some displays need this additional delay */ /* 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 */ }/* lcd_init */