diff --git a/CMakeLists.txt b/CMakeLists.txt index ed68433..73f2620 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1 +1 @@ -idf_component_register(SRCS "zh_1602a_i2c.c" INCLUDE_DIRS "include" REQUIRES zh_pcf8574) \ No newline at end of file +idf_component_register(SRCS "zh_160x_i2c.c" INCLUDE_DIRS "include" REQUIRES zh_pcf8574) \ No newline at end of file diff --git a/README.md b/README.md index 6fa0403..521826a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESP32 ESP-IDF and ESP8266 RTOS SDK component for liquid crystal display module 1602A via I2C connection (PCF8574) +# ESP32 ESP-IDF and ESP8266 RTOS SDK component for liquid crystal display module 1602(4)A via I2C connection (PCF8574) ## Tested on @@ -7,18 +7,18 @@ ## Features -1. Support of 16 LCD 1602A on one bus. +1. Support of 16 LCD 160X on one bus. ## Connection -| 1602A | PCF8574 | -| ----- | ------- | -| RS | P0 | -| E | P2 | -| D4 | P4 | -| D5 | P5 | -| D6 | P6 | -| D7 | P7 | +| 1602(4)A | PCF8574 | +| -------- | ------- | +| RS | P0 | +| E | P2 | +| D4 | P4 | +| D5 | P5 | +| D6 | P6 | +| D7 | P7 | ## Dependencies @@ -31,7 +31,7 @@ In an existing project, run the following command to install the components: ```text cd ../your_project/components -git clone http://git.zh.com.ru/alexey.zholtikov/zh_1602a_i2c +git clone http://git.zh.com.ru/alexey.zholtikov/zh_160x_i2c git clone http://git.zh.com.ru/alexey.zholtikov/zh_pcf8574 git clone http://git.zh.com.ru/alexey.zholtikov/zh_vector ``` @@ -39,7 +39,7 @@ git clone http://git.zh.com.ru/alexey.zholtikov/zh_vector In the application, add the component: ```c -#include "zh_1602a_i2c.h" +#include "zh_160x_i2c.h" ``` ## Examples @@ -47,7 +47,7 @@ In the application, add the component: One LCD on bus: ```c -#include "zh_1602a_i2c.h" +#include "zh_160x_i2c.h" #define I2C_PORT (I2C_NUM_MAX - 1) @@ -55,13 +55,13 @@ One LCD on bus: i2c_master_bus_handle_t i2c_bus_handle = NULL; #endif -zh_pcf8574_handle_t lcd_1602a_handle = {0}; +zh_pcf8574_handle_t lcd_160x_handle = {0}; void app_main(void) { - esp_log_level_set("zh_1602a_i2c", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. - esp_log_level_set("zh_pcf8574", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. - esp_log_level_set("zh_vector", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. + esp_log_level_set("zh_160x_i2c", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. + esp_log_level_set("zh_pcf8574", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. + esp_log_level_set("zh_vector", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. #ifdef CONFIG_IDF_TARGET_ESP8266 i2c_config_t i2c_config = { .mode = I2C_MODE_MASTER, @@ -90,27 +90,31 @@ void app_main(void) pcf8574_init_config.i2c_handle = i2c_bus_handle; #endif pcf8574_init_config.i2c_address = 0x27; - zh_pcf8574_init(&pcf8574_init_config, &lcd_1602a_handle); - zh_1602a_init(&lcd_1602a_handle); + zh_pcf8574_init(&pcf8574_init_config, &lcd_160x_handle); + zh_160x_init(&lcd_160x_handle, ZH_LCD_16X2); // For LCD 16X2. + // zh_160x_init(&lcd_160x_handle, ZH_LCD_16X4); // For LCD 16X4. for (;;) { - zh_1602a_set_cursor(&lcd_1602a_handle, 0, 0); - zh_1602a_print_char(&lcd_1602a_handle, "LCD 1602A"); - zh_1602a_set_cursor(&lcd_1602a_handle, 1, 0); - zh_1602a_print_char(&lcd_1602a_handle, "Hello World!"); + zh_160x_set_cursor(&lcd_160x_handle, 0, 0); + zh_160x_print_char(&lcd_160x_handle, "LCD 1602A"); + zh_160x_set_cursor(&lcd_160x_handle, 1, 0); + zh_160x_print_char(&lcd_160x_handle, "Hello World!"); vTaskDelay(5000 / portTICK_PERIOD_MS); - zh_1602a_clear_row(&lcd_1602a_handle, 0); - zh_1602a_print_char(&lcd_1602a_handle, "Progress: "); + zh_160x_set_cursor(&lcd_160x_handle, 0, 0); // For LCD 16X2. + // zh_160x_set_cursor(&lcd_160x_handle, 2, 0); // For LCD 16X4. + zh_160x_print_char(&lcd_160x_handle, "Progress: "); for (uint8_t i = 0; i <= 100; ++i) { - zh_1602a_set_cursor(&lcd_1602a_handle, 0, 10); - zh_1602a_print_int(&lcd_1602a_handle, i); - zh_1602a_print_char(&lcd_1602a_handle, "%"); - zh_1602a_print_progress_bar(&lcd_1602a_handle, 1, i); + zh_160x_set_cursor(&lcd_160x_handle, 0, 10); // For LCD 16X2. + // zh_160x_set_cursor(&lcd_160x_handle, 2, 10); // For LCD 16X4. + zh_160x_print_int(&lcd_160x_handle, i); + zh_160x_print_char(&lcd_160x_handle, "%"); + zh_160x_print_progress_bar(&lcd_160x_handle, 1, i); // For LCD 16X2. + // zh_160x_print_progress_bar(&lcd_160x_handle, 3, i); // For LCD 16X4. vTaskDelay(100 / portTICK_PERIOD_MS); } vTaskDelay(5000 / portTICK_PERIOD_MS); - zh_1602a_lcd_clear(&lcd_1602a_handle); + zh_160x_lcd_clear(&lcd_160x_handle); vTaskDelay(5000 / portTICK_PERIOD_MS); } } diff --git a/ds/Liquid crystal display module 1602A.pdf b/ds/Liquid crystal display module 1602A.pdf new file mode 100644 index 0000000..7ccb73f Binary files /dev/null and b/ds/Liquid crystal display module 1602A.pdf differ diff --git a/ds/Liquid crystal display module 1604A.pdf b/ds/Liquid crystal display module 1604A.pdf new file mode 100644 index 0000000..ed15bae Binary files /dev/null and b/ds/Liquid crystal display module 1604A.pdf differ diff --git a/include/zh_1602a_i2c.h b/include/zh_160x_i2c.h similarity index 73% rename from include/zh_1602a_i2c.h rename to include/zh_160x_i2c.h index 7fa266e..85f4165 100644 --- a/include/zh_1602a_i2c.h +++ b/include/zh_160x_i2c.h @@ -2,28 +2,32 @@ #include "zh_pcf8574.h" +#define ZH_LCD_16X2 1 +#define ZH_LCD_16X4 0 + #ifdef __cplusplus extern "C" { #endif /** - * @brief Initializes the LCD 1602A. + * @brief Initializes the LCD 160X. * * @param[in] handle Pointer to unique PCF8574 handle. + * @param[in] size LCD size (ZH_LCD_16X2 or ZH_LCD_16X4). * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_init(zh_pcf8574_handle_t *handle); + esp_err_t zh_160x_init(zh_pcf8574_handle_t *handle, bool size); /** * @brief Clears the LCD screen. * * @param[in] handle Pointer to unique PCF8574 handle. - * + * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_lcd_clear(zh_pcf8574_handle_t *handle); + esp_err_t zh_160x_lcd_clear(zh_pcf8574_handle_t *handle); /** * @brief Sets the cursor to a specific position on the LCD. @@ -34,7 +38,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_set_cursor(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t col); + esp_err_t zh_160x_set_cursor(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t col); /** * @brief Prints a string to the LCD. @@ -44,7 +48,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_print_char(zh_pcf8574_handle_t *handle, const char *str); + esp_err_t zh_160x_print_char(zh_pcf8574_handle_t *handle, const char *str); /** * @brief Prints an integer to the LCD. @@ -54,7 +58,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise.. */ - esp_err_t zh_1602a_print_int(zh_pcf8574_handle_t *handle, int num); + esp_err_t zh_160x_print_int(zh_pcf8574_handle_t *handle, int num); /** * @brief Prints a floating-point number to the LCD. @@ -65,7 +69,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision); + esp_err_t zh_160x_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision); /** * @brief Displays a progress bar on a specific row of the LCD. @@ -76,7 +80,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress); + esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress); /** * @brief Clears a specific row on the LCD. @@ -86,7 +90,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_1602a_clear_row(zh_pcf8574_handle_t *handle, uint8_t row); + esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row); #ifdef __cplusplus } diff --git a/version.txt b/version.txt index e6d5cb8..359a5b9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.2 \ No newline at end of file +2.0.0 \ No newline at end of file diff --git a/zh_1602a_i2c.c b/zh_1602a_i2c.c deleted file mode 100644 index e304070..0000000 --- a/zh_1602a_i2c.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "zh_1602a_i2c.h" - -static const char *TAG = "zh_1602a_i2c"; - -#define ZH_1602A_I2C_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) -#define ZH_1602A_I2C_LOGW(msg, ...) ESP_LOGW(TAG, msg, ##__VA_ARGS__) -#define ZH_1602A_I2C_LOGE(msg, ...) ESP_LOGE(TAG, msg, ##__VA_ARGS__) -#define ZH_1602A_I2C_LOGE_ERR(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__) - -#define ZH_1602A_I2C_CHECK(cond, err, msg, ...) \ - if (!(cond)) \ - { \ - ZH_1602A_I2C_LOGE_ERR(msg, err); \ - return err; \ - } - -#ifdef CONFIG_IDF_TARGET_ESP8266 -#define esp_delay_us(x) os_delay_us(x) -#else -#define esp_delay_us(x) esp_rom_delay_us(x) -#endif - -#define LCD_1602A_PULSE \ - zh_pcf8574_write_gpio(handle, 2, true); \ - esp_delay_us(300); \ - zh_pcf8574_write_gpio(handle, 2, false); \ - esp_delay_us(400); - -static void _zh_1602a_lcd_init(zh_pcf8574_handle_t *handle); -static void _zh_1602a_send_command(zh_pcf8574_handle_t *handle, uint8_t command); -static void _zh_1602a_send_data(zh_pcf8574_handle_t *handle, uint8_t data); - -esp_err_t zh_1602a_init(zh_pcf8574_handle_t *handle) -{ - ZH_1602A_I2C_LOGI("1602A initialization started."); - ZH_1602A_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "1602A initialization failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A initialization failed. PCF8574 not initialized."); - _zh_1602a_lcd_init(handle); - ZH_1602A_I2C_LOGI("1602A initialization completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_lcd_clear(zh_pcf8574_handle_t *handle) -{ - ZH_1602A_I2C_LOGI("1602A display cleaning started."); - ZH_1602A_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "1602A display cleaning failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A display cleaning failed. PCF8574 not initialized."); - _zh_1602a_send_command(handle, 0x01); - ZH_1602A_I2C_LOGI("1602A display cleaning completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_set_cursor(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t col) -{ - ZH_1602A_I2C_LOGI("1602A set cursor started."); - ZH_1602A_I2C_CHECK(row < 2 && col < 16 && handle != NULL, ESP_ERR_INVALID_ARG, "1602A set cursor failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A set cursor failed. PCF8574 not initialized."); - uint8_t address = (row == 0) ? col : (0x40 + col); - _zh_1602a_send_command(handle, 0x80 | address); - ZH_1602A_I2C_LOGI("1602A set cursor completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_print_char(zh_pcf8574_handle_t *handle, const char *str) -{ - ZH_1602A_I2C_LOGI("1602A print char started."); - ZH_1602A_I2C_CHECK(str != NULL && handle != NULL, ESP_ERR_INVALID_ARG, "1602A print char failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A print char failed. PCF8574 not initialized."); - while (*str != 0) - { - _zh_1602a_send_data(handle, (uint8_t)*str++); - } - ZH_1602A_I2C_LOGI("1602A print char completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_print_int(zh_pcf8574_handle_t *handle, int num) -{ - ZH_1602A_I2C_LOGI("1602A print int started."); - ZH_1602A_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "1602A print int failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A print int failed. PCF8574 not initialized."); - char buffer[12]; - sprintf(buffer, "%d", num); - zh_1602a_print_char(handle, buffer); - ZH_1602A_I2C_LOGI("1602A print int completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision) -{ - ZH_1602A_I2C_LOGI("1602A print float started."); - ZH_1602A_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "1602A print float failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A print float failed. PCF8574 not initialized."); - char buffer[16]; - sprintf(buffer, "%.*f", precision, num); - zh_1602a_print_char(handle, buffer); - ZH_1602A_I2C_LOGI("1602A print float completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress) -{ - ZH_1602A_I2C_LOGI("1602A print progress bar started."); - ZH_1602A_I2C_CHECK(row < 2 && progress <= 100 && handle != NULL, ESP_ERR_INVALID_ARG, "1602A print progress bar failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A print progress bar failed. PCF8574 not initialized."); - uint8_t blocks = (progress * 16) / 100; - zh_1602a_set_cursor(handle, row, 0); - for (uint8_t i = 0; i < 16; ++i) - { - if (i < blocks) - { - zh_1602a_print_char(handle, "\xFF"); - } - else - { - zh_1602a_print_char(handle, " "); - } - } - ZH_1602A_I2C_LOGI("1602A print progress bar completed successfully."); - return ESP_OK; -} - -esp_err_t zh_1602a_clear_row(zh_pcf8574_handle_t *handle, uint8_t row) -{ - ZH_1602A_I2C_LOGI("1602A clear row started."); - ZH_1602A_I2C_CHECK(row < 2 && handle != NULL, ESP_ERR_INVALID_ARG, "1602A clear row failed. Invalid argument."); - ZH_1602A_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "1602A clear row failed. PCF8574 not initialized."); - zh_1602a_set_cursor(handle, row, 0); - for (uint8_t i = 0; i < 16; ++i) - { - zh_1602a_print_char(handle, " "); - } - zh_1602a_set_cursor(handle, row, 0); - ZH_1602A_I2C_LOGI("1602A clear row completed successfully."); - return ESP_OK; -} - -static void _zh_1602a_lcd_init(zh_pcf8574_handle_t *handle) -{ - vTaskDelay(20 / portTICK_PERIOD_MS); - zh_pcf8574_write(handle, 0x30); - LCD_1602A_PULSE; - zh_pcf8574_write(handle, 0x30); - LCD_1602A_PULSE; - zh_pcf8574_write(handle, 0x30); - LCD_1602A_PULSE; - zh_pcf8574_write(handle, 0x20); - LCD_1602A_PULSE; - _zh_1602a_send_command(handle, 0x28); - _zh_1602a_send_command(handle, 0x28); - _zh_1602a_send_command(handle, 0x28); - _zh_1602a_send_command(handle, 0x0C); - _zh_1602a_send_command(handle, 0x01); - _zh_1602a_send_command(handle, 0x06); -} - -static void _zh_1602a_send_command(zh_pcf8574_handle_t *handle, uint8_t command) -{ - zh_pcf8574_write(handle, (command & 0xF0) | 0x08); - LCD_1602A_PULSE; - zh_pcf8574_write(handle, (command << 4) | 0x08); - LCD_1602A_PULSE; -} - -static void _zh_1602a_send_data(zh_pcf8574_handle_t *handle, uint8_t data) -{ - zh_pcf8574_write(handle, (data & 0xF0) | 0x09); - LCD_1602A_PULSE; - zh_pcf8574_write(handle, (data << 4) | 0x09); - LCD_1602A_PULSE; -} diff --git a/zh_160x_i2c.c b/zh_160x_i2c.c new file mode 100644 index 0000000..192d5a4 --- /dev/null +++ b/zh_160x_i2c.c @@ -0,0 +1,173 @@ +#include "zh_160x_i2c.h" + +static const char *TAG = "zh_160x_i2c"; + +#define ZH_160X_I2C_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) +#define ZH_160X_I2C_LOGW(msg, ...) ESP_LOGW(TAG, msg, ##__VA_ARGS__) +#define ZH_160X_I2C_LOGE(msg, ...) ESP_LOGE(TAG, msg, ##__VA_ARGS__) +#define ZH_160X_I2C_LOGE_ERR(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__) + +#define ZH_160X_I2C_CHECK(cond, err, msg, ...) \ + if (!(cond)) \ + { \ + ZH_160X_I2C_LOGE_ERR(msg, err); \ + return err; \ + } + +#ifdef CONFIG_IDF_TARGET_ESP8266 +#define esp_delay_us(x) os_delay_us(x) +#else +#define esp_delay_us(x) esp_rom_delay_us(x) +#endif + +#define LCD_160X_PULSE \ + zh_pcf8574_write_gpio(handle, 2, true); \ + esp_delay_us(300); \ + zh_pcf8574_write_gpio(handle, 2, false); \ + esp_delay_us(400); + +static void _zh_160x_lcd_init(zh_pcf8574_handle_t *handle); +static void _zh_160x_send_command(zh_pcf8574_handle_t *handle, uint8_t command); +static void _zh_160x_send_data(zh_pcf8574_handle_t *handle, uint8_t data); + +esp_err_t zh_160x_init(zh_pcf8574_handle_t *handle, bool size) +{ + ZH_160X_I2C_LOGI("160X initialization started."); + ZH_160X_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "160X initialization failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X initialization failed. PCF8574 not initialized."); + handle->system = size; + _zh_160x_lcd_init(handle); + ZH_160X_I2C_LOGI("160X initialization completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_lcd_clear(zh_pcf8574_handle_t *handle) +{ + ZH_160X_I2C_LOGI("160X display cleaning started."); + ZH_160X_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "160X display cleaning failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X display cleaning failed. PCF8574 not initialized."); + _zh_160x_send_command(handle, 0x01); + ZH_160X_I2C_LOGI("160X display cleaning completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_set_cursor(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t col) +{ + ZH_160X_I2C_LOGI("160X set cursor started."); + ZH_160X_I2C_CHECK(row < ((handle->system == ZH_LCD_16X2) ? 2 : 4) && col < 16 && handle != NULL, ESP_ERR_INVALID_ARG, "160X set cursor failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X set cursor failed. PCF8574 not initialized."); + _zh_160x_send_command(handle, 0x80 | ((row == 0) ? col : (row == 1) ? (0x40 + col) + : (row == 2) ? (0x10 + col) + : (0x50 + col))); + ZH_160X_I2C_LOGI("160X set cursor completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_print_char(zh_pcf8574_handle_t *handle, const char *str) +{ + ZH_160X_I2C_LOGI("160X print char started."); + ZH_160X_I2C_CHECK(str != NULL && handle != NULL, ESP_ERR_INVALID_ARG, "160X print char failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X print char failed. PCF8574 not initialized."); + while (*str != 0) + { + _zh_160x_send_data(handle, (uint8_t)*str++); + } + ZH_160X_I2C_LOGI("160X print char completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_print_int(zh_pcf8574_handle_t *handle, int num) +{ + ZH_160X_I2C_LOGI("160X print int started."); + ZH_160X_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "160X print int failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X print int failed. PCF8574 not initialized."); + char buffer[12]; + sprintf(buffer, "%d", num); + zh_160x_print_char(handle, buffer); + ZH_160X_I2C_LOGI("160X print int completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision) +{ + ZH_160X_I2C_LOGI("160X print float started."); + ZH_160X_I2C_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, "160X print float failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X print float failed. PCF8574 not initialized."); + char buffer[16]; + sprintf(buffer, "%.*f", precision, num); + zh_160x_print_char(handle, buffer); + ZH_160X_I2C_LOGI("160X print float completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress) +{ + ZH_160X_I2C_LOGI("160X print progress bar started."); + ZH_160X_I2C_CHECK(row < ((handle->system == ZH_LCD_16X2) ? 2 : 4) && progress <= 100 && handle != NULL, ESP_ERR_INVALID_ARG, "160X print progress bar failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X print progress bar failed. PCF8574 not initialized."); + uint8_t blocks = (progress * 16) / 100; + zh_160x_set_cursor(handle, row, 0); + for (uint8_t i = 0; i < 16; ++i) + { + if (i < blocks) + { + zh_160x_print_char(handle, "\xFF"); + } + else + { + zh_160x_print_char(handle, " "); + } + } + ZH_160X_I2C_LOGI("160X print progress bar completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row) +{ + ZH_160X_I2C_LOGI("160X clear row started."); + ZH_160X_I2C_CHECK(row < ((handle->system == ZH_LCD_16X2) ? 2 : 4) && handle != NULL, ESP_ERR_INVALID_ARG, "160X clear row failed. Invalid argument."); + ZH_160X_I2C_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, "160X clear row failed. PCF8574 not initialized."); + zh_160x_set_cursor(handle, row, 0); + for (uint8_t i = 0; i < 16; ++i) + { + zh_160x_print_char(handle, " "); + } + zh_160x_set_cursor(handle, row, 0); + ZH_160X_I2C_LOGI("160X clear row completed successfully."); + return ESP_OK; +} + +static void _zh_160x_lcd_init(zh_pcf8574_handle_t *handle) +{ + vTaskDelay(20 / portTICK_PERIOD_MS); + zh_pcf8574_write(handle, 0x30); + LCD_160X_PULSE; + zh_pcf8574_write(handle, 0x30); + LCD_160X_PULSE; + zh_pcf8574_write(handle, 0x30); + LCD_160X_PULSE; + zh_pcf8574_write(handle, 0x20); + LCD_160X_PULSE; + _zh_160x_send_command(handle, 0x28); + _zh_160x_send_command(handle, 0x28); + _zh_160x_send_command(handle, 0x28); + _zh_160x_send_command(handle, 0x0C); + _zh_160x_send_command(handle, 0x01); + _zh_160x_send_command(handle, 0x06); +} + +static void _zh_160x_send_command(zh_pcf8574_handle_t *handle, uint8_t command) +{ + zh_pcf8574_write(handle, (command & 0xF0) | 0x08); + LCD_160X_PULSE; + zh_pcf8574_write(handle, (command << 4) | 0x08); + LCD_160X_PULSE; +} + +static void _zh_160x_send_data(zh_pcf8574_handle_t *handle, uint8_t data) +{ + zh_pcf8574_write(handle, (data & 0xF0) | 0x09); + LCD_160X_PULSE; + zh_pcf8574_write(handle, (data << 4) | 0x09); + LCD_160X_PULSE; +}