diff --git a/examples/hd44780_lcd/Makefile b/examples/hd44780_lcd/Makefile new file mode 100644 index 0000000..02a868d --- /dev/null +++ b/examples/hd44780_lcd/Makefile @@ -0,0 +1,6 @@ +PROGRAM = hd44780_lcd +EXTRA_COMPONENTS = extras/hd44780 + +HD44780_I2C = 0 + +include ../../common.mk diff --git a/examples/hd44780_lcd/main.c b/examples/hd44780_lcd/main.c new file mode 100644 index 0000000..00ea8ad --- /dev/null +++ b/examples/hd44780_lcd/main.c @@ -0,0 +1,62 @@ +/* + * Example of using HD44780 driver with LCD + * connected directly to GPIO pins + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include + +#include + +static const uint8_t char_data[] = { + 0x04, 0x0e, 0x0e, 0x0e, 0x1f, 0x00, 0x04, 0x00, + 0x1f, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x1f, 0x00 +}; + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + hd44780_t lcd = { + .font = HD44780_FONT_5X8, + .lines = 2, + .pins = { + .rs = 5, + .e = 4, + .d4 = 0, + .d5 = 2, + .d6 = 14, + .d7 = 12, + .bl = HD44780_NOT_USED + } + }; + + hd44780_init(&lcd); + hd44780_upload_character(&lcd, 0, char_data); + hd44780_upload_character(&lcd, 1, char_data + 8); + + hd44780_gotoxy(&lcd, 0, 0); + hd44780_puts(&lcd, "\x08 Hello world!"); + hd44780_gotoxy(&lcd, 0, 1); + hd44780_puts(&lcd, "\x09 "); + + char time[16]; + + while (true) + { + hd44780_gotoxy(&lcd, 2, 1); + + snprintf(time, 7, "%u ", sdk_system_get_time() / 1000000); + time[sizeof(time) - 1] = 0; + + hd44780_puts(&lcd, time); + + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(1000); + } +} diff --git a/examples/hd44780_lcd/schematics.png b/examples/hd44780_lcd/schematics.png new file mode 100644 index 0000000..d3145cd Binary files /dev/null and b/examples/hd44780_lcd/schematics.png differ diff --git a/examples/i2c_lcd_test/Makefile b/examples/i2c_lcd_test/Makefile new file mode 100644 index 0000000..4e03494 --- /dev/null +++ b/examples/i2c_lcd_test/Makefile @@ -0,0 +1,6 @@ +PROGRAM = i2c_lcd_test +EXTRA_COMPONENTS = extras/i2c extras/pcf8574 extras/hd44780 + +HD44780_I2C = 1 + +include ../../common.mk diff --git a/examples/i2c_lcd_test/i2c_lcd.png b/examples/i2c_lcd_test/i2c_lcd.png new file mode 100644 index 0000000..815f4fe Binary files /dev/null and b/examples/i2c_lcd_test/i2c_lcd.png differ diff --git a/examples/i2c_lcd_test/main.c b/examples/i2c_lcd_test/main.c new file mode 100644 index 0000000..1951777 --- /dev/null +++ b/examples/i2c_lcd_test/main.c @@ -0,0 +1,71 @@ +/* + * Example of using driver for text LCD + * connected to I2C by PCF8574 + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include + +#include + +#define SCL_PIN 5 +#define SDA_PIN 4 +#define ADDR 0x27 + +static const uint8_t char_data[] = { + 0x04, 0x0e, 0x0e, 0x0e, 0x1f, 0x00, 0x04, 0x00, + 0x1f, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x1f, 0x00 +}; + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + i2c_init(SCL_PIN, SDA_PIN); + + hd44780_t lcd = { + .addr = ADDR, + .font = HD44780_FONT_5X8, + .lines = 2, + .pins = { + .rs = 0, + .e = 2, + .d4 = 4, + .d5 = 5, + .d6 = 6, + .d7 = 7, + .bl = 3 + }, + .backlight = true + }; + + hd44780_init(&lcd); + hd44780_upload_character(&lcd, 0, char_data); + hd44780_upload_character(&lcd, 1, char_data + 8); + + hd44780_gotoxy(&lcd, 0, 0); + hd44780_puts(&lcd, "\x08 Hello world!"); + hd44780_gotoxy(&lcd, 0, 1); + hd44780_puts(&lcd, "\x09 "); + + char time[16]; + + while (true) + { + hd44780_gotoxy(&lcd, 2, 1); + + snprintf(time, 7, "%u ", sdk_system_get_time() / 1000000); + time[sizeof(time) - 1] = 0; + + hd44780_puts(&lcd, time); + + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(1000); + } +} diff --git a/extras/hd44780/component.mk b/extras/hd44780/component.mk new file mode 100644 index 0000000..56f4b8a --- /dev/null +++ b/extras/hd44780/component.mk @@ -0,0 +1,8 @@ +INC_DIRS += $(hd44780_ROOT).. +hd44780_SRC_DIR = $(hd44780_ROOT) + +HD44780_I2C ?= 1 + +hd44780_CFLAGS = -DHD44780_I2C=${HD44780_I2C} $(CFLAGS) + +$(eval $(call component_compile_rules,hd44780)) diff --git a/extras/hd44780/hd44780.c b/extras/hd44780/hd44780.c new file mode 100644 index 0000000..b44f65a --- /dev/null +++ b/extras/hd44780/hd44780.c @@ -0,0 +1,189 @@ +#include "hd44780.h" + +#if (HD44780_I2C) +#include +#else +#include +#endif +#include + +#define MS 1000 + +#define DELAY_CMD_LONG (3 * MS) // >1.53ms according to datasheet +#define DELAY_CMD_SHORT (60) // >39us according to datasheet +#define DELAY_TOGGLE (10) +#define DELAY_INIT (5 * MS) + +#define CMD_CLEAR 0x01 +#define CMD_RETURN_HOME 0x02 +#define CMD_ENTRY_MODE 0x04 +#define CMD_DISPLAY_CTRL 0x08 +#define CMD_SHIFT 0x10 +#define CMD_FUNC_SET 0x20 +#define CMD_CGRAM_ADDR 0x40 +#define CMD_DDRAM_ADDR 0x80 + +// CMD_ENTRY_MODE +#define ARG_EM_INCREMENT (1 << 1) +#define ARG_EM_SHIFT (1) + +// CMD_DISPLAY_CTRL +#define ARG_DC_DISPLAY_ON (1 << 2) +#define ARG_DC_CURSOR_ON (1 << 1) +#define ARG_DC_CURSOR_BLINK (1) + +// CMD_FUNC_SET +#define ARG_FS_8_BIT (1 << 4) +#define ARG_FS_2_LINES (1 << 3) +#define ARG_FS_FONT_5X10 (1 << 2) + +#if (HD44780_I2C) + #define init_delay() do { sdk_os_delay_us(DELAY_INIT); } while (0) + #define short_delay() + #define long_delay() do { sdk_os_delay_us(DELAY_CMD_LONG); } while (0) +#else + #define init_delay() do { sdk_os_delay_us(DELAY_INIT); } while (0) + #define short_delay() do { sdk_os_delay_us(DELAY_CMD_SHORT); } while (0) + #define long_delay() do { sdk_os_delay_us(DELAY_CMD_LONG); } while (0) + #define toggle_delay() do { sdk_os_delay_us(DELAY_TOGGLE); } while (0) +#endif + +static const uint8_t line_addr[] = { 0x00, 0x40, 0x14, 0x54 }; + +static void write_nibble(const hd44780_t *lcd, uint8_t b, bool rs) +{ +#if (HD44780_I2C) + uint8_t data = (((b >> 3) & 1) << lcd->pins.d7) + | (((b >> 2) & 1) << lcd->pins.d6) + | (((b >> 1) & 1) << lcd->pins.d5) + | ((b & 1) << lcd->pins.d4) + | (rs ? 1 << lcd->pins.rs : 0) + | (lcd->backlight ? 1 << lcd->pins.bl : 0); + + pcf8574_port_write(lcd->addr, data | (1 << lcd->pins.e)); + pcf8574_port_write(lcd->addr, data); +#else + gpio_write(lcd->pins.d7, (b >> 3) & 1); + gpio_write(lcd->pins.d6, (b >> 2) & 1); + gpio_write(lcd->pins.d5, (b >> 1) & 1); + gpio_write(lcd->pins.d4, b & 1); + gpio_write(lcd->pins.rs, rs); + gpio_write(lcd->pins.e, true); + toggle_delay(); + gpio_write(lcd->pins.e, false); +#endif +} + +static void write_byte(const hd44780_t *lcd, uint8_t b, bool rs) +{ + write_nibble(lcd, b >> 4, rs); + write_nibble(lcd, b, rs); +} + +void hd44780_init(const hd44780_t *lcd) +{ +#if (!HD44780_I2C) + gpio_enable(lcd->pins.rs, GPIO_OUTPUT); + gpio_enable(lcd->pins.e, GPIO_OUTPUT); + gpio_enable(lcd->pins.d4, GPIO_OUTPUT); + gpio_enable(lcd->pins.d5, GPIO_OUTPUT); + gpio_enable(lcd->pins.d6, GPIO_OUTPUT); + gpio_enable(lcd->pins.d7, GPIO_OUTPUT); + if (lcd->pins.bl != HD44780_NOT_USED) + gpio_enable(lcd->pins.bl, GPIO_OUTPUT); +#endif + // switch to 4 bit mode + for (uint8_t i = 0; i < 3; i ++) + { + write_nibble(lcd, (CMD_FUNC_SET | ARG_FS_8_BIT) >> 4, false); + init_delay(); + } + write_nibble(lcd, CMD_FUNC_SET >> 4, false); + + // Specify the number of display lines and character font + write_byte(lcd, + CMD_FUNC_SET + | (lcd->lines > 1 ? ARG_FS_2_LINES : 0) + | (lcd->font == HD44780_FONT_5X10 ? ARG_FS_FONT_5X10 : 0), + false); + short_delay(); + // Display off + hd44780_control(lcd, false, false, false); + // Clear + hd44780_clear(lcd); + // Entry mode set + write_byte(lcd, CMD_ENTRY_MODE | ARG_EM_INCREMENT, false); + short_delay(); + // Display on + hd44780_control(lcd, true, false, false); +} + +void hd44780_control(const hd44780_t *lcd, bool on, bool cursor, bool cursor_blink) +{ + write_byte(lcd, + CMD_DISPLAY_CTRL + | (on ? ARG_DC_DISPLAY_ON : 0) + | (cursor ? ARG_DC_CURSOR_ON : 0) + | (cursor_blink ? ARG_DC_CURSOR_BLINK : 0), + false); + short_delay(); +} + +void hd44780_clear(const hd44780_t *lcd) +{ + write_byte(lcd, CMD_CLEAR, false); + long_delay(); +} + +void hd44780_gotoxy(const hd44780_t *lcd, uint8_t col, uint8_t line) +{ + if (line >= lcd->lines) line = lcd->lines - 1; + uint8_t addr = line < sizeof(line_addr) ? line_addr[line] : 0; + write_byte(lcd, CMD_DDRAM_ADDR + addr + col, false); + short_delay(); +} + +void hd44780_putc(const hd44780_t *lcd, char c) +{ + write_byte(lcd, c, true); + short_delay(); +} + +void hd44780_puts(const hd44780_t *lcd, const char *s) +{ + while (*s) + { + hd44780_putc(lcd, *s); + s++; + } +} + +void hd44780_set_backlight(hd44780_t *lcd, bool on) +{ + if (lcd->pins.bl == HD44780_NOT_USED) + return; + +#if (HD44780_I2C) + pcf8574_gpio_write(lcd->addr, lcd->pins.bl, on); +#else + gpio_write(lcd->pins.bl, on); +#endif + + lcd->backlight = on; +} + +void hd44780_upload_character(const hd44780_t *lcd, uint8_t num, const uint8_t *data) +{ + if (num > 7) return; + + uint8_t bytes = lcd->font == HD44780_FONT_5X8 ? 8 : 10; + write_byte(lcd, CMD_CGRAM_ADDR + num * bytes, false); + short_delay(); + for (uint8_t i = 0; i < bytes; i ++) + { + write_byte(lcd, data[i], true); + short_delay(); + } + + hd44780_gotoxy(lcd, 0, 0); +} diff --git a/extras/hd44780/hd44780.h b/extras/hd44780/hd44780.h new file mode 100644 index 0000000..a00302b --- /dev/null +++ b/extras/hd44780/hd44780.h @@ -0,0 +1,115 @@ +/* + * Driver for LCD text displays on LCD connected to I2C by PCF8574 + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef _EXTRAS_HD44780_H_ +#define _EXTRAS_HD44780_H_ + +#include +#include + +#ifndef HD44780_I2C +#define HD44780_I2C 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define HD44780_NOT_USED 0xff + +/** + * LCD font type. Please refer to the datasheet + * of your module. + */ +typedef enum +{ + HD44780_FONT_5X8 = 0, + HD44780_FONT_5X10 +} hd44780_font_t; + +/** + * LCD descriptor. Fill it before use. + */ +typedef struct +{ + uint8_t addr; //!< PCF8574 address (0b0100) + struct + { + uint8_t rs; //!< gpio/register bit used for RS pin + uint8_t e; //!< register bit used for E pin + uint8_t d4; //!< register bit used for D4 pin + uint8_t d5; //!< register bit used for D5 pin + uint8_t d6; //!< register bit used for D5 pin + uint8_t d7; //!< register bit used for D5 pin + uint8_t bl; //!< register bit used for backlight, 0xFF if not used + } pins; + hd44780_font_t font; //!< LCD Font type + uint8_t lines; //!< Number of lines for LCD. Many 16x1 LCD has two lines (like 8x2) + bool backlight; //!< Current backlight state +} hd44780_t; + +/** + * Init LCD. Set poition to (0, 0) + * \param lcd Pointer to the LCD descriptor + */ +void hd44780_init(const hd44780_t *lcd); + +/** + * On/off LCD, show/hide cursor, set cursor blink + * \param lcd Pointer to the LCD descriptor + */ +void hd44780_control(const hd44780_t *lcd, bool on, bool cursor, bool cursor_blink); + +/** + * Clear LCD memory and move char position to (0, 0) + * \param lcd Pointer to the LCD descriptor + */ +void hd44780_clear(const hd44780_t *lcd); + +/** + * Set current char position + * \param lcd Pointer to the LCD descriptor + * \param col Column + * \param line Line + */ +void hd44780_gotoxy(const hd44780_t *lcd, uint8_t col, uint8_t line); + +/** + * Print character + * \param lcd Pointer to the LCD descriptor + * \param c Character + */ +void hd44780_putc(const hd44780_t *lcd, char c); + +/** + * Print string + * \param lcd Pointer to the LCD descriptor + * \param s String + */ +void hd44780_puts(const hd44780_t *lcd, const char *s); + +/** + * Switch backlight + * \param lcd Pointer to the LCD descriptor + * \param on Turn backlight on if true + */ +void hd44780_set_backlight(hd44780_t *lcd, bool on); + +/** + * Upload character data to the CGRAM. + * Current position will be set to (0, 0) after uploading + * \param lcd Pointer to the LCD descriptor + * \param num Character number (0..7) + * \param data Character data: 8 or 10 bytes depending on the font + */ +void hd44780_upload_character(const hd44780_t *lcd, uint8_t num, const uint8_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXTRAS_HD44780_H_ */