lcd_hd44780: add I2C backpack support

This commit is contained in:
King Kévin 2019-12-12 18:58:04 +01:00
parent e087ff744d
commit a751b95352
2 changed files with 509 additions and 282 deletions

View File

@ -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
}
}

View File

@ -16,34 +16,35 @@
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @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 <libopencm3/stm32/i2c.h> // 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)