/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** library to communication with Hitacho HD44780 LCD controller * @file * @author King Kévin * @date 2019 * @note peripherals used: GPIO @ref lcd_hd44780_gpio */ /* standard libraries */ #include // standard integer types #include // general utilities #include // boolean utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // real-time control clock library #include // general purpose input output library /* own libraries */ #include "global.h" // common methods #include "lcd_hd44780.h" // own definitions /** 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 */ #define LCD_HD44780_BUSY_WAIT_INCLUDE 1 /** 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) * @note I have no idea why, but longer times increase the contrast darkness */ #define LCD_HD44780_BUSY_WAIT_LONG (1520 + 500) /* usual HD44780 pinout: * - 1 GND: ground * - 2 VCC: 5V (3.3V versions also exist, but a less common) * - 3 V0 : LCD bias voltage, connect to 10-20k potentiometer (VCC to GND) * - 4 RS : Register Select (high = data, low = instruction) * - 5 R/W: Read/Write (high = read, low = write) * - 6 E : enable (falling edge to latch data, high to output register) * - 7 DB0: Data Bit 0 (for 8-bit transfer) * - 8 DB1: Data Bit 1 (for 8-bit transfer) * - 9 DB2: Data Bit 2 (for 8-bit transfer) * - 10 DB3: Data Bit 3 (for 8-bit transfer) * - 11 DB4: Data Bit 4 (for 4-bit transfer) * - 12 DB5: Data Bit 5 (for 4-bit transfer) * - 13 DB6: Data Bit 6 (for 4-bit transfer) * - 14 DB7: Data Bit 7 (for 4-bit transfer) * - 15 BLA: Backlight Anode * - 16 BLK: Backlight Cathode */ /** @defgroup lcd_hd44780_gpio GPIO used to control the HD44780 * @{ */ /** register select * @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 /** enable pin * @warning needs an external 10k pull-up resistor */ #define LCD_HD44780_GPIO_E PB11 /** data bit * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB7 PB12 /** data bit 6 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB6 PB13 /** data bit 5 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB5 PB14 /** data bit 4 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB4 PB15 #if LCD_HD44780_INTERFACE_DL /** data bit 3 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB3 P /** data bit 2 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB2 P /** data bit 1 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB1 P /** data bit 0 * @note is pulled up by HD44780 */ #define LCD_HD44780_GPIO_DB0 P #endif /** @} */ /** direction of data lines */ static bool lcd_hd44780_db_input = true; /** 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 */ static void lcd_hd44780_db_direction(bool input) { if (lcd_hd44780_db_input == input) { return; } 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)); #endif } lcd_hd44780_db_input = input; // remember direction } /** write to controller * @param[in] command true if it is an command, false if it is data * @param[in] data data bit to write */ #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 if (command) { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to write command } else { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set high to write data } // 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)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); } if (data & 0x40) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); } if (data & 0x20) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); } if (data & 0x10) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); } #if LCD_HD44780_INTERFACE_DL if (data & 0x08) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3)); } if (data & 0x04) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2)); } if (data & 0x02) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1)); } if (data & 0x01) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0)); } #else 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 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 // 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)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7)); } if (data & 0x04) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6)); } if (data & 0x02) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5)); } if (data & 0x01) { gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); } else { gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4)); } } #endif // 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 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 // 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 } /** 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 * @param[in] f_5x10 5x10 (true) or 5x8 (false) dots font */ static void lcd_hd44780_function_set(bool dl_8bit, bool n_2lines, bool f_5x10) { uint8_t data = 0x20; if (dl_8bit) { data |= 0x10; } if (n_2lines) { data |= 0x08; } if (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) { // 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 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 // 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 lcd_hd44780_write(true, 0x20, true); // switch to 4-bit mode sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us #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); #endif } void lcd_hd44780_release(void) { // 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 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 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 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 } #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) { uint8_t command = 0x04; if (increment) { command |= 0x02; } if (shift) { command |= 0x01; } lcd_hd44780_write_command(command); } void lcd_hd44780_display_control(bool d, bool c, bool b) { uint8_t command = 0x08; if (d) { command |= 0x04; } if (c) { command |= 0x02; } if (b) { command |= 0x01; } lcd_hd44780_write_command(command); } void lcd_hd44780_set_cgram_address(uint8_t acg) { lcd_hd44780_write_command(0x40 | (acg & 0x3f)); } void lcd_hd44780_set_ddram_address(uint8_t add) { lcd_hd44780_write_command(0x80 | (add & 0x7f)); } void lcd_hd44780_write_line(bool line2, const char* data, uint8_t length) { if (line2 && !lcd_hd44780_n_2lines) { // writing line 2 when it does not exists is not possible 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 } }