From 82d679ab5fa43c33ae52fe7b66d7c358f6046a45 Mon Sep 17 00:00:00 2001 From: tslil clingman Date: Mon, 18 Jan 2021 23:14:12 -0500 Subject: First steps towards character LCD --- 3rd-party/LCDHD44780.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 527 insertions(+) create mode 100644 3rd-party/LCDHD44780.c (limited to '3rd-party/LCDHD44780.c') diff --git a/3rd-party/LCDHD44780.c b/3rd-party/LCDHD44780.c new file mode 100644 index 0000000..32c8ce2 --- /dev/null +++ b/3rd-party/LCDHD44780.c @@ -0,0 +1,527 @@ +/*##############################################################*/ +/* Author: Marcus Nasarek */ +/* File: LCDH44780.c */ +/* Compile with: */ +/* gcc -Wall -c LCDHD44780.c -o LCDHD44780.o */ +/* License: */ +/* */ +/* This program is free software; you can redistribute it */ +/* and/or modify it under the terms of the GNU General */ +/* Public License as published by the Free Software */ +/* Foundation; either version 3 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will */ +/* be useful, but WITHOUT ANY WARRANTY; without even the */ +/* implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General */ +/* Public License along with this program; if not, */ +/* see . */ +/* */ +/*##############################################################*/ + +#include "LCDHD44780.h" + +void init_gpio(void) +{ + // Set all pins to output + bcm2835_gpio_fsel(LCD_RS, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_RW, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D4, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D5, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D6, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D7, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_E, BCM2835_GPIO_FSEL_OUTP); + + bcm2835_delayMicroseconds(C_DELAY); + + // Set all pins to LOW + bcm2835_gpio_clr(LCD_RS); + bcm2835_gpio_clr(LCD_RW); + bcm2835_gpio_clr(LCD_D4); + bcm2835_gpio_clr(LCD_D5); + bcm2835_gpio_clr(LCD_D5); + bcm2835_gpio_clr(LCD_D7); + bcm2835_gpio_clr(LCD_E); + +} + +void set_data_pins_to_output(void) +{ + // set data pins to output + bcm2835_gpio_fsel(LCD_D7, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D6, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D5, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(LCD_D4, BCM2835_GPIO_FSEL_OUTP); +} + +void set_data_pins_to_input(void) +{ + // set data pins to input + bcm2835_gpio_fsel(LCD_D7, BCM2835_GPIO_FSEL_INPT); + bcm2835_gpio_fsel(LCD_D6, BCM2835_GPIO_FSEL_INPT); + bcm2835_gpio_fsel(LCD_D5, BCM2835_GPIO_FSEL_INPT); + bcm2835_gpio_fsel(LCD_D4, BCM2835_GPIO_FSEL_INPT); +} + +void pulse_e(void) +{ + // Toogle ENABLE pin in us + // bcm2835_gpio_fsel(LCD_E, BCM2835_GPIO_FSEL_OUTP); + bcm2835_delayMicroseconds(E_DELAY); + bcm2835_gpio_set(LCD_E); + bcm2835_delayMicroseconds(E_DATA_PULSE); + bcm2835_gpio_clr(LCD_E); + (LCD_DATA) ? bcm2835_delayMicroseconds(E_DATA_PULSE) :bcm2835_delayMicroseconds(E_CMD_PULSE); +} + +uint8_t busy_wait(void) +{ + // FIXME: this function does not work! + // With RW grounded it shouldn't work. + // But as long as the display is driven + // by 5V the GPIO ports on a raspberry pi + // would get to much voltage + + uint8_t addr; + bool wait = true; + uint16_t counter = 0; + + set_data_pins_to_input(); + + bcm2835_gpio_clr(LCD_RS); + bcm2835_gpio_set(LCD_RW); + + while(wait && (counter < MAX_WAIT)) + { + addr = 0; + pulse_e(); + + // read high bits + if (bcm2835_gpio_lev(LCD_D4)) + addr = (addr | 0x10); + if (bcm2835_gpio_lev(LCD_D5)) + addr = (addr | 0x20); + if (bcm2835_gpio_lev(LCD_D6)) + addr = (addr | 0x40); + if (bcm2835_gpio_lev(LCD_D7)) + addr = (addr | 0x80); + + // Toogle ENABLE pin in us + pulse_e(); + + // read low bits + if (bcm2835_gpio_lev(LCD_D4)) + addr = (addr | 0x01); + if (bcm2835_gpio_lev(LCD_D5)) + addr = (addr | 0x02); + if (bcm2835_gpio_lev(LCD_D6)) + addr = (addr | 0x04); + if (bcm2835_gpio_lev(LCD_D7)) + addr = (addr | 0x08); + + wait = (addr & 0x80); + counter += 1; + + bcm2835_delay(1); + } + + bcm2835_gpio_clr(LCD_RW); + set_data_pins_to_output(); + + // return address but mask out highest bit (busy flag) + // 0x7F = 0 1 1 1 1 1 1 1 + return (addr & 0x7F); +} + +void init_lcd(void) +{ + init_gpio(); + + /* + The busy flag (BF) is kept in the busy state + until the initialization ends (BF = 1). The + busy state lasts for 10 ms after VCC rises to 4.5 V + */ + + /* + --- symbols: + I/D = 1: Increment + I/D = 0: Decrement + S = 1: Accompanies display shift + S/C = 1: Display shift + S/C = 0: Cursor move + R/L = 1: Shift to the right + R/L = 0: Shift to the left + DL = 1: 8 bits, DL = 0: 4 bits + N = 1: 2 lines, N = 0: 1 line + F = 1: 5 + × + 10 dots, F = 0: 5 + × + 8 dots + BF = 1: Internally operating + BF = 0: Instructions acceptable + */ + + /* + --- Display init after turn on: + 1. Display clear + 2. Function set: + DL = 1; 8-bit interface data + N = 0; 1-line display + F = 0; 5×8 dot character font + 3. Display on/off control: + D = 0; Display off + C = 0; Cursor off + B = 0; Blinking off + 4. Entry mode set: + I/D = 1; Increment by 1 + S = 0; No shift + */ + + // begin of "warming up" + // see fig 24 on page 46 of datasheet + + // Function set - + // Sets interface data length + // (DL), number of display lines + // (N), and character font (F). + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 1 DL N F - - + // DL = 0 --> 4 bit data length (1 = 8 bit) + // N = 1 --> 2 line display (0 = 1 line) + // F = 0 --> 5x8 dot character font + // 0x33 = + // 0 0 0 0 1 1 0 0 1 1 + // --> ? + write_to_lcd(0x33, LCD_CMD); + bcm2835_delayMicroseconds(100); + + // Function set - + // Sets interface data length + // (DL), number of display lines + // (N), and character font (F). + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 1 DL N F - - + // DL = 0 --> 4 bit data length (1 = 8 bit) + // N = 1 --> 2 line display (0 = 1 line) + // F = 0 --> 5x8 dot character font + // 0x32 = + // 0 0 0 0 1 1 0 0 1 0 + // --> ? + write_to_lcd(0x32, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); + + // end of "warming up" + + lcd_function_set(TWO_LINES); + + lcd_control(DISPLAY_ON); + + lcd_entry_mode(ADDR_INCREMENT); + + clear_lcd(); + +} + +void lcd_function_set(uint8_t control_flags) +{ + // Function set - + // Sets interface data length + // (DL), number of display lines + // (N), and character font (F). + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 1 DL N F - - + // DL = 0 --> 4 bit data length (1 = 8 bit) + // N = 1 --> 2 line display (0 = 1 line) + // F = 0 --> 5x8 dot character font + // F = 1 --> 5x10 dot character font + // 0x28 = + // 0 0 0 0 1 0 1 0 0 0 + write_to_lcd(LCD_FUNCTION_SET | control_flags, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + +void lcd_control(uint8_t control_flags) +{ + // Display on/off control - + // Sets entire display (D) on/off, + // cursor on/off (C), and + // blinking of cursor position + // character (B). + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 0 0 1 D C B + // D = 1 --> display on + // C = 0 --> cursor off + // B = 0 --> blinking off + write_to_lcd(LCD_CONTROL | control_flags, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + +void lcd_shift_control(uint8_t control_flags) +{ + // Cursor or display shift - + // Moves the cursor and shifts + // display without chaning + // DDRAM contents. + // Execution time 37us + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 0 1 S/C R/L - - + // S/C = cursor/display shift + // S/C = 0 --> cursor shift + // R/L = shift direction + // R/L = 1 --> shift to right + write_to_lcd(CURSOR_DISPLAY_SHIFT | control_flags, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + +void lcd_entry_mode(uint8_t control_flags) +{ + // Entry mode set - + // Sets cursor move direction + // and specifies display shift. + // These operations are + // performed during data write + // and read. + // Execution time 37us + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 0 0 0 1 I/D S + // I/D = increment/decrement address pointer + // I/D = 1 --> increment + // S = 0 --> no shift + write_to_lcd(LCD_ENTRY_MODE | control_flags, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + +void clear_lcd(void) { + // Clear display - + // Clears entire display and + // sets DDRAM address 0 in + // address counter: + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 0 0 0 0 0 1 + write_to_lcd(0x01, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + +void return_home(void) +{ + // Return home - + // Sets DDRAM address to 0 + // in address counter. Also + // returns display from being + // shifted to original position + // DDRAM contents remains unchanged + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 0 0 0 0 0 0 1 - + write_to_lcd(0x02, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); +} + + +void display_off(void) +{ + lcd_control(DISPLAY_OFF); +} + +void show_cursor(bool on) +{ + uint8_t cursor_flag; + cursor_flag = ( on ? CURSOR_ON : 0x00); + lcd_control(DISPLAY_ON | cursor_flag); +} + +void cursor_blinking(bool on) +{ + uint8_t blinking_flag; + blinking_flag = ( on ? BLINKING_ON : 0x00); + lcd_control(DISPLAY_ON | blinking_flag); +} + +void set_cursor(uint8_t addr) +{ + // Set DDRAM address + // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 + // 0 0 1 ADD ADD ADD ADD ADD ADD ADD + write_to_lcd(addr, LCD_CMD); + bcm2835_delayMicroseconds(R_EXEC_TIME); + // fprintf(stdout,"cursor on addr: 0x%0x\n", addr); +} + +void shift_cursor(uint8_t direction) +{ + lcd_shift_control(CURSOR_SHIFT | direction); +} + +void shift_display(uint8_t direction) +{ + lcd_shift_control(DISPLAY_SHIFT | direction); +} + +void write_to_lcd(uint8_t byte, uint8_t mode) +{ + switch (mode) { + case LCD_CMD: + bcm2835_gpio_clr(LCD_RS); + // fprintf(stdout, "Set RS to cmd mode\n"); + break; + case LCD_DATA: + bcm2835_gpio_set(LCD_RS); + // fprintf(stdout, "Set RS to data mode for '%c'\n", byte); + break; + default: + // fprintf(stderr, "mode for setting GPIO pin unknown: %i\n", mode); + exit(EXIT_FAILURE); + } + + // write high bits + bcm2835_gpio_clr(LCD_D4); + bcm2835_gpio_clr(LCD_D5); + bcm2835_gpio_clr(LCD_D6); + bcm2835_gpio_clr(LCD_D7); + + if ((byte & 0x10) == 0x10) { + bcm2835_gpio_set(LCD_D4); + } + if ((byte & 0x20) == 0x20) { + bcm2835_gpio_set(LCD_D5); + } + if ((byte & 0x40) == 0x40) { + bcm2835_gpio_set(LCD_D6); + } + if ((byte & 0x80) == 0x80) { + bcm2835_gpio_set(LCD_D7); + } + + // Toogle ENABLE pin in us + pulse_e(); + + // write low bits + bcm2835_gpio_clr(LCD_D4); + bcm2835_gpio_clr(LCD_D5); + bcm2835_gpio_clr(LCD_D6); + bcm2835_gpio_clr(LCD_D7); + + if ((byte & 0x01) == 0x01) { + bcm2835_gpio_set(LCD_D4); + } + if ((byte & 0x02) == 0x02) { + bcm2835_gpio_set(LCD_D5); + } + if ((byte & 0x04) == 0x04) { + bcm2835_gpio_set(LCD_D6); + } + if ((byte & 0x08) == 0x08) { + bcm2835_gpio_set(LCD_D7); + } + + pulse_e(); + +} + +void write_string(char *str) +{ + uint16_t i; + uint8_t len = 0; + + len = (strlen(str) > LCD_WIDTH ? LCD_WIDTH : strlen(str)); + + for (i=0; i