#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>
;
}
+/*** 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
gpio_init();
usart_init();
tick_init();
+ display_init();
// neopixel_init();
usb_init();