Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c788e2f9d5 | ||
|
|
95fca401de | ||
|
|
f47875b700 | ||
|
|
eb70315c9a | ||
|
|
e7edc8399f | ||
|
|
923dd6542f | ||
|
|
e2455322ac | ||
|
|
f4f74e9d5a | ||
|
|
0785eeb3e5 | ||
|
|
a124922338 |
No files matched your search
@@ -1,30 +1,26 @@
|
|||||||
# ESP32 ESP-IDF component for PCF8574 8-bit I/O expander
|
# ESP32 ESP-IDF component for PCF8574 8-bit I/O expander
|
||||||
|
|
||||||
## Wiki
|
|
||||||
|
|
||||||
[EN](WIKI_EN.md) | [RU](WIKI_RU.md)
|
|
||||||
|
|
||||||
## 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. I2C communication via esp-idf i2c_master driver.
|
1. I2C communication via the ESP-IDF I2C master driver.
|
||||||
2. Configurable GPIO direction for each pin (P0-P7).
|
2. Configurable GPIO direction for each pin P0 through P7.
|
||||||
3. Interrupt handling with GPIO level detection.
|
3. Interrupt handling with GPIO level detection and event notification.
|
||||||
4. Event-based architecture using esp_event framework.
|
4. Event-based architecture using the esp_event framework.
|
||||||
5. Statistics tracking for debugging and monitoring.
|
5. Statistics tracking for debugging and monitoring.
|
||||||
|
6. Multiple device instances on a single I2C bus.
|
||||||
|
|
||||||
|
## Attention
|
||||||
|
|
||||||
|
1. Requires ESP-IDF v5.0 or later with the I2C master driver.
|
||||||
|
2. Interrupt handling runs in task context and is not IRAM-safe.
|
||||||
|
3. A single interrupt GPIO pin is shared by all initialized devices.
|
||||||
|
4. The internal task is deleted only when the last device instance is deinitialized.
|
||||||
|
|
||||||
## 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_pcf8574
|
git clone https://github.com/aZholtikov/zh_pcf8574
|
||||||
```
|
```
|
||||||
@@ -37,4 +33,275 @@ In the application, add the component:
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования)
|
### Basic Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
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 = 0x38;
|
||||||
|
esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t reg = 0;
|
||||||
|
ret = zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("GPIO status: 0x%02X\n", reg);
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_write(&pcf8574_handle, 0b10000010);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Write OK\n");
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("GPIO status after write: 0x%02X\n", reg);
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Deinit OK\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GPIO Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
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 = 0x38;
|
||||||
|
esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_HIGH);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("P4 set high\n");
|
||||||
|
}
|
||||||
|
bool status = false;
|
||||||
|
ret = zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &status);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("P4 status: %d\n", status);
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_LOW);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("P4 set low\n");
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &status);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("P4 status: %d\n", status);
|
||||||
|
}
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Reset Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
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 = 0x38;
|
||||||
|
esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zh_pcf8574_write(&pcf8574_handle, 0xFF);
|
||||||
|
uint8_t reg = 0;
|
||||||
|
ret = zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("GPIO before reset: 0x%02X\n", reg);
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_reset(&pcf8574_handle);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Reset OK\n");
|
||||||
|
}
|
||||||
|
ret = zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("GPIO after reset: 0x%02X\n", reg);
|
||||||
|
}
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Interrupt Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
zh_pcf8574_event_on_isr_t *event = event_data;
|
||||||
|
printf("Interrupt on address 0x%02X pin P%d level %d time %lld\n",
|
||||||
|
event->i2c_address, event->gpio_number, event->gpio_level, event->interrupt_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
esp_event_loop_create_default();
|
||||||
|
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL);
|
||||||
|
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
config.i2c_handle = i2c_bus_handle;
|
||||||
|
config.i2c_address = 0x38;
|
||||||
|
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
||||||
|
config.interrupt_gpio = GPIO_NUM_14;
|
||||||
|
esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stats Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
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 = 0x38;
|
||||||
|
esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t reg = 0;
|
||||||
|
zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
zh_pcf8574_write(&pcf8574_handle, 0xAA);
|
||||||
|
zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
|
||||||
|
printf("I2C driver errors: %ld\n", stats->i2c_driver_error);
|
||||||
|
printf("Event post errors: %ld\n", stats->event_post_error);
|
||||||
|
printf("Vector errors: %ld\n", stats->vector_error);
|
||||||
|
printf("Queue overflow errors: %ld\n", stats->queue_overflow_error);
|
||||||
|
printf("Min free stack size: %ld\n", stats->min_stack_size);
|
||||||
|
zh_pcf8574_reset_stats();
|
||||||
|
stats = zh_pcf8574_get_stats();
|
||||||
|
printf("After reset I2C driver errors: %ld\n", stats->i2c_driver_error);
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
-496
@@ -1,496 +0,0 @@
|
|||||||
# zh_pcf8574 - PCF8574(A) 8-bit I/O Expander Component for ESP-IDF
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Features](#features)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [API Reference](#api-reference)
|
|
||||||
- [Usage Examples](#usage-examples)
|
|
||||||
- [Technical Specifications](#technical-specifications)
|
|
||||||
- [Error Codes](#error-codes)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
`zh_pcf8574` is an ESP-IDF component for 8-bit I/O expanders PCF8574 and PCF8574A. It provides a convenient API for working with GPIO expanders via I2C bus. The component supports up to 16 expanders on a single I2C bus and can generate interrupts when input GPIO states change.
|
|
||||||
|
|
||||||
The component is designed specifically for ESP32 microcontrollers and uses ESP-IDF v5.0+ I2C driver API.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
1. **Support for 16 Expanders**: Up to 16 PCF8574(A) on one I2C bus (addresses 0x20-0x27 and 0x38-0x3F)
|
|
||||||
2. **GPIO Modes**: Support for output and input modes for each of the 8 GPIO pins
|
|
||||||
3. **Interrupt Generation**: Support for interrupts from input GPIOs (negative edge)
|
|
||||||
4. **Error Statistics**: Built-in counters for I2C, event, vector, and queue overflow errors
|
|
||||||
5. **Thread-Safe**: Uses ESP-IDF I2C driver and FreeRTOS
|
|
||||||
6. **Minimal Overhead**: Low memory and CPU requirements
|
|
||||||
7. **Simple API**: Easy read/write for all GPIO and individual pins
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Navigate to your project's components directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../your_project/components
|
|
||||||
```
|
|
||||||
|
|
||||||
Clone the repository:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/aZholtikov/zh_pcf8574.git
|
|
||||||
```
|
|
||||||
|
|
||||||
In your application, include the header:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
The component will be automatically built with your project.
|
|
||||||
|
|
||||||
### Required menuconfig Settings
|
|
||||||
|
|
||||||
For correct operation, enable the following settings in menuconfig:
|
|
||||||
|
|
||||||
```text
|
|
||||||
GPIO_CTRL_FUNC_IN_IRAM
|
|
||||||
I2C_ISR_IRAM_SAFE
|
|
||||||
I2C_MASTER_ISR_HANDLER_IN_IRAM
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Macros
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define ZH_PCF8574_GPIO_OUTPUT false
|
|
||||||
#define ZH_PCF8574_GPIO_INPUT true
|
|
||||||
#define ZH_PCF8574_GPIO_LOW false
|
|
||||||
#define ZH_PCF8574_GPIO_HIGH true
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_init_config_t Structure
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle
|
|
||||||
uint32_t i2c_frequency; // I2C frequency of the expander (max 100000 Hz)
|
|
||||||
uint32_t stack_size; // Stack size for interrupt processing task
|
|
||||||
uint8_t task_priority; // Priority of interrupt processing task
|
|
||||||
uint8_t i2c_address; // I2C address of the expander (0x20-0x27 or 0x38-0x3F)
|
|
||||||
bool p0_gpio_work_mode; // GPIO P0 work mode (false - output, true - input)
|
|
||||||
bool p1_gpio_work_mode; // GPIO P1 work mode
|
|
||||||
bool p2_gpio_work_mode; // GPIO P2 work mode
|
|
||||||
bool p3_gpio_work_mode; // GPIO P3 work mode
|
|
||||||
bool p4_gpio_work_mode; // GPIO P4 work mode
|
|
||||||
bool p5_gpio_work_mode; // GPIO P5 work mode
|
|
||||||
bool p6_gpio_work_mode; // GPIO P6 work mode
|
|
||||||
bool p7_gpio_work_mode; // GPIO P7 work mode
|
|
||||||
gpio_num_t interrupt_gpio; // Interrupt GPIO (GPIO_NUM_MAX - disable)
|
|
||||||
} zh_pcf8574_init_config_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `ZH_PCF8574_INIT_CONFIG_DEFAULT()` macro to initialize with default values:
|
|
||||||
|
|
||||||
- `i2c_frequency`: 100000 Hz
|
|
||||||
- `i2c_address`: 0xFF
|
|
||||||
- `p0_gpio_work_mode` ... `p7_gpio_work_mode`: `ZH_PCF8574_GPIO_OUTPUT`
|
|
||||||
- `interrupt_gpio`: `GPIO_NUM_MAX`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_handle_t Structure
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct _zh_pcf8574_handle_t zh_pcf8574_handle_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
Opaque handle for a PCF8574 device instance. The internal structure is minimal, storing only the I2C address — the full device state is maintained in an internal vector.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_stats_t Structure
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t i2c_driver_error; // Number of I2C driver errors
|
|
||||||
uint32_t event_post_error; // Number of event post errors
|
|
||||||
uint32_t vector_error; // Number of vector errors
|
|
||||||
uint32_t queue_overflow_error; // Number of queue overflow errors
|
|
||||||
uint32_t min_stack_size; // Minimum free stack of the task
|
|
||||||
} zh_pcf8574_stats_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_event_on_isr_t Structure
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint64_t interrupt_time; // Interrupt time (microseconds)
|
|
||||||
zh_pcf8574_gpio_num_t gpio_number; // GPIO number that caused the interrupt
|
|
||||||
uint8_t i2c_address; // I2C address of the expander
|
|
||||||
bool gpio_level; // GPIO level at interrupt time
|
|
||||||
} zh_pcf8574_event_on_isr_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_init()
|
|
||||||
|
|
||||||
Initializes the PCF8574 expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `config` - Pointer to PCF8574 initialization configuration structure. Must not be NULL
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL config or handle)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Expander already initialized
|
|
||||||
- `ESP_ERR_NO_MEM` - Insufficient memory
|
|
||||||
- `ESP_ERR_NOT_SUPPORTED` - I2C initialization error
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_deinit()
|
|
||||||
|
|
||||||
Deinitializes the PCF8574 expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or *handle)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_read()
|
|
||||||
|
|
||||||
Reads the state of all GPIO pins of the expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
- `reg` - Pointer to store GPIO status (bits 0-7). Must not be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or reg)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Expander not initialized
|
|
||||||
|
|
||||||
**Note:** For input GPIOs, the value will always be 1 (HIGH).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_write()
|
|
||||||
|
|
||||||
Sets the state of all output GPIO pins of the expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
- `reg` - GPIO status (bits 0-7)
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Expander not initialized
|
|
||||||
|
|
||||||
**Note:** Only output GPIOs are affected.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_reset()
|
|
||||||
|
|
||||||
Resets all GPIO pins of the expander to the initial state.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or *handle)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_read_gpio()
|
|
||||||
|
|
||||||
Reads the state of one GPIO pin of the expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
- `gpio` - GPIO number (ZH_PCF8574_GPIO_NUM_P0 ... ZH_PCF8574_GPIO_NUM_P7)
|
|
||||||
- `status` - Pointer to store GPIO status (true - HIGH, false - LOW). Must not be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, gpio, status or gpio out of range)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Expander not initialized
|
|
||||||
|
|
||||||
**Note:** For input GPIOs, the value will always be 1 (HIGH).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_write_gpio()
|
|
||||||
|
|
||||||
Sets the state of one output GPIO pin of the expander.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
|
|
||||||
- `gpio` - GPIO number (ZH_PCF8574_GPIO_NUM_P0 ... ZH_PCF8574_GPIO_NUM_P7)
|
|
||||||
- `status` - GPIO status (true - HIGH, false - LOW)
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, gpio, status or gpio out of range)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Expander not initialized
|
|
||||||
|
|
||||||
**Note:** Only output GPIOs are affected.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_get_stats()
|
|
||||||
|
|
||||||
Gets error statistics since last reset.
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- Pointer to the statistics structure
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_reset_stats()
|
|
||||||
|
|
||||||
Resets error statistics counters.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Basic Example: Single Expander
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
|
|
||||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
|
||||||
|
|
||||||
zh_pcf8574_handle_t pcf8574_handle = {0};
|
|
||||||
|
|
||||||
void print_gpio_status(const char *message, uint8_t reg)
|
|
||||||
{
|
|
||||||
printf("%s", message);
|
|
||||||
for (uint8_t i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
printf("%c", (reg & 0x80) ? '1' : '0');
|
|
||||||
reg <<= 1;
|
|
||||||
}
|
|
||||||
printf(".\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
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 = 0x38;
|
|
||||||
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT; // P0 - input
|
|
||||||
zh_pcf8574_init(&config, &pcf8574_handle);
|
|
||||||
uint8_t reg = 0;
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("GPIO status: ", reg);
|
|
||||||
printf("Set P7 to 1, P1 to 1 and P0 to 0.\n");
|
|
||||||
zh_pcf8574_write(&pcf8574_handle, 0b10000010);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("GPIO status: ", reg);
|
|
||||||
printf("Set P0 to 0.\n");
|
|
||||||
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P0, ZH_PCF8574_GPIO_LOW);
|
|
||||||
bool gpio = 0;
|
|
||||||
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P0, &gpio);
|
|
||||||
printf("P0 status: %d.\n", gpio);
|
|
||||||
printf("Set P1 to 0.\n");
|
|
||||||
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P1, ZH_PCF8574_GPIO_LOW);
|
|
||||||
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P1, &gpio);
|
|
||||||
printf("P1 status: %d.\n", gpio);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("GPIO status: ", reg);
|
|
||||||
printf("Reset all GPIO.\n");
|
|
||||||
zh_pcf8574_reset(&pcf8574_handle);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("GPIO status: ", reg);
|
|
||||||
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
|
|
||||||
printf("I2C errors: %ld.\n", stats->i2c_driver_error);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Example with Interrupts
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
|
|
||||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
|
||||||
|
|
||||||
zh_pcf8574_handle_t pcf8574_handle = {0};
|
|
||||||
|
|
||||||
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
|
||||||
{
|
|
||||||
zh_pcf8574_event_on_isr_t *event = event_data;
|
|
||||||
printf("Interrupt on device 0x%02X, GPIO %d, level %d, time %lld.\n",
|
|
||||||
event->i2c_address, event->gpio_number, event->gpio_level, event->interrupt_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
esp_event_loop_create_default();
|
|
||||||
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL);
|
|
||||||
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
|
||||||
config.i2c_handle = i2c_bus_handle;
|
|
||||||
config.i2c_address = 0x38;
|
|
||||||
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT; // P0 - input for interrupts
|
|
||||||
config.interrupt_gpio = GPIO_NUM_14; // Connect INT to GPIO 14
|
|
||||||
zh_pcf8574_init(&config, &pcf8574_handle);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
|
|
||||||
printf("I2C errors: %ld.\n", stats->i2c_driver_error);
|
|
||||||
printf("Event errors: %ld.\n", stats->event_post_error);
|
|
||||||
printf("Interrupt queue: %ld.\n", stats->queue_overflow_error);
|
|
||||||
printf("Minimum free stack: %ld.\n", stats->min_stack_size);
|
|
||||||
vTaskDelay(60000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Specifications
|
|
||||||
|
|
||||||
| Parameter | Value |
|
|
||||||
| ----------- | ------- |
|
|
||||||
| **Device Type** | 8-bit I2C GPIO expander |
|
|
||||||
| **Models** | PCF8574, PCF8574A |
|
|
||||||
| **Number of devices on bus** | Up to 16 (addresses 0x20-0x27, 0x38-0x3F) |
|
|
||||||
| **Number of GPIO** | 8 (P0-P7) |
|
|
||||||
| **I2C Address** | 0x20-0x27 (PCF8574), 0x38-0x3F (PCF8574A) |
|
|
||||||
| **I2C Frequency** | Up to 100 kHz |
|
|
||||||
| **GPIO Modes** | Input/Output (configurable per pin) |
|
|
||||||
| **Interrupts** | Yes (negative edge) |
|
|
||||||
| **ESP-IDF Version** | >= 5.0 |
|
|
||||||
| **Platform** | ESP32 series |
|
|
||||||
| **Language** | C (C99) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Codes
|
|
||||||
|
|
||||||
| Error Code | Description |
|
|
||||||
| ------------ | ------------- |
|
|
||||||
| `ESP_OK` | Operation successful |
|
|
||||||
| `ESP_ERR_INVALID_ARG` | Invalid argument (NULL pointer or invalid configuration) |
|
|
||||||
| `ESP_ERR_INVALID_STATE` | Device not initialized or already initialized |
|
|
||||||
| `ESP_ERR_NOT_FOUND` | Device not initialized |
|
|
||||||
| `ESP_ERR_NO_MEM` | Insufficient memory |
|
|
||||||
| `ESP_FAIL` | General failure (I2C communication error, initialization error, etc.) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome! To contribute:
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
||||||
5. Open a Pull Request
|
|
||||||
|
|
||||||
Please ensure your code follows the existing style and includes appropriate documentation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the Apache License, Version 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
||||||
|
|
||||||
### Apache License, Version 2.0
|
|
||||||
|
|
||||||
Copyright (c) 2026 Alexey Zholtikov
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Additional Notes
|
|
||||||
|
|
||||||
- **I2C Pull-up Resistors**: Ensure pull-up resistors are connected to SDA and SCL lines (typically 4.7kΩ)
|
|
||||||
- **Input GPIOs**: All input GPIOs are internally pulled up to VCC (~100kΩ resistor)
|
|
||||||
- **Interrupts**: All INT outputs of expanders must be connected to one GPIO on ESP32
|
|
||||||
- **I2C_ISR_IRAM_SAFE**: For correct operation, enable `I2C_ISR_IRAM_SAFE` and `I2C_MASTER_ISR_HANDLER_IN_IRAM` in menuconfig
|
|
||||||
- **GPIO_CTRL_FUNC_IN_IRAM**: For GPIO interrupts, enable `GPIO_CTRL_FUNC_IN_IRAM` in menuconfig
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
"*Generated for zh_pcf8574 v3.0.0*"
|
|
||||||
-496
@@ -1,496 +0,0 @@
|
|||||||
# zh_pcf8574 - Компонент 8-битного расширителя GPIO PCF8574(A) для ESP-IDF
|
|
||||||
|
|
||||||
## Содержание
|
|
||||||
|
|
||||||
- [Обзор](#обзор)
|
|
||||||
- [Возможности](#возможности)
|
|
||||||
- [Установка](#установка)
|
|
||||||
- [Справочник API](#справочник-api)
|
|
||||||
- [Примеры использования](#примеры-использования)
|
|
||||||
- [Технические характеристики](#технические-характеристики)
|
|
||||||
- [Коды ошибок](#коды-ошибок)
|
|
||||||
- [Вклад в проект](#вклад-в-проект)
|
|
||||||
- [Лицензия](#лицензия)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Обзор
|
|
||||||
|
|
||||||
`zh_pcf8574` - это компонент ESP-IDF для 8-битных расширителей GPIO PCF8574 и PCF8574A. Он предоставляет удобный API для работы с расширителем GPIO через шину I2C. Компонент поддерживает до 16 расширителей на одной шине I2C и может генерировать прерывания при изменении состояния входных GPIO.
|
|
||||||
|
|
||||||
Компонент разработан специально для микроконтроллеров ESP32 и использует API драйвера I2C ESP-IDF v5.0+.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
- **Поддержка 16 расширителей**: До 16 PCF8574(A) на одной шине I2C (адреса 0x20-0x27 и 0x38-0x3F)
|
|
||||||
- **Режимы работы GPIO**: Поддержка выходных и входных режимов для каждого из 8 GPIO
|
|
||||||
- **Генерация прерываний**: Поддержка прерываний от входных GPIO (отрицательный фронт)
|
|
||||||
- **Статистика ошибок**: Встроенные счетчики ошибок I2C, событий, векторов и переполнения очереди
|
|
||||||
- **Потокобезопасность**: Использует драйвер I2C ESP-IDF и FreeRTOS
|
|
||||||
- **Минимальные накладные расходы**: Низкие требования к памяти и процессору
|
|
||||||
- **Простой API**: Легкое чтение/запись состояния всех GPIO и отдельных пинов
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
Перейдите в каталог компонентов вашего проекта:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../ваш_проект/components
|
|
||||||
```
|
|
||||||
|
|
||||||
Клонируйте репозиторий:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/aZholtikov/zh_pcf8574.git
|
|
||||||
```
|
|
||||||
|
|
||||||
В вашем приложении подключите заголовочный файл:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
Компонент будет автоматически собран вместе с вашим проектом.
|
|
||||||
|
|
||||||
### Обязательные настройки в menuconfig
|
|
||||||
|
|
||||||
Для корректной работы компонента включите следующие настройки в menuconfig:
|
|
||||||
|
|
||||||
```text
|
|
||||||
GPIO_CTRL_FUNC_IN_IRAM
|
|
||||||
I2C_ISR_IRAM_SAFE
|
|
||||||
I2C_MASTER_ISR_HANDLER_IN_IRAM
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Справочник API
|
|
||||||
|
|
||||||
### Макросы
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define ZH_PCF8574_GPIO_OUTPUT false
|
|
||||||
#define ZH_PCF8574_GPIO_INPUT true
|
|
||||||
#define ZH_PCF8574_GPIO_LOW false
|
|
||||||
#define ZH_PCF8574_GPIO_HIGH true
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_pcf8574_init_config_t
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
i2c_master_bus_handle_t i2c_handle; // Уникальный дескриптор шины I2C
|
|
||||||
uint32_t i2c_frequency; // Частота I2C расширителя (макс. 100000 Гц)
|
|
||||||
uint32_t stack_size; // Размер стека задачи обработки прерываний
|
|
||||||
uint8_t task_priority; // Приоритет задачи обработки прерываний
|
|
||||||
uint8_t i2c_address; // Адрес устройства расширителя I2C (0x20-0x27 или 0x38-0x3F)
|
|
||||||
bool p0_gpio_work_mode; // Режим работы GPIO P0 (false - output, true - input)
|
|
||||||
bool p1_gpio_work_mode; // Режим работы GPIO P1
|
|
||||||
bool p2_gpio_work_mode; // Режим работы GPIO P2
|
|
||||||
bool p3_gpio_work_mode; // Режим работы GPIO P3
|
|
||||||
bool p4_gpio_work_mode; // Режим работы GPIO P4
|
|
||||||
bool p5_gpio_work_mode; // Режим работы GPIO P5
|
|
||||||
bool p6_gpio_work_mode; // Режим работы GPIO P6
|
|
||||||
bool p7_gpio_work_mode; // Режим работы GPIO P7
|
|
||||||
gpio_num_t interrupt_gpio; // GPIO для прерываний (GPIO_NUM_MAX - отключить)
|
|
||||||
} zh_pcf8574_init_config_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
Используйте макрос `ZH_PCF8574_INIT_CONFIG_DEFAULT()` для инициализации значениями по умолчанию:
|
|
||||||
|
|
||||||
- `i2c_frequency`: 100000 Гц
|
|
||||||
- `i2c_address`: 0xFF
|
|
||||||
- `p0_gpio_work_mode` ... `p7_gpio_work_mode`: `ZH_PCF8574_GPIO_OUTPUT`
|
|
||||||
- `interrupt_gpio`: `GPIO_NUM_MAX`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_pcf8574_handle_t
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct _zh_pcf8574_handle_t zh_pcf8574_handle_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
Непрозрачный дескриптор экземпляра устройства PCF8574. Внутренняя структура минимальна — хранит только I2C-адрес; полное состояние устройства поддерживается во внутреннем векторе.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_pcf8574_stats_t
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t i2c_driver_error; // Количество ошибок драйвера I2C
|
|
||||||
uint32_t event_post_error; // Количество ошибок отправки событий
|
|
||||||
uint32_t vector_error; // Количество ошибок вектора
|
|
||||||
uint32_t queue_overflow_error; // Количество переполнений очереди
|
|
||||||
uint32_t min_stack_size; // Минимальный свободный стек задачи
|
|
||||||
} zh_pcf8574_stats_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_pcf8574_event_on_isr_t
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint64_t interrupt_time; // Время прерывания (мкс)
|
|
||||||
zh_pcf8574_gpio_num_t gpio_number; // Номер GPIO, вызвавшего прерывание
|
|
||||||
uint8_t i2c_address; // I2C адрес расширителя
|
|
||||||
bool gpio_level; // Уровень GPIO при прерывании
|
|
||||||
} zh_pcf8574_event_on_isr_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_init()
|
|
||||||
|
|
||||||
Инициализирует расширитель PCF8574.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `config` - Указатель на структуру конфигурации инициализации PCF8574. Не должен быть NULL
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL config или handle)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Расширитель уже инициализирован
|
|
||||||
- `ESP_ERR_NO_MEM` - Недостаточно памяти
|
|
||||||
- `ESP_ERR_NOT_SUPPORTED` - Ошибка инициализации I2C
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_deinit()
|
|
||||||
|
|
||||||
Деинициализирует расширитель PCF8574.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или *handle)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_read()
|
|
||||||
|
|
||||||
Считывает состояние всех GPIO расширителя.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
- `reg` - Указатель для хранения состояния GPIO (биты 0-7). Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или reg)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
|
|
||||||
|
|
||||||
**Примечание:** Для входных GPIO всегда будет 1 (HIGH).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_write()
|
|
||||||
|
|
||||||
Устанавливает состояние всех выходных GPIO расширителя.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
- `reg` - Состояние GPIO (биты 0-7)
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
|
|
||||||
|
|
||||||
**Примечание:** Влияет только на выходные GPIO.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_reset()
|
|
||||||
|
|
||||||
Сбрасывает все GPIO расширителя в начальное состояние.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или *handle)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_read_gpio()
|
|
||||||
|
|
||||||
Считывает состояние одного GPIO расширителя.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
- `gpio` - Номер GPIO (ZH_PCF8574_GPIO_NUM_P0 ... ZH_PCF8574_GPIO_NUM_P7)
|
|
||||||
- `status` - Указатель для хранения состояния GPIO (true - HIGH, false - LOW). Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, gpio, status или выход gpio за пределы)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
|
|
||||||
|
|
||||||
**Примечание:** Для входных GPIO всегда будет 1 (HIGH).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_write_gpio()
|
|
||||||
|
|
||||||
Устанавливает состояние одного выходного GPIO расширителя.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
|
|
||||||
- `gpio` - Номер GPIO (ZH_PCF8574_GPIO_NUM_P0 ... ZH_PCF8574_GPIO_NUM_P7)
|
|
||||||
- `status` - Состояние GPIO (true - HIGH, false - LOW)
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, gpio, status или выход gpio за пределы)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
|
|
||||||
|
|
||||||
**Примечание:** Влияет только на выходные GPIO.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_get_stats()
|
|
||||||
|
|
||||||
Получает статистику ошибок с момента последнего сброса.
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- Указатель на структуру статистики
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_pcf8574_reset_stats()
|
|
||||||
|
|
||||||
Сбрасывает счетчики статистики ошибок.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Примеры использования
|
|
||||||
|
|
||||||
### Базовый пример: Один расширитель
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
|
|
||||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
|
||||||
|
|
||||||
zh_pcf8574_handle_t pcf8574_handle = {0};
|
|
||||||
|
|
||||||
void print_gpio_status(const char *message, uint8_t reg)
|
|
||||||
{
|
|
||||||
printf("%s", message);
|
|
||||||
for (uint8_t i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
printf("%c", (reg & 0x80) ? '1' : '0');
|
|
||||||
reg <<= 1;
|
|
||||||
}
|
|
||||||
printf(".\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
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 = 0x38;
|
|
||||||
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT; // P0 - вход
|
|
||||||
zh_pcf8574_init(&config, &pcf8574_handle);
|
|
||||||
uint8_t reg = 0;
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("Статус GPIO: ", reg);
|
|
||||||
printf("Установить P7 в 1, P1 в 1 и P0 в 0.\n");
|
|
||||||
zh_pcf8574_write(&pcf8574_handle, 0b10000010);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("Статус GPIO: ", reg);
|
|
||||||
printf("Установить P0 в 0.\n");
|
|
||||||
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P0, ZH_PCF8574_GPIO_LOW);
|
|
||||||
bool gpio = 0;
|
|
||||||
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P0, &gpio);
|
|
||||||
printf("Статус P0: %d.\n", gpio);
|
|
||||||
printf("Установить P1 в 0.\n");
|
|
||||||
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P1, ZH_PCF8574_GPIO_LOW);
|
|
||||||
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P1, &gpio);
|
|
||||||
printf("Статус P1: %d.\n", gpio);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("Статус GPIO: ", reg);
|
|
||||||
printf("Сброс всех GPIO.\n");
|
|
||||||
zh_pcf8574_reset(&pcf8574_handle);
|
|
||||||
zh_pcf8574_read(&pcf8574_handle, ®);
|
|
||||||
print_gpio_status("Статус GPIO: ", reg);
|
|
||||||
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
|
|
||||||
printf("Ошибки I2C: %ld.\n", stats->i2c_driver_error);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Пример с прерываниями
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pcf8574.h"
|
|
||||||
|
|
||||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
|
||||||
|
|
||||||
zh_pcf8574_handle_t pcf8574_handle = {0};
|
|
||||||
|
|
||||||
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
|
||||||
{
|
|
||||||
zh_pcf8574_event_on_isr_t *event = event_data;
|
|
||||||
printf("Прерывание на устройстве 0x%02X, GPIO %d, уровень %d, время %lld.\n",
|
|
||||||
event->i2c_address, event->gpio_number, event->gpio_level, event->interrupt_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
esp_event_loop_create_default();
|
|
||||||
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL);
|
|
||||||
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
|
||||||
config.i2c_handle = i2c_bus_handle;
|
|
||||||
config.i2c_address = 0x38;
|
|
||||||
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT; // P0 - вход для прерываний
|
|
||||||
config.interrupt_gpio = GPIO_NUM_14; // Подключение INT к GPIO 14
|
|
||||||
zh_pcf8574_init(&config, &pcf8574_handle);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
|
|
||||||
printf("Ошибки I2C: %ld.\n", stats->i2c_driver_error);
|
|
||||||
printf("Ошибки событий: %ld.\n", stats->event_post_error);
|
|
||||||
printf("Очередь прерываний: %ld.\n", stats->queue_overflow_error);
|
|
||||||
printf("Минимальный свободный стек: %ld.\n", stats->min_stack_size);
|
|
||||||
vTaskDelay(60000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Технические характеристики
|
|
||||||
|
|
||||||
| Параметр | Значение |
|
|
||||||
| ---------- | ---------- |
|
|
||||||
| **Тип устройства** | 8-битный I2C GPIO расширитель |
|
|
||||||
| **Модели** | PCF8574, PCF8574A |
|
|
||||||
| **Количество устройств на шине** | До 16 (адреса 0x20-0x27, 0x38-0x3F) |
|
|
||||||
| **Количество GPIO** | 8 (P0-P7) |
|
|
||||||
| **Адрес I2C** | 0x20-0x27 (PCF8574), 0x38-0x3F (PCF8574A) |
|
|
||||||
| **Частота I2C** | До 100 кГц |
|
|
||||||
| **Режимы GPIO** | Вход/выход (настраиваемые для каждого пина) |
|
|
||||||
| **Прерывания** | Да (отрицательный фронт) |
|
|
||||||
| **Версия ESP-IDF** | >= 5.0 |
|
|
||||||
| **Платформа** | Семейство ESP32 |
|
|
||||||
| **Язык** | C (C99) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Коды ошибок
|
|
||||||
|
|
||||||
| Код ошибки | Описание |
|
|
||||||
| ------------ | ---------- |
|
|
||||||
| `ESP_OK` | Операция выполнена успешно |
|
|
||||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или неверная конфигурация) |
|
|
||||||
| `ESP_ERR_INVALID_STATE` | Устройство не инициализировано или уже инициализировано |
|
|
||||||
| `ESP_ERR_NOT_FOUND` | Устройство не инициализировано |
|
|
||||||
| `ESP_ERR_NO_MEM` | Недостаточно памяти |
|
|
||||||
| `ESP_FAIL` | Общая ошибка (ошибка связи I2C, инициализации и т.д.) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Вклад в проект
|
|
||||||
|
|
||||||
Вклад приветствуется! Чтобы внести свой вклад:
|
|
||||||
|
|
||||||
1. Сделайте форк репозитория
|
|
||||||
2. Создайте ветку функции (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. Закоммитьте ваши изменения (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. Отправьте в ветку (`git push origin feature/AmazingFeature`)
|
|
||||||
5. Откройте Pull Request
|
|
||||||
|
|
||||||
Пожалуйста, убедитесь, что ваш код следует существующему стилю и включает соответствующую документацию.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
Этот проект лицензирован по лицензии Apache, версия 2.0 - см. файл [LICENSE](LICENSE) для подробной информации.
|
|
||||||
|
|
||||||
### Apache License, Version 2.0
|
|
||||||
|
|
||||||
Авторское право (c) 2026 Алексей Жолтиков
|
|
||||||
|
|
||||||
Лицензировано по лицензии Apache License, Version 2.0 (далее — "Лицензия");
|
|
||||||
вы не можете использовать этот файл, кроме случаев, предусмотренных Лицензией.
|
|
||||||
Копию Лицензии можно получить по адресу:
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Если иное не требуется действующим законодательством или не согласовано в письменном виде,
|
|
||||||
программное обеспечение, распространяемое по Лицензии, распространяется на условиях "КАК ЕСТЬ",
|
|
||||||
БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, явных или подразумеваемых, включая, но не ограничиваясь, гарантии
|
|
||||||
ТОВАРНОГО СОСТОЯНИЯ, ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И НЕНАРУШЕНИЯ ПРАВ.
|
|
||||||
Смотрите Лицензию для получения конкретных прав и ограничений.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Дополнительные заметки
|
|
||||||
|
|
||||||
- **Подтягивающие резисторы I2C**: Убедитесь, что к линиям SDA и SCL подключены подтягивающие резисторы (обычно 4.7кОм)
|
|
||||||
- **Входные GPIO**: Все входные GPIO всегда подтянуты к питанию (внутренний подтягивающий резистор ~100кОм)
|
|
||||||
- **Прерывания**: Все выводы INT расширителей должны быть подключены к одному GPIO на ESP32
|
|
||||||
- **I2C_ISR_IRAM_SAFE**: Для правильной работы включите `I2C_ISR_IRAM_SAFE` и `I2C_MASTER_ISR_HANDLER_IN_IRAM` в menuconfig
|
|
||||||
- **GPIO_CTRL_FUNC_IN_IRAM**: Для работы прерываний GPIO включите `GPIO_CTRL_FUNC_IN_IRAM` в menuconfig
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
"*Сгенерировано для zh_pcf8574 v3.0.0*"
|
|
||||||
+42
-28
@@ -16,7 +16,8 @@
|
|||||||
*
|
*
|
||||||
* @note The driver creates an internal task for interrupt processing.
|
* @note The driver creates an internal task for interrupt processing.
|
||||||
* @note Requires ESP-IDF v5.0+ with I2C master driver.
|
* @note Requires ESP-IDF v5.0+ with I2C master driver.
|
||||||
* @note Enable GPIO_CTRL_FUNC_IN_IRAM, I2C_ISR_IRAM_SAFE and I2C_MASTER_ISR_HANDLER_IN_IRAM in menuconfig.
|
* @warning All I2C and GPIO operations run in thread context with ESP_INTR_FLAG_LOWMED, so the
|
||||||
|
* interrupt service is not IRAM-safe and does not execute from IRAM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
{ \
|
{ \
|
||||||
.task_priority = 1, \
|
.task_priority = 1, \
|
||||||
.stack_size = configMINIMAL_STACK_SIZE, \
|
.stack_size = configMINIMAL_STACK_SIZE, \
|
||||||
.i2c_address = 0xFF, \
|
.i2c_address = 0x20, \
|
||||||
.i2c_frequency = 100000, \
|
.i2c_frequency = 100000, \
|
||||||
.p0_gpio_work_mode = ZH_PCF8574_GPIO_OUTPUT, \
|
.p0_gpio_work_mode = ZH_PCF8574_GPIO_OUTPUT, \
|
||||||
.p1_gpio_work_mode = ZH_PCF8574_GPIO_OUTPUT, \
|
.p1_gpio_work_mode = ZH_PCF8574_GPIO_OUTPUT, \
|
||||||
@@ -56,7 +57,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern TaskHandle_t zh_pcf8574; /*!< Handle of the internal PCF8574 processing task. */
|
extern TaskHandle_t zh_pcf8574; /*!< Handle of the internal PCF8574 processing task */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Opaque handle for PCF8574 device instance.
|
* @brief Opaque handle for PCF8574 device instance.
|
||||||
@@ -125,7 +126,7 @@ extern "C"
|
|||||||
uint32_t event_post_error; /*!< Counter of event posting errors */
|
uint32_t event_post_error; /*!< Counter of event posting errors */
|
||||||
uint32_t vector_error; /*!< Counter of vector operation errors */
|
uint32_t vector_error; /*!< Counter of vector operation errors */
|
||||||
uint32_t queue_overflow_error; /*!< Counter of queue overflow errors */
|
uint32_t queue_overflow_error; /*!< Counter of queue overflow errors */
|
||||||
uint32_t min_stack_size; /*!< Minimum remaining stack size recorded */
|
uint32_t min_stack_size; /*!< Minimum remaining stack size in words since last reset */
|
||||||
} zh_pcf8574_stats_t;
|
} zh_pcf8574_stats_t;
|
||||||
|
|
||||||
ESP_EVENT_DECLARE_BASE(ZH_PCF8574);
|
ESP_EVENT_DECLARE_BASE(ZH_PCF8574);
|
||||||
@@ -152,65 +153,76 @@ extern "C"
|
|||||||
* provided configuration.
|
* provided configuration.
|
||||||
*
|
*
|
||||||
* @param[in] config Pointer to initialization configuration structure (must not be NULL)
|
* @param[in] config Pointer to initialization configuration structure (must not be NULL)
|
||||||
* @param[out] handle Pointer to receive the device handle (must be NULL)
|
* @param[in,out] handle Pointer to the device handle (must not be NULL, the handle it points to must be NULL)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if config parameter is NULL
|
* @return ESP_ERR_INVALID_ARG if config is NULL or handle is NULL
|
||||||
|
* @return ESP_ERR_INVALID_STATE if the handle pointed to by handle is not NULL
|
||||||
* @return ESP_ERR_NO_MEM if memory allocation fails
|
* @return ESP_ERR_NO_MEM if memory allocation fails
|
||||||
* @return ESP_ERR_NOT_SUPPORTED if I2C initialization fails
|
* @return ESP_FAIL if configuration validation, I2C communication, or task creation fails
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t **handle);
|
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t **handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deinitialize PCF8574 device.
|
* @brief Deinitialize PCF8574 device.
|
||||||
*
|
*
|
||||||
* Deletes the internal task and releases all allocated resources.
|
* Releases I2C resources and the device handle. Deletes the internal
|
||||||
|
* task only when the last device instance is deinitialized.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle parameter is NULL
|
* @return ESP_ERR_INVALID_ARG if handle is NULL or the handle it points to is NULL
|
||||||
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if I2C removal or vector operations fail
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle);
|
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read data from PCF8574 device.
|
* @brief Read data from PCF8574 device.
|
||||||
*
|
*
|
||||||
* Reads a byte from the PCF8574 input ports via I2C communication.
|
* Reads a byte from the PCF8574 input ports via I2C communication
|
||||||
|
* and updates the internal device state.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
* @param[out] reg Pointer to receive the read value (must not be NULL)
|
* @param[out] reg Pointer to receive the read value (must not be NULL)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle or reg is NULL
|
* @return ESP_ERR_INVALID_ARG if handle or reg is NULL or the handle it points to is NULL
|
||||||
* @return ESP_ERR_INVALID_STATE if device is not initialized
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if I2C communication or vector operations fail
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t **handle, uint8_t *reg);
|
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t **handle, uint8_t *reg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write data to PCF8574 device.
|
* @brief Write data to PCF8574 device.
|
||||||
*
|
*
|
||||||
* Writes a byte to the PCF8574 output ports via I2C communication.
|
* Writes a byte to the PCF8574 output ports via I2C communication
|
||||||
|
* and updates the internal device state.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
* @param[in] reg Value to write to the output ports
|
* @param[in] reg Value to write to the output ports
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle is NULL
|
* @return ESP_ERR_INVALID_ARG if handle is NULL or the handle it points to is NULL
|
||||||
* @return ESP_ERR_INVALID_STATE if device is not initialized
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if I2C communication or vector operations fail
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t **handle, uint8_t reg);
|
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t **handle, uint8_t reg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset PCF8574 device.
|
* @brief Reset PCF8574 device.
|
||||||
*
|
*
|
||||||
* Resets the device to its default state, clearing all outputs
|
* Writes the configured GPIO direction mask to the port, returning
|
||||||
* and reinitializing internal structures.
|
* output pins to their low level and input pins to their high level,
|
||||||
|
* and updates the internal device state.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle is NULL
|
* @return ESP_ERR_INVALID_ARG if handle is NULL or the handle it points to is NULL
|
||||||
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if I2C communication or vector operations fail
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle);
|
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle);
|
||||||
|
|
||||||
@@ -219,13 +231,14 @@ extern "C"
|
|||||||
*
|
*
|
||||||
* Reads the current logical level of a specified GPIO pin.
|
* Reads the current logical level of a specified GPIO pin.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
* @param[in] gpio GPIO pin number to read
|
* @param[in] gpio GPIO pin number to read
|
||||||
* @param[out] status Pointer to receive the pin state (true = high, false = low)
|
* @param[out] status Pointer to receive the pin state (true = high, false = low)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle, gpio, or status is NULL
|
* @return ESP_ERR_INVALID_ARG if handle or status is NULL or the handle it points to is NULL
|
||||||
* @return ESP_ERR_INVALID_ARG if gpio number exceeds ZH_PCF8574_GPIO_NUM_MAX
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if gpio is invalid or I2C communication fails
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool *status);
|
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool *status);
|
||||||
|
|
||||||
@@ -234,13 +247,14 @@ extern "C"
|
|||||||
*
|
*
|
||||||
* Sets the logical level of a specified GPIO pin configured as output.
|
* Sets the logical level of a specified GPIO pin configured as output.
|
||||||
*
|
*
|
||||||
* @param[in,out] handle Pointer to the device handle (must not be NULL)
|
* @param[in] handle Pointer to the device handle (must not be NULL, the handle it points to must not be NULL)
|
||||||
* @param[in] gpio GPIO pin number to write
|
* @param[in] gpio GPIO pin number to write
|
||||||
* @param[in] status Logical level to set (true = high, false = low)
|
* @param[in] status Logical level to set (true = high, false = low)
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle, gpio, or status is NULL
|
* @return ESP_ERR_INVALID_ARG if handle is NULL or the handle it points to is NULL
|
||||||
* @return ESP_ERR_INVALID_ARG if gpio number exceeds ZH_PCF8574_GPIO_NUM_MAX
|
* @return ESP_ERR_NOT_FOUND if handle does not correspond to a valid device instance
|
||||||
|
* @return ESP_FAIL if gpio is invalid or I2C communication fails
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool status);
|
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool status);
|
||||||
|
|
||||||
|
|||||||
+301
@@ -0,0 +1,301 @@
|
|||||||
|
# Описание компонента zh_pcf8574
|
||||||
|
|
||||||
|
## Общие сведения
|
||||||
|
|
||||||
|
**zh_pcf8574** — это драйвер для ESP-IDF, реализующий работу с 8-битным I2C GPIO-экспандером PCF8574.
|
||||||
|
|
||||||
|
Компонент поддерживает чтение и запись состояний GPIO-пинов, обработку прерываний с определением уровня сигнала, а также предоставляет статистику для отладки и мониторинга. Драйвер создаёт внутренний FreeRTOS-задачу для обработки прерываний от всех подключённых устройств PCF8574 и использует событийную модель на базе `esp_event`.
|
||||||
|
|
||||||
|
## Основные возможности
|
||||||
|
|
||||||
|
1. **I2C-коммуникация** — работа через новый I2C master driver из ESP-IDF v5.
|
||||||
|
2. **Конфигурация направления GPIO** — каждая пин (P0–P7) может быть настроена как вход или выход.
|
||||||
|
3. **Обработка прерываний** — обнаружение изменения уровня на входных пинах с временной меткой.
|
||||||
|
4. **Событийная архитектура** — интеграция с `esp_event` для асинхронной обработки событий.
|
||||||
|
5. **Статистика ошибок** — подсчёт ошибок I2C, очереди, вектора и мониторинг стека задачи.
|
||||||
|
6. **Поддержка нескольких устройств** — работа с несколькими PCF8574 на одной I2C-шине с разными адресами.
|
||||||
|
|
||||||
|
## Внутреннее устройство
|
||||||
|
|
||||||
|
### Структура `zh_pcf8574_handle_t`
|
||||||
|
|
||||||
|
Публичная непрозрачная структура-хендл, используемая всеми API-функциями для идентификации экземпляра устройства.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `i2c_address` | `uint8_t` | 7-битный I2C-адрес данного экземпляра устройства |
|
||||||
|
|
||||||
|
### Структура `_zh_pcf8574_vector_data_t`
|
||||||
|
|
||||||
|
Внутренняя структура данных, хранимая в векторе `zh_vector` для каждого подключённого устройства.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `i2c_address` | `uint8_t` | I2C-адрес устройства |
|
||||||
|
| `gpio_work_mode` | `uint8_t` | Маска направлений GPIO (1 = вход, 0 = выход) |
|
||||||
|
| `gpio_status` | `uint8_t` | Текущее состояние GPIO-портов |
|
||||||
|
| `dev_handle` | `i2c_master_dev_handle_t` | I2C-хендл устройства для коммуникации |
|
||||||
|
|
||||||
|
### Управление памятью
|
||||||
|
|
||||||
|
- Хендл устройства (`zh_pcf8574_handle_t`) выделяется через `heap_caps_calloc()` при инициализации и освобождается через `heap_caps_free()` при деинициализации.
|
||||||
|
- Состояние всех устройств хранится в глобальном векторе `_vector` типа `zh_vector_t`, реализованном в компоненте `zh_vector`.
|
||||||
|
- Вектор инициализируется при первом вызове `zh_pcf8574_init()` и освобождается при удалении последнего устройства в `zh_pcf8574_deinit()`.
|
||||||
|
- При удалении устройства данные удаляются из вектора через `zh_vector_delete_item()`.
|
||||||
|
|
||||||
|
### Глобальные ресурсы
|
||||||
|
|
||||||
|
- `_vector` — глобальный вектор для хранения данных всех подключённых устройств.
|
||||||
|
- `_interrupt_semaphore` — бинарный семафор FreeRTOS для синхронизации ISR и задачи обработки прерываний.
|
||||||
|
- `_interrupt_gpio` — номер GPIO, используемого для прерываний (GPIO_NUM_MAX, если не используется).
|
||||||
|
- `_gpio_matrix` — статический массив `{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}` для побитовой маскировки пинов.
|
||||||
|
- `_stats` — глобальная структура статистики ошибок.
|
||||||
|
- `zh_pcf8574` — глобальный хендл FreeRTOS-задачи обработки прерываний.
|
||||||
|
|
||||||
|
## API (интерфейс)
|
||||||
|
|
||||||
|
### Инициализация и освобождение
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_pcf8574_init()` | Инициализирует устройство PCF8574: создаёт I2C-соединение, настраивает GPIO и при необходимости создаёт задачу обработки прерываний. |
|
||||||
|
| `zh_pcf8574_deinit()` | Освобождает I2C-ресурсы и хендл устройства. Удаляет задачу обработки прерываний только при удалении последнего экземпляра. |
|
||||||
|
|
||||||
|
### Чтение и запись
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_pcf8574_read()` | Считывает байт с входных портов PCF8574 через I2C и обновляет внутреннее состояние устройства. |
|
||||||
|
| `zh_pcf8574_write()` | Записывает байт в выходные порты PCF8574 через I2C и обновляет внутреннее состояние устройства. |
|
||||||
|
| `zh_pcf8574_reset()` | Сбрасывает все выходные пины в низкий уровень, а входные — в высокий, записывая маску направлений в порт. |
|
||||||
|
|
||||||
|
### Доступ к отдельным пинам
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_pcf8574_read_gpio()` | Считывает текущий логический уровень указанного GPIO-пина. |
|
||||||
|
| `zh_pcf8574_write_gpio()` | Устанавливает логический уровень указанного GPIO-пина, настроенного как выход. |
|
||||||
|
|
||||||
|
### Статистика
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_pcf8574_get_stats()` | Возвращает указатель на структуру со счётчиками ошибок и данными мониторинга. |
|
||||||
|
| `zh_pcf8574_reset_stats()` | Сбрасывает все счётчики ошибок и данные мониторинга. |
|
||||||
|
|
||||||
|
## Потокобезопасность
|
||||||
|
|
||||||
|
1. **Семафор FreeRTOS** — бинарный семафор `_interrupt_semaphore` защищает синхронизацию между ISR (вызываемой по прерыванию) и задачей `_zh_pcf8574_isr_processing_task`. ISR отправляет сигнал через `xSemaphoreGiveFromISR()`, задача блокируется на `xSemaphoreTake()`.
|
||||||
|
2. **Глобальный вектор** — доступ к вектору `_vector` не защищён мьютексом. Все операции с вектором выполняются либо в контексте API-функций (синхронных), либо в контексте задачи обработки прерываний. Одновременный доступ к вектору из разных задач не предусмотрен.
|
||||||
|
3. **Глобальная статистика** — счётчики в `_stats` обновляются без блокировок. Чтение и запись происходят атомарно на уровне `uint32_t`, что безопасно для ESP32.
|
||||||
|
|
||||||
|
## Алгоритмы обработки прерываний
|
||||||
|
|
||||||
|
### Задача обработки прерываний
|
||||||
|
|
||||||
|
1. Задача блокируется на семафоре `xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY)`.
|
||||||
|
2. При получении сигнала задача считывает размер вектора устройств.
|
||||||
|
3. Для каждого устройства в векторе:
|
||||||
|
- Считываются старые данные из вектора.
|
||||||
|
- Выполняется чтение регистра через I2C (`_zh_pcf8574_read_register()`).
|
||||||
|
- Данные обновляются в векторе через `zh_vector_change_item()`.
|
||||||
|
4. Для каждого пина, настроенного как вход:
|
||||||
|
- Сравниваются биты старого и нового значения регистра.
|
||||||
|
- При обнаружении различий формируется событие `zh_pcf8574_event_on_isr_t` с временной меткой, адресом устройства, номером пина и текущим уровнем.
|
||||||
|
- Событие отправляется через `esp_event_post()` с таймаутом 10 мс.
|
||||||
|
5. После обработки всех устройств обновляется статистика минимального размера стека задачи (`uxTaskGetStackHighWaterMark()`).
|
||||||
|
|
||||||
|
### Запись в регистр
|
||||||
|
|
||||||
|
При записи в регистр (`zh_pcf8574_write()` и `zh_pcf8574_write_gpio()`) значение маскируется с маской направлений (`gpio_work_mode`), чтобы входные пины не были перезаписаны нулём. Это гарантирует, что только выходные пины изменяют состояние.
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Базовый пример: инициализация и чтение/запись
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// Настройка уровня логирования
|
||||||
|
esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR);
|
||||||
|
|
||||||
|
// Настройка I2C-шины
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Инициализация конфигурации PCF8574
|
||||||
|
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
config.i2c_handle = i2c_bus_handle;
|
||||||
|
config.i2c_address = 0x38;
|
||||||
|
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT;
|
||||||
|
config.p1_gpio_work_mode = ZH_PCF8574_GPIO_OUTPUT;
|
||||||
|
|
||||||
|
// Инициализация устройства
|
||||||
|
esp_err_t err = zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE("APP", "Failed to initialize PCF8574: %s", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Чтение текущего состояния портов
|
||||||
|
uint8_t reg = 0;
|
||||||
|
zh_pcf8574_read(&pcf8574_handle, ®);
|
||||||
|
|
||||||
|
// Запись значения в порты (P7=1, P1=1, P0=0)
|
||||||
|
zh_pcf8574_write(&pcf8574_handle, 0b10000010);
|
||||||
|
|
||||||
|
// Деинициализация устройства
|
||||||
|
zh_pcf8574_deinit(&pcf8574_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Работа с отдельными пинами
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// ... инициализация I2C и PCF8574 ...
|
||||||
|
|
||||||
|
bool gpio_status = false;
|
||||||
|
|
||||||
|
// Чтение состояния пина P0
|
||||||
|
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P0, &gpio_status);
|
||||||
|
printf("P0 status: %d\n", gpio_status);
|
||||||
|
|
||||||
|
// Установка пина P7 в высокий уровень
|
||||||
|
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P7, ZH_PCF8574_GPIO_HIGH);
|
||||||
|
|
||||||
|
// Сброс пина P4 в низкий уровень
|
||||||
|
zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_LOW);
|
||||||
|
|
||||||
|
// Чтение состояния пина P4 после записи
|
||||||
|
zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &gpio_status);
|
||||||
|
printf("P4 status: %d\n", gpio_status);
|
||||||
|
|
||||||
|
// Сброс всех пинов
|
||||||
|
zh_pcf8574_reset(&pcf8574_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка прерываний
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
|
||||||
|
zh_pcf8574_handle_t *pcf8574_handle = NULL;
|
||||||
|
|
||||||
|
// Регистратор обработчика событий
|
||||||
|
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data);
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// ... инициализация I2C ...
|
||||||
|
|
||||||
|
// Создание событийной петли
|
||||||
|
esp_event_loop_create_default();
|
||||||
|
|
||||||
|
// Регистрация обработчика событий PCF8574
|
||||||
|
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID,
|
||||||
|
&zh_pcf8574_event_handler, NULL, NULL);
|
||||||
|
|
||||||
|
// Инициализация с поддержкой прерываний
|
||||||
|
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
|
config.i2c_handle = i2c_bus_handle;
|
||||||
|
config.i2c_address = 0x38;
|
||||||
|
config.p0_gpio_work_mode = ZH_PCF8574_GPIO_INPUT; // Вход для прерываний
|
||||||
|
config.interrupt_gpio = GPIO_NUM_14; // GPIO для прерывания
|
||||||
|
|
||||||
|
zh_pcf8574_init(&config, &pcf8574_handle);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
vTaskDelay(60000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик событий прерываний
|
||||||
|
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
zh_pcf8574_event_on_isr_t *event = (zh_pcf8574_event_on_isr_t *)event_data;
|
||||||
|
printf("Interrupt on 0x%02X, GPIO %d, level %d, time %lld us\n",
|
||||||
|
event->i2c_address, event->gpio_number,
|
||||||
|
event->gpio_level, event->interrupt_time);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Логирование
|
||||||
|
|
||||||
|
Компонент использует ESP-IDF логирование с тегом `"zh_pcf8574"`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Установка уровня логирования ERROR
|
||||||
|
esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR);
|
||||||
|
|
||||||
|
// Установка уровня логирования INFO
|
||||||
|
esp_log_level_set("zh_pcf8574", ESP_LOG_INFO);
|
||||||
|
```
|
||||||
|
|
||||||
|
Макросы логирования:
|
||||||
|
- `ZH_LOGI` — информационные сообщения (через `ESP_LOGI`).
|
||||||
|
- `ZH_LOGE` — сообщения об ошибках с кодом ошибки, файлом и строкой (через `ESP_LOGE`).
|
||||||
|
|
||||||
|
## Ошибки
|
||||||
|
|
||||||
|
| Код | Описание |
|
||||||
|
|-----|----------|
|
||||||
|
| `ESP_OK` | Успешное выполнение |
|
||||||
|
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL-указатели, недопустимые значения) |
|
||||||
|
| `ESP_ERR_INVALID_STATE` | Устройство уже инициализировано |
|
||||||
|
| `ESP_ERR_NOT_FOUND` | Хендл не соответствует валидному экземпляру устройства |
|
||||||
|
| `ESP_ERR_NO_MEM` | Не удалось выделить память |
|
||||||
|
| `ESP_FAIL` | Ошибка I2C-коммуникации, конфигурации или создания задачи |
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
1. **Только I2C Fast Mode (100 кГц)** — частота ограничена максимальным значением 100 000 Гц.
|
||||||
|
2. **Ограниченный диапазон адресов** — поддерживаются только адреса `0x20–0x27` и `0x38–0x3F` (стандартные адреса PCF8574 с тремя адресными пинами).
|
||||||
|
3. **Одно GPIO для всех прерываний** — все устройства PCF8574 делят один GPIO-пин для прерываний. Все подключённые устройства должны иметь соединённые выводы INT.
|
||||||
|
4. **Задача обработки прерываний создаётся один раз** — задача создаётся только при инициализации первого устройства с прерываниями. Все последующие устройства используют ту же задачу.
|
||||||
|
5. **Не IRAM-safe** — обработка прерываний не выполняется в IRAM. ISR использует `ESP_INTR_FLAG_LOWMED` и не является безопасной для вызова из IRAM-контекста.
|
||||||
|
6. **Нет мьютекса для вектора** — вектор данных устройств не защищён мьютексом. Одновременный доступ из нескольких задач не поддерживается.
|
||||||
|
7. **Только отрицательный фронт прерывания** — ISR настроен на детектирование отрицательного фронта (`GPIO_INTR_NEGEDGE`).
|
||||||
|
|
||||||
|
## Требования для сборки
|
||||||
|
|
||||||
|
- ESP-IDF v5.0+ (с новым I2C master driver)
|
||||||
|
- FreeRTOS (входит в состав ESP-IDF)
|
||||||
|
- Компонент `zh_vector` (встроенный компонент для работы с векторами)
|
||||||
|
- `esp_timer` (для временных меток прерываний)
|
||||||
|
- `esp_event` (событийная framework)
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ваш_проект/components
|
||||||
|
git clone https://github.com/aZholtikov/zh_pcf8574
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_pcf8574.h"
|
||||||
|
```
|
||||||
+62
-52
@@ -18,7 +18,6 @@ static const char *TAG = "zh_pcf8574";
|
|||||||
{ \
|
{ \
|
||||||
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
||||||
cleanup; \
|
cleanup; \
|
||||||
continue; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +25,9 @@ static const char *TAG = "zh_pcf8574";
|
|||||||
*
|
*
|
||||||
* Minimal handle storing the I2C address of the device instance.
|
* Minimal handle storing the I2C address of the device instance.
|
||||||
* The full state is maintained in the internal vector.
|
* The full state is maintained in the internal vector.
|
||||||
|
*
|
||||||
|
* @note The handle is allocated by zh_pcf8574_init() and freed by zh_pcf8574_deinit().
|
||||||
|
* @warning The handle becomes invalid after deinitialization and must not be used afterwards.
|
||||||
*/
|
*/
|
||||||
struct _zh_pcf8574_handle_t
|
struct _zh_pcf8574_handle_t
|
||||||
{
|
{
|
||||||
@@ -46,14 +48,14 @@ typedef struct
|
|||||||
i2c_master_dev_handle_t dev_handle; /*!< I2C device handle for communication */
|
i2c_master_dev_handle_t dev_handle; /*!< I2C device handle for communication */
|
||||||
} _zh_pcf8574_vector_data_t;
|
} _zh_pcf8574_vector_data_t;
|
||||||
|
|
||||||
TaskHandle_t zh_pcf8574 = NULL; /*!< Handle of the internal PCF8574 processing task */
|
TaskHandle_t zh_pcf8574 = NULL;
|
||||||
static SemaphoreHandle_t _interrupt_semaphore = NULL; /*!< Semaphore for interrupt-to-task synchronization */
|
static SemaphoreHandle_t _interrupt_semaphore = NULL;
|
||||||
|
|
||||||
volatile static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX; /*!< GPIO pin used for interrupt detection */
|
volatile static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX;
|
||||||
static const uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; /*!< Bit mask for each GPIO pin (P0-P7) */
|
static const uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||||
static zh_pcf8574_stats_t _stats = {0}; /*!< Statistics structure for error tracking */
|
static zh_pcf8574_stats_t _stats = {0};
|
||||||
|
|
||||||
static zh_vector_t *_vector = NULL; /*!< Vector storing PCF8574 device data */
|
static zh_vector_t *_vector = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate initialization configuration.
|
* @brief Validate initialization configuration.
|
||||||
@@ -123,7 +125,6 @@ static esp_err_t _zh_pcf8574_task_init(const zh_pcf8574_init_config_t *config);
|
|||||||
*
|
*
|
||||||
* Called when an interrupt occurs on the configured GPIO pin.
|
* Called when an interrupt occurs on the configured GPIO pin.
|
||||||
* Posts a signal to the semaphore to wake up the processing task.
|
* Posts a signal to the semaphore to wake up the processing task.
|
||||||
* Executed in ISR context (IRAM).
|
|
||||||
*
|
*
|
||||||
* @param arg Unused callback argument
|
* @param arg Unused callback argument
|
||||||
*/
|
*/
|
||||||
@@ -134,7 +135,7 @@ static void _zh_pcf8574_isr_handler(void *arg);
|
|||||||
*
|
*
|
||||||
* Waits for interrupt signals, reads all PCF8574 devices, detects
|
* Waits for interrupt signals, reads all PCF8574 devices, detects
|
||||||
* level changes on input pins, and posts events for each change.
|
* level changes on input pins, and posts events for each change.
|
||||||
* Monitors stack usage continuously.
|
* Updates the remaining stack size statistic after each iteration.
|
||||||
*
|
*
|
||||||
* @param pvParameter Unused task parameter
|
* @param pvParameter Unused task parameter
|
||||||
*/
|
*/
|
||||||
@@ -170,7 +171,7 @@ static esp_err_t _zh_pcf8574_write_register(_zh_pcf8574_vector_data_t *vector_da
|
|||||||
|
|
||||||
ESP_EVENT_DEFINE_BASE(ZH_PCF8574);
|
ESP_EVENT_DEFINE_BASE(ZH_PCF8574);
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t **handle) // -V2008
|
esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t **handle)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 initialization started.");
|
ZH_LOGI("PCF8574 initialization started.");
|
||||||
ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 initialization failed. Invalid argument.");
|
ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 initialization failed. Invalid argument.");
|
||||||
@@ -184,25 +185,29 @@ esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_han
|
|||||||
ZH_ERROR_CHECK(_zh_pcf8574_i2c_init(config, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Failed to add I2C device.");
|
ZH_ERROR_CHECK(_zh_pcf8574_i2c_init(config, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Failed to add I2C device.");
|
||||||
*handle = heap_caps_calloc(1, sizeof(zh_pcf8574_handle_t), MALLOC_CAP_8BIT);
|
*handle = heap_caps_calloc(1, sizeof(zh_pcf8574_handle_t), MALLOC_CAP_8BIT);
|
||||||
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM,
|
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "PCF8574 initialization failed. Failed to allocate PCF8574 handle.");
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "PCF8574 initialization failed. Failed to allocate PCF8574 handle.");
|
||||||
ZH_ERROR_CHECK(zh_vector_push_back(&_vector, &vector_data) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(zh_vector_push_back(&_vector, &vector_data) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")};
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "I2C remove device failed.")};
|
||||||
heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Failed to add vector data.");
|
heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Failed to add vector data.");
|
||||||
if (_interrupt_gpio == GPIO_NUM_MAX && config->interrupt_gpio < GPIO_NUM_MAX && vector_data.gpio_work_mode != 0)
|
if (_interrupt_gpio == GPIO_NUM_MAX && config->interrupt_gpio < GPIO_NUM_MAX && vector_data.gpio_work_mode != 0)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_gpio_init(config) == ESP_OK, ESP_FAIL,
|
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")};
|
|
||||||
zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Interrupt GPIO initialization failed.");
|
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_resources_init() == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(_zh_pcf8574_resources_init() == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")};
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "I2C remove device failed.")};
|
||||||
{ZH_ERROR_CHECK(gpio_isr_handler_remove(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Remove GPIO isr handler failed.")};
|
{ZH_ERROR_CHECK_CONT(zh_vector_delete_back(&_vector) == ESP_OK, NULL, "Failed delete vector data.")};
|
||||||
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed.")};
|
heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Resources initialization failed.");
|
||||||
zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Resources initialization failed.");
|
ZH_ERROR_CHECK(_zh_pcf8574_gpio_init(config) == ESP_OK, ESP_FAIL,
|
||||||
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "I2C remove device failed.")};
|
||||||
|
vSemaphoreDelete(_interrupt_semaphore); _interrupt_semaphore = NULL;
|
||||||
|
{ZH_ERROR_CHECK_CONT(zh_vector_delete_back(&_vector) == ESP_OK, NULL, "Failed delete vector data.")};
|
||||||
|
heap_caps_free(*handle);
|
||||||
|
*handle = NULL, "PCF8574 initialization failed. Interrupt GPIO initialization failed.");
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_task_init(config) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(_zh_pcf8574_task_init(config) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")};
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "I2C remove device failed.")};
|
||||||
{ZH_ERROR_CHECK(gpio_isr_handler_remove(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Remove gpio isr handler failed.")};
|
{ZH_ERROR_CHECK_CONT(gpio_isr_handler_remove(config->interrupt_gpio) == ESP_OK, NULL, "Remove gpio isr handler failed.")};
|
||||||
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.")};
|
{ZH_ERROR_CHECK_CONT(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, NULL, "Reset gpio failed.")};
|
||||||
vSemaphoreDelete(_interrupt_semaphore); _interrupt_semaphore = NULL; zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Task initialization failed.");
|
vSemaphoreDelete(_interrupt_semaphore); _interrupt_semaphore = NULL;
|
||||||
|
{ZH_ERROR_CHECK_CONT(zh_vector_delete_back(&_vector) == ESP_OK, NULL, "Failed delete vector data.")};
|
||||||
|
heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Task initialization failed.");
|
||||||
_interrupt_gpio = config->interrupt_gpio;
|
_interrupt_gpio = config->interrupt_gpio;
|
||||||
}
|
}
|
||||||
if (_stats.min_stack_size == 0)
|
if (_stats.min_stack_size == 0)
|
||||||
@@ -214,7 +219,7 @@ esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_han
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle) // -V2008
|
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 deinitialization started.");
|
ZH_LOGI("PCF8574 deinitialization started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 deinitialization failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 deinitialization failed. Invalid argument.");
|
||||||
@@ -223,16 +228,16 @@ esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle) // -V2008
|
|||||||
ZH_ERROR_CHECK(zh_vector_find_item_in_field(&_vector, &vector_data, &vector_data.i2c_address, sizeof(vector_data.i2c_address), &(*handle)->i2c_address, 0, &index) == ESP_OK,
|
ZH_ERROR_CHECK(zh_vector_find_item_in_field(&_vector, &vector_data, &vector_data.i2c_address, sizeof(vector_data.i2c_address), &(*handle)->i2c_address, 0, &index) == ESP_OK,
|
||||||
ESP_ERR_NOT_FOUND, NULL, "PCF8574 deinitialization failed. Failed to find vector item.");
|
ESP_ERR_NOT_FOUND, NULL, "PCF8574 deinitialization failed. Failed to find vector item.");
|
||||||
ZH_ERROR_CHECK(zh_vector_get_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Failed to get vector item data.");
|
ZH_ERROR_CHECK(zh_vector_get_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Failed to get vector item data.");
|
||||||
ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. I2C remove device failed.");
|
ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, NULL, "PCF8574 deinitialization failed. I2C remove device failed.");
|
||||||
ZH_ERROR_CHECK(zh_vector_delete_item(&_vector, (uint16_t)index) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Vector delete item failed.");
|
ZH_ERROR_CHECK_CONT(zh_vector_delete_item(&_vector, (uint16_t)index) == ESP_OK, NULL, "PCF8574 deinitialization failed. Vector delete item failed.");
|
||||||
uint16_t vector_size = 0;
|
uint16_t vector_size = 0;
|
||||||
ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Failed to get vector size.");
|
ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Failed to get vector size.");
|
||||||
if (vector_size == 0)
|
if (vector_size == 0)
|
||||||
{
|
{
|
||||||
if (_interrupt_gpio != GPIO_NUM_MAX)
|
if (_interrupt_gpio != GPIO_NUM_MAX)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(gpio_isr_handler_remove(_interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Remove GPIO isr handler failed.");
|
ZH_ERROR_CHECK_CONT(gpio_isr_handler_remove(_interrupt_gpio) == ESP_OK, NULL, "PCF8574 deinitialization failed. Remove GPIO isr handler failed.");
|
||||||
ZH_ERROR_CHECK(gpio_reset_pin(_interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Reset GPIO failed.");
|
ZH_ERROR_CHECK_CONT(gpio_reset_pin(_interrupt_gpio) == ESP_OK, NULL, "PCF8574 deinitialization failed. Reset GPIO failed.");
|
||||||
vTaskDelete(zh_pcf8574);
|
vTaskDelete(zh_pcf8574);
|
||||||
zh_pcf8574 = NULL;
|
zh_pcf8574 = NULL;
|
||||||
vSemaphoreDelete(_interrupt_semaphore);
|
vSemaphoreDelete(_interrupt_semaphore);
|
||||||
@@ -247,7 +252,7 @@ esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle) // -V2008
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t **handle, uint8_t *reg) // -V2008
|
esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t **handle, uint8_t *reg)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 read register started.");
|
ZH_LOGI("PCF8574 read register started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && reg != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read register failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && reg != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read register failed. Invalid argument.");
|
||||||
@@ -262,7 +267,7 @@ esp_err_t zh_pcf8574_read(zh_pcf8574_handle_t **handle, uint8_t *reg) // -V2008
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t **handle, uint8_t reg) // -V2008
|
esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t **handle, uint8_t reg)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 write register started.");
|
ZH_LOGI("PCF8574 write register started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write register failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write register failed. Invalid argument.");
|
||||||
@@ -277,7 +282,7 @@ esp_err_t zh_pcf8574_write(zh_pcf8574_handle_t **handle, uint8_t reg) // -V2008
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle) // -V2008
|
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 reset register started.");
|
ZH_LOGI("PCF8574 reset register started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 reset register failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 reset register failed. Invalid argument.");
|
||||||
@@ -292,7 +297,7 @@ esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle) // -V2008
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool *status) // -V2008
|
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool *status)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 read GPIO started.");
|
ZH_LOGI("PCF8574 read GPIO started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && status != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read GPIO failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && status != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read GPIO failed. Invalid argument.");
|
||||||
@@ -310,7 +315,7 @@ esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool status) // -V2008
|
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_num_t gpio, bool status)
|
||||||
{
|
{
|
||||||
ZH_LOGI("PCF8574 write GPIO started.");
|
ZH_LOGI("PCF8574 write GPIO started.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write GPIO failed. Invalid argument.");
|
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write GPIO failed. Invalid argument.");
|
||||||
@@ -322,11 +327,11 @@ esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t **handle, zh_pcf8574_gpio_nu
|
|||||||
ZH_ERROR_CHECK(zh_vector_get_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. Failed to get vector item data.");
|
ZH_ERROR_CHECK(zh_vector_get_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. Failed to get vector item data.");
|
||||||
if (status == true)
|
if (status == true)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, vector_data.gpio_status | vector_data.gpio_work_mode | _gpio_matrix[(uint8_t)gpio]) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. PCF8574 write register failed."); /*!< Set GPIO pin high, preserving work mode. */
|
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, vector_data.gpio_status | vector_data.gpio_work_mode | _gpio_matrix[(uint8_t)gpio]) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. PCF8574 write register failed.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, (vector_data.gpio_status & ~_gpio_matrix[(uint8_t)gpio]) | vector_data.gpio_work_mode) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. PCF8574 write register failed."); /*!< Clear GPIO pin, preserving work mode. */
|
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, (vector_data.gpio_status & ~_gpio_matrix[(uint8_t)gpio]) | vector_data.gpio_work_mode) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. PCF8574 write register failed.");
|
||||||
}
|
}
|
||||||
ZH_ERROR_CHECK(zh_vector_change_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. Failed to change vector item data.");
|
ZH_ERROR_CHECK(zh_vector_change_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed. Failed to change vector item data.");
|
||||||
ZH_LOGI("PCF8574 write GPIO completed successfully.");
|
ZH_LOGI("PCF8574 write GPIO completed successfully.");
|
||||||
@@ -349,10 +354,10 @@ void zh_pcf8574_reset_stats(void)
|
|||||||
ZH_LOGI("Error statistic reset successfully.");
|
ZH_LOGI("Error statistic reset successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *config) // -V2008
|
static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *config)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK((config->i2c_address >= 0x20 && config->i2c_address <= 0x27) || (config->i2c_address >= 0x38 && config->i2c_address <= 0x3F), ESP_ERR_INVALID_ARG, NULL, "Invalid I2C address.");
|
ZH_ERROR_CHECK((config->i2c_address >= 0x20 && config->i2c_address <= 0x27) || (config->i2c_address >= 0x38 && config->i2c_address <= 0x3F), ESP_ERR_INVALID_ARG, NULL, "Invalid I2C address.");
|
||||||
ZH_ERROR_CHECK(config->i2c_frequency <= 100000, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C frequency.");
|
ZH_ERROR_CHECK(config->i2c_frequency <= 100000 && config->i2c_frequency > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C frequency.");
|
||||||
ZH_ERROR_CHECK(config->task_priority >= 1 && config->stack_size >= configMINIMAL_STACK_SIZE, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
|
ZH_ERROR_CHECK(config->task_priority >= 1 && config->stack_size >= configMINIMAL_STACK_SIZE, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
|
||||||
ZH_ERROR_CHECK(config->interrupt_gpio <= GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.");
|
ZH_ERROR_CHECK(config->interrupt_gpio <= GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.");
|
||||||
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
|
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
|
||||||
@@ -366,7 +371,7 @@ static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *con
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config) // -V2008
|
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config)
|
||||||
{
|
{
|
||||||
gpio_config_t interrupt_gpio_config = {
|
gpio_config_t interrupt_gpio_config = {
|
||||||
.intr_type = GPIO_INTR_NEGEDGE,
|
.intr_type = GPIO_INTR_NEGEDGE,
|
||||||
@@ -378,13 +383,13 @@ static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config) /
|
|||||||
ZH_ERROR_CHECK(gpio_config(&interrupt_gpio_config) == ESP_OK, ESP_FAIL, NULL, "GPIO configuration failed.")
|
ZH_ERROR_CHECK(gpio_config(&interrupt_gpio_config) == ESP_OK, ESP_FAIL, NULL, "GPIO configuration failed.")
|
||||||
esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
|
esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
|
||||||
ZH_ERROR_CHECK(err == ESP_OK || err == ESP_ERR_INVALID_STATE, ESP_FAIL,
|
ZH_ERROR_CHECK(err == ESP_OK || err == ESP_ERR_INVALID_STATE, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.")}, "Failed install isr service.")
|
{ZH_ERROR_CHECK_CONT(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, NULL, "Reset gpio failed.")}, "Failed install isr service.")
|
||||||
ZH_ERROR_CHECK(gpio_isr_handler_add(config->interrupt_gpio, _zh_pcf8574_isr_handler, NULL) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(gpio_isr_handler_add(config->interrupt_gpio, _zh_pcf8574_isr_handler, NULL) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.")}, "Failed add isr handler.")
|
{ZH_ERROR_CHECK_CONT(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, NULL, "Reset gpio failed.")}, "Failed add isr handler.")
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, _zh_pcf8574_vector_data_t *vector_data) // -V2008
|
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, _zh_pcf8574_vector_data_t *vector_data)
|
||||||
{
|
{
|
||||||
i2c_device_config_t pcf8574_config = {
|
i2c_device_config_t pcf8574_config = {
|
||||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||||
@@ -393,18 +398,18 @@ static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, _z
|
|||||||
};
|
};
|
||||||
i2c_master_dev_handle_t _dev_handle = NULL;
|
i2c_master_dev_handle_t _dev_handle = NULL;
|
||||||
ZH_ERROR_CHECK(i2c_master_bus_add_device(config->i2c_handle, &pcf8574_config, &_dev_handle) == ESP_OK, ESP_FAIL, NULL, "Failed to add I2C device.");
|
ZH_ERROR_CHECK(i2c_master_bus_add_device(config->i2c_handle, &pcf8574_config, &_dev_handle) == ESP_OK, ESP_FAIL, NULL, "Failed to add I2C device.");
|
||||||
ZH_ERROR_CHECK(i2c_master_probe(config->i2c_handle, config->i2c_address, 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(i2c_master_probe(config->i2c_handle, config->i2c_address, 100) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "Expander not connected or not responding.");
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "Expander not connected or not responding.");
|
||||||
vector_data->gpio_work_mode = (config->p7_gpio_work_mode << 7) | (config->p6_gpio_work_mode << 6) | (config->p5_gpio_work_mode << 5) |
|
vector_data->gpio_work_mode = (config->p7_gpio_work_mode << 7) | (config->p6_gpio_work_mode << 6) | (config->p5_gpio_work_mode << 5) |
|
||||||
(config->p4_gpio_work_mode << 4) | (config->p3_gpio_work_mode << 3) | (config->p2_gpio_work_mode << 2) |
|
(config->p4_gpio_work_mode << 4) | (config->p3_gpio_work_mode << 3) | (config->p2_gpio_work_mode << 2) |
|
||||||
(config->p1_gpio_work_mode << 1) | (config->p0_gpio_work_mode << 0);
|
(config->p1_gpio_work_mode << 1) | (config->p0_gpio_work_mode << 0);
|
||||||
vector_data->dev_handle = _dev_handle;
|
vector_data->dev_handle = _dev_handle;
|
||||||
vector_data->i2c_address = config->i2c_address;
|
vector_data->i2c_address = config->i2c_address;
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_write_register(vector_data, vector_data->gpio_work_mode) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(_zh_pcf8574_write_register(vector_data, vector_data->gpio_work_mode) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "Failed extender initial GPIO setup.");
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "Failed extender initial GPIO setup.");
|
||||||
uint8_t reg_temp = 0;
|
uint8_t reg_temp = 0;
|
||||||
ZH_ERROR_CHECK(_zh_pcf8574_read_register(vector_data, ®_temp) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(_zh_pcf8574_read_register(vector_data, ®_temp) == ESP_OK, ESP_FAIL,
|
||||||
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "Failed to read expander register.");
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "Failed to read expander register.");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,8 +427,9 @@ static esp_err_t _zh_pcf8574_task_init(const zh_pcf8574_init_config_t *config)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IRAM_ATTR _zh_pcf8574_isr_handler(void *arg)
|
static void _zh_pcf8574_isr_handler(void *arg)
|
||||||
{
|
{
|
||||||
|
(void)arg;
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
if (xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken) != pdTRUE)
|
if (xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken) != pdTRUE)
|
||||||
{
|
{
|
||||||
@@ -435,8 +441,9 @@ static void IRAM_ATTR _zh_pcf8574_isr_handler(void *arg)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter) // -V2008
|
static void _zh_pcf8574_isr_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
|
(void)pvParameter;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY);
|
xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY);
|
||||||
@@ -446,12 +453,14 @@ static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter) // -V20
|
|||||||
{
|
{
|
||||||
_zh_pcf8574_vector_data_t vector_data = {0};
|
_zh_pcf8574_vector_data_t vector_data = {0};
|
||||||
ZH_ERROR_CHECK_CONT(zh_vector_get_item(&_vector, i, &vector_data) == ESP_OK, ++_stats.vector_error, "PCF8574 isr processing failed. Failed to get vector item data.");
|
ZH_ERROR_CHECK_CONT(zh_vector_get_item(&_vector, i, &vector_data) == ESP_OK, ++_stats.vector_error, "PCF8574 isr processing failed. Failed to get vector item data.");
|
||||||
|
if (vector_data.gpio_work_mode != 0)
|
||||||
|
{
|
||||||
zh_pcf8574_event_on_isr_t event = {0};
|
zh_pcf8574_event_on_isr_t event = {0};
|
||||||
event.i2c_address = vector_data.i2c_address;
|
event.i2c_address = vector_data.i2c_address;
|
||||||
uint8_t old_reg = vector_data.gpio_status;
|
uint8_t old_reg = vector_data.gpio_status;
|
||||||
uint8_t new_reg = 0;
|
uint8_t new_reg = 0;
|
||||||
ZH_ERROR_CHECK_CONT(_zh_pcf8574_read_register(&vector_data, &new_reg) == ESP_OK, NULL, "PCF8574 isr processing failed. Failed to read expander register.");
|
ZH_ERROR_CHECK_CONT(_zh_pcf8574_read_register(&vector_data, &new_reg) == ESP_OK, continue, "PCF8574 isr processing failed. Failed to read expander register.");
|
||||||
ZH_ERROR_CHECK_CONT(zh_vector_change_item(&_vector, i, &vector_data) == ESP_OK, ++_stats.vector_error, "PCF8574 isr processing failed. Failed to change vector item data.");
|
ZH_ERROR_CHECK_CONT(zh_vector_change_item(&_vector, i, &vector_data) == ESP_OK, ++_stats.vector_error; continue, "PCF8574 isr processing failed. Failed to change vector item data.");
|
||||||
for (uint8_t j = 0; j <= 7; ++j)
|
for (uint8_t j = 0; j <= 7; ++j)
|
||||||
{
|
{
|
||||||
if ((vector_data.gpio_work_mode & _gpio_matrix[j]) != 0)
|
if ((vector_data.gpio_work_mode & _gpio_matrix[j]) != 0)
|
||||||
@@ -461,7 +470,8 @@ static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter) // -V20
|
|||||||
event.gpio_number = j;
|
event.gpio_number = j;
|
||||||
event.gpio_level = new_reg & _gpio_matrix[j];
|
event.gpio_level = new_reg & _gpio_matrix[j];
|
||||||
event.interrupt_time = esp_timer_get_time();
|
event.interrupt_time = esp_timer_get_time();
|
||||||
ZH_ERROR_CHECK_CONT(esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), 1000 / portTICK_PERIOD_MS) == ESP_OK, ++_stats.event_post_error, "PCF8574 isr processing failed. Failed to post interrupt event.");
|
ZH_ERROR_CHECK_CONT(esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), pdMS_TO_TICKS(10)) == ESP_OK, ++_stats.event_post_error, "PCF8574 isr processing failed. Failed to post interrupt event.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,7 +483,7 @@ static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter) // -V20
|
|||||||
|
|
||||||
static esp_err_t _zh_pcf8574_read_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t *reg)
|
static esp_err_t _zh_pcf8574_read_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t *reg)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(i2c_master_receive(vector_data->dev_handle, &vector_data->gpio_status, sizeof(vector_data->gpio_status), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(i2c_master_receive(vector_data->dev_handle, &vector_data->gpio_status, sizeof(vector_data->gpio_status), 100) == ESP_OK, ESP_FAIL,
|
||||||
++_stats.i2c_driver_error, "I2C driver error.");
|
++_stats.i2c_driver_error, "I2C driver error.");
|
||||||
*reg = vector_data->gpio_status;
|
*reg = vector_data->gpio_status;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -481,7 +491,7 @@ static esp_err_t _zh_pcf8574_read_register(_zh_pcf8574_vector_data_t *vector_dat
|
|||||||
|
|
||||||
static esp_err_t _zh_pcf8574_write_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t reg)
|
static esp_err_t _zh_pcf8574_write_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t reg)
|
||||||
{
|
{
|
||||||
ZH_ERROR_CHECK(i2c_master_transmit(vector_data->dev_handle, ®, sizeof(reg), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
|
ZH_ERROR_CHECK(i2c_master_transmit(vector_data->dev_handle, ®, sizeof(reg), 100) == ESP_OK, ESP_FAIL,
|
||||||
++_stats.i2c_driver_error, "I2C driver error.");
|
++_stats.i2c_driver_error, "I2C driver error.");
|
||||||
vector_data->gpio_status = reg;
|
vector_data->gpio_status = reg;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|||||||
Reference in new issue
Block a user