From a751b95352b98bd3e5093149663c2a3a256a942d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 12 Dec 2019 18:58:04 +0100 Subject: [PATCH] lcd_hd44780: add I2C backpack support --- lib/lcd_hd44780.c | 764 ++++++++++++++++++++++++++++++---------------- lib/lcd_hd44780.h | 27 +- 2 files changed, 509 insertions(+), 282 deletions(-) diff --git a/lib/lcd_hd44780.c b/lib/lcd_hd44780.c index fdb10f9..d5efa18 100644 --- a/lib/lcd_hd44780.c +++ b/lib/lcd_hd44780.c @@ -31,13 +31,19 @@ /* own libraries */ #include "global.h" // common methods #include "lcd_hd44780.h" // own definitions +#if LCD_HD44780_I2C +#include "i2c_master.h" // I²C utilities +#endif -/** interface data length: 1 for 8-bit, 0 for 4-bit */ -#define LCD_HD44780_INTERFACE_DL 0 /** include busy time waiting in writes - * @note this removes the need to call lcd_hd44780_wait_busy but prevents you to to something else meanwhile, particularly when reading is enabled + * @note this removes the need to call lcd_hd44780_wait_busy but prevents you to do something else meanwhile, particularly when reading is enabled + * @note because I²C is already slow enough, there is no need to wait further */ -#define LCD_HD44780_BUSY_WAIT_INCLUDE 1 +#if LCD_HD44780_I2C +#define LCD_HD44780_BUSY_WAIT_INCLUDE 1 // lcd_hd44780_wait_busy works, but the I²C bus is often slower +#else // LCD_HD44780_I2C +#define LCD_HD44780_BUSY_WAIT_INCLUDE 1 // you can change this value +#endif // LCD_HD44780_I2C /** busy wait time for most short writes (37 us with margin) */ #define LCD_HD44780_BUSY_WAIT_SHORT (37 + 5) /** busy wait time for some long writes (1520 us, but experience shows it's more) @@ -64,211 +70,531 @@ * - 16 BLK: Backlight Cathode */ +/** @defgroup lcd_hd44780_signals HD44780 signals + * @note can be combined using OR + * @{ + */ +#define LCD_HD44780_RS (1 << 0) /**< RS : Register Select (high = data, low = instruction) */ +#define LCD_HD44780_RnW (1 << 1) /**< R/W: Read/Write (high = read, low = write) */ +#define LCD_HD44780_E (1 << 2) /**< enable (falling edge to latch data, high to output register) */ +#define LCD_HD44780_DB0 (1 << 3) /**< Data Bit 0 */ +#define LCD_HD44780_DB1 (1 << 4) /**< Data Bit 1 */ +#define LCD_HD44780_DB2 (1 << 5) /**< Data Bit 2 */ +#define LCD_HD44780_DB3 (1 << 6) /**< Data Bit 3 */ +#define LCD_HD44780_DB4 (1 << 7) /**< Data Bit 4 */ +#define LCD_HD44780_DB5 (1 << 8) /**< Data Bit 5 */ +#define LCD_HD44780_DB6 (1 << 9) /**< Data Bit 6 */ +#define LCD_HD44780_DB7 (1 << 10) /**< Data Bit 7 */ +#define LCD_HD44780_LED (1 << 11) /**< Backlight Cathode */ +/** @} */ + +#if LCD_HD44780_I2C +/* I²C backpack PCF8574 GPIO expander pinout: + * - P0: RS + * - P1: RnW + * - P2: E + * - P3: LED + * - P4: D4 + * - P5: D5 + * - P6: D6 + * - P7: D7 + */ +/** register select */ +#define LCD_HD44780_I2C_RS (1 << 0) +/** select read or write */ +#define LCD_HD44780_I2C_RnW (1 << 1) +/** enable pin */ +#define LCD_HD44780_I2C_E (1 << 2) +/** enable LED backlight */ +#define LCD_HD44780_I2C_LED (1 << 3) +/** data bit 4 */ +#define LCD_HD44780_I2C_DB4 (1 << 4) +/** data bit 5 */ +#define LCD_HD44780_I2C_DB5 (1 << 5) +/** data bit 6 */ +#define LCD_HD44780_I2C_DB6 (1 << 6) +/** data bit 7 */ +#define LCD_HD44780_I2C_DB7 (1 << 7) +/** I²C peripheral base address */ +#define LCD_HD44780_I2C_PERIPH I2C1 +/** I2C address of I²C backpack adapter (7 bits are LSB) */ +uint8_t lcd_hd44780_i2c_addr = 0x3f; // default PCF8574A I²C backpack slave address +/** I2C GPIO output state */ +static uint8_t lcd_hd44780_i2c_output = 0xff; +#else // LCD_HD44780_I2C /** @defgroup lcd_hd44780_gpio GPIO used to control the HD44780 * @{ */ /** register select + * @note pulled up by HD44780 + */ +#define LCD_HD44780_GPIO_RS PB9 +/** pin allowing writing data bits (on low) or reading them (on high) + * @note remove definition if tied to ground + * @note this enables read back data, but more importantly read the busy flag to know when the controller finished processing the command. Tying R/nW to ground instead of a GPIO saves a pin, but no data can be read back. Also every command needs to wait a minimum time before being able to send the next one, even if the controlled might actually already have processed it and is not busy anymore. * @note is pulled up by HD44780 */ -#define LCD_HD44780_GPIO_RS PB9 /**< register select */ -#if !LCD_HD44780_WRITE_ONLY -/** select read or write - * @note is pulled up by HD44780 - */ - -#define LCD_HD44780_GPIO_RW PB10 /**< select read or write */ -#endif +#define LCD_HD44780_GPIO_RnW PB10 /** enable pin * @warning needs an external 10k pull-up resistor */ #define LCD_HD44780_GPIO_E PB11 -/** data bit - * @note is pulled up by HD44780 +/** data bit 7 + * @note pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB7 PB12 /** data bit 6 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB6 PB13 /** data bit 5 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB5 PB14 /** data bit 4 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB4 PB15 #if LCD_HD44780_INTERFACE_DL /** data bit 3 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 + * @note leave undefined when only 4-bit mode is used */ -#define LCD_HD44780_GPIO_DB3 P +//#define LCD_HD44780_GPIO_DB3 P /** data bit 2 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 + * @note leave undefined when only 4-bit mode is used */ -#define LCD_HD44780_GPIO_DB2 P +//#define LCD_HD44780_GPIO_DB2 P /** data bit 1 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 + * @note leave undefined when only 4-bit mode is used */ -#define LCD_HD44780_GPIO_DB1 P +//#define LCD_HD44780_GPIO_DB1 P /** data bit 0 - * @note is pulled up by HD44780 + * @note pulled up by HD44780 + * @note leave undefined when only 4-bit mode is used */ -#define LCD_HD44780_GPIO_DB0 P -#endif +//#define LCD_HD44780_GPIO_DB0 P +#endif // LCD_HD44780_INTERFACE_DL /** @} */ +#endif // LCD_HD44780_I2C -/** direction of data lines */ -static bool lcd_hd44780_db_input = true; +/** set for 8-bit interface data + * @note I²C implies 4-bit + */ +#if defined(LCD_HD44780_GPIO_DB3) && defined(LCD_HD44780_GPIO_DB2) && defined(LCD_HD44780_GPIO_DB1) && defined(LCD_HD44780_GPIO_DB0) +#define LCD_HD44780_INTERFACE_DL +#endif /** if the display is configured having 2 lines */ static bool lcd_hd44780_n_2lines = true; -/** switch direction of data bits - * @param[in] input true to switch direction to input, false to output +/** set signals + * @param[in] signals */ -static void lcd_hd44780_db_direction(bool input) +static void lcd_hd44780_set_signal(uint16_t signals) { - if (lcd_hd44780_db_input == input) { - return; +#if LCD_HD44780_I2C + if (signals & LCD_HD44780_RS) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_RS; } + if (signals & LCD_HD44780_RnW) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_RnW; + } + if (signals & LCD_HD44780_E) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_E; + } + if (signals & LCD_HD44780_DB4) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_DB4; + } + if (signals & LCD_HD44780_DB5) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_DB5; + } + if (signals & LCD_HD44780_DB6) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_DB6; + } + if (signals & LCD_HD44780_DB7) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_DB7; + } + if (signals & LCD_HD44780_LED) { + lcd_hd44780_i2c_output |= LCD_HD44780_I2C_LED; + } +#else // LCD_HD44780_I2C + if (signals & LCD_HD44780_RS) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); + } +#ifdef LCD_HD44780_GPIO_RnW + if (signals & LCD_HD44780_RnW) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RnW), GPIO_PIN(LCD_HD44780_GPIO_RnW)); + } +#endif // LCD_HD44780_GPIO_RnW + if (signals & LCD_HD44780_E) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); + } +#if LCD_HD44780_INTERFACE_DL + if (signals & LCD_HD44780_DB0) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); + } + if (signals & LCD_HD44780_DB1) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); + } + if (signals & LCD_HD44780_DB2) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); + } + if (signals & LCD_HD44780_DB3) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); + } +#endif // LCD_HD44780_INTERFACE_DL + if (signals & LCD_HD44780_DB4) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + } + if (signals & LCD_HD44780_DB5) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + } + if (signals & LCD_HD44780_DB6) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + } + if (signals & LCD_HD44780_DB7) { + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + } + if (signals & LCD_HD44780_LED) { + // no LED GPIO defined + } +#endif // LCD_HD44780_I2C +} +/** clear signals + * @param[in] signals + */ +static void lcd_hd44780_clear_signal(uint16_t signals) +{ +#if LCD_HD44780_I2C + if (signals & LCD_HD44780_RS) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_RS; + } + if (signals & LCD_HD44780_RnW) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_RnW; + } + if (signals & LCD_HD44780_E) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_E; + } + if (signals & LCD_HD44780_DB4) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_DB4; + } + if (signals & LCD_HD44780_DB5) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_DB5; + } + if (signals & LCD_HD44780_DB6) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_DB6; + } + if (signals & LCD_HD44780_DB7) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_DB7; + } + if (signals & LCD_HD44780_LED) { + lcd_hd44780_i2c_output &= ~LCD_HD44780_I2C_LED; + } +#else // LCD_HD44780_I2C + if (signals & LCD_HD44780_RS) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); + } +#ifdef LCD_HD44780_GPIO_RnW + if (signals & LCD_HD44780_RnW) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RnW), GPIO_PIN(LCD_HD44780_GPIO_RnW)); + } +#endif // LCD_HD44780_GPIO_RnW + if (signals & LCD_HD44780_E) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); + } +#if LCD_HD44780_INTERFACE_DL + if (signals & LCD_HD44780_DB0) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); + } + if (signals & LCD_HD44780_DB1) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); + } + if (signals & LCD_HD44780_DB2) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); + } + if (signals & LCD_HD44780_DB3) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); + } +#endif // LCD_HD44780_INTERFACE_DL + if (signals & LCD_HD44780_DB4) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + } + if (signals & LCD_HD44780_DB5) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + } + if (signals & LCD_HD44780_DB6) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + } + if (signals & LCD_HD44780_DB7) { + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + } + if (signals & LCD_HD44780_LED) { + // no LED GPIO defined + } +#endif // LCD_HD44780_I2C +} - if (input) { // configure GPIO to input to read data -#if LCD_HD44780_WRITE_ONLY - return; // we can't read -#endif - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4)); -#if LCD_HD44780_INTERFACE_DL - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0)); -#endif -#if !LCD_HD44780_WRITE_ONLY - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set high to read -#endif - } else { // configure GPIO to output to write data -#if !LCD_HD44780_WRITE_ONLY - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set low write -#endif - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB7)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB6)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB5)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB4)); -#if LCD_HD44780_INTERFACE_DL - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB3)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB2)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB1)); - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB0)); +/** flush the set and cleared signals + * @note this only applies to I²C and helps gain time + */ +static void lcd_hd44780_flush_signal(void) +{ +#if LCD_HD44780_I2C + i2c_master_slave_write(LCD_HD44780_I2C_PERIPH, lcd_hd44780_i2c_addr, false, &lcd_hd44780_i2c_output, 1); // write the set signals #endif +} + +/** read from controller + * @param[in] data read data (true) or busy flag and address counter (false) + * @return data/AC read + */ +static uint8_t lcd_hd44780_read(bool data) +{ +#if defined(LCD_HD44780_GPIO_RnW) || defined(LCD_HD44780_I2C) + lcd_hd44780_set_signal(LCD_HD44780_RnW | LCD_HD44780_DB7 | LCD_HD44780_DB6 | LCD_HD44780_DB5 | LCD_HD44780_DB4); // switch DB direction to input to read data + + if (data) { + lcd_hd44780_set_signal(LCD_HD44780_RS); // set high to read data + } else { + lcd_hd44780_clear_signal(LCD_HD44780_RS); // set low to read busy flag and AC } - lcd_hd44780_db_input = input; // remember direction + lcd_hd44780_flush_signal(); + // no need to wait tAS = 40 ns before next step since the instructions are slower + + lcd_hd44780_set_signal(LCD_HD44780_E); // set high to have data output + lcd_hd44780_flush_signal(); + sleep_us(0); // wait t_DDR = 160 ns before reading + // read data bits + uint8_t input = 0; // to store read data +#if defined(LCD_HD44780_I2C) + uint8_t i2c_data; + if (I2C_MASTER_RC_NONE == i2c_master_slave_read(LCD_HD44780_I2C_PERIPH, lcd_hd44780_i2c_addr, false, &i2c_data, 1)) { + if (i2c_data & LCD_HD44780_I2C_DB7) { + input |= 0x80; + } + if (i2c_data & LCD_HD44780_I2C_DB6) { + input |= 0x40; + } + if (i2c_data & LCD_HD44780_I2C_DB5) { + input |= 0x20; + } + if (i2c_data & LCD_HD44780_I2C_DB4) { + input |= 0x10; + } + } +#else // LCD_HD44780_I2C + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) { + input |= 0x80; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { + input |= 0x40; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5))) { + input |= 0x20; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4))) { + input |= 0x10; + } +#endif // LCD_HD44780_I2C +#if defined(LCD_HD44780_INTERFACE_DL) + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3))) { + input |= 0x08; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2))) { + input |= 0x04; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1))) { + input |= 0x02; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0))) { + input |= 0x01; + } +#else // LCD_HD44780_INTERFACE_DL + // get second nibble + // don't wait PW_EH = 230 ns since reading took longer + lcd_hd44780_clear_signal(LCD_HD44780_E); // end current data read + lcd_hd44780_flush_signal(); + sleep_us(1); // wait t_cycE = 500 ns + lcd_hd44780_set_signal(LCD_HD44780_E); // have next data output + lcd_hd44780_flush_signal(); + sleep_us(0); // wait t_DDR = 160 ns before reading + + // read second nibble +#if defined(LCD_HD44780_I2C) + if (I2C_MASTER_RC_NONE == i2c_master_slave_read(LCD_HD44780_I2C_PERIPH, lcd_hd44780_i2c_addr, false, &i2c_data, 1)) { + if (i2c_data & LCD_HD44780_I2C_DB7) { + input |= 0x08; + } + if (i2c_data & LCD_HD44780_I2C_DB6) { + input |= 0x04; + } + if (i2c_data & LCD_HD44780_I2C_DB5) { + input |= 0x02; + } + if (i2c_data & LCD_HD44780_I2C_DB4) { + input |= 0x01; + } + } +#else // LCD_HD44780_I2C + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) { + input |= 0x80; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { + input |= 0x40; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5))) { + input |= 0x20; + } + if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4))) { + input |= 0x10; + } +#endif // LCD_HD44780_I2C +#endif // LCD_HD44780_INTERFACE_DL + // don't wait PW_EH = 230 ns since reading took longer + lcd_hd44780_clear_signal(LCD_HD44780_E); // end data read + lcd_hd44780_flush_signal(); + + return input; +#else // LCD_HD44780_GPIO_RnW || LCD_HD44780_I2C + return 0; // read is not supported +#endif +} + +uint8_t lcd_hd44780_read_data(void) +{ + return lcd_hd44780_read(true); } /** write to controller * @param[in] command true if it is an command, false if it is data * @param[in] data data bit to write + * @param[in] first_nibble_only if only the first nibble of the data should be written (only applies to 4-bit mode) */ -#if LCD_HD44780_INTERFACE_DL -static void lcd_hd44780_write(bool command, uint8_t data) -#else static void lcd_hd44780_write(bool command, uint8_t data, bool first_nibble_only) -#endif { - lcd_hd44780_db_direction(false); // switch DB direction to output to write data + lcd_hd44780_clear_signal(LCD_HD44780_RnW); // switch DB direction to output to write data if (command) { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to write command + lcd_hd44780_clear_signal(LCD_HD44780_RS); } else { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set high to write data + lcd_hd44780_set_signal(LCD_HD44780_RS); } + lcd_hd44780_flush_signal(); // no need to wait tAS = 40 ns before next step since the instructions are slower // write data if (data & 0x80) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + lcd_hd44780_set_signal(LCD_HD44780_DB7); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + lcd_hd44780_clear_signal(LCD_HD44780_DB7); } if (data & 0x40) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); + lcd_hd44780_set_signal(LCD_HD44780_DB6); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); + lcd_hd44780_clear_signal(LCD_HD44780_DB6); } if (data & 0x20) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + lcd_hd44780_set_signal(LCD_HD44780_DB5); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + lcd_hd44780_clear_signal(LCD_HD44780_DB5); } if (data & 0x10) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + lcd_hd44780_set_signal(LCD_HD44780_DB4); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + lcd_hd44780_clear_signal(LCD_HD44780_DB4); } -#if LCD_HD44780_INTERFACE_DL +#ifdef LCD_HD44780_INTERFACE_DL if (data & 0x08) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); + lcd_hd44780_set_signal(LCD_HD44780_DB3); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); + lcd_hd44780_clear_signal(LCD_HD44780_DB3); } if (data & 0x04) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); + lcd_hd44780_set_signal(LCD_HD44780_DB2); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); + lcd_hd44780_clear_signal(LCD_HD44780_DB2); } if (data & 0x02) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); + lcd_hd44780_set_signal(LCD_HD44780_DB1); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); + lcd_hd44780_clear_signal(LCD_HD44780_DB1); } if (data & 0x01) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); + lcd_hd44780_set_signal(LCD_HD44780_DB0); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); + lcd_hd44780_clear_signal(LCD_HD44780_DB0); } -#else +#else // LCD_HD44780_INTERFACE_DL if (!first_nibble_only) { // write second nibble // pulse E to send data sleep_us(1); // wait t_cycE = 500 ns - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high change output data + lcd_hd44780_set_signal(LCD_HD44780_E); // set high change output data + lcd_hd44780_flush_signal(); sleep_us(1); // wait PW_EH = 230 ns - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set low to latch data + lcd_hd44780_clear_signal(LCD_HD44780_E); // set low to latch data + lcd_hd44780_flush_signal(); // no need to wait t_H = 10 ns before next step since next instructions are slower // send second nibble if (data & 0x08) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + lcd_hd44780_set_signal(LCD_HD44780_DB7); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); + lcd_hd44780_clear_signal(LCD_HD44780_DB7); } if (data & 0x04) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); + lcd_hd44780_set_signal(LCD_HD44780_DB6); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); + lcd_hd44780_clear_signal(LCD_HD44780_DB6); } if (data & 0x02) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + lcd_hd44780_set_signal(LCD_HD44780_DB5); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); + lcd_hd44780_clear_signal(LCD_HD44780_DB5); } if (data & 0x01) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + lcd_hd44780_set_signal(LCD_HD44780_DB4); } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); + lcd_hd44780_clear_signal(LCD_HD44780_DB4); } } -#endif +#endif // LCD_HD44780_INTERFACE_DL // pulse E to send data sleep_us(1); // wait t_cycE = 500 ns - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high change output data + lcd_hd44780_set_signal(LCD_HD44780_E); // set high change output data + lcd_hd44780_flush_signal(); sleep_us(1); // wait PW_EH = 230 ns - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set low to latch data + lcd_hd44780_clear_signal(LCD_HD44780_E); // set low to latch data + lcd_hd44780_flush_signal(); // no need to wait t_H = 10 ns before next step since next instructions are slower // no need to wait t_AH = 10 ns before next step since next instructions are slower } +/** wait until controller is not busy anymore + * @param[in] timeout maximum time to wait in us + * @return address count, or >= 0x80 if timeout expired + */ +static uint8_t lcd_hd44780_wait_busy(uint16_t timeout) +{ +#if defined(LCD_HD44780_GPIO_RnW) + uint8_t ac = 0x80; // address counter to return + while (timeout && ((ac = lcd_hd44780_read(false)) >= 0x80)) { // wait until busy flag is low or timeout + // wait a bit + sleep_us(100); + if (timeout > 100) { + timeout -= 100; + } else { + timeout = 0; + } + } + return ac; +#else // I²C is also to slow to read (at least 6400 us per read) + sleep_us(timeout); // just wait + return 0; +#endif +} + /** write function set command * @param[in] dl_8bit 8-bit (true) or 4-bit (false) data length * @param[in] n_2lines 2 (true) or 1 (false) lines @@ -287,235 +613,139 @@ static void lcd_hd44780_function_set(bool dl_8bit, bool n_2lines, bool f_5x10) data |= 0x04; } lcd_hd44780_write(true, data, false); -#if LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif } -void lcd_hd44780_setup(bool n_2lines, bool f_5x10) +bool lcd_hd44780_setup(bool n_2lines, bool f_5x10) { - // enable all GPIO - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB7)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB6)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB5)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB4)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4)); // set as input to read -#if LCD_HD44780_INTERFACE_DL - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB3)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB2)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB1)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1)); // set as input to read - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB0)); // enable clock for GPIO port - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0)); // set as input to read -#endif - lcd_hd44780_db_input = true; // all lines are inputs - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RS)); // enable clock for GPIO port - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to read busy flag - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_RS)); // set GPIO as output -#if !LCD_HD44780_WRITE_ONLY - rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RW)); // enable clock for GPIO port - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set high to read - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_RW)); // set GPIO as output -#endif + sleep_ms(40 + 2); // wait for display to initialise after power on: 15 ms for VCC > 4.5 V, 40 ms for VCC > 2.7 V +#ifdef LCD_HD44780_I2C + // configure I²C peripheral + if (!i2c_master_check_signals(LCD_HD44780_I2C_PERIPH)) { // check if there are pull-ups to operator I²C + return false; + } + i2c_master_setup(LCD_HD44780_I2C_PERIPH, 100); // setup I²C bus (PCF8574 supports an I²C clock up to 100 kHz) + lcd_hd44780_i2c_output = 0xff; // put GPIO to input (sort of open drain output) + if (I2C_MASTER_RC_NONE != i2c_master_slave_write(LCD_HD44780_I2C_PERIPH, lcd_hd44780_i2c_addr, false, &lcd_hd44780_i2c_output, 1)) { // check if the device is present and set inputs + return false; + } +#else // LCD_HD44780_I2C + // enable all GPIO rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_E)); // enable clock for GPIO port gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // start idle low gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_E)); // set GPIO as output + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB7)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB7)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB6)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB6)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB5)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB5)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB4)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB4)); // open drain allows to read and write +#ifdef LCD_HD44780_INTERFACE_DL + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB3)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB3)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB2)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB2)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB1)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB1)); // open drain allows to read and write + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB0)); // enable clock for GPIO port + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); // idle high + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_DB0)); // open drain allows to read and write +#endif // LCD_HD44780_INTERFACE_DL + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RS)); // enable clock for GPIO port + gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to read busy flag + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_RS)); // set GPIO as output +#ifdef LCD_HD44780_GPIO_RnW + rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RnW)); // enable clock for GPIO port + gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RnW), GPIO_PIN(LCD_HD44780_GPIO_RnW)); // set high to read + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RnW), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(LCD_HD44780_GPIO_RnW)); // set GPIO as output +#endif // LCD_HD44780_GPIO_RnW +#endif // LCD_HD44780_I2C // initialize device - sleep_ms(40 + 2); // wait for display to initialise after power on: 15 ms for VCC > 4.5 V, 40 ms for VCC > 2.7 V lcd_hd44780_write(true, 0x30, true); // 1st function write set to go to state 1 (8-bit) or 2 (4-bit first nibble) (BF cannot be checked) sleep_ms(4 + 1); // wait 4.1 ms lcd_hd44780_write(true, 0x30, true); // 2st function write set to go to state 1 (8-bit) or 3 (4-bit second nibble) (BF cannot be checked) sleep_us(100 + 10); // wait 100 us lcd_hd44780_write(true, 0x30, true); // 3rd function write set to go to state 1 (8-bit) (BF cannot be checked) sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us -#if !LCD_HD44780_INTERFACE_DL +#ifndef LCD_HD44780_INTERFACE_DL lcd_hd44780_write(true, 0x20, true); // switch to 4-bit mode sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us +#endif // !LCD_HD44780_INTERFACE_DL +#ifdef LCD_HD44780_INTERFACE_DL + lcd_hd44780_function_set(1, n_2lines, f_5x10); // function set +#else + lcd_hd44780_function_set(0, n_2lines, f_5x10); // function set #endif - lcd_hd44780_function_set(LCD_HD44780_INTERFACE_DL, n_2lines, f_5x10); // function set lcd_hd44780_n_2lines = n_2lines; // remember number of lines -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif lcd_hd44780_display_control(false, false, false); // display off -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif lcd_hd44780_clear_display(); // display clear -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG); -#endif lcd_hd44780_entry_mode_set(true, false); // entry mode set -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); + +#if LCD_HD44780_I2C + return true; // I²C configuration succeeded #endif } void lcd_hd44780_release(void) { +#if LCD_HD44780_I2C + i2c_master_release(LCD_HD44780_I2C_PERIPH); // release I²C peripheral +#else // LCD_HD44780_I2C // switch back GPIO back to input gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4)); -#if LCD_HD44780_INTERFACE_DL +#ifdef LCD_HD44780_INTERFACE_DL gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1)); gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0)); -#endif +#endif // LCD_HD44780_INTERFACE_DL gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_RS)); -#if !LCD_HD44780_WRITE_ONLY - gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_RW)); -#endif +#ifdef LCD_HD44780_GPIO_RnW + gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RnW), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_RnW)); +#endif // LCD_HD44780_GPIO_RnW gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_E)); - // don't disable clock for GPIO domain since other pins might be used by others +#endif // LCD_HD44780_I2C: } -#if !LCD_HD44780_WRITE_ONLY -/** read from controller - * @param[in] data read data (true) or busy flag and address counter (false) - * @return data/AC read - */ -static uint8_t lcd_hd44780_read(bool data) -{ - lcd_hd44780_db_direction(true); // switch DB direction to input to read data - if (data) { - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set high to read data - } else { - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to read busy flag and AC - } - // no need to wait t_AS = 40 ns before next step since the instructions are slower - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high to have data output - sleep_us(0); // wait t_DDR = 160 ns before reading - // read data bits - uint8_t input = 0; // to store read data - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) { - input |= 0x80; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { - input |= 0x40; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5))) { - input |= 0x20; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4))) { - input |= 0x10; - } -#if LCD_HD44780_INTERFACE_DL - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3))) { - input |= 0x08; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2))) { - input |= 0x04; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1))) { - input |= 0x02; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0))) { - input |= 0x01; - } -#else - // get second nibble - // don't wait PW_EH = 230 ns since reading took longer - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // end current data read - sleep_us(1); // wait t_cycE = 500 ns - gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // have next data output - sleep_us(0); // wait t_DDR = 160 ns before reading - // read second nibble - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) { - input |= 0x08; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { - input |= 0x04; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { - input |= 0x02; - } - if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) { - input |= 0x01; - } -#endif - // don't wait PW_EH = 230 ns since reading took longer - gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // end data read - - return input; -} -#endif - -uint8_t lcd_hd44780_read_data(void) -{ - return lcd_hd44780_read(true); -} - -#if LCD_HD44780_WRITE_ONLY -uint8_t lcd_hd44780_wait_busy(uint16_t timeout) -{ - sleep_us(timeout); // just wait - return 0; -} -#else -uint8_t lcd_hd44780_wait_busy(uint16_t timeout) -{ - uint8_t ac = 0x80; // address counter to return - while (timeout && ((ac = lcd_hd44780_read(false)) >= 0x80)) { // wait until busy flag is low or timeout - // wait a bit - sleep_us(10); - if (timeout > 10) { - timeout -= 10; - } else { - timeout = 0; - } - } - - return ac; -} -#endif - void lcd_hd44780_write_data(uint8_t data) { -#if LCD_HD44780_INTERFACE_DL - lcd_hd44780_write(false, data); -#else lcd_hd44780_write(false, data, false); -#endif -#if LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif } void lcd_hd44780_write_command(uint8_t data) { -#if LCD_HD44780_INTERFACE_DL - lcd_hd44780_write(true, data); -#else lcd_hd44780_write(true, data, false); -#endif -#if LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif } void lcd_hd44780_clear_display(void) { lcd_hd44780_write_command(0x01); -#if LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG); -#endif } void lcd_hd44780_return_home(void) { lcd_hd44780_write_command(0x02); -#if LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG); -#endif } void lcd_hd44780_entry_mode_set(bool increment, bool shift) @@ -561,13 +791,9 @@ void lcd_hd44780_write_line(bool line2, const char* data, uint8_t length) return; } lcd_hd44780_set_ddram_address(line2 ? 0x40 : 0x00); // set start of line -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif for (uint8_t i = 0; i < length && i < (lcd_hd44780_n_2lines ? 0x27 : 0x4f); i++) { // write line lcd_hd44780_write_data(data[i]); // write character -#if !LCD_HD44780_BUSY_WAIT_INCLUDE lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT); -#endif } } diff --git a/lib/lcd_hd44780.h b/lib/lcd_hd44780.h index f4d44b5..7c472ff 100644 --- a/lib/lcd_hd44780.h +++ b/lib/lcd_hd44780.h @@ -16,34 +16,35 @@ * @file * @author King Kévin * @date 2019 - * @note peripherals used: GPIO @ref lcd_hd44780_gpio + * @note peripherals used: GPIO @ref lcd_hd44780_gpio, I²C @ref lcd_hd44780_i2c */ #pragma once +#include // I²C definitions -/** set to 1 if R/nW is connected to ground, 0 else - * @note the R/nW pin allows to read or write from/to the controller. this enables read back data, but more importantly read the busy flag to know when the controller finished processing the command. - * @note tying R/nW to ground instead of a GPIO saves a pin, but no data can be read back. Also every command needs to wait a minimum time before being able to send the next one, even if the controlled might actually already have processed it and is not busy anymore. +/** @defgroup lcd_hd44780_i2c I²C peripheral used to control backpack adapter for the HD44780 + * @{ */ -#define LCD_HD44780_WRITE_ONLY 0 +/** set I²C peripheral if I²C backpack adapter(s) is(/are) used */ +#define LCD_HD44780_I2C I2C1 +/** } */ + +#ifdef LCD_HD44780_I2C +/** I²C address of I²C backpack adapter (7 bits are LSb) */ +extern uint8_t lcd_hd44780_i2c_addr; +#endif /** setup peripherals to start communicating with HD4478 * @param[in] n_2lines 2 (true) or 1 (false) lines * @param[in] f_5x10 5x10 (true) or 5x8 (false) dots font + * @return if the display setup is successful, else the display is probably not on the I²C bus (or the I²C bus does not work) */ -void lcd_hd44780_setup(bool n_2lines, bool f_5x10); +bool lcd_hd44780_setup(bool n_2lines, bool f_5x10); /** release peripherals */ void lcd_hd44780_release(void); -/** wait until controller is not busy anymore - * @param[in] timeout maximum time to wait in us - * @return address count, or >= 0x80 if timeout expired - */ -uint8_t lcd_hd44780_wait_busy(uint16_t timeout); -#if !LCD_HD44780_WRITE_ONLY /** read data (from CG or DDRAM) * @return read data */ uint8_t lcd_hd44780_read_data(void); -#endif /** write data (to CG or DDRAM) * @param[in] data data to write * @warning does not wait for operation to complete: use lcd_hd44780_wait_busy(37)