add error checking and initial backlight state to char lcd

This commit is contained in:
2026-09-29 23:54:27 -05:00
parent 8d635d41c6
commit ef79b2a835
6 changed files with 135 additions and 98 deletions
+83 -59
View File
@@ -49,7 +49,8 @@ LCD2004I2C::LCD2004I2C() {
entry_mode = 0;
}
void LCD2004I2C::init(uint8_t addr) {
esp_err_t LCD2004I2C::init(uint8_t addr, bool initial_backlight) {
if (initialized) return ESP_ERR_INVALID_STATE;
i2c_device_config_t dev_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = addr,
@@ -60,42 +61,52 @@ void LCD2004I2C::init(uint8_t addr) {
}
};
// TODO: replace all these ESP_ERROR_CHECK with proper error handling that doesn't just crash the program
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &lcd_device_handle));
esp_err_t err = i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &lcd_device_handle);
if (err != ESP_OK) return err;
ESP_LOGD(TAG, "LCD2004 device added to bus");
backlight = initial_backlight ? BACKLIGHT_MASK : 0;
// Initialize the LCD
// set to 8-bit mode to bring it to a known state
for (int i = 0; i < 3; i++) {
send4_cmd(CMD_FUNCTION_SET | FUNCTION_SET_DL);
err = send4_cmd(CMD_FUNCTION_SET | FUNCTION_SET_DL);
if (err != ESP_OK) return err;
vTaskDelay(pdMS_TO_TICKS(5));
}
// set to 4-bit mode, while in 8 bit mode
send4_cmd(CMD_FUNCTION_SET);
err = send4_cmd(CMD_FUNCTION_SET);
if (err != ESP_OK) return err;
// use all 4 rows
send_cmd(CMD_FUNCTION_SET | FUNCTION_SET_N);
err = send_cmd(CMD_FUNCTION_SET | FUNCTION_SET_N);
if (err != ESP_OK) return err;
// display, set cursor visible
send_cmd(CMD_DISPLAY_CONTROL | DISPLAY_CONTROL_SHOW);
err = send_cmd(CMD_DISPLAY_CONTROL | DISPLAY_CONTROL_SHOW);
if (err != ESP_OK) return err;
// clear display
send_cmd(CMD_CLEAR_DISPLAY);
err = send_cmd(CMD_CLEAR_DISPLAY);
if (err != ESP_OK) return err;
esp_rom_delay_us(1600);
// set cursor to increment to the right
send_cmd(CMD_ENTRY_MODE_SET | ENTRY_MODE_INC_DEC);
err = send_cmd(CMD_ENTRY_MODE_SET | ENTRY_MODE_INC_DEC);
if (err != ESP_OK) return err;
display_control = DISPLAY_CONTROL_SHOW;
entry_mode = ENTRY_MODE_INC_DEC;
initialized = true;
ESP_LOGI(TAG, "LCD2004 initialized!");
return ESP_OK;
}
// i2c wrappers
/// Sends `cmd` to the device as a command.
void LCD2004I2C::send_cmd(uint8_t cmd) {
esp_err_t LCD2004I2C::send_cmd(uint8_t cmd) {
uint8_t control_bits = backlight;
uint8_t high = control_bits | (cmd & 0xF0);
@@ -110,10 +121,10 @@ void LCD2004I2C::send_cmd(uint8_t cmd) {
low
};
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS);
}
void LCD2004I2C::send_data(uint8_t data) {
esp_err_t LCD2004I2C::send_data(uint8_t data) {
uint8_t control_bits = DATA_MASK | backlight;
uint8_t high = control_bits | (data & 0xF0);
@@ -129,11 +140,11 @@ void LCD2004I2C::send_data(uint8_t data) {
low
};
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS);
}
/// Sends the ***HIGH*** 4 bits of `cmd` as a command.
void LCD2004I2C::send4_cmd(uint8_t cmd) {
esp_err_t LCD2004I2C::send4_cmd(uint8_t cmd) {
uint8_t control_bits = backlight;
uint8_t high = control_bits | (cmd & 0xF0);
@@ -145,7 +156,7 @@ void LCD2004I2C::send4_cmd(uint8_t cmd) {
high
};
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
return i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS);
}
// LCD2004 class implementations
@@ -154,35 +165,41 @@ bool LCD2004I2C::get_backlight() {
return backlight != 0;
}
void LCD2004I2C::set_backlight(bool backlight) {
esp_err_t LCD2004I2C::set_backlight(bool backlight) {
if (get_backlight() == backlight) {
return;
return ESP_OK;
}
this->backlight = backlight ? BACKLIGHT_MASK : 0;
uint8_t data = this->backlight;
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, &data, 1, I2C_MASTER_TIMEOUT_MS));
uint8_t data = backlight ? BACKLIGHT_MASK : 0;
esp_err_t err = i2c_master_transmit(lcd_device_handle, &data, 1, I2C_MASTER_TIMEOUT_MS);
if (err == ESP_OK) {
this->backlight = data;
}
return err;
}
void LCD2004I2C::clear() {
send_cmd(CMD_CLEAR_DISPLAY);
esp_err_t LCD2004I2C::clear() {
esp_err_t err = send_cmd(CMD_CLEAR_DISPLAY);
if (err != ESP_OK) return err;
esp_rom_delay_us(1600);
entry_mode |= ENTRY_MODE_INC_DEC;
return ESP_OK;
}
void LCD2004I2C::move_cursor(uint8_t row, uint8_t col) {
esp_err_t LCD2004I2C::move_cursor(uint8_t row, uint8_t col) {
const uint8_t ROW_OFFSETS[4] = {0x00, 0x40, 0x14, 0x54};
if (row >= LCD2004_ROWS || col >= LCD2004_COLS) return ESP_ERR_INVALID_ARG;
uint8_t addr = ROW_OFFSETS[row] + col;
send_cmd(CMD_SET_DDRAM_ADDRESS | addr);
return send_cmd(CMD_SET_DDRAM_ADDRESS | addr);
}
bool LCD2004I2C::get_show_hide() {
return (display_control & DISPLAY_CONTROL_SHOW) != 0;
}
void LCD2004I2C::show_hide(bool show) {
esp_err_t LCD2004I2C::show_hide(bool show) {
if (get_show_hide() == show) {
return;
return ESP_OK;
}
if (show) {
@@ -190,16 +207,16 @@ void LCD2004I2C::show_hide(bool show) {
} else {
display_control &= ~DISPLAY_CONTROL_SHOW;
}
send_cmd(CMD_DISPLAY_CONTROL | display_control);
return send_cmd(CMD_DISPLAY_CONTROL | display_control);
}
bool LCD2004I2C::get_show_cursor() {
return (display_control & DISPLAY_CONTROL_CURSOR) != 0;
}
void LCD2004I2C::show_cursor(bool show_cursor) {
esp_err_t LCD2004I2C::show_cursor(bool show_cursor) {
if (get_show_cursor() == show_cursor) {
return;
return ESP_OK;
}
if (show_cursor) {
@@ -207,16 +224,16 @@ void LCD2004I2C::show_cursor(bool show_cursor) {
} else {
display_control &= ~DISPLAY_CONTROL_CURSOR;
}
send_cmd(CMD_DISPLAY_CONTROL | display_control);
return send_cmd(CMD_DISPLAY_CONTROL | display_control);
}
bool LCD2004I2C::get_blink_cursor() {
return (display_control & DISPLAY_CONTROL_BLINK) != 0;
}
void LCD2004I2C::blink_cursor(bool blink_cursor) {
esp_err_t LCD2004I2C::blink_cursor(bool blink_cursor) {
if (get_blink_cursor() == blink_cursor) {
return;
return ESP_OK;
}
if (blink_cursor) {
@@ -224,12 +241,12 @@ void LCD2004I2C::blink_cursor(bool blink_cursor) {
} else {
display_control &= ~DISPLAY_CONTROL_BLINK;
}
send_cmd(CMD_DISPLAY_CONTROL | display_control);
return send_cmd(CMD_DISPLAY_CONTROL | display_control);
}
void LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) {
esp_err_t LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) {
if (get_show_cursor() == show_cursor && get_blink_cursor() == blink_cursor) {
return;
return ESP_OK;
}
if (blink_cursor) {
@@ -242,16 +259,16 @@ void LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) {
} else {
display_control &= ~DISPLAY_CONTROL_CURSOR;
}
send_cmd(CMD_DISPLAY_CONTROL | display_control);
return send_cmd(CMD_DISPLAY_CONTROL | display_control);
}
bool LCD2004I2C::get_scroll_direction() {
return (entry_mode & ENTRY_MODE_INC_DEC) != 0;
}
void LCD2004I2C::scroll_direction(bool left_to_right) {
esp_err_t LCD2004I2C::scroll_direction(bool left_to_right) {
if (get_scroll_direction() == left_to_right) {
return;
return ESP_OK;
}
if (left_to_right) {
@@ -259,16 +276,16 @@ void LCD2004I2C::scroll_direction(bool left_to_right) {
} else {
entry_mode &= ~ENTRY_MODE_INC_DEC;
}
send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
return send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
}
bool LCD2004I2C::get_display_shift() {
return (entry_mode & ENTRY_MODE_SHIFT) != 0;
}
void LCD2004I2C::shift_display(bool shift_display) {
esp_err_t LCD2004I2C::shift_display(bool shift_display) {
if (get_display_shift() == shift_display) {
return;
return ESP_OK;
}
if (shift_display) {
@@ -276,53 +293,60 @@ void LCD2004I2C::shift_display(bool shift_display) {
} else {
entry_mode &= ~ENTRY_MODE_SHIFT;
}
send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
return send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
}
void LCD2004I2C::shift_cursor_left() {
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT);
esp_err_t LCD2004I2C::shift_cursor_left() {
return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT);
}
void LCD2004I2C::shift_cursor_right() {
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_RL);
esp_err_t LCD2004I2C::shift_cursor_right() {
return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_RL);
}
void LCD2004I2C::shift_display_left() {
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC);
esp_err_t LCD2004I2C::shift_display_left() {
return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC);
}
void LCD2004I2C::shift_display_right() {
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC | CURSOR_OR_DISPLAY_SHIFT_RL);
esp_err_t LCD2004I2C::shift_display_right() {
return send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC | CURSOR_OR_DISPLAY_SHIFT_RL);
}
void LCD2004I2C::create_custom_char(uint8_t location, const uint8_t char_map[8]) {
esp_err_t LCD2004I2C::create_custom_char(uint8_t location, const uint8_t char_map[8]) {
if (char_map == nullptr) return ESP_ERR_INVALID_ARG;
uint8_t loc = location % 8;
send_cmd(CMD_SET_CGRAM_ADDRESS | (loc << 3));
esp_err_t err = send_cmd(CMD_SET_CGRAM_ADDRESS | (loc << 3));
if (err != ESP_OK) return err;
for (int i = 0; i < 8; i++) {
send_data(char_map[i]);
err = send_data(char_map[i]);
if (err != ESP_OK) return err;
}
return ESP_OK;
}
void LCD2004I2C::print_char(char c) {
esp_err_t LCD2004I2C::print_char(char c) {
if (c == '\x08') {
// custom char 0 is represented by \x08 since \x00 is a null terminator
c = 0;
}
send_data(static_cast<uint8_t>(c));
return send_data(static_cast<uint8_t>(c));
}
void LCD2004I2C::print_u8(uint8_t b) {
esp_err_t LCD2004I2C::print_u8(uint8_t b) {
if (b == 0x08) {
// custom char 0 is represented by \x08 since \x00 is a null terminator
b = 0;
}
send_data(b);
return send_data(b);
}
void LCD2004I2C::write_str(const char* s) {
esp_err_t LCD2004I2C::write_str(const char* s) {
// TODO: replace this with a single I2C transmission.
if (s == nullptr) return ESP_ERR_INVALID_ARG;
while (*s) {
print_char(*s);
esp_err_t err = print_char(*s);
if (err != ESP_OK) return err;
s++;
}
return ESP_OK;
}