From eab672a607e846abe13e27c924b45899baa56703 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Fri, 18 Apr 2025 21:34:54 +0200 Subject: [PATCH] Waiting room: Display --- waiting/firmware/Makefile | 2 +- waiting/firmware/main.c | 142 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/waiting/firmware/Makefile b/waiting/firmware/Makefile index 7462405..cd12b41 100644 --- a/waiting/firmware/Makefile +++ b/waiting/firmware/Makefile @@ -5,6 +5,6 @@ LIB_OBJS=util-debug.o WITH_BOOT_LOADER=1 WITH_DFU_FLASH=1 -DFU_ARGS=-d 4242:0013 +DFU_ARGS=-d 4242:0013,4242:0012 include $(ROOT)/mk/bluepill.mk diff --git a/waiting/firmware/main.c b/waiting/firmware/main.c index b9b82da..c8b1b64 100644 --- a/waiting/firmware/main.c +++ b/waiting/firmware/main.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,146 @@ static void delay_ms(uint ms) ; } +/*** Display ***/ + +/* + * The display is connected via the PCF8574 I2C I/O expander. + * Expander I/O lines are connected to the display as follows: + * + * bit 0 RS (0=instruction input, 1=data input) + * bit 1 R/W* + * bit 2 E (enable) + * bit 3 backlight enable + * b. 4-7 DB4-7 + */ + +enum lcd_bit { + LCD_BIT_RS = 0x01, + LCD_BIT_R = 0x02, + LCD_BIT_E = 0x04, + LCD_BIT_BACKLIGHT = 0x08, +}; + +enum lcd_command { + LCD_CMD_CLEAR_DISPLAY = 0x01, + LCD_CMD_RETURN_HOME = 0x02, + LCD_CMD_ENTRY_MODE_SET = 0x04, + LCD_CMD_DISPLAY_CONTROL = 0x08, + LCD_CMD_CURSOR_SHIFT = 0x10, + LCD_CMD_FUNCTION_SET = 0x20, + LCD_CMD_SET_CGRAM_ADDR = 0x40, + LCD_CMD_SET_DDRAM_ADDR = 0x80, +}; + +enum lcd_entry_mode { + LCD_ENTRY_INCREMENT = 0x02, + LCD_ENTRY_DECREMENT = 0x00, + LCD_ENTRY_SHIFT = 0x01, +}; + +enum lcd_control { + LCD_CTRL_DISPLAY_ON = 0x04, + LCD_CTRL_DISPLAY_OFF = 0x00, + LCD_CTRL_CURSOR_ON = 0x02, + LCD_CTRL_CURSOR_OFF = 0x00, + LCD_CTRL_BLINK_ON = 0x01, + LCD_CTRL_BLINK_OFF = 0x00, +}; + +// flags for display/cursor shift +enum lcd_cursor_shift { + LCD_CURSOR_DISPLAY_MOVE = 0x08, + LCD_CURSOR_CURSOR_MOVE = 0x00, + LCD_CURSOR_MOVE_RIGHT = 0x04, + LCD_CURSOR_MOVE_LEFT = 0x00, +}; + +enum lcd_mode { + LCD_MODE_8BITMODE = 0x10, + LCD_MODE_4BITMODE = 0x00, + LCD_MODE_2LINE = 0x08, + LCD_MODE_1LINE = 0x00, + LCD_MODE_5x10DOTS = 0x04, + LCD_MODE_5x8DOTS = 0x00, +}; + +static byte display_backlight = LCD_BIT_BACKLIGHT; // or 0 + +static void display_pcf_write(byte value) +{ + value |= display_backlight; + i2c_transfer7(I2C2, 0x27, &value, 1, NULL, 0); +} + +static void display_write_nibble(byte value) +{ + // Sends a nibble of data with a combination of control signals + display_pcf_write(value); + display_pcf_write(value | LCD_BIT_E); + delay_ms(1); // FIXME + display_pcf_write(value); + delay_ms(1); // FIXME +} + +static void display_command(byte cmd) +{ + display_write_nibble(cmd & 0xf0); + display_write_nibble((cmd << 4) & 0xf0); +} + +static void display_char(byte ch) +{ + display_write_nibble((ch & 0xf0) | LCD_BIT_RS); + display_write_nibble(((ch << 4) & 0xf0) | LCD_BIT_RS); +} + +static void display_init(void) +{ + debug_puts("Display init\n"); + i2c_peripheral_disable(I2C2); + i2c_set_speed(I2C2, i2c_speed_sm_100k, rcc_apb1_frequency / 1000000); + i2c_peripheral_enable(I2C2); + + // We follow initialization sequence described in the datasheet. + // We need to wait at least 40ms after power rises above 2.7V. + delay_ms(50); + + // RS and R/W should be low + display_pcf_write(0); + delay_ms(1000); + + // Put display controller to 4-bit mode + display_write_nibble(0x30); + delay_ms(5); + display_write_nibble(0x30); + delay_ms(5); + display_write_nibble(0x30); + delay_ms(1); + display_write_nibble(0x20); + + // Set mode + display_command(LCD_CMD_FUNCTION_SET | LCD_MODE_4BITMODE | LCD_MODE_2LINE | LCD_MODE_5x8DOTS); + + // Turn display on and set cursor + display_command(LCD_CMD_DISPLAY_CONTROL | LCD_CTRL_DISPLAY_ON | LCD_CTRL_CURSOR_ON | LCD_CTRL_BLINK_ON); + + // Configure text direction + display_command(LCD_CMD_ENTRY_MODE_SET | LCD_ENTRY_INCREMENT); + + // Go home (this takes a long time) + display_command(LCD_CMD_RETURN_HOME); + delay_ms(2); + + display_char('M'); + display_char('n'); + display_char('a'); + display_char('u'); + display_char('!'); + display_command(LCD_CMD_SET_DDRAM_ADDR | 1); + + debug_puts("Display ready\n"); +} + /*** Neopixels ***/ #if 0 @@ -393,6 +534,7 @@ int main(void) gpio_init(); usart_init(); tick_init(); + display_init(); // neopixel_init(); usb_init(); -- 2.39.5