/* * lcd.c - Subroutines for communicating with HD44780U based LCD panels * Copyright (C) 2024 Alexander Rosenberg * * 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. See the LICENSE file for more information. * * This file was created with help from the document located here: * https://www.sparkfun.com/datasheets/LCD/HD44780.pdf */ #include "lcd.h" #include "util.h" #include static void lcd_set_read(LCD *lcd, bool is_read, bool force) { if (force || lcd->is_read != is_read) { gpio_config_t cfg = {.g_flags = is_read ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT}; cfg.g_pin = lcd->d0; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d1; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d2; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d3; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d4; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d5; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d6; gpio_pin_set_flags(lcd->handle, &cfg); cfg.g_pin = lcd->d7; gpio_pin_set_flags(lcd->handle, &cfg); lcd->is_read = is_read; } } static void lcd_pulse_enable(LCD *lcd) { gpio_pin_high(lcd->handle, lcd->en); usleep(1); // min of 1000ns gpio_pin_low(lcd->handle, lcd->en); } LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7) { LCD *lcd = malloc_checked(sizeof(LCD)); lcd->handle = handle; lcd->rs = rs; lcd->rw = rw; lcd->en = en; lcd->d0 = d0; lcd->d1 = d1; lcd->d2 = d2; lcd->d3 = d3; lcd->d4 = d4; lcd->d5 = d5; lcd->d6 = d6; lcd->d7 = d7; lcd_set_read(lcd, false, true); lcd_clear(lcd); lcd_call(lcd, 0, 0, 0, 0, 1, 1, 1, 0, 0); // 5x8 font, 2 lines, 8bit lcd_entry_mode(lcd, LCD_INCREMENT, LCD_CURSOR_MOVE); lcd_display_control(lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON); return lcd; } void lcd_close(LCD *lcd) { free(lcd); } void lcd_call(LCD *lcd, int rs, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7) { while(lcd_is_busy(lcd)) { usleep(5); } lcd_set_read(lcd, false, false); gpio_pin_low(lcd->handle, lcd->en); gpio_pin_low(lcd->handle, lcd->rw); gpio_pin_set(lcd->handle, lcd->rs, rs); gpio_pin_set(lcd->handle, lcd->d0, d0); gpio_pin_set(lcd->handle, lcd->d1, d1); gpio_pin_set(lcd->handle, lcd->d2, d2); gpio_pin_set(lcd->handle, lcd->d3, d3); gpio_pin_set(lcd->handle, lcd->d4, d4); gpio_pin_set(lcd->handle, lcd->d5, d5); gpio_pin_set(lcd->handle, lcd->d6, d6); gpio_pin_set(lcd->handle, lcd->d7, d7); lcd_pulse_enable(lcd); } void lcd_write_char(LCD *lcd, char c) { lcd_call(lcd, 1, c & 1, (c >> 1) & 1, (c >> 2) & 1, (c >> 3) & 1, (c >> 4) & 1, (c >> 5) & 1, (c >> 6) & 1, (c >> 7) & 1); } size_t lcd_write_string(LCD *lcd, const char *str) { size_t count; for (count = 0; *str; ++str, ++count) { lcd_write_char(lcd, *str); } return count; } void lcd_move_to(LCD *lcd, int line, int col) { lcd_call(lcd, 0, col & 1, (col >> 1) & 1, (col >> 2) & 1, (col >> 3) & 1, (col >> 4) & 1, (col >> 5) & 1, line, 1); } void lcd_clear(LCD *lcd) { lcd_call(lcd, 0, 1, 0, 0, 0, 0, 0, 0, 0); } void lcd_display_control(LCD *lcd, int b, int c, int d) { lcd_call(lcd, 0, b, c, d, 1, 0, 0, 0, 0); } void lcd_entry_mode(LCD *lcd, int id, int s) { lcd_call(lcd, 0, s, id, 1, 0, 0, 0, 0, 0); } void lcd_cursor_shift(LCD *lcd, int sc, int rl) { lcd_call(lcd, 0, 0, 0, rl, sc, 1, 0, 0, 0); } bool lcd_is_busy(LCD *lcd) { lcd_set_read(lcd, true, false); gpio_pin_low(lcd->handle, lcd->rs); gpio_pin_high(lcd->handle, lcd->rw); gpio_pin_high(lcd->handle, lcd->en); usleep(1); gpio_value_t busy = gpio_pin_get(lcd->handle, lcd->d7); gpio_pin_low(lcd->handle, lcd->en); usleep(1); return busy; }