summaryrefslogtreecommitdiff
path: root/firmware/s1224/src/common/display.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-07 18:09:58 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-07 18:09:58 +0000
commita7362dc14bca1233eb34b41aefebe9cfb22684ac (patch)
treef727c87f92aeb503038deeecc24d3e074dc0197c /firmware/s1224/src/common/display.c
Initial pushmain
Diffstat (limited to 'firmware/s1224/src/common/display.c')
-rw-r--r--firmware/s1224/src/common/display.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/firmware/s1224/src/common/display.c b/firmware/s1224/src/common/display.c
new file mode 100644
index 0000000..e2875be
--- /dev/null
+++ b/firmware/s1224/src/common/display.c
@@ -0,0 +1,187 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+/* 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 s1224_config_t *cfg = config_get();
+ char l0[17];
+ char l1[17];
+
+ s1224_state_t st = status_get();
+ if (st == S1224_STATE_FAULT) {
+ snprintf(l0, sizeof(l0), "%.16s", cfg->fault);
+ } else if (ptt_get()) {
+ snprintf(l0, sizeof(l0), "%.16s", cfg->sending);
+ } else if (st == S1224_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);
+ }
+ }
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com