Compare commits
10
Commits
9e6543a1ff
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7649522057 | ||
|
|
107e1e3e9c | ||
|
|
9696b8a0fe | ||
|
|
04ce9746b1 | ||
|
|
50f2062e46 | ||
|
|
facf8b802b | ||
|
|
2da7f28835 | ||
|
|
4abc0209b6 | ||
|
|
a37cab782c | ||
|
|
e50266a42f |
No files matched your search
@@ -1,53 +1,26 @@
|
|||||||
# 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)
|
||||||
|
|
||||||
## Tested on
|
|
||||||
|
|
||||||
1. [ESP32 ESP-IDF v6.0.0](https://docs.espressif.com/projects/esp-idf/en/v6.0/esp32/index.html)
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
1. Support of 16 LCD 160X on one bus.
|
1. Support for HD44780-compatible 16x2 and 16x4 LCD displays via I2C.
|
||||||
|
2. 4-bit interface mode for efficient data transfer through PCF8574 expander.
|
||||||
|
3. Print strings, integers, and floating-point numbers to the display.
|
||||||
|
4. Progress bar display with custom characters on any row.
|
||||||
|
5. Cursor visibility and blink control.
|
||||||
|
6. Row-specific clear operations in addition to full screen clear.
|
||||||
|
|
||||||
## Attention
|
## Attention
|
||||||
|
|
||||||
For correct operation, please enable the following settings in the menuconfig:
|
1. The PCF8574 expander handle must remain valid until the LCD handle is deinitialized.
|
||||||
|
2. DDRAM location 0xFF must be configured as a custom character in CGRAM for progress bar to display correctly.
|
||||||
```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_vector](https://github.com/aZholtikov/zh_vector)
|
|
||||||
2. [zh_pcf8574](https://github.com/aZholtikov/zh_pcf8574)
|
|
||||||
|
|
||||||
## Using
|
## 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
|
```bash
|
||||||
cd ../your_project/components
|
cd ../your_project/components
|
||||||
git clone https://github.com/aZholtikov/zh_160x_i2c
|
git clone https://github.com/aZholtikov/zh_160x_i2c
|
||||||
git clone https://github.com/aZholtikov/zh_pcf8574
|
|
||||||
git clone https://github.com/aZholtikov/zh_vector
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the application, add the component:
|
In the application, add the component:
|
||||||
@@ -58,20 +31,20 @@ In the application, add the component:
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
One LCD on bus:
|
### Basic Text Output Example
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include "zh_160x_i2c.h"
|
#include "zh_160x_i2c.h"
|
||||||
|
|
||||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
zh_pcf8574_handle_t lcd_160x_handle = {0};
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
zh_160x_i2c_handle_t *lcd_handle = NULL;
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
||||||
esp_log_level_set("zh_pcf8574", 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 = {
|
i2c_master_bus_config_t i2c_bus_config = {
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
.i2c_port = I2C_PORT,
|
.i2c_port = I2C_PORT,
|
||||||
@@ -81,34 +54,157 @@ void app_main(void)
|
|||||||
.flags.enable_internal_pullup = true,
|
.flags.enable_internal_pullup = true,
|
||||||
};
|
};
|
||||||
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||||
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
if (ret != ESP_OK)
|
||||||
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);
|
printf("I2C bus initialization error\n");
|
||||||
zh_160x_set_cursor(&lcd_160x_handle, 0, 0);
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
zh_pcf8574_init_config_t pcf_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
pcf_config.i2c_handle = i2c_bus_handle;
|
||||||
|
pcf_config.i2c_address = 0x27;
|
||||||
|
ret = zh_pcf8574_init(&pcf_config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error\n");
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = zh_160x_init(&pcf8574_handle, &lcd_handle, ZH_LCD_16X2);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("LCD initialization error\n");
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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_int(&lcd_handle, 42);
|
||||||
|
zh_160x_off_cursor(&lcd_handle);
|
||||||
|
zh_160x_deinit(&lcd_handle);
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Float and Cursor Control Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_160x_i2c.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
zh_160x_i2c_handle_t *lcd_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
||||||
|
esp_log_level_set("zh_pcf8574", 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;
|
||||||
|
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("I2C bus initialization error\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zh_pcf8574_init_config_t pcf_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
pcf_config.i2c_handle = i2c_bus_handle;
|
||||||
|
pcf_config.i2c_address = 0x27;
|
||||||
|
ret = zh_pcf8574_init(&pcf_config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error\n");
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = zh_160x_init(&pcf8574_handle, &lcd_handle, ZH_LCD_16X2);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("LCD initialization error\n");
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zh_160x_on_cursor(&lcd_handle, true);
|
||||||
|
zh_160x_set_cursor(&lcd_handle, 0, 0);
|
||||||
|
zh_160x_print_char(&lcd_handle, "Temp: ");
|
||||||
|
zh_160x_print_float(&lcd_handle, 23.67f, 2);
|
||||||
|
zh_160x_clear_row(&lcd_handle, 1);
|
||||||
|
zh_160x_set_cursor(&lcd_handle, 1, 0);
|
||||||
|
zh_160x_print_char(&lcd_handle, "Row cleared");
|
||||||
|
zh_160x_off_cursor(&lcd_handle);
|
||||||
|
zh_160x_deinit(&lcd_handle);
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Progress Bar Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_160x_i2c.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
zh_160x_i2c_handle_t *lcd_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
||||||
|
esp_log_level_set("zh_pcf8574", 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;
|
||||||
|
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("I2C bus initialization error\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zh_pcf8574_init_config_t pcf_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
pcf_config.i2c_handle = i2c_bus_handle;
|
||||||
|
pcf_config.i2c_address = 0x27;
|
||||||
|
ret = zh_pcf8574_init(&pcf_config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error\n");
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = zh_160x_init(&pcf8574_handle, &lcd_handle, ZH_LCD_16X2);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("LCD initialization error\n");
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zh_160x_set_cursor(&lcd_handle, 0, 0);
|
||||||
|
zh_160x_print_char(&lcd_handle, "Progress: ");
|
||||||
|
zh_160x_print_progress_bar(&lcd_handle, 1, 75);
|
||||||
|
zh_160x_lcd_clear(&lcd_handle);
|
||||||
|
zh_160x_deinit(&lcd_handle);
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
i2c_del_master_bus(i2c_bus_handle);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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: "5.0.1"
|
||||||
|
license: "Apache-2.0"
|
||||||
|
maintainers:
|
||||||
|
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
|
||||||
|
dependencies:
|
||||||
|
idf: ">=5.0"
|
||||||
|
zh_pcf8574:
|
||||||
|
git: "https://github.com/aZholtikov/zh_pcf8574.git"
|
||||||
+163
-54
@@ -1,119 +1,228 @@
|
|||||||
/**
|
/**
|
||||||
* @file zh_160x_i2c.h
|
* @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
|
||||||
|
*
|
||||||
|
* @note All display operations require an initialized handle obtained through zh_160x_init().
|
||||||
|
* @note The text position counter of the HD44780 controller is shared by all rows,
|
||||||
|
* so row addresses are remapped internally.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "zh_pcf8574.h"
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
#define ZH_LCD_16X2 1 /*!< LCD size 16x2. */
|
|
||||||
#define ZH_LCD_16X4 0 /*!< LCD size 16x4. */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initializes the LCD 160X.
|
* @brief Opaque handle for LCD 160x I2C display.
|
||||||
*
|
*
|
||||||
* @param[in] handle Pointer to unique PCF8574 handle.
|
* Internal structure containing the PCF8574 expander handle and
|
||||||
* @param[in] size LCD size (ZH_LCD_16X2 or ZH_LCD_16X4).
|
* current LCD size configuration.
|
||||||
*
|
|
||||||
* @return ESP_OK if success or an error code otherwise.
|
|
||||||
*/
|
*/
|
||||||
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.
|
* @brief Supported LCD display sizes.
|
||||||
*
|
|
||||||
* @param[in] handle Pointer to unique PCF8574 handle.
|
|
||||||
*
|
|
||||||
* @return ESP_OK if success or an error code otherwise.
|
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_160x_lcd_clear(zh_pcf8574_handle_t *handle);
|
typedef enum
|
||||||
|
{
|
||||||
|
ZH_LCD_16X2, /*!< LCD size 16x2 */
|
||||||
|
ZH_LCD_16X4, /*!< LCD size 16x4 */
|
||||||
|
ZH_LCD_NUM_MAX /*!< Number of supported LCD sizes */
|
||||||
|
} 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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[out] handle Pointer to the output LCD handle pointer (must not be NULL and must point to a NULL pointer)
|
||||||
|
* @param[in] size LCD size configuration (must be < ZH_LCD_NUM_MAX)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if any parameter pointer is NULL or size is invalid
|
||||||
|
* @return ESP_ERR_INVALID_STATE if the output pointer already holds an initialized handle
|
||||||
|
* @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 pointer is NULL or points to a NULL handle
|
||||||
|
*/
|
||||||
|
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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if handle pointer is NULL or points to a NULL handle
|
||||||
|
* @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.
|
* @brief Sets the cursor to a specific position on the LCD.
|
||||||
*
|
*
|
||||||
* @param[in] handle Pointer to unique PCF8574 handle.
|
* Moves the cursor to the specified row and column.
|
||||||
* @param[in] row The row number.
|
* For 16x2 displays: rows 0–1, columns 0–15.
|
||||||
* @param[in] col The column number (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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] row Row number (must be < 2 for 16x2 and < 4 for 16x4)
|
||||||
|
* @param[in] col Column number (must be < 16)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if any parameter pointer 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.
|
* Transmits each character of the null-terminated string to the LCD
|
||||||
* @param[in] str Pointer to the string to be displayed.
|
* 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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] str Null-terminated string to display (must not be NULL)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if any parameter is NULL or handle points to a NULL handle
|
||||||
|
* @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.
|
* Converts the integer to a decimal string and displays it.
|
||||||
* @param[in] num The integer to be displayed.
|
* Supports the full range of signed 32-bit values
|
||||||
|
* (maximum 10 digits plus an optional sign).
|
||||||
*
|
*
|
||||||
* @return ESP_OK if success or an error code otherwise.
|
* @param[in] handle Pointer to the LCD handle pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] num Integer value to display
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if handle pointer is NULL or points to a NULL handle
|
||||||
|
* @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.
|
* Converts the floating-point value to a decimal string with the
|
||||||
* @param[in] num The floating-point number to be displayed.
|
* specified number of decimal places and displays it. The converted
|
||||||
* @param[in] precision The number of decimal places to display.
|
* text must fit into an internal 16-character buffer.
|
||||||
*
|
*
|
||||||
* @return ESP_OK if success or an error code otherwise.
|
* @param[in] handle Pointer to the LCD handle pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] num Floating-point value to display
|
||||||
|
* @param[in] precision Number of decimal places passed to snprintf (0–6 recommended)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if handle pointer is NULL or points to a NULL handle
|
||||||
|
* @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.
|
* @brief Displays a progress bar on a specific row of the LCD.
|
||||||
*
|
*
|
||||||
* @param[in] handle Pointer to unique PCF8574 handle.
|
* Renders a 16-block progress bar where filled blocks represent the
|
||||||
* @param[in] row The row number.
|
* progress percentage and empty blocks show the remainder. Filled
|
||||||
* @param[in] progress The progress percentage (0–100).
|
* blocks are printed as byte 0xFF, which displays as a solid block
|
||||||
|
* only when DDRAM location 0xFF is configured as a custom character
|
||||||
|
* (CGRAM pattern 7 on HD44780 controllers).
|
||||||
|
* Leaves the cursor at the end of the row.
|
||||||
*
|
*
|
||||||
* @return ESP_OK if success or an error code otherwise.
|
* @param[in] handle Pointer to the LCD handle pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] row Row number (must be < 2 for 16x2 and < 4 for 16x4)
|
||||||
|
* @param[in] progress Progress percentage (must be <= 100)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if any parameter pointer 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.
|
* @brief Clears a specific row on the LCD.
|
||||||
*
|
*
|
||||||
* @param[in] handle Pointer to unique PCF8574 handle.
|
* Moves the cursor to the beginning of the specified row and fills
|
||||||
* @param[in] row The row number.
|
* 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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] row Row number (must be < 2 for 16x2 and < 4 for 16x4)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if any parameter pointer 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.
|
* Turns on the cursor and optionally enables blinking.
|
||||||
* @param[in] blink Blink mode.
|
* 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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
* @param[in] blink Enable cursor blink mode
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if handle pointer is NULL or points to a NULL handle
|
||||||
|
* @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 pointer (must not be NULL and must point to a non-NULL handle)
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_ERR_INVALID_ARG if handle pointer is NULL or points to a NULL handle
|
||||||
|
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
4.1.0
|
5.0.1
|
||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
# Описание компонента zh_160x_i2c
|
||||||
|
|
||||||
|
## Общие сведения
|
||||||
|
|
||||||
|
**zh_160x_i2c** — это драйвер для ESP-IDF, реализующий управление 16x2 и 16x4 LCD-дисплеями (HD44780-совместимыми) через I2C-расширитель PCF8574.
|
||||||
|
|
||||||
|
Компонент предоставляет высокоуровневый API для вывода текста, управления курсором, отображения индикатора прогресса и работы с числовыми данными на жидкокристаллических дисплеях.
|
||||||
|
|
||||||
|
## Основные возможности
|
||||||
|
|
||||||
|
1. **Поддержка дисплеев 16x2 и 16x4** — автоматическое определение конфигурации экрана.
|
||||||
|
2. **4-битный режим интерфейса** — эффективная передача данных через линии D4–D7 (пин P4–P7 PCF8574).
|
||||||
|
3. **Вывод строк, целых и вещественных чисел** — функции для форматированного вывода числовых данных.
|
||||||
|
4. **Отображение индикатора прогресса** — 16-блочный прогресс-бар с настраиваемым процентом.
|
||||||
|
5. **Управление курсором** — включение/выключение курсора и режима мерцания (blink).
|
||||||
|
6. **Построчная очистка** — очистка произвольной строки дисплея.
|
||||||
|
|
||||||
|
## Внутреннее устройство
|
||||||
|
|
||||||
|
### Структура `zh_160x_i2c_handle_t`
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `zh_pcf8574_handle` | `zh_pcf8574_handle_t *` | Указатель на управляющую структуру расширителя PCF8574 |
|
||||||
|
| `zh_160x_i2c_lcd_size` | `zh_160x_i2c_lcd_size_t` | Конфигурация размера LCD (16x2 или 16x4) |
|
||||||
|
|
||||||
|
Управляющая структура содержит ссылку на расширитель PCF8574 (который принадлежит вызывающей стороне) и текущую конфигурацию размера дисплея для расчёта адресов строк.
|
||||||
|
|
||||||
|
### Управление памятью
|
||||||
|
|
||||||
|
Память под структуру `zh_160x_i2c_handle_t` выделяется функцией `heap_caps_calloc()` с флагом `MALLOC_CAP_8BIT` при вызове `zh_160x_init()`. Освобождение происходит через `heap_caps_free()` в функции `zh_160x_deinit()`, после чего указатель устанавливается в `NULL` для предотвращения use-after-free.
|
||||||
|
|
||||||
|
### Адресация строк HD44780
|
||||||
|
|
||||||
|
HD44780-совместимые контроллеры используют нестандартную адресную карту строк:
|
||||||
|
|
||||||
|
| Физическая строка | Адрес DDRAM |
|
||||||
|
| ------------------- | ------------- |
|
||||||
|
| 0 | `0x00` |
|
||||||
|
| 1 | `0x40` |
|
||||||
|
| 2 | `0x10` |
|
||||||
|
| 3 | `0x50` |
|
||||||
|
|
||||||
|
Массив `_lcd_row_addresses` содержит эти адреса и используется функцией `zh_160x_set_cursor()` для корректного позиционирования курсора.
|
||||||
|
|
||||||
|
### Протокол инициализации LCD
|
||||||
|
|
||||||
|
Инициализация follows HD44780 initialization protocol:
|
||||||
|
|
||||||
|
1. Задержка 20 мс для стабилизации питания.
|
||||||
|
2. Три команды `0x30` с импульсами Enable — перевод в 4-битный режим (последовательность «warm-up»).
|
||||||
|
3. Команда `0x20` — установка 4-битного режима.
|
||||||
|
4. Команда `0x28` — функция set: 4-бит, 2 строки, 5x8 точки.
|
||||||
|
5. Команда `0x0C` — display on, cursor off, blink off.
|
||||||
|
6. Команда `0x01` — clear display.
|
||||||
|
7. Команда `0x06` — entry mode: cursor increment, no shift.
|
||||||
|
|
||||||
|
## API (интерфейс)
|
||||||
|
|
||||||
|
### Инициализация и освобождение
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_160x_init()` | Инициализирует LCD-дисплей, выделяет и настраивает управляющую структуру, отправляет последовательность инициализации контроллеру LCD через расширитель PCF8574. |
|
||||||
|
| `zh_160x_deinit()` | Освобождает выделенную память управляющей структуры и устанавливает указатель в `NULL`. |
|
||||||
|
|
||||||
|
### Позиционирование и очистка
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
| --------- | ---------- |
|
||||||
|
| `zh_160x_lcd_clear()` | Очищает весь экран LCD и перемещает курсор в домашнюю позицию (строка 0, столбец 0). |
|
||||||
|
| `zh_160x_set_cursor()` | Устанавливает курсор в указанную позицию (строка и столбец). Для 16x2: строки 0–1, столбцы 0–15. Для 16x4: строки 0–3, столбцы 0–15. |
|
||||||
|
| `zh_160x_clear_row()` | Очищает указанную строку (заполняет 16 пробелами) и возвращает курсор в начало строки. |
|
||||||
|
|
||||||
|
### Вывод данных
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
| --------- | ---------- |
|
||||||
|
| `zh_160x_print_char()` | Выводит строку символов в текущую позицию курсора. Курсор автоматически перемещается после каждого символа. |
|
||||||
|
| `zh_160x_print_int()` | Преобразует целое число (signed 32-bit) в десятичную строку и выводит на дисплей. |
|
||||||
|
| `zh_160x_print_float()` | Преобразует число с плавающей точкой в строку с указанным количеством знаков после запятой и выводит на дисплей. |
|
||||||
|
|
||||||
|
### Прогресс-бар и курсор
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
| --------- | ---------- |
|
||||||
|
| `zh_160x_print_progress_bar()` | Отображает 16-блочный прогресс-бар на указанной строке. Заполненные блоки отображаются как символ с кодом `0xFF` (требует настройки CGRAM). |
|
||||||
|
| `zh_160x_on_cursor()` | Включает курсор с опциональным режимом мерцания (blink). |
|
||||||
|
| `zh_160x_off_cursor()` | Выключает курсор и режим мерцания. |
|
||||||
|
|
||||||
|
## Потокобезопасность
|
||||||
|
|
||||||
|
Компонент **не является потокобезопасным**. Все операции с LCD-дисплеем требуют внешних механизмов синхронизации (мьютексов, критических секций) при доступе из нескольких задач FreeRTOS. Управляющая структура `zh_160x_i2c_handle_t` не содержит внутренних примитивов синхронизации.
|
||||||
|
|
||||||
|
## Алгоритмы
|
||||||
|
|
||||||
|
### Отправка данных в 4-битном режиме
|
||||||
|
|
||||||
|
Команды и данные передаются в два этапа (старший и младший нибблы) через линии D4–D7:
|
||||||
|
|
||||||
|
1. **Отправка команды (`_zh_160x_send_command`)** — старший ниббл команды записывается на шины с маской `0x08` (RS=0, P3=1), затем генерируется импульс Enable. Затем младший ниббл (сдвинутый на 4 бита) записывается с той же маской и снова импульс Enable.
|
||||||
|
2. **Отправка данных (`_zh_160x_send_data`)** — аналогично, но с маской `0x09` (RS=1 для режима данных, P3=1).
|
||||||
|
3. **Импульс Enable (`_zh_160x_pulse`)** — установка пина P2 в HIGH, задержка 1 мс, установка в LOW, задержка 1 мс. Этот импульс Latch-ит данные в LCD-контроллер.
|
||||||
|
|
||||||
|
### Прогресс-бар
|
||||||
|
|
||||||
|
Процент прогресса конвертируется в количество заполненных блоков по формуле:
|
||||||
|
|
||||||
|
```
|
||||||
|
blocks = (progress * 16) / 100
|
||||||
|
```
|
||||||
|
|
||||||
|
Где `progress` — значение от 0 до 100. Заполненные блоки выводятся как `0xFF` (сплошной блок), пустые — как пробелы.
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Базовый вывод текста
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_160x_i2c.h"
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
zh_160x_i2c_handle_t *lcd_160x_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// Настройка I2C-шины и инициализация PCF8574...
|
||||||
|
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, &pcf8574_handle);
|
||||||
|
|
||||||
|
// Инициализация LCD 16x2
|
||||||
|
zh_160x_init(&pcf8574_handle, &lcd_160x_handle, ZH_LCD_16X2);
|
||||||
|
|
||||||
|
// Вывод текста на дисплей
|
||||||
|
zh_160x_set_cursor(&lcd_160x_handle, 0, 0);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, "Hello, LCD!");
|
||||||
|
zh_160x_set_cursor(&lcd_160x_handle, 1, 0);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, "ESP32 Ready");
|
||||||
|
|
||||||
|
// Очистка и освобождение ресурсов
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
zh_160x_lcd_clear(&lcd_160x_handle);
|
||||||
|
zh_160x_deinit(&lcd_160x_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Вывод числовых данных
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Вывод целого числа
|
||||||
|
int temperature = 25;
|
||||||
|
zh_160x_set_cursor(&lcd_160x_handle, 0, 0);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, "Temp: ");
|
||||||
|
zh_160x_print_int(&lcd_160x_handle, temperature);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, " C");
|
||||||
|
|
||||||
|
// Вывод числа с плавающей точкой (2 знака после запятой)
|
||||||
|
float voltage = 3.315f;
|
||||||
|
zh_160x_set_cursor(&lcd_160x_handle, 1, 0);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, "VCC: ");
|
||||||
|
zh_160x_print_float(&lcd_160x_handle, voltage, 2);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, " V");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отображение прогресс-бара
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Прогресс-бар требует настройки CGRAM:
|
||||||
|
// DDRAM адрес 0xFF должен быть сопоставлен с пользовательским символом (CGRAM pattern 7)
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i <= 100; i += 10)
|
||||||
|
{
|
||||||
|
zh_160x_set_cursor(&lcd_160x_handle, 0, 0);
|
||||||
|
zh_160x_print_char(&lcd_160x_handle, "Progress: ");
|
||||||
|
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(500 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
zh_160x_lcd_clear(&lcd_160x_handle);
|
||||||
|
zh_160x_deinit(&lcd_160x_handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Логирование
|
||||||
|
|
||||||
|
Компонент использует ESP-IDF логирование с тегом `"zh_160x_i2c"`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
esp_log_level_set("zh_160x_i2c", ESP_LOG_ERROR);
|
||||||
|
```
|
||||||
|
|
||||||
|
Для включения подробного логирования установите уровень `ESP_LOG_DEBUG`.
|
||||||
|
|
||||||
|
## Ошибки
|
||||||
|
|
||||||
|
| Код | Описание |
|
||||||
|
| ----- | ---------- |
|
||||||
|
| `ESP_OK` | Успешное выполнение |
|
||||||
|
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL-указатель, недопустимый размер дисплея, координаты вне диапазона) |
|
||||||
|
| `ESP_ERR_INVALID_STATE` | LCD уже инициализирован (повторная инициализация без деинициализации) |
|
||||||
|
| `ESP_ERR_NO_MEM` | Не удалось выделить память под управляющую структуру |
|
||||||
|
| `ESP_FAIL` | Ошибка записи/чтения через PCF8574 или сбой последовательности инициализации |
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
1. **Зависимость от компонента `zh_pcf8574`** — требуется установка и инициализация драйвера PCF8574 перед использованием LCD.
|
||||||
|
2. **Буфер 16 символов** — функция `zh_160x_print_float()` использует внутренний буфер фиксированной длины; длинные числа могут быть усечены.
|
||||||
|
3. **Отсутствие потокобезопасности** — все операции с LCD требуют внешней синхронизации при работе из нескольких задач FreeRTOS.
|
||||||
|
4. **Прогресс-бар требует CGRAM** — для отображения заполненных блоков символ с кодом `0xFF` должен быть настроен как пользовательский символ в CGRAM (паттерн 7 HD44780).
|
||||||
|
5. **Один I2C-адрес** — компонент рассчитан на один LCD-модуль на шину; множественные дисплеи требуют дополнительных расширителей или мультиплексирования.
|
||||||
|
|
||||||
|
## Требования для сборки
|
||||||
|
|
||||||
|
- ESP-IDF v5.0 или новее
|
||||||
|
- Компонент `zh_pcf8574` (зависимость, устанавливается автоматически через idf_component_manager)
|
||||||
|
- FreeRTOS (входит в состав ESP-IDF)
|
||||||
|
- Настройка `CONFIG_FREERTOS_HZ=1000` в menuconfig
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ваш_проект/components
|
||||||
|
git clone https://github.com/aZholtikov/zh_160x_i2c
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_160x_i2c.h"
|
||||||
|
```
|
||||||
+134
-65
@@ -13,89 +13,163 @@ static const char *TAG = "zh_160x_i2c";
|
|||||||
return err; \
|
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);
|
* @brief LCD 160x I2C handle structure.
|
||||||
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);
|
* Internal handle containing the PCF8574 expander pointer and
|
||||||
|
* the configured LCD size for row address calculations.
|
||||||
|
*
|
||||||
|
* @warning The expander handle is owned by the caller and must remain valid
|
||||||
|
* until the LCD handle is deinitialized.
|
||||||
|
*/
|
||||||
|
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
|
static const uint8_t _lcd_row_addresses[] = {0x00, 0x40, 0x10, 0x50};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 any expander write or pulse operation fails
|
||||||
|
*/
|
||||||
|
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. The high and low
|
||||||
|
* nibbles are sent sequentially on data lines D4-D7 (PCF8574 pins P4-P7),
|
||||||
|
* each followed by an enable pulse. Bit mask 0x08 keeps pin P3 high;
|
||||||
|
* RS (pin P0) and E (pin P2) are held low between pulses.
|
||||||
|
*
|
||||||
|
* @param handle Pointer to the 160x I2C handle
|
||||||
|
* @param command Command byte to send
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_FAIL if any expander write or pulse operation fails
|
||||||
|
*/
|
||||||
|
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. The high and low
|
||||||
|
* nibbles are sent sequentially on data lines D4-D7 (PCF8574 pins P4-P7),
|
||||||
|
* each followed by an enable pulse. Bit mask 0x09 sets RS (pin P0) high
|
||||||
|
* for data mode and keeps pin P3 high; E (pin P2) is held low between pulses.
|
||||||
|
*
|
||||||
|
* @param handle Pointer to the 160x I2C handle
|
||||||
|
* @param data Data byte to send
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
* @return ESP_FAIL if any expander write or pulse operation fails
|
||||||
|
*/
|
||||||
|
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 enable line (PCF8574 pin P2) to latch data into the LCD.
|
||||||
|
* Sets the line high, delays 1ms, then sets it 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 any GPIO write operation fails
|
||||||
|
*/
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
ZH_LOGI("160X initialization started.");
|
ZH_LOGI("160X initialization started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "160X initialization failed. Invalid argument.");
|
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->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X initialization failed. PCF8574 not initialized.");
|
ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "160X initialization failed. 160X is already initialized.");
|
||||||
handle->system = heap_caps_calloc(1, sizeof(uint8_t), MALLOC_CAP_8BIT);
|
*handle = heap_caps_calloc(1, sizeof(zh_160x_i2c_handle_t), MALLOC_CAP_8BIT);
|
||||||
ZH_ERROR_CHECK(handle->system != NULL, ESP_ERR_INVALID_ARG, NULL, "160X initialization failed. Memory allocation fail.");
|
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "160X initialization failed. Failed to allocate 160X handle.");
|
||||||
*(uint8_t *)handle->system = size;
|
(*handle)->zh_pcf8574_handle = *expander;
|
||||||
ZH_ERROR_CHECK(_zh_160x_lcd_init(handle) == ESP_OK, ESP_FAIL, NULL, "160X initialization failed. PCF8574 error.");
|
(*handle)->zh_160x_i2c_lcd_size = size;
|
||||||
|
ZH_ERROR_CHECK(_zh_160x_lcd_init(*handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "160X initialization failed. PCF8574 error.");
|
||||||
ZH_LOGI("160X initialization completed successfully.");
|
ZH_LOGI("160X initialization completed successfully.");
|
||||||
return ESP_OK;
|
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_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 != NULL && *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(_zh_160x_send_command(handle, 0x01) == ESP_OK, ESP_FAIL, NULL, "160X display cleaning failed. PCF8574 error.");
|
|
||||||
ZH_LOGI("160X display cleaning completed successfully.");
|
ZH_LOGI("160X display cleaning completed successfully.");
|
||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
ZH_LOGI("160X set cursor started.");
|
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 != 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.");
|
||||||
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 | (_lcd_row_addresses[row] + col)) == ESP_OK, ESP_FAIL, NULL, "160X set cursor failed. PCF8574 error.");
|
||||||
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.");
|
|
||||||
ZH_LOGI("160X set cursor completed successfully.");
|
ZH_LOGI("160X set cursor completed successfully.");
|
||||||
return ESP_OK;
|
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_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(str != NULL && handle != 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.");
|
|
||||||
while (*str != 0)
|
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.");
|
ZH_LOGI("160X print char completed successfully.");
|
||||||
return ESP_OK;
|
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_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 != NULL && *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.");
|
char buffer[16];
|
||||||
char buffer[12];
|
snprintf(buffer, sizeof(buffer), "%d", num);
|
||||||
sprintf(buffer, "%d", num);
|
|
||||||
ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print int failed. PCF8574 error.");
|
ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print int failed. PCF8574 error.");
|
||||||
ZH_LOGI("160X print int completed successfully.");
|
ZH_LOGI("160X print int completed successfully.");
|
||||||
return ESP_OK;
|
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_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 != NULL && *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.");
|
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
sprintf(buffer, "%.*f", precision, num);
|
snprintf(buffer, sizeof(buffer), "%.*f", precision, num);
|
||||||
ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print float failed. PCF8574 error.");
|
ZH_ERROR_CHECK(zh_160x_print_char(handle, buffer) == ESP_OK, ESP_FAIL, NULL, "160X print float failed. PCF8574 error.");
|
||||||
ZH_LOGI("160X print float completed successfully.");
|
ZH_LOGI("160X print float completed successfully.");
|
||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
ZH_LOGI("160X print progress bar started.");
|
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 != 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.");
|
||||||
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X print progress bar failed. PCF8574 not initialized.");
|
|
||||||
uint8_t blocks = (progress * 16) / 100;
|
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.");
|
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)
|
for (uint8_t i = 0; i < 16; ++i)
|
||||||
@@ -113,11 +187,10 @@ esp_err_t zh_160x_print_progress_bar(zh_pcf8574_handle_t *handle, uint8_t row, u
|
|||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
ZH_LOGI("160X clear row started.");
|
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 != 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(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "160X clear row failed. PCF8574 not initialized.");
|
|
||||||
ZH_ERROR_CHECK(zh_160x_set_cursor(handle, row, 0) == ESP_OK, ESP_FAIL, NULL, "160X clear row failed. PCF8574 error.");
|
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)
|
for (uint8_t i = 0; i < 16; ++i)
|
||||||
{
|
{
|
||||||
@@ -128,69 +201,65 @@ esp_err_t zh_160x_clear_row(zh_pcf8574_handle_t *handle, uint8_t row) // -V2008
|
|||||||
return ESP_OK;
|
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_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 != NULL && *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(_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.");
|
ZH_LOGI("160X enable cursor completed successfully.");
|
||||||
return ESP_OK;
|
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_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 != NULL && *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(_zh_160x_send_command(handle, 0x0c) == ESP_OK, ESP_FAIL, NULL, "160X disable cursor failed. PCF8574 error.");
|
|
||||||
ZH_LOGI("160X disable cursor completed successfully.");
|
ZH_LOGI("160X disable cursor completed successfully.");
|
||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
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.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == 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, 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.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == 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, 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.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == 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, 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.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == 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_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.");
|
||||||
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.");
|
|
||||||
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x0C) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x0C) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x01) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x01) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
||||||
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x06) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
ZH_ERROR_CHECK(_zh_160x_send_command(handle, 0x06) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
||||||
return ESP_OK;
|
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_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.");
|
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
||||||
return ESP_OK;
|
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_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.");
|
ZH_ERROR_CHECK(_zh_160x_pulse(handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 error.");
|
||||||
return ESP_OK;
|
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);
|
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);
|
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user