diff --git a/i2c-lcd1602.c b/i2c-lcd1602.c index 17f8cb1..317ac7d 100644 --- a/i2c-lcd1602.c +++ b/i2c-lcd1602.c @@ -93,73 +93,71 @@ #include "freertos/task.h" #include "esp_system.h" #include "esp_log.h" -#include "rom/ets_sys.h" #include "i2c-lcd1602.h" #define TAG "i2c-lcd1602" // Delays (microseconds) -#define DELAY_POWER_ON 50000 // wait at least 40us after VCC rises to 2.7V -#define DELAY_INIT_1 4500 // wait at least 4.1ms (fig 24, page 46) -#define DELAY_INIT_2 4500 // wait at least 4.1ms (fig 24, page 46) -#define DELAY_INIT_3 120 // wait at least 100us (fig 24, page 46) +#define DELAY_POWER_ON 50000 // wait at least 40us after VCC rises to 2.7V +#define DELAY_INIT_1 4500 // wait at least 4.1ms (fig 24, page 46) +#define DELAY_INIT_2 4500 // wait at least 4.1ms (fig 24, page 46) +#define DELAY_INIT_3 120 // wait at least 100us (fig 24, page 46) -#define DELAY_CLEAR_DISPLAY 2000 -#define DELAY_RETURN_HOME 2000 - -#define DELAY_ENABLE_PULSE_WIDTH 1 // enable pulse must be at least 450ns wide -#define DELAY_ENABLE_PULSE_SETTLE 50 // command requires > 37us to settle (table 6 in datasheet) +#define DELAY_CLEAR_DISPLAY 2000 +#define DELAY_RETURN_HOME 2000 +#define DELAY_ENABLE_PULSE_WIDTH 1 // enable pulse must be at least 450ns wide +#define DELAY_ENABLE_PULSE_SETTLE 50 // command requires > 37us to settle (table 6 in datasheet) // Commands -#define COMMAND_CLEAR_DISPLAY 0x01 -#define COMMAND_RETURN_HOME 0x02 -#define COMMAND_ENTRY_MODE_SET 0x04 -#define COMMAND_DISPLAY_CONTROL 0x08 -#define COMMAND_SHIFT 0x10 -#define COMMAND_FUNCTION_SET 0x20 -#define COMMAND_SET_CGRAM_ADDR 0x40 -#define COMMAND_SET_DDRAM_ADDR 0x80 +#define COMMAND_CLEAR_DISPLAY 0x01 +#define COMMAND_RETURN_HOME 0x02 +#define COMMAND_ENTRY_MODE_SET 0x04 +#define COMMAND_DISPLAY_CONTROL 0x08 +#define COMMAND_SHIFT 0x10 +#define COMMAND_FUNCTION_SET 0x20 +#define COMMAND_SET_CGRAM_ADDR 0x40 +#define COMMAND_SET_DDRAM_ADDR 0x80 // COMMAND_ENTRY_MODE_SET flags -#define FLAG_ENTRY_MODE_SET_ENTRY_INCREMENT 0x02 -#define FLAG_ENTRY_MODE_SET_ENTRY_DECREMENT 0x00 -#define FLAG_ENTRY_MODE_SET_ENTRY_SHIFT_ON 0x01 -#define FLAG_ENTRY_MODE_SET_ENTRY_SHIFT_OFF 0x00 +#define FLAG_ENTRY_MODE_SET_ENTRY_INCREMENT 0x02 +#define FLAG_ENTRY_MODE_SET_ENTRY_DECREMENT 0x00 +#define FLAG_ENTRY_MODE_SET_ENTRY_SHIFT_ON 0x01 +#define FLAG_ENTRY_MODE_SET_ENTRY_SHIFT_OFF 0x00 // COMMAND_DISPLAY_CONTROL flags -#define FLAG_DISPLAY_CONTROL_DISPLAY_ON 0x04 +#define FLAG_DISPLAY_CONTROL_DISPLAY_ON 0x04 #define FLAG_DISPLAY_CONTROL_DISPLAY_OFF 0x00 -#define FLAG_DISPLAY_CONTROL_CURSOR_ON 0x02 -#define FLAG_DISPLAY_CONTROL_CURSOR_OFF 0x00 -#define FLAG_DISPLAY_CONTROL_BLINK_ON 0x01 -#define FLAG_DISPLAY_CONTROL_BLINK_OFF 0x00 +#define FLAG_DISPLAY_CONTROL_CURSOR_ON 0x02 +#define FLAG_DISPLAY_CONTROL_CURSOR_OFF 0x00 +#define FLAG_DISPLAY_CONTROL_BLINK_ON 0x01 +#define FLAG_DISPLAY_CONTROL_BLINK_OFF 0x00 // COMMAND_SHIFT flags -#define FLAG_SHIFT_MOVE_DISPLAY 0x08 -#define FLAG_SHIFT_MOVE_CURSOR 0x00 -#define FLAG_SHIFT_MOVE_LEFT 0x04 -#define FLAG_SHIFT_MOVE_RIGHT 0x00 +#define FLAG_SHIFT_MOVE_DISPLAY 0x08 +#define FLAG_SHIFT_MOVE_CURSOR 0x00 +#define FLAG_SHIFT_MOVE_LEFT 0x04 +#define FLAG_SHIFT_MOVE_RIGHT 0x00 // COMMAND_FUNCTION_SET flags -#define FLAG_FUNCTION_SET_MODE_8BIT 0x10 -#define FLAG_FUNCTION_SET_MODE_4BIT 0x00 -#define FLAG_FUNCTION_SET_LINES_2 0x08 -#define FLAG_FUNCTION_SET_LINES_1 0x00 -#define FLAG_FUNCTION_SET_DOTS_5X10 0x04 -#define FLAG_FUNCTION_SET_DOTS_5X8 0x00 +#define FLAG_FUNCTION_SET_MODE_8BIT 0x10 +#define FLAG_FUNCTION_SET_MODE_4BIT 0x00 +#define FLAG_FUNCTION_SET_LINES_2 0x08 +#define FLAG_FUNCTION_SET_LINES_1 0x00 +#define FLAG_FUNCTION_SET_DOTS_5X10 0x04 +#define FLAG_FUNCTION_SET_DOTS_5X8 0x00 // Control flags -#define FLAG_BACKLIGHT_ON 0b00001000 // backlight enabled (disabled if clear) -#define FLAG_BACKLIGHT_OFF 0b00000000 // backlight disabled -#define FLAG_ENABLE 0b00000100 -#define FLAG_READ 0b00000010 // read (write if clear) -#define FLAG_WRITE 0b00000000 // write -#define FLAG_RS_DATA 0b00000001 // data (command if clear) -#define FLAG_RS_COMMAND 0b00000000 // command - -static bool _is_init(const i2c_lcd1602_info_t * i2c_lcd1602_info) +#define FLAG_BACKLIGHT_ON 0b00001000 // backlight enabled (disabled if clear) +#define FLAG_BACKLIGHT_OFF 0b00000000 // backlight disabled +#define FLAG_ENABLE 0b00000100 +#define FLAG_READ 0b00000010 // read (write if clear) +#define FLAG_WRITE 0b00000000 // write +#define FLAG_RS_DATA 0b00000001 // data (command if clear) +#define FLAG_RS_COMMAND 0b00000000 // command + +static bool _is_init(const i2c_lcd1602_info_t *i2c_lcd1602_info) { bool ok = false; if (i2c_lcd1602_info != NULL) @@ -180,22 +178,22 @@ static bool _is_init(const i2c_lcd1602_info_t * i2c_lcd1602_info) return ok; } - // Set or clear the specified flag depending on condition - static uint8_t _set_or_clear(uint8_t flags, bool condition, uint8_t flag) - { - if (condition) - { - flags |= flag; - } - else - { - flags &= ~flag; - } - return flags; - } +// Set or clear the specified flag depending on condition +static uint8_t _set_or_clear(uint8_t flags, bool condition, uint8_t flag) +{ + if (condition) + { + flags |= flag; + } + else + { + flags &= ~flag; + } + return flags; +} // send data to the I/O Expander -static esp_err_t _write_to_expander(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t data) +static esp_err_t _write_to_expander(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t data) { // backlight flag must be included with every write to maintain backlight state ESP_LOGD(TAG, "_write_to_expander 0x%02x", data | i2c_lcd1602_info->backlight_flag); @@ -206,17 +204,17 @@ static esp_err_t _write_to_expander(const i2c_lcd1602_info_t * i2c_lcd1602_info, // 2 x nibble sequence. // clock data from expander to LCD by causing a falling edge on Enable -static esp_err_t _strobe_enable(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t data) +static esp_err_t _strobe_enable(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t data) { esp_err_t err1 = _write_to_expander(i2c_lcd1602_info, data | FLAG_ENABLE); - ets_delay_us(DELAY_ENABLE_PULSE_WIDTH); + esp_rom_delay_us(DELAY_ENABLE_PULSE_WIDTH); esp_err_t err2 = _write_to_expander(i2c_lcd1602_info, data & ~FLAG_ENABLE); - ets_delay_us(DELAY_ENABLE_PULSE_SETTLE); + esp_rom_delay_us(DELAY_ENABLE_PULSE_SETTLE); return err1 ? err1 : err2; } // send top nibble to the LCD controller -static esp_err_t _write_top_nibble(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t data) +static esp_err_t _write_top_nibble(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t data) { ESP_LOGD(TAG, "_write_top_nibble 0x%02x", data); esp_err_t err1 = _write_to_expander(i2c_lcd1602_info, data); @@ -225,7 +223,7 @@ static esp_err_t _write_top_nibble(const i2c_lcd1602_info_t * i2c_lcd1602_info, } // send command or data to controller -static esp_err_t _write(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t value, uint8_t register_select_flag) +static esp_err_t _write(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t value, uint8_t register_select_flag) { ESP_LOGD(TAG, "_write 0x%02x | 0x%02x", value, register_select_flag); esp_err_t err1 = _write_top_nibble(i2c_lcd1602_info, (value & 0xf0) | register_select_flag); @@ -234,25 +232,24 @@ static esp_err_t _write(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t val } // send command to controller -static esp_err_t _write_command(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t command) +static esp_err_t _write_command(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t command) { ESP_LOGD(TAG, "_write_command 0x%02x", command); return _write(i2c_lcd1602_info, command, FLAG_RS_COMMAND); } // send data to controller -static esp_err_t _write_data(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t data) +static esp_err_t _write_data(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t data) { ESP_LOGD(TAG, "_write_data 0x%02x", data); return _write(i2c_lcd1602_info, data, FLAG_RS_DATA); } - // Public API -i2c_lcd1602_info_t * i2c_lcd1602_malloc(void) +i2c_lcd1602_info_t *i2c_lcd1602_malloc(void) { - i2c_lcd1602_info_t * i2c_lcd1602_info = malloc(sizeof(*i2c_lcd1602_info)); + i2c_lcd1602_info_t *i2c_lcd1602_info = malloc(sizeof(*i2c_lcd1602_info)); if (i2c_lcd1602_info != NULL) { memset(i2c_lcd1602_info, 0, sizeof(*i2c_lcd1602_info)); @@ -265,7 +262,7 @@ i2c_lcd1602_info_t * i2c_lcd1602_malloc(void) return i2c_lcd1602_info; } -void i2c_lcd1602_free(i2c_lcd1602_info_t ** i2c_lcd1602_info) +void i2c_lcd1602_free(i2c_lcd1602_info_t **i2c_lcd1602_info) { if (i2c_lcd1602_info != NULL && (*i2c_lcd1602_info != NULL)) { @@ -279,7 +276,7 @@ void i2c_lcd1602_free(i2c_lcd1602_info_t ** i2c_lcd1602_info) } } -esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t * smbus_info, +esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t *i2c_lcd1602_info, smbus_info_t *smbus_info, bool backlight, uint8_t num_rows, uint8_t num_columns, uint8_t num_visible_columns) { esp_err_t err = ESP_FAIL; @@ -302,7 +299,7 @@ esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t * // See page 45/46 of HD44780 data sheet for the initialisation procedure. // Wait at least 40ms after power rises above 2.7V before sending commands. - ets_delay_us(DELAY_POWER_ON); + esp_rom_delay_us(DELAY_POWER_ON); err = i2c_lcd1602_reset(i2c_lcd1602_info); } @@ -314,7 +311,7 @@ esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t * i2c_lcd1602_info, smbus_info_t * return err; } -esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t first_err = ESP_OK; esp_err_t last_err = ESP_FAIL; @@ -327,7 +324,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) ESP_LOGE(TAG, "reset: _write_to_expander 1 failed: %d", last_err); } - ets_delay_us(1000); + esp_rom_delay_us(1000); // select 4-bit mode on LCD controller - see datasheet page 46, figure 24. if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK) @@ -337,7 +334,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) ESP_LOGE(TAG, "reset: _write_top_nibble 1 failed: %d", last_err); } - ets_delay_us(DELAY_INIT_1); + esp_rom_delay_us(DELAY_INIT_1); // repeat if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK) @@ -347,7 +344,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) ESP_LOGE(TAG, "reset: _write_top_nibble 2 failed: %d", last_err); } - ets_delay_us(DELAY_INIT_2); + esp_rom_delay_us(DELAY_INIT_2); // repeat if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x03 << 4)) != ESP_OK) @@ -357,7 +354,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) ESP_LOGE(TAG, "reset: _write_top_nibble 3 failed: %d", last_err); } - ets_delay_us(DELAY_INIT_3); + esp_rom_delay_us(DELAY_INIT_3); // select 4-bit mode if ((last_err = _write_top_nibble(i2c_lcd1602_info, 0x02 << 4)) != ESP_OK) @@ -406,7 +403,7 @@ esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t * i2c_lcd1602_info) return first_err; } -esp_err_t i2c_lcd1602_clear(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_clear(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -414,13 +411,13 @@ esp_err_t i2c_lcd1602_clear(const i2c_lcd1602_info_t * i2c_lcd1602_info) err = _write_command(i2c_lcd1602_info, COMMAND_CLEAR_DISPLAY); if (err == ESP_OK) { - ets_delay_us(DELAY_CLEAR_DISPLAY); + esp_rom_delay_us(DELAY_CLEAR_DISPLAY); } } return err; } -esp_err_t i2c_lcd1602_home(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_home(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -428,18 +425,18 @@ esp_err_t i2c_lcd1602_home(const i2c_lcd1602_info_t * i2c_lcd1602_info) err = _write_command(i2c_lcd1602_info, COMMAND_RETURN_HOME); if (err == ESP_OK) { - ets_delay_us(DELAY_RETURN_HOME); + esp_rom_delay_us(DELAY_RETURN_HOME); } } return err; } -esp_err_t i2c_lcd1602_move_cursor(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t col, uint8_t row) +esp_err_t i2c_lcd1602_move_cursor(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t col, uint8_t row) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) { - const int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + const int row_offsets[] = {0x00, 0x40, 0x14, 0x54}; if (row > i2c_lcd1602_info->num_rows) { row = i2c_lcd1602_info->num_rows - 1; @@ -453,7 +450,7 @@ esp_err_t i2c_lcd1602_move_cursor(const i2c_lcd1602_info_t * i2c_lcd1602_info, u return err; } -esp_err_t i2c_lcd1602_set_backlight(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enable) +esp_err_t i2c_lcd1602_set_backlight(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -464,7 +461,7 @@ esp_err_t i2c_lcd1602_set_backlight(i2c_lcd1602_info_t * i2c_lcd1602_info, bool return err; } -esp_err_t i2c_lcd1602_set_display(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enable) +esp_err_t i2c_lcd1602_set_display(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -475,7 +472,7 @@ esp_err_t i2c_lcd1602_set_display(i2c_lcd1602_info_t * i2c_lcd1602_info, bool en return err; } -esp_err_t i2c_lcd1602_set_cursor(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enable) +esp_err_t i2c_lcd1602_set_cursor(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -486,7 +483,7 @@ esp_err_t i2c_lcd1602_set_cursor(i2c_lcd1602_info_t * i2c_lcd1602_info, bool ena return err; } -esp_err_t i2c_lcd1602_set_blink(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enable) +esp_err_t i2c_lcd1602_set_blink(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -497,7 +494,7 @@ esp_err_t i2c_lcd1602_set_blink(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enab return err; } -esp_err_t i2c_lcd1602_set_left_to_right(i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_set_left_to_right(i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -508,7 +505,7 @@ esp_err_t i2c_lcd1602_set_left_to_right(i2c_lcd1602_info_t * i2c_lcd1602_info) return err; } -esp_err_t i2c_lcd1602_set_right_to_left(i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_set_right_to_left(i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -519,7 +516,7 @@ esp_err_t i2c_lcd1602_set_right_to_left(i2c_lcd1602_info_t * i2c_lcd1602_info) return err; } -esp_err_t i2c_lcd1602_set_auto_scroll(i2c_lcd1602_info_t * i2c_lcd1602_info, bool enable) +esp_err_t i2c_lcd1602_set_auto_scroll(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -530,7 +527,7 @@ esp_err_t i2c_lcd1602_set_auto_scroll(i2c_lcd1602_info_t * i2c_lcd1602_info, boo return err; } -esp_err_t i2c_lcd1602_scroll_display_left(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_scroll_display_left(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -541,7 +538,7 @@ esp_err_t i2c_lcd1602_scroll_display_left(const i2c_lcd1602_info_t * i2c_lcd1602 return err; } -esp_err_t i2c_lcd1602_scroll_display_right(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_scroll_display_right(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -552,7 +549,7 @@ esp_err_t i2c_lcd1602_scroll_display_right(const i2c_lcd1602_info_t * i2c_lcd160 return err; } -esp_err_t i2c_lcd1602_move_cursor_left(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_move_cursor_left(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -563,7 +560,7 @@ esp_err_t i2c_lcd1602_move_cursor_left(const i2c_lcd1602_info_t * i2c_lcd1602_in return err; } -esp_err_t i2c_lcd1602_move_cursor_right(const i2c_lcd1602_info_t * i2c_lcd1602_info) +esp_err_t i2c_lcd1602_move_cursor_right(const i2c_lcd1602_info_t *i2c_lcd1602_info) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -574,12 +571,12 @@ esp_err_t i2c_lcd1602_move_cursor_right(const i2c_lcd1602_info_t * i2c_lcd1602_i return err; } -esp_err_t i2c_lcd1602_define_char(const i2c_lcd1602_info_t * i2c_lcd1602_info, i2c_lcd1602_custom_index_t index, const uint8_t pixelmap[]) +esp_err_t i2c_lcd1602_define_char(const i2c_lcd1602_info_t *i2c_lcd1602_info, i2c_lcd1602_custom_index_t index, const uint8_t pixelmap[]) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) { - index &= 0x07; // only the first 8 indexes can be used for custom characters + index &= 0x07; // only the first 8 indexes can be used for custom characters err = _write_command(i2c_lcd1602_info, COMMAND_SET_CGRAM_ADDR | (index << 3)); for (int i = 0; err == ESP_OK && i < 8; ++i) { @@ -589,7 +586,7 @@ esp_err_t i2c_lcd1602_define_char(const i2c_lcd1602_info_t * i2c_lcd1602_info, i return err; } -esp_err_t i2c_lcd1602_write_char(const i2c_lcd1602_info_t * i2c_lcd1602_info, uint8_t chr) +esp_err_t i2c_lcd1602_write_char(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t chr) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) @@ -599,12 +596,12 @@ esp_err_t i2c_lcd1602_write_char(const i2c_lcd1602_info_t * i2c_lcd1602_info, ui return err; } -esp_err_t i2c_lcd1602_write_string(const i2c_lcd1602_info_t * i2c_lcd1602_info, const char * string) +esp_err_t i2c_lcd1602_write_string(const i2c_lcd1602_info_t *i2c_lcd1602_info, const char *string) { esp_err_t err = ESP_FAIL; if (_is_init(i2c_lcd1602_info)) { - //ESP_LOGI(TAG, "i2c_lcd1602_write_string: %s", string); + // ESP_LOGI(TAG, "i2c_lcd1602_write_string: %s", string); err = ESP_OK; for (int i = 0; err == ESP_OK && string[i]; ++i) { @@ -614,7 +611,6 @@ esp_err_t i2c_lcd1602_write_string(const i2c_lcd1602_info_t * i2c_lcd1602_info, return err; } - // TEMPLATE #if 0 esp_err_t i2c_lcd1602_XXX(i2c_lcd1602_info_t * i2c_lcd1602_info) @@ -625,4 +621,4 @@ esp_err_t i2c_lcd1602_XXX(i2c_lcd1602_info_t * i2c_lcd1602_info) } return err; } -#endif \ No newline at end of file +#endif diff --git a/i2c-lcd1602.h b/i2c-lcd1602.h new file mode 100644 index 0000000..d13db2a --- /dev/null +++ b/i2c-lcd1602.h @@ -0,0 +1,336 @@ +/* + * MIT License + * + * Copyright (c) 2018 David Antliff + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file + * @brief Interface definitions for the ESP32-compatible I2C LCD1602 component. + * + * This component provides structures and functions that are useful for communicating with the device. + * + * Technically, the LCD1602 device is an I2C not SMBus device, however some SMBus protocols can be used + * to communicate with the device, so it makes sense to use an SMBus interface to manage communication. + */ + +#ifndef I2C_LCD1602_H +#define I2C_LCD1602_H + +#include "stdbool.h" +#include "../esp32-smbus/smbus.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Structure containing information related to the I2C-LCD1602 device. + */ + typedef struct + { + bool init; ///< True if struct has been initialised, otherwise false + smbus_info_t *smbus_info; ///< Pointer to associated SMBus info + uint8_t backlight_flag; ///< Non-zero if backlight is to be enabled, otherwise zero + uint8_t num_rows; ///< Number of configured columns + uint8_t num_columns; ///< Number of configured columns, including offscreen columns + uint8_t num_visible_columns; ///< Number of visible columns + uint8_t display_control_flags; ///< Currently active display control flags + uint8_t entry_mode_flags; ///< Currently active entry mode flags + } i2c_lcd1602_info_t; + +// Special characters for ROM Code A00 + +// Use the second set (0bxxxx1xxx) to avoid placing the null character within a string +#define I2C_LCD1602_CHARACTER_CUSTOM_0 0b00001000 ///< User-defined custom symbol in index 0 +#define I2C_LCD1602_CHARACTER_CUSTOM_1 0b00001001 ///< User-defined custom symbol in index 1 +#define I2C_LCD1602_CHARACTER_CUSTOM_2 0b00001010 ///< User-defined custom symbol in index 2 +#define I2C_LCD1602_CHARACTER_CUSTOM_3 0b00001011 ///< User-defined custom symbol in index 3 +#define I2C_LCD1602_CHARACTER_CUSTOM_4 0b00001100 ///< User-defined custom symbol in index 4 +#define I2C_LCD1602_CHARACTER_CUSTOM_5 0b00001101 ///< User-defined custom symbol in index 5 +#define I2C_LCD1602_CHARACTER_CUSTOM_6 0b00001110 ///< User-defined custom symbol in index 6 +#define I2C_LCD1602_CHARACTER_CUSTOM_7 0b00001111 ///< User-defined custom symbol in index 7 + +#define I2C_LCD1602_CHARACTER_ALPHA 0b11100000 ///< Lower-case alpha symbol +#define I2C_LCD1602_CHARACTER_BETA 0b11100010 ///< Lower-case beta symbol +#define I2C_LCD1602_CHARACTER_THETA 0b11110010 ///< Lower-case theta symbol +#define I2C_LCD1602_CHARACTER_PI 0b11110111 ///< Lower-case pi symbol +#define I2C_LCD1602_CHARACTER_OMEGA 0b11110100 ///< Upper-case omega symbol +#define I2C_LCD1602_CHARACTER_SIGMA 0b11110110 ///< Upper-case sigma symbol +#define I2C_LCD1602_CHARACTER_INFINITY 0b11110011 ///< Infinity symbol +#define I2C_LCD1602_CHARACTER_DEGREE 0b11011111 ///< Degree symbol +#define I2C_LCD1602_CHARACTER_ARROW_RIGHT 0b01111110 ///< Arrow pointing right symbol +#define I2C_LCD1602_CHARACTER_ARROW_LEFT 0b01111111 ///< Arrow pointing left symbol +#define I2C_LCD1602_CHARACTER_SQUARE 0b11011011 ///< Square outline symbol +#define I2C_LCD1602_CHARACTER_DOT 0b10100101 ///< Centred dot symbol +#define I2C_LCD1602_CHARACTER_DIVIDE 0b11111101 ///< Division sign symbol +#define I2C_LCD1602_CHARACTER_BLOCK 0b11111111 ///< 5x8 filled block + + /** + * @brief Enum of valid indexes for definitions of user-defined characters. + */ + typedef enum + { + I2C_LCD1602_INDEX_CUSTOM_0 = 0, ///< Index of first user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_1, ///< Index of second user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_2, ///< Index of third user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_3, ///< Index of fourth user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_4, ///< Index of fifth user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_5, ///< Index of sixth user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_6, ///< Index of seventh user-defined custom symbol + I2C_LCD1602_INDEX_CUSTOM_7, ///< Index of eighth user-defined custom symbol + } i2c_lcd1602_custom_index_t; + +#define I2C_LCD1602_ERROR_CHECK(x) \ + do \ + { \ + esp_err_t rc = (x); \ + if (rc != ESP_OK) \ + { \ + ESP_LOGW(TAG, "I2C error %d at %s:%d", rc, __FILE__, __LINE__); \ + } \ + } while (0); + + /** + * @brief Construct a new I2C-LCD1602 info instance. + * New instance should be initialised before calling other functions. + * + * @return Pointer to new device info instance, or NULL if it cannot be created. + */ + i2c_lcd1602_info_t *i2c_lcd1602_malloc(void); + + /** + * @brief Delete an existing I2C-LCD1602 info instance. + * + * @param[in,out] tsl2561_info Pointer to I2C-LCD1602 info instance that will be freed and set to NULL. + */ + void i2c_lcd1602_free(i2c_lcd1602_info_t **tsl2561_info); + + /** + * @brief Initialise a I2C-LCD1602 info instance with the specified SMBus information. + * + * @param[in] i2c_lcd1602_info Pointer to I2C-LCD1602 info instance. + * @param[in] smbus_info Pointer to SMBus info instance. + * @param[in] backlight Initial backlight state. + * @param[in] num_rows Maximum number of supported rows for this device. Typical values include 2 (1602) or 4 (2004). + * @param[in] num_columns Maximum number of supported columns for this device. Typical values include 40 (1602, 2004). + * @param[in] num_visible_columns Number of columns visible at any one time. Typical values include 16 (1602) or 20 (2004). + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_init(i2c_lcd1602_info_t *i2c_lcd1602_info, smbus_info_t *smbus_info, + bool backlight, uint8_t num_rows, uint8_t num_columns, uint8_t num_visible_columns); + + /** + * @brief Reset the display. Custom characters will be cleared. + * + * @param[in] i2c_lcd1602_info Pointer to I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_reset(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Clears entire display (clears DDRAM) and returns cursor to home position. + * DDRAM content is cleared, CGRAM content is not changed. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_clear(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Move cursor to home position. Also resets any display shift that may have occurred. + * DDRAM content is not changed. CGRAM content is not changed. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_home(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Move cursor to specified column and row position. This is where a new character will appear. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] col Zero-based horizontal index of intended cursor position. Column 0 is the left column. + * @param[in] row Zero-based vertical index of intended cursor position. Row 0 is the top row. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_move_cursor(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t col, uint8_t row); + + /** + * @brief Enable or disable the LED backlight. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] enable True to enable, false to disable. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_backlight(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable); + + /** + * @brief Enable or disable the display. When disabled, the backlight is not affected, but any + * contents of the DDRAM is not displayed, nor is the cursor. The display is "blank". + * Re-enabling the display does not affect the contents of DDRAM or the state or position of the cursor. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] enable True to enable, false to disable. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_display(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable); + + /** + * @brief Enable or disable display of the underline cursor. + * If enabled, this visually indicates where the next character written to the display will appear. + * It may be enabled alongside the blinking cursor, however the visual result is inelegant. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] enable True to enable, false to disable. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_cursor(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable); + + /** + * @brief Enable or disable display of the blinking block cursor. + * If enabled, this visually indicates where the next character written to the display will appear. + * It may be enabled alongside the underline cursor, however the visual result is inelegant. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] enable True to enable, false to disable. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_blink(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable); + + /** + * @brief Set cursor movement direction following each character write to produce left-to-right text. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_left_to_right(i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Set cursor movement direction following each character write to produce right-to-left text. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_right_to_left(i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Enable or disable auto-scroll of display. + * When enabled, the display will scroll as characters are written to maintain the cursor position on-screen. + * Left-to-right text will appear to be right-justified from the cursor position. + * When disabled, the display will not scroll and the cursor will move on-screen. + * Left-to-right text will appear to be left-justified from the cursor position. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] enable True to enable, false to disable. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_set_auto_scroll(i2c_lcd1602_info_t *i2c_lcd1602_info, bool enable); + + /** + * @brief Scroll the display one position to the left. On-screen text will appear to move to the right. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_scroll_display_left(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Scroll the display one position to the right. On-screen text will appear to move to the left. + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_scroll_display_right(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Move the cursor one position to the left, even if it is invisible. + * This affects where the next character written to the display will appear. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_move_cursor_left(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Move the cursor one position to the right, even if it is invisible. + * This affects where the next character written to the display will appear. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_move_cursor_right(const i2c_lcd1602_info_t *i2c_lcd1602_info); + + /** + * @brief Define a custom character from an array of pixel data. + * + * There are eight possible custom characters, and the zero-based index is used + * to select a character to define. Any existing character definition at that index will be lost. + * Characters are 5 pixels wide and 8 pixels tall. + * The pixelmap array consists of up to eight bytes, each byte representing the pixel states per row. + * The first byte represents the top row. The eighth byte is often left as zero (to leave space for the underline cursor). + * For each row, the lowest five bits represent pixels that are to be illuminated. The least significant bit represents + * the right-most pixel. Empty rows will be zero. + * + * NOTE: After calling this function, the DDRAM will not be selected and the cursor position will be undefined. Therefore it + * is important that the DDRAM address is set following this function, if text is to be written to the display. + * This can be performed with a call to i2c_lcd1602_home() or i2c_lcd1602_move_cursor(). + * + * Custom characters are written using the I2C_LCD1602_CHARACTER_CUSTOM_X definitions. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @param[in] index Zero-based index of the character to define. Only values 0-7 are valid. + * @param[in] pixelmap An 8-byte array defining the pixel map for the new character definition. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_define_char(const i2c_lcd1602_info_t *i2c_lcd1602_info, i2c_lcd1602_custom_index_t index, const uint8_t pixelmap[]); + + /** + * @brief Write a single character to the display at the current position of the cursor. + * Depending on the active mode, the cursor may move left or right, or the display may shift left or right. + * Custom characters can be written using the I2C_LCD1602_CHARACTER_CUSTOM_X definitions. + * + * The display is I2C_LCD1602_NUM_COLUMNS wide, and upon encountering the end of the first line, the cursor + * will automatically move to the beginning of the second line. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_write_char(const i2c_lcd1602_info_t *i2c_lcd1602_info, uint8_t chr); + + /** + * @brief Write a string of characters to the display, starting at the current position of the cursor. + * Depending on the active mode, the cursor may move left or right, or the display may shift left or right, + * after each character is written. + * + * The display is I2C_LCD1602_NUM_COLUMNS wide, and upon encountering the end of the first line, the cursor + * will automatically move to the beginning of the second line. + * + * @param[in] i2c_lcd1602_info Pointer to initialised I2C-LCD1602 info instance. + * @return ESP_OK if successful, otherwise an error constant. + */ + esp_err_t i2c_lcd1602_write_string(const i2c_lcd1602_info_t *i2c_lcd1602_info, const char *string); + +#ifdef __cplusplus +} +#endif + +#endif // I2C_LCD1602_H