/* * HD44780 LCD via PCF8574 I2C backpack + USB mirror. * SPDX-License-Identifier: GPL-3.0-or-later */ #include "display.h" #include "board_pins.h" #include "config.h" #include "status.h" #include "sense.h" #include "ptt.h" #include "hardware/i2c.h" #include "pico/stdlib.h" #include #include #include /* PCF8574 bit map (common backpack): P0=RS P1=RW P2=E P3=BL P4-7=D4-D7 */ #define LCD_RS 0x01 #define LCD_RW 0x02 #define LCD_EN 0x04 #define LCD_BL 0x08 static bool g_lcd; static uint8_t g_addr = I2C_LCD_ADDR_DEFAULT; static char g_last_l0[17]; static char g_last_l1[17]; static bool i2c_write_u8(uint8_t addr, uint8_t v) { return i2c_write_blocking(i2c0, addr, &v, 1, false) >= 0; } static bool i2c_probe(uint8_t addr) { uint8_t b = 0; return i2c_write_blocking(i2c0, addr, &b, 1, false) >= 0; } static void lcd_write4(uint8_t nibble, uint8_t mode) { uint8_t data = (uint8_t)((nibble & 0xF0) | LCD_BL | mode); i2c_write_u8(g_addr, (uint8_t)(data | LCD_EN)); sleep_us(1); i2c_write_u8(g_addr, (uint8_t)(data & (uint8_t)~LCD_EN)); sleep_us(50); } static void lcd_send(uint8_t value, uint8_t mode) { lcd_write4((uint8_t)(value & 0xF0), mode); lcd_write4((uint8_t)((value << 4) & 0xF0), mode); } static void lcd_cmd(uint8_t c) { lcd_send(c, 0); } static void lcd_data(uint8_t c) { lcd_send(c, LCD_RS); } static void lcd_set_cursor(uint8_t col, uint8_t row) { static const uint8_t row_addr[] = {0x00, 0x40, 0x14, 0x54}; if (row > 1) { row = 1; } lcd_cmd((uint8_t)(0x80 | (row_addr[row] + col))); } static void lcd_print_line(uint8_t row, const char *s) { char buf[17]; size_t n = strlen(s); if (n > 16) { n = 16; } memset(buf, ' ', 16); memcpy(buf, s, n); buf[16] = '\0'; lcd_set_cursor(0, row); for (int i = 0; i < 16; i++) { lcd_data((uint8_t)buf[i]); } } static bool lcd_init_seq(uint8_t addr) { g_addr = addr; sleep_ms(50); lcd_write4(0x30, 0); sleep_ms(5); lcd_write4(0x30, 0); sleep_us(150); lcd_write4(0x30, 0); sleep_us(150); lcd_write4(0x20, 0); /* 4-bit */ lcd_cmd(0x28); /* 2 line, 5x8 */ lcd_cmd(0x0C); /* display on, cursor off */ lcd_cmd(0x06); /* entry mode */ lcd_cmd(0x01); /* clear */ sleep_ms(2); return true; } void display_init(void) { g_lcd = false; g_last_l0[0] = '\0'; g_last_l1[0] = '\0'; uint8_t addrs[] = {0x27, 0x3F}; for (unsigned i = 0; i < sizeof(addrs); i++) { if (i2c_probe(addrs[i])) { if (lcd_init_seq(addrs[i])) { g_lcd = true; if (config_unsolicited_ok()) { printf("LCD: HD44780 @ 0x%02X\r\n", addrs[i]); } return; } } } if (config_unsolicited_ok()) { printf("LCD: none — USB status only\r\n"); } } bool display_present(void) { return g_lcd; } void display_show(const char *line) { if (!line) { return; } if (g_lcd) { lcd_print_line(0, line); } } void display_update(void) { const c1224_config_t *cfg = config_get(); char l0[17]; char l1[17]; c1224_state_t st = status_get(); if (st == C1224_STATE_FAULT) { snprintf(l0, sizeof(l0), "%.16s", cfg->fault); } else if (ptt_get()) { snprintf(l0, sizeof(l0), "%.16s", cfg->sending); } else if (st == C1224_STATE_STARTING) { snprintf(l0, sizeof(l0), "%.16s", cfg->starting); } else { /* Power/ready on line 0 */ snprintf(l0, sizeof(l0), "%.16s", cfg->ready); } /* Line 1: Signal + Packet from sense */ bool sig = sense_signal_active(); bool pkt = sense_packet_active(); if (sig && pkt) { snprintf(l1, sizeof(l1), "%.7s %.7s", cfg->signal, cfg->packet); } else if (sig) { snprintf(l1, sizeof(l1), "%.16s", cfg->signal); } else if (pkt) { snprintf(l1, sizeof(l1), "%.16s", cfg->packet); } else { snprintf(l1, sizeof(l1), "%.16s", cfg->power); } if (strcmp(l0, g_last_l0) != 0 || strcmp(l1, g_last_l1) != 0) { strncpy(g_last_l0, l0, sizeof(g_last_l0)); strncpy(g_last_l1, l1, sizeof(g_last_l1)); g_last_l0[16] = '\0'; g_last_l1[16] = '\0'; if (g_lcd) { lcd_print_line(0, l0); lcd_print_line(1, l1); } } }