]> mj.ucw.cz Git - home-hw.git/commitdiff
Waiting room: Display
authorMartin Mares <mj@ucw.cz>
Fri, 18 Apr 2025 19:34:54 +0000 (21:34 +0200)
committerMartin Mares <mj@ucw.cz>
Fri, 18 Apr 2025 19:34:54 +0000 (21:34 +0200)
waiting/firmware/Makefile
waiting/firmware/main.c

index 74624059b103f28d89d34cab1bd3e85fa27bad6f..cd12b41d9bbc9219fc2630fa7112846db8362413 100644 (file)
@@ -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
index b9b82dac96d4bb2ae52985a6d91a75e323a8a4cd..c8b1b64e51506635ae24af7b3075f7bc03a4ea68 100644 (file)
@@ -12,6 +12,7 @@
 #include <libopencm3/cm3/scb.h>
 #include <libopencm3/stm32/dma.h>
 #include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/i2c.h>
 #include <libopencm3/stm32/rcc.h>
 #include <libopencm3/stm32/timer.h>
 #include <libopencm3/stm32/usart.h>
@@ -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();