diff --git a/README.md b/README.md index a04d362..48e7e75 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESP32 ESP-IDF component for liquid crystal display module 1602(4)A via I2C connection (PCF8574(A)) +# ESP32 ESP-IDF component for LCD 1602A/1604A via I2C (PCF8574) ## Wiki @@ -10,41 +10,20 @@ ## SAST Tools -[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. +[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) — static analyzer for C, C++, C#, and Java code. ## Features -1. Support of 16 LCD 160X on one bus. - -## Attention - -For correct operation, please enable the following settings in the menuconfig: - -```text -CONFIG_FREERTOS_HZ=1000 -CONFIG_GPIO_CTRL_FUNC_IN_IRAM -CONFIG_I2C_ISR_IRAM_SAFE -CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM -``` - -## Connection - -| 1602(4)A | PCF8574 | -| -------- | ------- | -| RS | P0 | -| E | P2 | -| D4 | P4 | -| D5 | P5 | -| D6 | P6 | -| D7 | P7 | - -## Dependencies - -1. [zh_pcf8574](https://github.com/aZholtikov/zh_pcf8574) +1. Support for up to 16 LCD 160X displays on one I2C bus. +2. 4-bit interface mode for efficient data transfer via PCF8574 expander. +3. Print strings, integers, and floating-point numbers. +4. Progress bar display with custom characters. +5. Cursor visibility and blink control. +6. Row-specific clear operations. ## Using -In an existing project, run the following command to install the components: +In an existing project, run the following command to install the component: ```text cd ../your_project/components @@ -59,57 +38,4 @@ In the application, add the component: ## Examples -One LCD on bus: - -```c -#include "zh_160x_i2c.h" - -#define I2C_PORT (I2C_NUM_MAX - 1) - -zh_pcf8574_handle_t lcd_160x_handle = {0}; - -void app_main(void) -{ - esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR); - esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR); - esp_log_level_set("zh_vector", ESP_LOG_ERROR); - i2c_master_bus_config_t i2c_bus_config = { - .clk_source = I2C_CLK_SRC_DEFAULT, - .i2c_port = I2C_PORT, - .scl_io_num = GPIO_NUM_22, - .sda_io_num = GPIO_NUM_21, - .glitch_ignore_cnt = 7, - .flags.enable_internal_pullup = true, - }; - i2c_master_bus_handle_t i2c_bus_handle = NULL; - i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); - zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); - config.i2c_handle = i2c_bus_handle; - config.i2c_address = 0x27; - zh_pcf8574_init(&config, &lcd_160x_handle); - zh_160x_init(&lcd_160x_handle, ZH_LCD_16X2); - for (;;) - { - zh_160x_on_cursor(&lcd_160x_handle, true); - zh_160x_set_cursor(&lcd_160x_handle, 0, 0); - zh_160x_print_char(&lcd_160x_handle, "LCD 160X"); - zh_160x_set_cursor(&lcd_160x_handle, 1, 0); - zh_160x_print_char(&lcd_160x_handle, "Hello World!"); - vTaskDelay(5000 / portTICK_PERIOD_MS); - zh_160x_off_cursor(&lcd_160x_handle); - zh_160x_set_cursor(&lcd_160x_handle, 0, 0); - zh_160x_print_char(&lcd_160x_handle, "Progress: "); - for (uint8_t i = 0; i <= 100; ++i) - { - zh_160x_set_cursor(&lcd_160x_handle, 0, 10); - 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); - vTaskDelay(100 / portTICK_PERIOD_MS); - } - vTaskDelay(5000 / portTICK_PERIOD_MS); - zh_160x_lcd_clear(&lcd_160x_handle); - vTaskDelay(5000 / portTICK_PERIOD_MS); - } -} -``` +See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования) diff --git a/WIKI_EN.md b/WIKI_EN.md index 0560f0a..813453b 100644 --- a/WIKI_EN.md +++ b/WIKI_EN.md @@ -1,4 +1,4 @@ -# zh_160x_i2c - ESP32 ESP-IDF Component for LCD 1602A/1604A via I2C (PCF8574) +# zh_160x_i2c - LCD 1602A/1604A via I2C (PCF8574) Component for ESP-IDF ## Table of Contents @@ -25,39 +25,39 @@ The component is designed specifically for ESP32 microcontrollers and uses ESP-I ## Features -1. **Simple API**: Easy-to-use functions for common LCD operations -2. **I2C Interface**: Uses only 2 GPIO pins (SCL and SDA) via PCF8574 expander -3. **Support for 1602A and 1604A**: Supports both 16x2 and 16x4 LCD modules -4. **Multiple LCD Support**: Up to 8 LCD modules on one I2C bus (PCF8574) or 4 (PCF8574A) -5. **Text Display**: Print strings, integers, and floating-point numbers -6. **Cursor Control**: Enable/disable cursor with optional blink -7. **Progress Bar**: Display progress bars on LCD -8. **Row Operations**: Clear specific rows -9. **ESP-IDF Optimized**: Uses ESP-IDF's I2C master driver and FreeRTOS +1. **[Simple API]**: Easy-to-use functions for common LCD operations +2. **[I2C Interface]**: Uses only 2 GPIO pins (SCL and SDA) via PCF8574 expander +3. **[Support for 1602A and 1604A]**: Supports both 16x2 and 16x4 LCD modules +4. **[Multiple LCD Support]**: Up to 16 LCD modules on one I2C bus +5. **[Text Display]**: Print strings, integers, and floating-point numbers +6. **[Cursor Control]**: Enable/disable cursor with optional blink +7. **[Progress Bar]**: Display progress bars on LCD +8. **[Row Operations]**: Clear specific rows +9. **[ESP-IDF Optimized]**: Uses ESP-IDF's I2C master driver and FreeRTOS --- ## Installation -1. Navigate to your project's components directory: +Navigate to your project's components directory: ```bash cd ../your_project/components ``` -2. Clone the repository: +Clone the repository: ```bash git clone https://github.com/aZholtikov/zh_160x_i2c ``` -3. In your application, include the header: +In your application, include the header: ```c #include "zh_160x_i2c.h" ``` -4. The component will be automatically built with your project. +The component will be automatically built with your project. ### Required menuconfig Settings @@ -73,55 +73,97 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM ## API Reference -### Configuration Constants +### Data Types + +#### zh_160x_i2c_handle_t ```c -#define ZH_LCD_16X2 1 // LCD size 16x2 -#define ZH_LCD_16X4 0 // LCD size 16x4 +typedef struct _zh_160x_i2c_handle_t zh_160x_i2c_handle_t; ``` +Opaque handle for LCD 160x I2C display. Internal structure containing the PCF8574 expander handle and the current LCD size configuration. + +--- + +#### zh_160x_i2c_lcd_size_t + +```c +typedef enum +{ + ZH_LCD_16X2 = 0, /*!< LCD size 16x2 */ + ZH_LCD_16X4, /*!< LCD size 16x4 */ + ZH_LCD_NUM_MAX +} zh_160x_i2c_lcd_size_t; +``` + +Enumeration of supported LCD display sizes. + +| Value | Description | +|-------|-------------| +| `ZH_LCD_16X2` | LCD size 16x2 (2 rows of 16 characters) | +| `ZH_LCD_16X4` | LCD size 16x4 (4 rows of 16 characters) | + --- ### zh_160x_init() -Initializes the LCD 160X module. +Initializes the LCD 160x display. + +Allocates and configures the LCD handle, then sends the initialization sequence to the LCD controller via the PCF8574 I2C expander. Configures 4-bit mode and sets default display parameters. **Parameters:** -- `handle` - Pointer to unique PCF8574 handle (must be already initialized) +- `expander` - Pointer to the PCF8574 expander handle. Must not be NULL +- `handle` - Output pointer for the newly allocated LCD handle. Must be NULL - `size` - LCD size (ZH_LCD_16X2 or ZH_LCD_16X4) **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or handle not initialized) -- `ESP_FAIL` - Initialization failed +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL pointer or invalid size) +- `ESP_ERR_INVALID_STATE` - Handle already initialized +- `ESP_ERR_NO_MEM` - Memory allocation failure +- `ESP_FAIL` - LCD initialization sequence failure **Example:** ```c -zh_pcf8574_handle_t lcd_handle = {0}; -// Initialize PCF8574 first -zh_pcf8574_init(&config, &lcd_handle); -// Then initialize LCD -zh_160x_init(&lcd_handle, ZH_LCD_16X2); +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; +zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); +config.i2c_handle = i2c_bus_handle; +config.i2c_address = 0x27; +esp_err_t err = zh_pcf8574_init(&config, &lcd_expander); +if (err != ESP_OK) { + // Error handling +} +err = zh_160x_init(&lcd_expander, &lcd_handle, ZH_LCD_16X2); +if (err != ESP_OK) { + // Error handling +} ``` +**Note:** + +The PCF8574 expander must be initialized before initializing the LCD. + --- ### zh_160x_lcd_clear() -Clears the entire LCD screen. +Clears the LCD screen and moves the cursor to the home position. + +Sends the LCD clear command (0x01) to erase all displayed text and reset the cursor to the upper-left corner (row 0, column 0). **Parameters:** -- `handle` - Pointer to unique PCF8574 handle +- `handle` - Pointer to the LCD handle. Must not be NULL **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - Command send failure --- @@ -129,69 +171,84 @@ Clears the entire LCD screen. Sets the cursor to a specific position on the LCD. +Moves the cursor to the specified row and column. +For 16x2 displays: rows 0–1, columns 0–15. +For 16x4 displays: rows 0–3, columns 0–15. + **Parameters:** -- `handle` - Pointer to unique PCF8574 handle -- `row` - Row number (0-1 for 16x2, 0-3 for 16x4) -- `col` - Column number (0-15) +- `handle` - Pointer to the LCD handle. Must not be NULL +- `row` - Row number (0–1 for 16x2, 0–3 for 16x4) +- `col` - Column number (0–15) **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, invalid row/col) -- `ESP_FAIL` - Operation failed +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or coordinates out of range) +- `ESP_FAIL` - Command send failure + +**Note:** + +Row address mapping: row 0 = 0x00, row 1 = 0x40, row 2 = 0x10, row 3 = 0x50. --- ### zh_160x_print_char() -Prints a string to the LCD. +Prints a string to the LCD at the current cursor position. + +Transmits each character of the null-terminated string to the LCD in 4-bit mode. The cursor advances automatically after each character. **Parameters:** -- `handle` - Pointer to unique PCF8574 handle -- `str` - Pointer to the null-terminated string to display +- `handle` - Pointer to the LCD handle. Must not be NULL +- `str` - Pointer to the null-terminated string to display. Must not be NULL **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or str) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - Character transmission failure --- ### zh_160x_print_int() -Prints an integer to the LCD. +Prints an integer to the LCD at the current cursor position. + +Converts the integer to a decimal string and displays it. +Supports negative numbers (maximum 11 digits plus sign). **Parameters:** -- `handle` - Pointer to unique PCF8574 handle +- `handle` - Pointer to the LCD handle. Must not be NULL - `num` - Integer value to display **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - String conversion or print failure --- ### zh_160x_print_float() -Prints a floating-point number to the LCD. +Prints a floating-point number to the LCD at the current cursor position. + +Converts the floating-point value to a string with the specified decimal precision and displays it. **Parameters:** -- `handle` - Pointer to unique PCF8574 handle +- `handle` - Pointer to the LCD handle. Must not be NULL - `num` - Floating-point value to display -- `precision` - Number of decimal places (0-9) +- `precision` - Number of decimal places (0–6 recommended) **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - String conversion or print failure --- @@ -199,17 +256,20 @@ Prints a floating-point number to the LCD. Displays a progress bar on a specific row of the LCD. +Renders a 16-block progress bar using a custom character (0xFF). +Filled blocks represent the progress percentage, empty blocks show the remainder. + **Parameters:** -- `handle` - Pointer to unique PCF8574 handle -- `row` - Row number (0-1 for 16x2, 0-3 for 16x4) -- `progress` - Progress percentage (0-100) +- `handle` - Pointer to the LCD handle. Must not be NULL +- `row` - Row number (0–1 for 16x2, 0–3 for 16x4) +- `progress` - Progress percentage (0–100) **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, invalid row or progress) -- `ESP_FAIL` - Operation failed +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, row out of range, or progress > 100) +- `ESP_FAIL` - Cursor positioning or character print failure --- @@ -217,16 +277,19 @@ Displays a progress bar on a specific row of the LCD. Clears a specific row on the LCD. +Moves the cursor to the beginning of the specified row and fills all 16 positions with spaces to erase existing content. +The cursor remains at the start of the cleared row. + **Parameters:** -- `handle` - Pointer to unique PCF8574 handle -- `row` - Row number (0-1 for 16x2, 0-3 for 16x4) +- `handle` - Pointer to the LCD handle. Must not be NULL +- `row` - Row number (0–1 for 16x2, 0–3 for 16x4) **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, invalid row) -- `ESP_FAIL` - Operation failed +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or row out of range) +- `ESP_FAIL` - Cursor positioning or character print failure --- @@ -234,32 +297,37 @@ Clears a specific row on the LCD. Enables the cursor with optional blink mode. +Turns on the cursor and optionally enables blinking. +Command 0x0F enables cursor with blink, 0x0E enables cursor only. + **Parameters:** -- `handle` - Pointer to unique PCF8574 handle -- `blink` - If true, cursor blinks; if false, cursor is steady +- `handle` - Pointer to the LCD handle. Must not be NULL +- `blink` - If true, enables blinking cursor; if false, solid cursor **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - Command send failure --- ### zh_160x_off_cursor() -Disables the cursor. +Hides the cursor and disables blinking. + +Sends command 0x0C to turn off both cursor and cursor blink. **Parameters:** -- `handle` - Pointer to unique PCF8574 handle +- `handle` - Pointer to the LCD handle (not NULL) **Returns:** - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle) -- `ESP_FAIL` - Operation failed +- `ESP_FAIL` - Command send failure --- @@ -272,7 +340,8 @@ Disables the cursor. #define I2C_PORT (I2C_NUM_MAX - 1) -zh_pcf8574_handle_t lcd_160x_handle = {0}; +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; void app_main(void) { @@ -287,19 +356,26 @@ void app_main(void) .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; - i2c_master_bus_handle_t i2c_bus_handle = NULL; i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); config.i2c_handle = i2c_bus_handle; config.i2c_address = 0x27; - zh_pcf8574_init(&config, &lcd_160x_handle); - zh_160x_init(&lcd_160x_handle, ZH_LCD_16X2); + esp_err_t err = zh_pcf8574_init(&config, &lcd_expander); + if (err != ESP_OK) { + return; + } + err = zh_160x_init(&lcd_expander, &lcd_handle, ZH_LCD_16X2); + if (err != ESP_OK) { + return; + } // Display text - zh_160x_set_cursor(&lcd_160x_handle, 0, 0); - zh_160x_print_char(&lcd_160x_handle, "LCD 160X"); - zh_160x_set_cursor(&lcd_160x_handle, 1, 0); - zh_160x_print_char(&lcd_160x_handle, "Hello World!"); + zh_160x_set_cursor(&lcd_handle, 0, 0); + zh_160x_print_char(&lcd_handle, "LCD 160X"); + zh_160x_set_cursor(&lcd_handle, 1, 0); + zh_160x_print_char(&lcd_handle, "Hello World!"); + // Cleanup + zh_160x_deinit(&lcd_handle); } ``` @@ -308,9 +384,13 @@ void app_main(void) ```c #include "zh_160x_i2c.h" +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; + void app_main(void) { - zh_pcf8574_handle_t lcd_handle = {0}; // ... initialization code ... for (uint8_t i = 0; i <= 100; ++i) { @@ -320,6 +400,8 @@ void app_main(void) zh_160x_print_progress_bar(&lcd_handle, 1, i); vTaskDelay(100 / portTICK_PERIOD_MS); } + // Cleanup + zh_160x_deinit(&lcd_handle); } ``` @@ -328,9 +410,13 @@ void app_main(void) ```c #include "zh_160x_i2c.h" +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; + void app_main(void) { - zh_pcf8574_handle_t lcd_handle = {0}; // ... initialization code ... // Display string zh_160x_set_cursor(&lcd_handle, 0, 0); @@ -347,6 +433,8 @@ void app_main(void) vTaskDelay(2000 / portTICK_PERIOD_MS); // Disable cursor zh_160x_off_cursor(&lcd_handle); + // Cleanup + zh_160x_deinit(&lcd_handle); } ``` @@ -357,8 +445,10 @@ void app_main(void) #define I2C_PORT (I2C_NUM_MAX - 1) -zh_pcf8574_handle_t lcd1_handle = {0}; -zh_pcf8574_handle_t lcd2_handle = {0}; +zh_pcf8574_handle_t *lcd1_expander = NULL; +zh_160x_i2c_handle_t *lcd1_handle = NULL; +zh_pcf8574_handle_t *lcd2_expander = NULL; +zh_160x_i2c_handle_t *lcd2_handle = NULL; void app_main(void) { @@ -379,18 +469,21 @@ void app_main(void) zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); config.i2c_handle = i2c_bus_handle; config.i2c_address = 0x27; - zh_pcf8574_init(&config, &lcd1_handle); - zh_160x_init(&lcd1_handle, ZH_LCD_16X2); + zh_pcf8574_init(&config, &lcd1_expander); + zh_160x_init(&lcd1_expander, &lcd1_handle, ZH_LCD_16X2); // Initialize second LCD at address 0x26 config.i2c_address = 0x26; - zh_pcf8574_init(&config, &lcd2_handle); - zh_160x_init(&lcd2_handle, ZH_LCD_16X2); + zh_pcf8574_init(&config, &lcd2_expander); + zh_160x_init(&lcd2_expander, &lcd2_handle, ZH_LCD_16X2); // Display on first LCD zh_160x_set_cursor(&lcd1_handle, 0, 0); zh_160x_print_char(&lcd1_handle, "LCD 1"); // Display on second LCD zh_160x_set_cursor(&lcd2_handle, 0, 0); zh_160x_print_char(&lcd2_handle, "LCD 2"); + // Cleanup + zh_160x_deinit(&lcd1_handle); + zh_160x_deinit(&lcd2_handle); } ``` @@ -399,14 +492,14 @@ void app_main(void) ## Technical Specifications | Parameter | Value | -|-----------|-------| +| ----------- | ------- | | **LCD Types** | 1602A (16x2), 1604A (16x4) | | **Interface** | I2C via PCF8574(A) | | **I2C Address Range** | 0x20-0x27 (PCF8574), 0x38-0x3F (PCF8574A) | | **Max LCDs per Bus** | 16 | | **GPIO Usage** | 2 (SCL, SDA) | -| **ESP-IDF Version** | >= 5.0| -| **Platform** | ESP32 family | +| **ESP-IDF Version** | >= 5.0 | +| **Platform** | ESP32 series | | **Language** | C (C99) | --- @@ -414,7 +507,7 @@ void app_main(void) ## Error Codes | Error Code | Description | -|------------|-------------| +| ------------ | ------------- | | `ESP_OK` | Operation completed successfully | | `ESP_ERR_INVALID_ARG` | Invalid argument (NULL pointer or invalid value) | | `ESP_ERR_INVALID_STATE` | Handle not initialized | @@ -498,3 +591,7 @@ limitations under the License. - Always initialize PCF8574 before initializing LCD - Check I2C address using a scanner if LCD does not respond - Consider I2C bus speed limitations when using multiple devices + +--- + +*Generated for zh_160x_i2c v5.0.0* diff --git a/WIKI_RU.md b/WIKI_RU.md index b323a11..bb8729f 100644 --- a/WIKI_RU.md +++ b/WIKI_RU.md @@ -1,4 +1,4 @@ -# zh_160x_i2c - Компонент ESP32 ESP-IDF для LCD 1602A/1604A через I2C (PCF8574) +# zh_160x_i2c - Компонент LCD 1602A/1604A через I2C (PCF8574) для ESP-IDF ## Содержание @@ -25,39 +25,39 @@ ## Возможности -1. **Простой API**: Удобные функции для распространенных операций с LCD -2. **Интерфейс I2C**: Использует только 2 GPIO-пина (SCL и SDA) через расширитель PCF8574 -3. **Поддержка 1602A и 1604A**: Поддерживает LCD-модули 16x2 и 16x4 -4. **Множественные LCD**: До 16 LCD-модулей на одной шине I2C -5. **Отображение текста**: Вывод строк, целых чисел и чисел с плавающей точкой -6. **Управление курсором**: Включение/выключение курсора с опциональным миганием -7. **Индикатор прогресса**: Отображение индикаторов прогресса на LCD -8. **Операции со строками**: Очистка конкретных строк -9. **Оптимизировано под ESP-IDF**: Использует драйвер I2C-мастера ESP-IDF и FreeRTOS +1. **[Простой API]**: Удобные функции для распространенных операций с LCD +2. **[Интерфейс I2C]**: Использует только 2 GPIO-пина (SCL и SDA) через расширитель PCF8574 +3. **[Поддержка 1602A и 1604A]**: Поддерживает LCD-модули 16x2 и 16x4 +4. **[Множественные LCD]**: До 16 LCD-модулей на одной шине I2C +5. **[Отображение текста]**: Вывод строк, целых чисел и чисел с плавающей точкой +6. **[Управление курсором]**: Включение/выключение курсора с опциональным миганием +7. **[Индикатор прогресса]**: Отображение индикаторов прогресса на LCD +8. **[Операции со строками]**: Очистка конкретных строк +9. **[Оптимизировано под ESP-IDF]**: Использует драйвер I2C-мастера ESP-IDF и FreeRTOS --- ## Установка -1. Перейдите в каталог компонентов вашего проекта: +Перейдите в каталог компонентов вашего проекта: ```bash cd ../ваш_проект/components ``` -2. Клонируйте репозиторий: +Клонируйте репозиторий: ```bash git clone https://github.com/aZholtikov/zh_160x_i2c ``` -3. В вашем приложении подключите заголовочный файл: +В вашем приложении подключите заголовочный файл: ```c #include "zh_160x_i2c.h" ``` -4. Компонент будет автоматически собран вместе с вашим проектом. +Компонент будет автоматически собран вместе с вашим проектом. ### Обязательные настройки в menuconfig @@ -73,160 +73,223 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM ## Справочник API -### Константы конфигурации +### Типы данных + +#### zh_160x_i2c_handle_t ```c -#define ZH_LCD_16X2 1 // Размер LCD 16x2 -#define ZH_LCD_16X4 0 // Размер LCD 16x4 +typedef struct _zh_160x_i2c_handle_t zh_160x_i2c_handle_t; ``` +Непрозрачный дескриптор LCD-дисплея 160x I2C. Внутренняя структура, содержащая дескриптор расширителя PCF8574 и текущую конфигурацию размера LCD. + +--- + +#### zh_160x_i2c_lcd_size_t + +```c +typedef enum +{ + ZH_LCD_16X2 = 0, /*!< Размер LCD 16x2 */ + ZH_LCD_16X4, /*!< Размер LCD 16x4 */ + ZH_LCD_NUM_MAX +} zh_160x_i2c_lcd_size_t; +``` + +Перечисление поддерживаемых размеров LCD-дисплеев. + +| Значение | Описание | +| ---------- | ---------- | +| `ZH_LCD_16X2` | LCD размером 16x2 (2 строки по 16 символов) | +| `ZH_LCD_16X4` | LCD размером 16x4 (4 строки по 16 символов) | + --- ### zh_160x_init() -Инициализирует модуль LCD 160X. +Инициализирует LCD-дисплей 160x. + +Выделяет и настраивает дескриптор LCD, затем отправляет последовательность инициализации на контроллер LCD через расширитель I2C PCF8574. Настраивает 4-битный режим и устанавливает параметры дисплея по умолчанию. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 (должен быть уже инициализирован) +- `expander` - Указатель на дескриптор расширителя PCF8574. Не должен быть NULL +- `handle` - Выходной указатель для нового выделенного дескриптора LCD. Должен быть NULL - `size` - Размер LCD (ZH_LCD_16X2 или ZH_LCD_16X4) **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или handle не инициализирован) -- `ESP_FAIL` - Ошибка инициализации +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель или неверный размер) +- `ESP_ERR_INVALID_STATE` - Дескриптор уже инициализирован +- `ESP_ERR_NO_MEM` - Ошибка выделения памяти +- `ESP_FAIL` - Сбой последовательности инициализации LCD **Пример:** ```c -zh_pcf8574_handle_t lcd_handle = {0}; -// Сначала инициализируйте PCF8574 -zh_pcf8574_init(&config, &lcd_handle); -// Затем инициализируйте LCD -zh_160x_init(&lcd_handle, ZH_LCD_16X2); +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; +zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); +config.i2c_handle = i2c_bus_handle; +config.i2c_address = 0x27; +esp_err_t err = zh_pcf8574_init(&config, &lcd_expander); +if (err != ESP_OK) { + // Обработка ошибки +} +err = zh_160x_init(&lcd_expander, &lcd_handle, ZH_LCD_16X2); +if (err != ESP_OK) { + // Обработка ошибки +} ``` +**Примечание:** + +Перед инициализацией LCD необходимо инициализировать расширитель PCF8574. + --- ### zh_160x_lcd_clear() -Очищает весь экран LCD. +Очищает экран LCD и перемещает курсор в домашнюю позицию. + +Отправляет команду очистки LCD (0x01) для стирания всего отображаемого текста и сброса курсора в верхний левый угол (строка 0, столбец 0). **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка отправки команды --- ### zh_160x_set_cursor() -Устанавливает курсор в определенную позицию на LCD. +Устанавливает курсор в указанную позицию на LCD. + +Перемещает курсор в указанную строку и столбец. +Для дисплеев 16x2: строки 0–1, столбцы 0–15. +Для дисплеев 16x4: строки 0–3, столбцы 0–15. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 -- `row` - Номер строки (0-1 для 16x2, 0-3 для 16x4) -- `col` - Номер столбца (0-15) +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL +- `row` - Номер строки (0–1 для 16x2, 0–3 для 16x4) +- `col` - Номер столбца (0–15) **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, неверный row/col) -- `ESP_FAIL` - Операция не удалась +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или координаты вне диапазона) +- `ESP_FAIL` - Ошибка отправки команды + +**Примечание:** + +Адресация строк: строка 0 = 0x00, строка 1 = 0x40, строка 2 = 0x10, строка 3 = 0x50. --- ### zh_160x_print_char() -Выводит строку на LCD. +Выводит строку на LCD в текущую позицию курсора. + +Передаёт каждый символ строки, завершающейся нулём, на LCD в 4-битном режиме. Курсор автоматически перемещается после каждого символа. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 -- `str` - Указатель на null-terminated строку для отображения +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL +- `str` - Указатель на null-terminated строку для отображения. Не должен быть NULL **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или str) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка передачи символа --- ### zh_160x_print_int() -Выводит целое число на LCD. +Выводит целое число на LCD в текущую позицию курсора. + +Преобразует целое число в десятичную строку и отображает её. +Поддерживает отрицательные числа (максимум 11 цифр плюс знак). **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL - `num` - Целочисленное значение для отображения **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка преобразования строки или печати --- ### zh_160x_print_float() -Выводит число с плавающей точкой на LCD. +Выводит число с плавающей точкой на LCD в текущую позицию курсора. + +Преобразует значение с плавающей точкой в строку с указанной точностью десятичных знаков и отображает её. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL - `num` - Значение с плавающей точкой для отображения -- `precision` - Количество знаков после запятой (0-9) +- `precision` - Количество знаков после запятой (0–6 рекомендуется) **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка преобразования строки или печати --- ### zh_160x_print_progress_bar() -Отображает индикатор прогресса в определенной строке LCD. +Отображает индикатор прогресса в указанной строке LCD. + +Отрисовывает 16-блочный индикатор прогресса с использованием пользовательского символа (0xFF). +Заполненные блоки представляют процент прогресса, пустые блоки показывают остаток. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 -- `row` - Номер строки (0-1 для 16x2, 0-3 для 16x4) -- `progress` - Процент прогресса (0-100) +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL +- `row` - Номер строки (0–1 для 16x2, 0–3 для 16x4) +- `progress` - Процент прогресса (0–100) **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, неверный row или progress) -- `ESP_FAIL` - Операция не удалась +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, row вне диапазона или progress > 100) +- `ESP_FAIL` - Ошибка установки курсора или печати символов --- ### zh_160x_clear_row() -Очищает определенную строку на LCD. +Очищает указанную строку на LCD. + +Перемещает курсор в начало указанной строки и заполняет все 16 позиций пробелами для стирания содержимого. +Курсор остаётся в начале очищенной строки. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 -- `row` - Номер строки (0-1 для 16x2, 0-3 для 16x4) +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL +- `row` - Номер строки (0–1 для 16x2, 0–3 для 16x4) **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, неверный row) -- `ESP_FAIL` - Операция не удалась +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или row вне диапазона) +- `ESP_FAIL` - Ошибка установки курсора или печати символов --- @@ -234,32 +297,37 @@ zh_160x_init(&lcd_handle, ZH_LCD_16X2); Включает курсор с опциональным режимом мигания. +Включает курсор и при необходимости активирует мигание. +Команда 0x0F включает курсор с миганием, 0x0E включает курсор без мигания. + **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 -- `blink` - Если true, курсор мигает; если false, курсор статичный +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL +- `blink` - Если true, включается мигающий курсор; если false — статичный курсор **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка отправки команды --- ### zh_160x_off_cursor() -Отключает курсор. +Отключает курсор и отключает мигание. + +Отправляет команду 0x0C для отключения курсора и мигания курсора. **Параметры:** -- `handle` - Указатель на уникальный дескриптор PCF8574 +- `handle` - Указатель на дескриптор LCD. Не должен быть NULL **Возвращает:** - `ESP_OK` - Успех - `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle) -- `ESP_FAIL` - Операция не удалась +- `ESP_FAIL` - Ошибка отправки команды --- @@ -272,7 +340,8 @@ zh_160x_init(&lcd_handle, ZH_LCD_16X2); #define I2C_PORT (I2C_NUM_MAX - 1) -zh_pcf8574_handle_t lcd_160x_handle = {0}; +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; void app_main(void) { @@ -287,19 +356,26 @@ void app_main(void) .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; - i2c_master_bus_handle_t i2c_bus_handle = NULL; i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); config.i2c_handle = i2c_bus_handle; config.i2c_address = 0x27; - zh_pcf8574_init(&config, &lcd_160x_handle); - zh_160x_init(&lcd_160x_handle, ZH_LCD_16X2); + esp_err_t err = zh_pcf8574_init(&config, &lcd_expander); + if (err != ESP_OK) { + return; + } + err = zh_160x_init(&lcd_expander, &lcd_handle, ZH_LCD_16X2); + if (err != ESP_OK) { + return; + } // Отображение текста - zh_160x_set_cursor(&lcd_160x_handle, 0, 0); - zh_160x_print_char(&lcd_160x_handle, "LCD 160X"); - zh_160x_set_cursor(&lcd_160x_handle, 1, 0); - zh_160x_print_char(&lcd_160x_handle, "Привет Мир!"); + zh_160x_set_cursor(&lcd_handle, 0, 0); + zh_160x_print_char(&lcd_handle, "LCD 160X"); + zh_160x_set_cursor(&lcd_handle, 1, 0); + zh_160x_print_char(&lcd_handle, "Привет Мир!"); + // Очистка ресурсов + zh_160x_deinit(&lcd_handle); } ``` @@ -308,9 +384,13 @@ void app_main(void) ```c #include "zh_160x_i2c.h" +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; + void app_main(void) { - zh_pcf8574_handle_t lcd_handle = {0}; // ... код инициализации ... for (uint8_t i = 0; i <= 100; ++i) { @@ -320,6 +400,8 @@ void app_main(void) zh_160x_print_progress_bar(&lcd_handle, 1, i); vTaskDelay(100 / portTICK_PERIOD_MS); } + // Очистка ресурсов + zh_160x_deinit(&lcd_handle); } ``` @@ -328,9 +410,13 @@ void app_main(void) ```c #include "zh_160x_i2c.h" +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *lcd_expander = NULL; +zh_160x_i2c_handle_t *lcd_handle = NULL; + void app_main(void) { - zh_pcf8574_handle_t lcd_handle = {0}; // ... код инициализации ... // Отображение строки zh_160x_set_cursor(&lcd_handle, 0, 0); @@ -347,6 +433,8 @@ void app_main(void) vTaskDelay(2000 / portTICK_PERIOD_MS); // Отключение курсора zh_160x_off_cursor(&lcd_handle); + // Очистка ресурсов + zh_160x_deinit(&lcd_handle); } ``` @@ -357,8 +445,10 @@ void app_main(void) #define I2C_PORT (I2C_NUM_MAX - 1) -zh_pcf8574_handle_t lcd1_handle = {0}; -zh_pcf8574_handle_t lcd2_handle = {0}; +zh_pcf8574_handle_t *lcd1_expander = NULL; +zh_160x_i2c_handle_t *lcd1_handle = NULL; +zh_pcf8574_handle_t *lcd2_expander = NULL; +zh_160x_i2c_handle_t *lcd2_handle = NULL; void app_main(void) { @@ -379,18 +469,21 @@ void app_main(void) zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); config.i2c_handle = i2c_bus_handle; config.i2c_address = 0x27; - zh_pcf8574_init(&config, &lcd1_handle); - zh_160x_init(&lcd1_handle, ZH_LCD_16X2); + zh_pcf8574_init(&config, &lcd1_expander); + zh_160x_init(&lcd1_expander, &lcd1_handle, ZH_LCD_16X2); // Инициализация второго LCD по адресу 0x26 config.i2c_address = 0x26; - zh_pcf8574_init(&config, &lcd2_handle); - zh_160x_init(&lcd2_handle, ZH_LCD_16X2); + zh_pcf8574_init(&config, &lcd2_expander); + zh_160x_init(&lcd2_expander, &lcd2_handle, ZH_LCD_16X2); // Отображение на первом LCD zh_160x_set_cursor(&lcd1_handle, 0, 0); zh_160x_print_char(&lcd1_handle, "LCD 1"); // Отображение на втором LCD zh_160x_set_cursor(&lcd2_handle, 0, 0); zh_160x_print_char(&lcd2_handle, "LCD 2"); + // Очистка ресурсов + zh_160x_deinit(&lcd1_handle); + zh_160x_deinit(&lcd2_handle); } ``` @@ -399,14 +492,14 @@ void app_main(void) ## Технические характеристики | Параметр | Значение | -|----------|----------| +| ---------- | ---------- | | **Типы LCD** | 1602A (16x2), 1604A (16x4) | | **Интерфейс** | I2C через PCF8574(A) | | **Диапазон адресов I2C** | 0x20-0x27 (PCF8574), 0x38-0x3F (PCF8574A) | | **Макс. LCD на шине** | 16 | | **Использование GPIO** | 2 (SCL, SDA) | | **Версия ESP-IDF** | >= 5.0 | -| **Платформа** | Семейство ESP32 | +| **Платформа** | Серия ESP32 | | **Язык** | C (C99) | --- @@ -414,7 +507,7 @@ void app_main(void) ## Коды ошибок | Код ошибки | Описание | -|------------|----------| +| ------------ | ---------- | | `ESP_OK` | Операция выполнена успешно | | `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или неверное значение) | | `ESP_ERR_INVALID_STATE` | Дескриптор не инициализирован | @@ -498,3 +591,7 @@ void app_main(void) - Всегда инициализируйте PCF8574 перед инициализацией LCD - Проверьте адрес I2C с помощью сканера, если LCD не отвечает - Учитывайте ограничения скорости шины I2C при использовании нескольких устройств + +--- + +*Generated for zh_160x_i2c v5.0.0* diff --git a/idf_component.yml b/idf_component.yml index c535f9c..4cbb15d 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,6 +1,6 @@ description: ESP32 ESP-IDF component for LCD 1602A/1604A displays via I2C using PCF8574(A) expanders url: https://github.com/aZholtikov/zh_160x_i2c -version: "4.1.0" +version: "5.0.0" license: "Apache-2.0" maintainers: - "Alexey Zholtikov " diff --git a/include/zh_160x_i2c.h b/include/zh_160x_i2c.h index a44acb2..7ff9e5b 100644 --- a/include/zh_160x_i2c.h +++ b/include/zh_160x_i2c.h @@ -1,119 +1,219 @@ /** * @file zh_160x_i2c.h + * + * @brief Driver for 16x2 and 16x4 LCD displays via I2C PCF8574 expander. + * + * Provides a high-level API for controlling HD44780-compatible LCD modules + * connected through a PCF8574 I/O expander. Supports text output, cursor + * control, and progress bar display. + * + * Key features: + * - 4-bit interface mode for efficient data transfer + * - Support for 16x2 and 16x4 LCD configurations + * - Text, integer, and floating-point number printing + * - Custom progress bar rendering + * - Cursor visibility and blink control */ #pragma once #include "zh_pcf8574.h" -#define ZH_LCD_16X2 1 /*!< LCD size 16x2. */ -#define ZH_LCD_16X4 0 /*!< LCD size 16x4. */ - #ifdef __cplusplus extern "C" { #endif /** - * @brief Initializes the LCD 160X. + * @brief Opaque handle for LCD 160x I2C display. * - * @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. + * Internal structure containing the PCF8574 expander handle and + * current LCD size configuration. */ - esp_err_t zh_160x_init(zh_pcf8574_handle_t *handle, bool size); + typedef struct _zh_160x_i2c_handle_t zh_160x_i2c_handle_t; /** - * @brief Clears the LCD screen. - * - * @param[in] handle Pointer to unique PCF8574 handle. - * - * @return ESP_OK if success or an error code otherwise. + * @brief Supported LCD display sizes. */ - esp_err_t zh_160x_lcd_clear(zh_pcf8574_handle_t *handle); + typedef enum + { + ZH_LCD_16X2 = 0, /*!< LCD size 16x2 */ + ZH_LCD_16X4, /*!< LCD size 16x4 */ + ZH_LCD_NUM_MAX + } zh_160x_i2c_lcd_size_t; + + /** + * @brief Initializes the LCD 160x display. + * + * Allocates and configures the LCD handle, then sends the initialization + * sequence to the LCD controller via the PCF8574 I2C expander. + * Configures 4-bit mode and sets default display parameters. + * + * @param[in] expander Pointer to the PCF8574 expander handle (must not be NULL) + * @param[out] handle Output pointer for the newly allocated LCD handle (must be NULL) + * @param[in] size LCD size (ZH_LCD_16X2 or ZH_LCD_16X4) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if any parameter is NULL or size is invalid + * @return ESP_ERR_INVALID_STATE if the handle is already initialized + * @return ESP_ERR_NO_MEM if memory allocation fails + * @return ESP_FAIL if LCD initialization sequence fails + */ + esp_err_t zh_160x_init(zh_pcf8574_handle_t **expander, zh_160x_i2c_handle_t **handle, zh_160x_i2c_lcd_size_t size); + + /** + * @brief Deinitializes the LCD 160x display and frees the handle. + * + * Releases the allocated memory for the LCD handle and sets the output + * pointer to NULL to prevent use-after-free. + * + * @param[in,out] handle Pointer to the LCD handle (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + */ + esp_err_t zh_160x_deinit(zh_160x_i2c_handle_t **handle); + + /** + * @brief Clears the LCD screen and moves cursor to home position. + * + * Sends the LCD clear command (0x01) to erase all displayed text + * and reset the cursor to the upper-left corner (row 0, column 0). + * + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + * @return ESP_FAIL if sending the command fails + */ + esp_err_t zh_160x_lcd_clear(zh_160x_i2c_handle_t **handle); /** * @brief Sets the cursor to a specific position on the LCD. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] row The row number. - * @param[in] col The column number (0–15). + * Moves the cursor to the specified row and column. + * For 16x2 displays: rows 0–1, columns 0–15. + * For 16x4 displays: rows 0–3, columns 0–15. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] row Row number (0 to 1 for 16x2, 0 to 3 for 16x4) + * @param[in] col Column number (0–15) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL or coordinates are out of range + * @return ESP_FAIL if sending the command fails + * + * @note Row address mapping: row 0 = 0x00, row 1 = 0x40, + * row 2 = 0x10, row 3 = 0x50. */ - esp_err_t zh_160x_set_cursor(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t col); + esp_err_t zh_160x_set_cursor(zh_160x_i2c_handle_t **handle, uint8_t row, uint8_t col); /** - * @brief Prints a string to the LCD. + * @brief Prints a string to the LCD at the current cursor position. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] str Pointer to the string to be displayed. + * Transmits each character of the null-terminated string to the LCD + * in 4-bit mode. The cursor advances automatically after each character. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] str Null-terminated string to display (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle or str is NULL + * @return ESP_FAIL if any character transmission fails */ - esp_err_t zh_160x_print_char(zh_pcf8574_handle_t *handle, const char *str); + esp_err_t zh_160x_print_char(zh_160x_i2c_handle_t **handle, const char *str); /** - * @brief Prints an integer to the LCD. + * @brief Prints an integer to the LCD at the current cursor position. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] num The integer to be displayed. + * Converts the integer to a decimal string and displays it. + * Supports negative numbers (maximum 11 digits plus sign). * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] num Integer value to display + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + * @return ESP_FAIL if string conversion or printing fails */ - esp_err_t zh_160x_print_int(zh_pcf8574_handle_t *handle, int num); + esp_err_t zh_160x_print_int(zh_160x_i2c_handle_t **handle, int num); /** - * @brief Prints a floating-point number to the LCD. + * @brief Prints a floating-point number to the LCD at the current cursor position. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] num The floating-point number to be displayed. - * @param[in] precision The number of decimal places to display. + * Converts the floating-point value to a string with the specified + * decimal precision and displays it. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] num Floating-point value to display + * @param[in] precision Number of decimal places (0–6 recommended) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + * @return ESP_FAIL if string conversion or printing fails */ - esp_err_t zh_160x_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision); + esp_err_t zh_160x_print_float(zh_160x_i2c_handle_t **handle, float num, uint8_t precision); /** * @brief Displays a progress bar on a specific row of the LCD. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] row The row number. - * @param[in] progress The progress percentage (0–100). + * Renders a 16-block progress bar using custom character (0xFF). + * Filled blocks represent the progress percentage, empty blocks show remaining. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] row Row number (0 to 1 for 16x2, 0 to 3 for 16x4) + * @param[in] progress Progress percentage (0–100) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL, row is out of range, + * or progress exceeds 100 + * @return ESP_FAIL if cursor positioning or character printing fails */ - esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress); + esp_err_t zh_160x_print_progress_bar(zh_160x_i2c_handle_t **handle, uint8_t row, uint8_t progress); /** * @brief Clears a specific row on the LCD. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] row The row number. + * Moves the cursor to the beginning of the specified row and fills + * all 16 positions with spaces to erase existing content. + * Leaves the cursor at the start of the cleared row. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] row Row number (0 to 1 for 16x2, 0 to 3 for 16x4) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL or row is out of range + * @return ESP_FAIL if cursor positioning or character printing fails */ - esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row); + esp_err_t zh_160x_clear_row(zh_160x_i2c_handle_t **handle, uint8_t row); /** - * @brief Enable the cursor. + * @brief Enables the cursor with optional blink mode. * - * @param[in] handle Pointer to unique PCF8574 handle. - * @param[in] blink Blink mode. + * Turns on the cursor and optionally enables blinking. + * Command 0x0F enables cursor with blink, 0x0E enables cursor only. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * @param[in] blink If true, enables blinking cursor; if false, solid cursor + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + * @return ESP_FAIL if sending the command fails */ - esp_err_t zh_160x_on_cursor(zh_pcf8574_handle_t *handle, bool blink); + esp_err_t zh_160x_on_cursor(zh_160x_i2c_handle_t **handle, bool blink); /** - * @brief Disable the cursor. + * @brief Hides the cursor and disables blinking. * - * @param[in] handle Pointer to unique PCF8574 handle. + * Sends command 0x0C to turn off both cursor and cursor blink. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the LCD handle (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle is NULL + * @return ESP_FAIL if sending the command fails */ - esp_err_t zh_160x_off_cursor(zh_pcf8574_handle_t *handle); + esp_err_t zh_160x_off_cursor(zh_160x_i2c_handle_t **handle); #ifdef __cplusplus } diff --git a/version.txt b/version.txt index 99eba4d..28cbf7c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -4.1.0 \ No newline at end of file +5.0.0 \ No newline at end of file diff --git a/zh_160x_i2c.c b/zh_160x_i2c.c index 4164322..2a3f763 100644 --- a/zh_160x_i2c.c +++ b/zh_160x_i2c.c @@ -13,65 +13,135 @@ static const char *TAG = "zh_160x_i2c"; return err; \ } -static esp_err_t _zh_160x_lcd_init(zh_pcf8574_handle_t *handle); -static esp_err_t _zh_160x_send_command(zh_pcf8574_handle_t *handle, uint8_t command); -static esp_err_t _zh_160x_send_data(zh_pcf8574_handle_t *handle, uint8_t data); -static esp_err_t _zh_160x_pulse(zh_pcf8574_handle_t *handle); +/** + * @brief LCD 160x I2C handle structure. + * + * Internal handle containing the PCF8574 expander pointer and + * the configured LCD size for row address calculations. + */ +struct _zh_160x_i2c_handle_t +{ + zh_pcf8574_handle_t *zh_pcf8574_handle; /*!< PCF8574 expander handle */ + zh_160x_i2c_lcd_size_t zh_160x_i2c_lcd_size; /*!< Configured LCD size (16x2 or 16x4) */ +}; -esp_err_t zh_160x_init(zh_pcf8574_handle_t *handle, bool size) // -V2008 +/** + * @brief Initialize the LCD 160x display. + * + * Sends initialization sequence to the LCD controller via PCF8574 expander. + * Configures 4-bit mode and sets default display parameters. + * Follows HD44780 initialization protocol with function set commands. + * + * @param handle Pointer to the 160x I2C handle + * + * @return ESP_OK on success + * @return ESP_FAIL if error + */ +static esp_err_t _zh_160x_lcd_init(zh_160x_i2c_handle_t *handle); + +/** + * @brief Send a command to the LCD. + * + * Transmits a command byte to the LCD in 4-bit mode. + * RS pin is set to low (0x08) to indicate command mode. + * + * @param handle Pointer to the 160x I2C handle + * @param command Command byte to send + * + * @return ESP_OK on success + * @return ESP_FAIL if error + */ +static esp_err_t _zh_160x_send_command(zh_160x_i2c_handle_t *handle, uint8_t command); + +/** + * @brief Send data to the LCD. + * + * Transmits a data byte to the LCD in 4-bit mode. + * RS pin is set to high (0x09) to indicate data mode. + * + * @param handle Pointer to the 160x I2C handle + * @param data Data byte to send + * + * @return ESP_OK on success + * @return ESP_FAIL if error + */ +static esp_err_t _zh_160x_send_data(zh_160x_i2c_handle_t *handle, uint8_t data); + +/** + * @brief Generate an Enable pulse for the LCD. + * + * Toggles the E pin (GPIO2 on PCF8574) to latch data into the LCD. + * Sets E high, delays 1ms, then sets E low with another 1ms delay. + * This pulse signals the LCD to process the data on the D4-D7 pins. + * + * @param handle Pointer to the 160x I2C handle + * + * @return ESP_OK on success + * @return ESP_FAIL if error + */ +static esp_err_t _zh_160x_pulse(zh_160x_i2c_handle_t *handle); + +esp_err_t zh_160x_init(zh_pcf8574_handle_t **expander, zh_160x_i2c_handle_t **handle, zh_160x_i2c_lcd_size_t size) // -V2008 { ZH_LOGI("160X initialization started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X initialization failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X initialization failed. PCF8574 not initialized."); - handle->system = heap_caps_calloc(1, sizeof(uint8_t), MALLOC_CAP_8BIT); - ZH_ERROR_CHECK(handle->system != NULL, ESP_ERR_INVALID_ARG, NULL, "160X initialization failed. Memory allocation fail."); - *(uint8_t *)handle->system = size; - ZH_ERROR_CHECK(_zh_160x_lcd_init(handle) == ESP_OK, ESP_FAIL, NULL, "160X initialization failed. PCF8574 error."); + ZH_ERROR_CHECK(handle != NULL && expander != NULL && *expander != NULL && size < ZH_LCD_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "160X initialization failed. Invalid argument."); + ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "160X initialization failed. 160X is already initialized."); + *handle = heap_caps_calloc(1, sizeof(zh_160x_i2c_handle_t), MALLOC_CAP_8BIT); + ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "160X initialization failed. Failed to allocate 160X handle."); + (*handle)->zh_pcf8574_handle = *expander; + (*handle)->zh_160x_i2c_lcd_size = size; + ZH_ERROR_CHECK(_zh_160x_lcd_init(*handle) == ESP_OK, ESP_FAIL, NULL, "160X initialization failed. PCF8574 error."); ZH_LOGI("160X initialization completed successfully."); return ESP_OK; } -esp_err_t zh_160x_lcd_clear(zh_pcf8574_handle_t *handle) +esp_err_t zh_160x_deinit(zh_160x_i2c_handle_t **handle) +{ + ZH_LOGI("160X deinitialization started."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X deinitialization failed. Invalid argument."); + heap_caps_free(*handle); + *handle = NULL; + ZH_LOGI("160X deinitialization completed successfully."); + return ESP_OK; +} + +esp_err_t zh_160x_lcd_clear(zh_160x_i2c_handle_t **handle) { ZH_LOGI("160X display cleaning started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X display cleaning failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X display cleaning failed. PCF8574 not initialized."); - ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x01) == ESP_OK, ESP_FAIL, NULL, "160X display cleaning failed. PCF8574 error."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X display cleaning failed. Invalid argument."); + ZH_ERROR_CHECK(_zh_160x_send_command(*handle, 0x01) == ESP_OK, ESP_FAIL, NULL, "160X display cleaning failed. PCF8574 error."); ZH_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) // -V2008 +esp_err_t zh_160x_set_cursor(zh_160x_i2c_handle_t **handle, uint8_t row, uint8_t col) // -V2008 { ZH_LOGI("160X set cursor started."); - ZH_ERROR_CHECK(handle != NULL && row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4) && col < 16, ESP_ERR_INVALID_ARG, NULL, "160X set cursor failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X set cursor failed. PCF8574 not initialized."); - ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x80 | ((row == 0) ? col : (row == 1) ? (0x40 + col) - : (row == 2) ? (0x10 + col) - : (0x50 + col))) == ESP_OK, + ZH_ERROR_CHECK(handle != NULL && *handle != NULL && row < (((*handle)->zh_160x_i2c_lcd_size == ZH_LCD_16X2) ? 2 : 4) && col < 16, ESP_ERR_INVALID_ARG, NULL, "160X set cursor failed. Invalid argument."); + // clang-format off + ZH_ERROR_CHECK(_zh_160x_send_command(*handle, 0x80 | ((row == 0) ? col : (row == 1) ? (0x40 + col) : (row == 2) ? (0x10 + col) : (0x50 + col))) == ESP_OK, ESP_FAIL, NULL, "160X set cursor failed. PCF8574 error."); + // clang-format on ZH_LOGI("160X set cursor completed successfully."); return ESP_OK; } -esp_err_t zh_160x_print_char(zh_pcf8574_handle_t *handle, const char *str) +esp_err_t zh_160x_print_char(zh_160x_i2c_handle_t **handle, const char *str) { ZH_LOGI("160X print char started."); - ZH_ERROR_CHECK(str != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print char failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X print char failed. PCF8574 not initialized."); + ZH_ERROR_CHECK(str != NULL && handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print char failed. Invalid argument."); while (*str != 0) { - ZH_ERROR_CHECK(_zh_160x_send_data(handle, (uint8_t)*str++) == ESP_OK, ESP_FAIL, NULL, "160X print char failed. PCF8574 error."); + ZH_ERROR_CHECK(_zh_160x_send_data(*handle, (uint8_t)*str++) == ESP_OK, ESP_FAIL, NULL, "160X print char failed. PCF8574 error."); } ZH_LOGI("160X print char completed successfully."); return ESP_OK; } -esp_err_t zh_160x_print_int(zh_pcf8574_handle_t *handle, int num) +esp_err_t zh_160x_print_int(zh_160x_i2c_handle_t **handle, int num) { ZH_LOGI("160X print int started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print int failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X print int failed. PCF8574 not initialized."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print int failed. Invalid argument."); char buffer[12]; sprintf(buffer, "%d", num); ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print int failed. PCF8574 error."); @@ -79,11 +149,10 @@ esp_err_t zh_160x_print_int(zh_pcf8574_handle_t *handle, int num) return ESP_OK; } -esp_err_t zh_160x_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t precision) +esp_err_t zh_160x_print_float(zh_160x_i2c_handle_t **handle, float num, uint8_t precision) { ZH_LOGI("160X print float started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print float failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X print float failed. PCF8574 not initialized."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X print float failed. Invalid argument."); char buffer[16]; sprintf(buffer, "%.*f", precision, num); ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print float failed. PCF8574 error."); @@ -91,11 +160,10 @@ esp_err_t zh_160x_print_float(zh_pcf8574_handle_t *handle, float num, uint8_t pr return ESP_OK; } -esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, uint8_t progress) // -V2008 +esp_err_t zh_160x_print_progress_bar(zh_160x_i2c_handle_t **handle, uint8_t row, uint8_t progress) // -V2008 { ZH_LOGI("160X print progress bar started."); - ZH_ERROR_CHECK(handle != NULL && row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4) && progress <= 100, ESP_ERR_INVALID_ARG, NULL, "160X print progress bar failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X print progress bar failed. PCF8574 not initialized."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL && row < (((*handle)->zh_160x_i2c_lcd_size == ZH_LCD_16X2) ? 2 : 4) && progress <= 100, ESP_ERR_INVALID_ARG, NULL, "160X print progress bar failed. Invalid argument."); uint8_t blocks = (progress * 16) / 100; ZH_ERROR_CHECK(zh_160x_set_cursor(handle, row, 0) == ESP_OK, ESP_FAIL, NULL, "160X print progress bar failed. PCF8574 error."); for (uint8_t i = 0; i < 16; ++i) @@ -113,11 +181,10 @@ esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, u return ESP_OK; } -esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row) // -V2008 +esp_err_t zh_160x_clear_row(zh_160x_i2c_handle_t **handle, uint8_t row) // -V2008 { ZH_LOGI("160X clear row started."); - ZH_ERROR_CHECK(handle != NULL && row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4), ESP_ERR_INVALID_ARG, NULL, "160X clear row failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X clear row failed. PCF8574 not initialized."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL && row < (((*handle)->zh_160x_i2c_lcd_size == ZH_LCD_16X2) ? 2 : 4), ESP_ERR_INVALID_ARG, NULL, "160X clear row failed. Invalid argument."); ZH_ERROR_CHECK(zh_160x_set_cursor(handle, row, 0) == ESP_OK, ESP_FAIL, NULL, "160X clear row failed. PCF8574 error."); for (uint8_t i = 0; i < 16; ++i) { @@ -128,36 +195,34 @@ esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row) // -V2008 return ESP_OK; } -esp_err_t zh_160x_on_cursor(zh_pcf8574_handle_t *handle, bool blink) +esp_err_t zh_160x_on_cursor(zh_160x_i2c_handle_t **handle, bool blink) { ZH_LOGI("160X enable cursor started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X enable cursor failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X enable cursor failed. PCF8574 not initialized."); - ZH_ERROR_CHECK(_zh_160x_send_command(handle, (blink == true) ? 0x0f : 0x0e) == ESP_OK, ESP_FAIL, NULL, "160X enable cursor failed. PCF8574 error."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X enable cursor failed. Invalid argument."); + ZH_ERROR_CHECK(_zh_160x_send_command(*handle, (blink == true) ? 0x0f : 0x0e) == ESP_OK, ESP_FAIL, NULL, "160X enable cursor failed. PCF8574 error."); ZH_LOGI("160X enable cursor completed successfully."); return ESP_OK; } -esp_err_t zh_160x_off_cursor(zh_pcf8574_handle_t *handle) +esp_err_t zh_160x_off_cursor(zh_160x_i2c_handle_t **handle) { ZH_LOGI("160X disable cursor started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X disable cursor failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X disable cursor failed. PCF8574 not initialized."); - ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x0c) == ESP_OK, ESP_FAIL, NULL, "160X disable cursor failed. PCF8574 error."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X disable cursor failed. Invalid argument."); + ZH_ERROR_CHECK(_zh_160x_send_command(*handle, 0x0c) == ESP_OK, ESP_FAIL, NULL, "160X disable cursor failed. PCF8574 error."); ZH_LOGI("160X disable cursor completed successfully."); return ESP_OK; } -static esp_err_t _zh_160x_lcd_init(zh_pcf8574_handle_t *handle) // -V2008 +static esp_err_t _zh_160x_lcd_init(zh_160x_i2c_handle_t *handle) // -V2008 { vTaskDelay(20 / portTICK_PERIOD_MS); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); // -V525 ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); // -V525 ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, 0x30) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); // -V525 ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, 0x20) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, 0x20) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); // -V525 ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x28) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x28) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); @@ -168,29 +233,29 @@ static esp_err_t _zh_160x_lcd_init(zh_pcf8574_handle_t *handle) // -V2008 return ESP_OK; } -static esp_err_t _zh_160x_send_command(zh_pcf8574_handle_t *handle, uint8_t command) +static esp_err_t _zh_160x_send_command(zh_160x_i2c_handle_t *handle, uint8_t command) { - ZH_ERROR_CHECK(zh_pcf8574_write(handle, (command & 0xF0) | 0x08) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, (command & 0xF0) | 0x08) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, (command << 4) | 0x08) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, (command << 4) | 0x08) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); return ESP_OK; } -static esp_err_t _zh_160x_send_data(zh_pcf8574_handle_t *handle, uint8_t data) +static esp_err_t _zh_160x_send_data(zh_160x_i2c_handle_t *handle, uint8_t data) { - ZH_ERROR_CHECK(zh_pcf8574_write(handle, (data & 0xF0) | 0x09) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, (data & 0xF0) | 0x09) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); - ZH_ERROR_CHECK(zh_pcf8574_write(handle, (data << 4) | 0x09) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write(&handle->zh_pcf8574_handle, (data << 4) | 0x09) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); return ESP_OK; } -static esp_err_t _zh_160x_pulse(zh_pcf8574_handle_t *handle) +static esp_err_t _zh_160x_pulse(zh_160x_i2c_handle_t *handle) { - ZH_ERROR_CHECK(zh_pcf8574_write_gpio(handle, ZH_PCF8574_GPIO_NUM_P2, ZH_PCF8574_GPIO_HIGH) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write_gpio(&handle->zh_pcf8574_handle, ZH_PCF8574_GPIO_NUM_P2, ZH_PCF8574_GPIO_HIGH) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); vTaskDelay(1 / portTICK_PERIOD_MS); - ZH_ERROR_CHECK(zh_pcf8574_write_gpio(handle, ZH_PCF8574_GPIO_NUM_P2, ZH_PCF8574_GPIO_LOW) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); + ZH_ERROR_CHECK(zh_pcf8574_write_gpio(&handle->zh_pcf8574_handle, ZH_PCF8574_GPIO_NUM_P2, ZH_PCF8574_GPIO_LOW) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error."); vTaskDelay(1 / portTICK_PERIOD_MS); return ESP_OK; }