797 lines
29 KiB
C
797 lines
29 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
/** library to communication with Hitacho HD44780 LCD controller
|
|
* @file
|
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
|
* @date 2019-2020
|
|
* @note peripherals used: GPIO @ref lcd_hd44780_gpio
|
|
*/
|
|
/* standard libraries */
|
|
#include <stdint.h> // standard integer types
|
|
#include <stdlib.h> // general utilities
|
|
#include <stdbool.h> // boolean utilities
|
|
|
|
/* STM32 (including CM3) libraries */
|
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
|
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
|
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
|
|
|
/* own libraries */
|
|
#include "global.h" // common methods
|
|
#include "lcd_hd44780.h" // own definitions
|
|
#if defined(LCD_HD44780_I2C) && LCD_HD44780_I2C
|
|
#include "i2c_master.h" // I²C utilities
|
|
#endif
|
|
|
|
/** include busy time waiting in writes
|
|
* @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
|
|
*/
|
|
#if defined(LCD_HD44780_I2C) && 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)
|
|
* @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_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 defined(LCD_HD44780_I2C) && 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_RnW PB10
|
|
/** enable pin
|
|
* @warning needs an external 10k pull-up resistor
|
|
*/
|
|
#define LCD_HD44780_GPIO_E PB11
|
|
/** data bit 7
|
|
* @note pulled up by HD44780
|
|
*/
|
|
#define LCD_HD44780_GPIO_DB7 PB12
|
|
/** data bit 6
|
|
* @note pulled up by HD44780
|
|
*/
|
|
#define LCD_HD44780_GPIO_DB6 PB13
|
|
/** data bit 5
|
|
* @note pulled up by HD44780
|
|
*/
|
|
#define LCD_HD44780_GPIO_DB5 PB14
|
|
/** data bit 4
|
|
* @note pulled up by HD44780
|
|
*/
|
|
#define LCD_HD44780_GPIO_DB4 PB15
|
|
/** data bit 3
|
|
* @note pulled up by HD44780
|
|
* @note leave undefined when only 4-bit mode is used
|
|
*/
|
|
//#define LCD_HD44780_GPIO_DB3 P
|
|
/** data bit 2
|
|
* @note pulled up by HD44780
|
|
* @note leave undefined when only 4-bit mode is used
|
|
*/
|
|
//#define LCD_HD44780_GPIO_DB2 P
|
|
/** data bit 1
|
|
* @note pulled up by HD44780
|
|
* @note leave undefined when only 4-bit mode is used
|
|
*/
|
|
//#define LCD_HD44780_GPIO_DB1 P
|
|
/** data bit 0
|
|
* @note pulled up by HD44780
|
|
* @note leave undefined when only 4-bit mode is used
|
|
*/
|
|
//#define LCD_HD44780_GPIO_DB0 P
|
|
/** @} */
|
|
#endif // LCD_HD44780_I2C
|
|
|
|
/** set for 8-bit interface data
|
|
* @note I²C implies 4-bit
|
|
*/
|
|
#if defined(LCD_HD44780_I2C) && LCD_HD44780_I2C
|
|
#define LCD_HD44780_INTERFACE_DL 0
|
|
#elif 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 1
|
|
#else
|
|
#define LCD_HD44780_INTERFACE_DL 0
|
|
#endif
|
|
|
|
/** if the display is configured having 2 lines */
|
|
static bool lcd_hd44780_n_2lines = true;
|
|
|
|
/** set signals
|
|
* @param[in] signals
|
|
*/
|
|
static void lcd_hd44780_set_signal(uint16_t signals)
|
|
{
|
|
#if defined(LCD_HD44780_I2C) && 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 defined(LCD_HD44780_INTERFACE_DL) && 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_DB6));
|
|
}
|
|
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 defined(LCD_HD44780_I2C) && 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 defined(LCD_HD44780_INTERFACE_DL) && 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_DB6));
|
|
}
|
|
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
|
|
}
|
|
|
|
/** 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 defined(LCD_HD44780_I2C) && 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_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) && 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)
|
|
*/
|
|
static void lcd_hd44780_write(bool command, uint8_t data, bool first_nibble_only)
|
|
{
|
|
lcd_hd44780_clear_signal(LCD_HD44780_RnW); // switch DB direction to output to write data
|
|
if (command) {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_RS);
|
|
} else {
|
|
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) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB7);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB7);
|
|
}
|
|
if (data & 0x40) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB6);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB6);
|
|
}
|
|
if (data & 0x20) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB5);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB5);
|
|
}
|
|
if (data & 0x10) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB4);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB4);
|
|
}
|
|
#if defined(LCD_HD44780_INTERFACE_DL) && LCD_HD44780_INTERFACE_DL
|
|
if (data & 0x08) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB3);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB3);
|
|
}
|
|
if (data & 0x04) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB2);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB2);
|
|
}
|
|
if (data & 0x02) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB1);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB1);
|
|
}
|
|
if (data & 0x01) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB0);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB0);
|
|
}
|
|
#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
|
|
lcd_hd44780_set_signal(LCD_HD44780_E); // set high change output data
|
|
lcd_hd44780_flush_signal();
|
|
sleep_us(1); // wait PW_EH = 230 ns
|
|
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) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB7);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB7);
|
|
}
|
|
if (data & 0x04) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB6);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB6);
|
|
}
|
|
if (data & 0x02) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB5);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB5);
|
|
}
|
|
if (data & 0x01) {
|
|
lcd_hd44780_set_signal(LCD_HD44780_DB4);
|
|
} else {
|
|
lcd_hd44780_clear_signal(LCD_HD44780_DB4);
|
|
}
|
|
}
|
|
#endif // LCD_HD44780_INTERFACE_DL
|
|
// pulse E to send data
|
|
sleep_us(1); // wait t_cycE = 500 ns
|
|
lcd_hd44780_set_signal(LCD_HD44780_E); // set high change output data
|
|
lcd_hd44780_flush_signal();
|
|
sleep_us(1); // wait PW_EH = 230 ns
|
|
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
|
|
* @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);
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
}
|
|
|
|
bool lcd_hd44780_setup(bool n_2lines, bool f_5x10)
|
|
{
|
|
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
|
|
#if defined(LCD_HD44780_I2C) && 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(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(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(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(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
|
|
#if defined(LCD_HD44780_INTERFACE_DL) && LCD_HD44780_INTERFACE_DL
|
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB3)); // enable clock for GPIO port
|
|
gpio_set(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(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(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(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
|
|
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 defined(LCD_HD44780_INTERFACE_DL) && LCD_HD44780_INTERFACE_DL
|
|
lcd_hd44780_function_set(1, n_2lines, f_5x10); // function set
|
|
#else
|
|
lcd_hd44780_write(true, 0x20, true); // switch to 4-bit mode
|
|
sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us
|
|
lcd_hd44780_function_set(0, n_2lines, f_5x10); // function set
|
|
#endif
|
|
lcd_hd44780_n_2lines = n_2lines; // remember number of lines
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
lcd_hd44780_display_control(false, false, false); // display off
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
lcd_hd44780_clear_display(); // display clear
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
|
lcd_hd44780_entry_mode_set(true, false); // entry mode set
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
|
|
return true; // I²C configuration succeeded
|
|
}
|
|
|
|
void lcd_hd44780_release(void)
|
|
{
|
|
#if defined(LCD_HD44780_I2C) && 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 defined(LCD_HD44780_INTERFACE_DL) && 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 // 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));
|
|
#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));
|
|
#endif // LCD_HD44780_I2C:
|
|
}
|
|
|
|
void lcd_hd44780_write_data(uint8_t data)
|
|
{
|
|
lcd_hd44780_write(false, data, false);
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
}
|
|
|
|
void lcd_hd44780_write_command(uint8_t data)
|
|
{
|
|
lcd_hd44780_write(true, data, false);
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
}
|
|
|
|
void lcd_hd44780_clear_display(void)
|
|
{
|
|
lcd_hd44780_write_command(0x01);
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
|
}
|
|
|
|
void lcd_hd44780_return_home(void)
|
|
{
|
|
lcd_hd44780_write_command(0x02);
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
|
}
|
|
|
|
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
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
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
|
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
|
}
|
|
}
|