146 lines
4.7 KiB
C
146 lines
4.7 KiB
C
/*
|
|
* 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 <unistd.h>
|
|
|
|
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, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en,
|
|
gpio_pin_t d0, gpio_pin_t d1, gpio_pin_t d2, gpio_pin_t d3,
|
|
gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t 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, true, 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);
|
|
LOG_VERBOSE("Initializing LCD: rs=%d, rw=%d, en=%d, d={%d, %d, %d, %d, %d,"
|
|
"%d, %d, %d}\n", rs, rw, en, d0, d1, d2, d3, d4, d5, d6, d7);
|
|
return lcd;
|
|
}
|
|
|
|
void lcd_close(LCD *lcd) {
|
|
LOG_VERBOSE("Closing LCD\n");
|
|
free(lcd);
|
|
}
|
|
|
|
void lcd_call(LCD *lcd, gpio_value_t rs, gpio_value_t d0, gpio_value_t d1,
|
|
gpio_value_t d2, gpio_value_t d3, gpio_value_t d4,
|
|
gpio_value_t d5, gpio_value_t d6, gpio_value_t 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, gpio_value_t line, gpio_value_t 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, gpio_value_t b, gpio_value_t c, gpio_value_t d) {
|
|
lcd_call(lcd, 0, b, c, d, 1, 0, 0, 0, 0);
|
|
}
|
|
|
|
void lcd_entry_mode(LCD *lcd, gpio_value_t id, gpio_value_t s) {
|
|
lcd_call(lcd, 0, s, id, 1, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
void lcd_cursor_shift(LCD *lcd, gpio_value_t sc, gpio_value_t 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;
|
|
|
|
}
|