feat!: handle changed to double pointer

This commit is contained in:
aZholtikov committed 2026-08-02 09:12:51 +03:00
1 parent e7c19680ae
commit f57671206b
7 files changed
+497 -506

No files matched your search

+8 -110
View File
@@ -1,4 +1,4 @@
# ESP32 ESP-IDF component for PCF8574(A) 8-bit I/O expander
# ESP32 ESP-IDF component for PCF8574 8-bit I/O expander
## Wiki
@@ -10,33 +10,15 @@
## SAST Tools
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) — static analyzer for C, C++, C#, and Java code.
## Features
1. Support of 16 expanders on one bus.
2. Support of output and input GPIO's work mode.
3. Support of interrupts from input GPIO's.
## Note
1. Enable interrupt support only if input GPIO's are used.
2. All the INT GPIO's on the extenders must be connected to the one GPIO on ESP.
3. The input GPIO's are always pullup to the power supply.
## Attention
For correct operation, please enable the following settings in the menuconfig:
```text
GPIO_CTRL_FUNC_IN_IRAM
I2C_ISR_IRAM_SAFE
I2C_MASTER_ISR_HANDLER_IN_IRAM
```
## Dependencies
1. [zh_vector](https://github.com/aZholtikov/zh_vector)
1. I2C communication via esp-idf i2c_master driver.
2. Configurable GPIO direction for each pin (P0-P7).
3. Interrupt handling with GPIO level detection.
4. Event-based architecture using esp_event framework.
5. Statistics tracking for debugging and monitoring.
## Using
@@ -55,88 +37,4 @@ In the application, add the component:
## Examples
One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enable:
```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);
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);
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;
zh_pcf8574_init(&config, &pcf8574_handle);
uint8_t reg = 0;
zh_pcf8574_read(&pcf8574_handle, &reg);
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, &reg);
print_gpio_status("GPIO status: ", reg);
printf("Sets 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, &reg);
print_gpio_status("GPIO status: ", reg);
printf("Reset all GPIO.\n");
zh_pcf8574_reset(&pcf8574_handle);
zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg);
for (;;)
{
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
printf("Number of i2c driver error: %ld.\n", stats->i2c_driver_error);
printf("Number of event post error: %ld.\n", stats->event_post_error);
printf("Number of vector error: %ld.\n", stats->vector_error);
printf("Number of queue overflow error: %ld.\n", stats->queue_overflow_error);
printf("Minimum free stack size: %ld.\n", stats->min_stack_size);
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 = event_data;
printf("Interrupt happened on device address 0x%02X on GPIO number %d at level %d at time %lld.\n", event->i2c_address, event->gpio_number,
event->gpio_level, event->interrupt_time);
}
```
See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования)
+27 -53
View File
@@ -88,7 +88,7 @@ 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)
uint16_t stack_size; // Stack size for interrupt processing task
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)
@@ -115,17 +115,11 @@ Use `ZH_PCF8574_INIT_CONFIG_DEFAULT()` macro to initialize with default values:
### zh_pcf8574_handle_t Structure
```c
typedef struct
{
uint8_t i2c_address; // I2C address of the expander
uint8_t gpio_work_mode; // GPIO work modes (bits 0-7)
uint8_t gpio_status; // Current GPIO status (bits 0-7)
bool is_initialized; // Expander initialization flag
i2c_master_dev_handle_t dev_handle; // Unique I2C device handle
void *system; // System pointer for use in other components
} zh_pcf8574_handle_t;
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
@@ -163,15 +157,16 @@ Initializes the PCF8574 expander.
**Parameters:**
- `config` - Pointer to PCF8574 initialization configuration structure
- `handle` - Pointer to unique PCF8574 handle
- `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_FAIL` - Initialization failed
- `ESP_ERR_NO_MEM` - Insufficient memory
- `ESP_ERR_NOT_SUPPORTED` - I2C initialization error
---
@@ -181,14 +176,12 @@ Deinitializes the PCF8574 expander.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle)
- `ESP_ERR_INVALID_STATE` - Expander not initialized
- `ESP_FAIL` - Deinitialization failed
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or *handle)
---
@@ -198,15 +191,14 @@ Reads the state of all GPIO pins of the expander.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `reg` - Pointer to store GPIO status (bits 0-7)
- `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_NOT_FOUND` - Expander not initialized
- `ESP_FAIL` - I2C communication error
- `ESP_ERR_INVALID_STATE` - Expander not initialized
**Note:** For input GPIOs, the value will always be 1 (HIGH).
@@ -218,15 +210,14 @@ Sets the state of all output GPIO pins of the expander.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `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_NOT_FOUND` - Expander not initialized
- `ESP_FAIL` - I2C communication error
- `ESP_ERR_INVALID_STATE` - Expander not initialized
**Note:** Only output GPIOs are affected.
@@ -238,14 +229,12 @@ Resets all GPIO pins of the expander to the initial state.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `handle` - Pointer to pointer to PCF8574 handle. Must not be NULL
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle)
- `ESP_ERR_NOT_FOUND` - Expander not initialized
- `ESP_FAIL` - I2C communication error
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle or *handle)
---
@@ -255,16 +244,15 @@ Reads the state of one GPIO pin of the expander.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `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)
- `status` - Pointer to store GPIO status (true - HIGH, false - LOW). Must not be NULL
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument
- `ESP_ERR_NOT_FOUND` - Expander not initialized
- `ESP_FAIL` - I2C communication error
- `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).
@@ -276,16 +264,15 @@ Sets the state of one output GPIO pin of the expander.
**Parameters:**
- `handle` - Pointer to unique PCF8574 handle
- `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
- `ESP_ERR_NOT_FOUND` - Expander not initialized
- `ESP_FAIL` - I2C communication error
- `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.
@@ -299,25 +286,12 @@ Gets error statistics since last reset.
- Pointer to the statistics structure
**Example:**
```c
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
printf("I2C errors: %ld\n", stats->i2c_driver_error);
```
---
### zh_pcf8574_reset_stats()
Resets error statistics counters.
**Example:**
```c
zh_pcf8574_reset_stats();
```
---
## Usage Examples
@@ -445,7 +419,7 @@ void app_main(void)
## 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) |
@@ -463,7 +437,7 @@ void app_main(void)
## 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 |
@@ -519,4 +493,4 @@ limitations under the License.
---
*Generated for zh_pcf8574 v2.8.2*
"*Generated for zh_pcf8574 v3.0.0*"
+34 -60
View File
@@ -24,13 +24,13 @@
## Возможности
1. **Поддержка 16 расширителей**: До 16 PCF8574(A) на одной шине I2C (адреса 0x20-0x27 и 0x38-0x3F)
2. **Режимы работы GPIO**: Поддержка выходных и входных режимов для каждого из 8 GPIO
3. **Генерация прерываний**: Поддержка прерываний от входных GPIO (отрицательный фронт)
4. **Статистика ошибок**: Встроенные счетчики ошибок I2C, событий, векторов и переполнения очереди
5. **Потокобезопасность**: Использует драйвер I2C ESP-IDF и FreeRTOS
6. **Минимальные накладные расходы**: Низкие требования к памяти и процессору
7. **Простой API**: Легкое чтение/запись состояния всех GPIO и отдельных пинов
- **Поддержка 16 расширителей**: До 16 PCF8574(A) на одной шине I2C (адреса 0x20-0x27 и 0x38-0x3F)
- **Режимы работы GPIO**: Поддержка выходных и входных режимов для каждого из 8 GPIO
- **Генерация прерываний**: Поддержка прерываний от входных GPIO (отрицательный фронт)
- **Статистика ошибок**: Встроенные счетчики ошибок I2C, событий, векторов и переполнения очереди
- **Потокобезопасность**: Использует драйвер I2C ESP-IDF и FreeRTOS
- **Минимальные накладные расходы**: Низкие требования к памяти и процессору
- **Простой API**: Легкое чтение/запись состояния всех GPIO и отдельных пинов
---
@@ -88,7 +88,7 @@ typedef struct
{
i2c_master_bus_handle_t i2c_handle; // Уникальный дескриптор шины I2C
uint32_t i2c_frequency; // Частота I2C расширителя (макс. 100000 Гц)
uint16_t stack_size; // Размер стека задачи обработки прерываний
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)
@@ -115,17 +115,11 @@ typedef struct
### Структура zh_pcf8574_handle_t
```c
typedef struct
{
uint8_t i2c_address; // Адрес I2C расширителя
uint8_t gpio_work_mode; // Режимы работы GPIO (биты 0-7)
uint8_t gpio_status; // Текущее состояние GPIO (биты 0-7)
bool is_initialized; // Флаг инициализации расширителя
i2c_master_dev_handle_t dev_handle; // Уникальный дескриптор устройства I2C
void *system; // Системный указатель для использования в других компонентах
} zh_pcf8574_handle_t;
typedef struct _zh_pcf8574_handle_t zh_pcf8574_handle_t;
```
Непрозрачный дескриптор экземпляра устройства PCF8574. Внутренняя структура минимальна — хранит только I2C-адрес; полное состояние устройства поддерживается во внутреннем векторе.
---
### Структура zh_pcf8574_stats_t
@@ -163,15 +157,16 @@ typedef struct
**Параметры:**
- `config` - Указатель на структуру конфигурации инициализации PCF8574
- `handle` - Указатель на уникальный дескриптор PCF8574
- `config` - Указатель на структуру конфигурации инициализации PCF8574. Не должен быть NULL
- `handle` - Указатель на указатель на дескриптор PCF8574. Должен быть NULL
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL config или handle)
- `ESP_ERR_INVALID_STATE` - Расширитель уже инициализирован
- `ESP_FAIL` - Ошибка инициализации
- `ESP_ERR_NO_MEM` - Недостаточно памяти
- `ESP_ERR_NOT_SUPPORTED` - Ошибка инициализации I2C
---
@@ -181,14 +176,12 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка деинициализации
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или *handle)
---
@@ -198,15 +191,14 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `reg` - Указатель для хранения состояния GPIO (биты 0-7)
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
- `reg` - Указатель для хранения состояния GPIO (биты 0-7). Не должен быть NULL
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или reg)
- `ESP_ERR_NOT_FOUND` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
**Примечание:** Для входных GPIO всегда будет 1 (HIGH).
@@ -218,15 +210,14 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
- `reg` - Состояние GPIO (биты 0-7)
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
- `ESP_ERR_NOT_FOUND` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
**Примечание:** Влияет только на выходные GPIO.
@@ -238,14 +229,12 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
- `ESP_ERR_NOT_FOUND` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle или *handle)
---
@@ -255,16 +244,15 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `handle` - Указатель на указатель на дескриптор PCF8574. Не должен быть NULL
- `gpio` - Номер GPIO (ZH_PCF8574_GPIO_NUM_P0 ... ZH_PCF8574_GPIO_NUM_P7)
- `status` - Указатель для хранения состояния GPIO (true - HIGH, false - LOW)
- `status` - Указатель для хранения состояния GPIO (true - HIGH, false - LOW). Не должен быть NULL
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент
- `ESP_ERR_NOT_FOUND` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, gpio, status или выход gpio за пределы)
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
**Примечание:** Для входных GPIO всегда будет 1 (HIGH).
@@ -276,16 +264,15 @@ typedef struct
**Параметры:**
- `handle` - Указатель на уникальный дескриптор PCF8574
- `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` - Неверный аргумент
- `ESP_ERR_NOT_FOUND` - Расширитель не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, gpio, status или выход gpio за пределы)
- `ESP_ERR_INVALID_STATE` - Расширитель не инициализирован
**Примечание:** Влияет только на выходные GPIO.
@@ -299,25 +286,12 @@ typedef struct
- Указатель на структуру статистики
**Пример:**
```c
const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats();
printf("Ошибки I2C: %ld\n", stats->i2c_driver_error);
```
---
### zh_pcf8574_reset_stats()
Сбрасывает счетчики статистики ошибок.
**Пример:**
```c
zh_pcf8574_reset_stats();
```
---
## Примеры использования
@@ -445,7 +419,7 @@ void app_main(void)
## Технические характеристики
| Параметр | Значение |
|----------|----------|
| ---------- | ---------- |
| **Тип устройства** | 8-битный I2C GPIO расширитель |
| **Модели** | PCF8574, PCF8574A |
| **Количество устройств на шине** | До 16 (адреса 0x20-0x27, 0x38-0x3F) |
@@ -463,7 +437,7 @@ void app_main(void)
## Коды ошибок
| Код ошибки | Описание |
|------------|----------|
| ------------ | ---------- |
| `ESP_OK` | Операция выполнена успешно |
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или неверная конфигурация) |
| `ESP_ERR_INVALID_STATE` | Устройство не инициализировано или уже инициализировано |
@@ -519,4 +493,4 @@ void app_main(void)
---
*Сгенерировано для zh_pcf8574 v2.8.2*
"*Сгенерировано для zh_pcf8574 v3.0.0*"
+1 -1
View File
@@ -1,6 +1,6 @@
description: ESP32 ESP-IDF component for PCF8574(A) 8-bit I/O expander
url: https://github.com/aZholtikov/zh_pcf8574
version: "2.8.2"
version: "3.0.0"
license: "Apache-2.0"
maintainers:
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
+149 -105
View File
@@ -1,5 +1,22 @@
/**
* @file zh_pcf8574.h
*
* @brief Header file for PCF8574 I2C GPIO expander driver.
*
* This module provides a driver for the PCF8574 8-bit I2C GPIO
* expander, supporting both input and output operations, interrupt
* handling, and configuration of individual pin directions.
*
* Key features:
* - I2C communication via esp-idf i2c_master driver
* - Configurable GPIO direction for each pin (P0-P7)
* - Interrupt handling with GPIO level detection
* - Event-based architecture using esp_event framework
* - Statistics tracking for debugging and monitoring
*
* @note The driver creates an internal task for interrupt processing.
* @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.
*/
#pragma once
@@ -13,14 +30,11 @@
#include "esp_event.h"
#include "zh_vector.h"
#define ZH_PCF8574_GPIO_OUTPUT false
#define ZH_PCF8574_GPIO_INPUT true
#define ZH_PCF8574_GPIO_LOW false
#define ZH_PCF8574_GPIO_HIGH true
#define ZH_PCF8574_GPIO_OUTPUT false /*!< Configures GPIO pin as output */
#define ZH_PCF8574_GPIO_INPUT true /*!< Configures GPIO pin as input */
#define ZH_PCF8574_GPIO_LOW false /*!< Logical low level */
#define ZH_PCF8574_GPIO_HIGH true /*!< Logical high level */
/**
* @brief PCF8574 expander initial default values.
*/
#define ZH_PCF8574_INIT_CONFIG_DEFAULT() \
{ \
.task_priority = 1, \
@@ -42,178 +56,208 @@ extern "C"
{
#endif
extern TaskHandle_t zh_pcf8574; /*!< Unique task handle. */
extern TaskHandle_t zh_pcf8574; /*!< Handle of the internal PCF8574 processing task. */
/**
* @brief Enumeration of PCF8574 expander GPIO.
* @brief Opaque handle for PCF8574 device instance.
*
* Forward declaration of the internal device handle structure.
* Used by all public API functions to reference a specific
* PCF8574 device instance.
*/
typedef struct _zh_pcf8574_handle_t zh_pcf8574_handle_t;
/**
* @brief PCF8574 GPIO pin numbering enumeration.
*
* Defines the pin numbering scheme for accessing individual
* GPIO pins (P0-P7) of the PCF8574 device.
*/
typedef enum
{
ZH_PCF8574_GPIO_NUM_P0 = 0,
ZH_PCF8574_GPIO_NUM_P1,
ZH_PCF8574_GPIO_NUM_P2,
ZH_PCF8574_GPIO_NUM_P3,
ZH_PCF8574_GPIO_NUM_P4,
ZH_PCF8574_GPIO_NUM_P5,
ZH_PCF8574_GPIO_NUM_P6,
ZH_PCF8574_GPIO_NUM_P7,
ZH_PCF8574_GPIO_NUM_MAX
ZH_PCF8574_GPIO_NUM_P0 = 0, /*!< GPIO pin P0 */
ZH_PCF8574_GPIO_NUM_P1, /*!< GPIO pin P1 */
ZH_PCF8574_GPIO_NUM_P2, /*!< GPIO pin P2 */
ZH_PCF8574_GPIO_NUM_P3, /*!< GPIO pin P3 */
ZH_PCF8574_GPIO_NUM_P4, /*!< GPIO pin P4 */
ZH_PCF8574_GPIO_NUM_P5, /*!< GPIO pin P5 */
ZH_PCF8574_GPIO_NUM_P6, /*!< GPIO pin P6 */
ZH_PCF8574_GPIO_NUM_P7, /*!< GPIO pin P7 */
ZH_PCF8574_GPIO_NUM_MAX /*!< Maximum number of GPIO pins */
} zh_pcf8574_gpio_num_t;
/**
* @brief Structure for initial initialization of PCF8574 expander.
* @brief Initialization configuration structure for PCF8574 device.
*
* Defines all parameters required to initialize and configure
* the PCF8574 I2C GPIO expander, including I2C settings,
* individual GPIO direction modes, and interrupt configuration.
*
* @note The I2C bus must be initialized before calling zh_pcf8574_init().
*/
typedef struct
{
i2c_master_bus_handle_t i2c_handle; /*!< Unique I2C bus handle. @attention Must be same for all PCF8574 expanders. */
uint32_t i2c_frequency; /*!< Expander I2C frequency. */
uint16_t stack_size; /*!< Stack size for task for the PCF8574 expander isr processing processing. @note The minimum size is configMINIMAL_STACK_SIZE. */
uint8_t task_priority; /*!< Task priority for the PCF8574 expander isr processing. @note Minimum value is 1. */
uint8_t i2c_address; /*!< Expander I2C address. */
bool p0_gpio_work_mode; /*!< Expander GPIO PO work mode. */
bool p1_gpio_work_mode; /*!< Expander GPIO P1 work mode. */
bool p2_gpio_work_mode; /*!< Expander GPIO P2 work mode. */
bool p3_gpio_work_mode; /*!< Expander GPIO P3 work mode. */
bool p4_gpio_work_mode; /*!< Expander GPIO P4 work mode. */
bool p5_gpio_work_mode; /*!< Expander GPIO P5 work mode. */
bool p6_gpio_work_mode; /*!< Expander GPIO P6 work mode. */
bool p7_gpio_work_mode; /*!< Expander GPIO P7 work mode. */
gpio_num_t interrupt_gpio; /*!< Interrupt GPIO. @attention Must be same for all PCF8574 expanders. */
i2c_master_bus_handle_t i2c_handle; /*!< I2C bus handle for communication */
uint32_t i2c_frequency; /*!< I2C clock frequency in Hz */
uint32_t stack_size; /*!< Stack size for the internal task */
uint8_t task_priority; /*!< Priority of the internal task */
uint8_t i2c_address; /*!< 7-bit I2C address of the PCF8574 device */
bool p0_gpio_work_mode; /*!< Operating mode for GPIO P0 (input/output) */
bool p1_gpio_work_mode; /*!< Operating mode for GPIO P1 (input/output) */
bool p2_gpio_work_mode; /*!< Operating mode for GPIO P2 (input/output) */
bool p3_gpio_work_mode; /*!< Operating mode for GPIO P3 (input/output) */
bool p4_gpio_work_mode; /*!< Operating mode for GPIO P4 (input/output) */
bool p5_gpio_work_mode; /*!< Operating mode for GPIO P5 (input/output) */
bool p6_gpio_work_mode; /*!< Operating mode for GPIO P6 (input/output) */
bool p7_gpio_work_mode; /*!< Operating mode for GPIO P7 (input/output) */
gpio_num_t interrupt_gpio; /*!< GPIO pin for interrupt detection (GPIO_NUM_MAX if not used) */
} zh_pcf8574_init_config_t;
/**
* @brief PCF8574 expander handle.
* @brief Statistics structure for error tracking and diagnostics.
*
* Maintains counters for various error types and monitoring data
* to assist with debugging and system health monitoring.
*/
typedef struct
{
uint8_t i2c_address; /*!< Expander I2C address. */
uint8_t gpio_work_mode; /*!< Expander GPIO's work mode. */
uint8_t gpio_status; /*!< Expander GPIO's status. */
bool is_initialized; /*!< Expander initialization flag. */
i2c_master_dev_handle_t dev_handle; /*!< Unique I2C device handle. */
void *system; /*!< System pointer for use in another components. */
} zh_pcf8574_handle_t;
/**
* @brief Structure for error statistics storage.
*/
typedef struct
{
uint32_t i2c_driver_error; /*!< Number of i2c driver error. */
uint32_t event_post_error; /*!< Number of event post error. */
uint32_t vector_error; /*!< Number of vector error. */
uint32_t queue_overflow_error; /*!< Number of queue overflow error. */
uint32_t min_stack_size; /*!< Minimum free stack size. */
uint32_t i2c_driver_error; /*!< Counter of I2C driver errors */
uint32_t event_post_error; /*!< Counter of event posting errors */
uint32_t vector_error; /*!< Counter of vector operation errors */
uint32_t queue_overflow_error; /*!< Counter of queue overflow errors */
uint32_t min_stack_size; /*!< Minimum remaining stack size recorded */
} zh_pcf8574_stats_t;
ESP_EVENT_DECLARE_BASE(ZH_PCF8574);
/**
* @brief Structure for sending data to the event handler when cause an interrupt.
* @brief Event structure for GPIO interrupt events.
*
* @note Should be used with ZH_PCF8574 event base.
* Passed as event data when a GPIO level change is detected
* on any input pin of the PCF8574 device.
*/
typedef struct
{
uint64_t interrupt_time; /*!< Interrupt time. */
zh_pcf8574_gpio_num_t gpio_number; /*!< The GPIO that caused the interrupt. */
uint8_t i2c_address; /*!< The i2c address of PCF8574 expander that caused the interrupt. */
bool gpio_level; /*!< The GPIO level that caused the interrupt. */
uint64_t interrupt_time; /*!< Timestamp of the interrupt occurrence */
zh_pcf8574_gpio_num_t gpio_number; /*!< GPIO pin that triggered the interrupt */
uint8_t i2c_address; /*!< I2C address of the device that generated the interrupt */
bool gpio_level; /*!< Current level of the GPIO pin */
} zh_pcf8574_event_on_isr_t;
/**
* @brief Initialize PCF8574 expander.
* @brief Initialize PCF8574 device.
*
* @param[in] config Pointer to PCF8574 initialized configuration structure. Can point to a temporary variable.
* @param[out] handle Pointer to unique PCF8574 handle.
* Creates an internal task for interrupt processing and configures
* the I2C communication with the PCF8574 device according to the
* provided configuration.
*
* @attention I2C driver must be initialized first.
* @param[in] config Pointer to initialization configuration structure (must not be NULL)
* @param[out] handle Pointer to receive the device handle (must be NULL)
*
* @note Before initialize the expander recommend initialize zh_pcf8574_init_config_t structure with default values.
*
* @code zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT() @endcode
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if config parameter is NULL
* @return ESP_ERR_NO_MEM if memory allocation fails
* @return ESP_ERR_NOT_SUPPORTED if I2C initialization 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 expander.
* @brief Deinitialize PCF8574 device.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* Deletes the internal task and releases all allocated resources.
*
* @return ESP_OK if success or an error code otherwise.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle parameter is NULL
*/
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t *handle);
esp_err_t zh_pcf8574_deinit(zh_pcf8574_handle_t **handle);
/**
* @brief Read PCF8574 all GPIO's status.
* @brief Read data from PCF8574 device.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[out] reg Pointer to GPIO's status.
* Reads a byte from the PCF8574 input ports via I2C communication.
*
* @note For input GPIO's status will be 1 (HIGH) always.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
* @param[out] reg Pointer to receive the read value (must not be NULL)
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle or reg is NULL
* @return ESP_ERR_INVALID_STATE if device is not initialized
*/
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 Set PCF8574 all GPIO's status.
* @brief Write data to PCF8574 device.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] reg GPIO's status.
* Writes a byte to the PCF8574 output ports via I2C communication.
*
* @attention Only the GPIO outputs are affected.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
* @param[in] reg Value to write to the output ports
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle is NULL
* @return ESP_ERR_INVALID_STATE if device is not initialized
*/
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 (set to initial) PCF8574 all GPIO's.
* @brief Reset PCF8574 device.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* Resets the device to its default state, clearing all outputs
* and reinitializing internal structures.
*
* @return ESP_OK if success or an error code otherwise.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle is NULL
*/
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle);
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle);
/**
* @brief Read PCF8574 GPIO status.
* @brief Read the state of a specific GPIO pin.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number.
* @param[out] status Pointer to GPIO status (true - HIGH, false - LOW).
* Reads the current logical level of a specified GPIO pin.
*
* @note For input GPIO's status will be 1 (HIGH) always.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
* @param[in] gpio GPIO pin number to read
* @param[out] status Pointer to receive the pin state (true = high, false = low)
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle, gpio, or status is NULL
* @return ESP_ERR_INVALID_ARG if gpio number exceeds ZH_PCF8574_GPIO_NUM_MAX
*/
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);
/**
* @brief Set PCF8574 GPIO status.
* @brief Write state to a specific GPIO pin.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number.
* @param[in] status GPIO status (true - HIGH, false - LOW).
* Sets the logical level of a specified GPIO pin configured as output.
*
* @attention Only the GPIO output is affected.
* @param[in,out] handle Pointer to the device handle (must not be NULL)
* @param[in] gpio GPIO pin number to write
* @param[in] status Logical level to set (true = high, false = low)
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle, gpio, or status is NULL
* @return ESP_ERR_INVALID_ARG if gpio number exceeds ZH_PCF8574_GPIO_NUM_MAX
*/
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);
/**
* @brief Get error statistics.
* @brief Get device statistics.
*
* @return Pointer to the statistics structure.
* Returns a pointer to the structure containing error counters
* and monitoring data for debugging and diagnostics.
*
* @return Pointer to the statistics structure (valid until reset)
*/
const zh_pcf8574_stats_t *zh_pcf8574_get_stats(void);
/**
* @brief Reset error statistics.
* @brief Reset device statistics.
*
* Clears all error counters and monitoring data.
*/
void zh_pcf8574_reset_stats(void);
+1 -1
View File
@@ -1 +1 @@
2.8.2
3.0.0
+277 -176
View File
@@ -13,178 +13,322 @@ static const char *TAG = "zh_pcf8574";
return err; \
}
TaskHandle_t zh_pcf8574 = NULL;
static SemaphoreHandle_t _interrupt_semaphore = NULL;
#define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \
if (!(cond)) \
{ \
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
cleanup; \
continue; \
}
volatile static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX;
volatile static uint8_t _i2c_matrix[16] = {0};
static const uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
static zh_pcf8574_stats_t _stats = {0};
/**
* @brief Internal handle structure for PCF8574 device.
*
* Minimal handle storing the I2C address of the device instance.
* The full state is maintained in the internal vector.
*/
struct _zh_pcf8574_handle_t
{
uint8_t i2c_address; /*!< I2C address of this device instance */
};
static zh_vector_t *_vector = NULL;
/**
* @brief Internal structure for per-device data stored in the vector.
*
* Holds device-specific state including I2C address, GPIO configuration
* mask, current status, and I2C device handle.
*/
typedef struct
{
uint8_t i2c_address; /*!< I2C address of the device */
uint8_t gpio_work_mode; /*!< GPIO direction mask (1=input, 0=output) */
uint8_t gpio_status; /*!< Current GPIO port state */
i2c_master_dev_handle_t dev_handle; /*!< I2C device handle for communication */
} _zh_pcf8574_vector_data_t;
TaskHandle_t zh_pcf8574 = NULL; /*!< Handle of the internal PCF8574 processing task */
static SemaphoreHandle_t _interrupt_semaphore = NULL; /*!< Semaphore for interrupt-to-task synchronization */
volatile static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX; /*!< GPIO pin used for interrupt detection */
static const uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; /*!< Bit mask for each GPIO pin (P0-P7) */
static zh_pcf8574_stats_t _stats = {0}; /*!< Statistics structure for error tracking */
static zh_vector_t *_vector = NULL; /*!< Vector storing PCF8574 device data */
/**
* @brief Validate initialization configuration.
*
* Checks all configuration parameters for validity, including I2C address range,
* frequency limits, task settings, and checks for address conflicts.
*
* @param config Pointer to initialization configuration structure
*
* @return ESP_OK if configuration is valid
* @return ESP_ERR_INVALID_ARG if any parameter is invalid
*/
static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *config);
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle);
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle);
static esp_err_t _zh_pcf8574_resources_init(const zh_pcf8574_init_config_t *config);
/**
* @brief Configure interrupt GPIO pin.
*
* Sets up the GPIO pin for interrupt detection with negative edge trigger,
* pull-up enabled, and installs the ISR handler.
*
* @param config Pointer to initialization configuration with interrupt_gpio set
*
* @return ESP_OK on success
* @return ESP_FAIL on configuration or ISR installation failure
*/
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config);
/**
* @brief Initialize I2C communication with PCF8574 device.
*
* Adds the device to the I2C bus, probes for device presence, configures
* GPIO directions, and performs initial read/write verification.
*
* @param config Pointer to initialization configuration
* @param vector_data Pointer to receive initialized vector data
*
* @return ESP_OK on success
* @return ESP_FAIL on I2C communication or device probing failure
*/
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, _zh_pcf8574_vector_data_t *vector_data);
/**
* @brief Initialize interrupt resources.
*
* Creates a binary semaphore used for interrupt-to-task synchronization.
*
* @return ESP_OK on success
* @return ESP_ERR_NO_MEM if semaphore creation fails
*/
static esp_err_t _zh_pcf8574_resources_init(void);
/**
* @brief Create interrupt processing task.
*
* Creates a FreeRTOS task that processes interrupts from all connected
* PCF8574 devices and posts corresponding events.
*
* @param config Pointer to initialization configuration
*
* @return ESP_OK on success
* @return ESP_FAIL if task creation fails
*/
static esp_err_t _zh_pcf8574_task_init(const zh_pcf8574_init_config_t *config);
/**
* @brief ISR handler for GPIO interrupt.
*
* Called when an interrupt occurs on the configured GPIO pin.
* Posts a signal to the semaphore to wake up the processing task.
* Executed in ISR context (IRAM).
*
* @param arg Unused callback argument
*/
static void _zh_pcf8574_isr_handler(void *arg);
/**
* @brief Interrupt processing task.
*
* Waits for interrupt signals, reads all PCF8574 devices, detects
* level changes on input pins, and posts events for each change.
* Monitors stack usage continuously.
*
* @param pvParameter Unused task parameter
*/
static void _zh_pcf8574_isr_processing_task(void *pvParameter);
static esp_err_t _zh_pcf8574_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg);
static esp_err_t _zh_pcf8574_write_register(zh_pcf8574_handle_t *handle, uint8_t reg);
/**
* @brief Read register from PCF8574 device via I2C.
*
* Reads the current GPIO port state from the device and updates
* the internal status in vector_data.
*
* @param vector_data Pointer to vector data containing device handle
* @param reg Pointer to receive the register value
*
* @return ESP_OK on success
* @return ESP_FAIL on I2C communication failure
*/
static esp_err_t _zh_pcf8574_read_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t *reg);
/**
* @brief Write register to PCF8574 device via I2C.
*
* Writes a value to the GPIO port output register and updates
* the internal status in vector_data.
*
* @param vector_data Pointer to vector data containing device handle
* @param reg Value to write to the register
*
* @return ESP_OK on success
* @return ESP_FAIL on I2C communication failure
*/
static esp_err_t _zh_pcf8574_write_register(_zh_pcf8574_vector_data_t *vector_data, uint8_t reg);
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) // -V2008
{
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(handle->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "PCF8574 initialization failed. PCF8574 is already initialized.");
ZH_ERROR_CHECK(_zh_pcf8574_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Initial configuration check failed.");
ZH_ERROR_CHECK(_zh_pcf8574_i2c_init(config, handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Failed to add I2C device.");
ZH_ERROR_CHECK(_zh_pcf8574_write_register(handle, handle->gpio_work_mode) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed."), "PCF8574 initialization failed. Failed extender initial GPIO setup.");
if (config->interrupt_gpio < GPIO_NUM_MAX && handle->gpio_work_mode != 0)
ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "PCF8574 initialization failed. PCF8574 is already initialized.");
ZH_ERROR_CHECK(_zh_pcf8574_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Invalid config.");
if (_vector == NULL)
{
ZH_ERROR_CHECK(_zh_pcf8574_gpio_init(config, handle) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed."), "PCF8574 initialization failed. Interrupt GPIO initialization failed.");
ZH_ERROR_CHECK(_zh_pcf8574_resources_init(config) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, 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(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed.");
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed."), "PCF8574 initialization failed. Resources initialization failed.");
ZH_ERROR_CHECK(zh_vector_init(&_vector, sizeof(_zh_pcf8574_vector_data_t)) == ESP_OK, ESP_FAIL, NULL, "PCF8574 initialization failed. Failed to create vector.");
}
_zh_pcf8574_vector_data_t vector_data = {0};
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);
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(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.")};
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)
{
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(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, 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(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed.")};
zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "PCF8574 initialization failed. Resources initialization failed.");
ZH_ERROR_CHECK(_zh_pcf8574_task_init(config) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, 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(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.");
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed.");
vSemaphoreDelete(_interrupt_semaphore), "PCF8574 initialization failed. Task initialization failed.");
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(vector_data.dev_handle) == ESP_OK, ESP_FAIL, 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(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, 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.");
_interrupt_gpio = config->interrupt_gpio;
}
if (_stats.min_stack_size == 0)
{
_stats.min_stack_size = config->stack_size;
}
handle->is_initialized = true;
for (uint8_t i = 0; i < sizeof(_i2c_matrix); ++i)
{
if (_i2c_matrix[i] == 0)
{
_i2c_matrix[i] = handle->i2c_address;
break;
}
}
(*handle)->i2c_address = config->i2c_address;
ZH_LOGI("PCF8574 initialization completed successfully.");
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) // -V2008
{
ZH_LOGI("PCF8574 deinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 deinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "PCF8574 deinitialization failed. PCF8574 not initialized.");
if (_interrupt_gpio < GPIO_NUM_MAX && handle->gpio_work_mode != 0)
{
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 deinitialization failed. Invalid argument.");
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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.");
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(zh_vector_delete_item(&_vector, (uint16_t)index) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Vector delete item failed.");
uint16_t vector_size = 0;
ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_ERR_INVALID_STATE, NULL, "PCF8574 deinitialization failed. Vector get size fail.");
for (uint16_t i = 0; i < vector_size; ++i)
{
zh_pcf8574_handle_t temp_handle = {0};
ZH_ERROR_CHECK(zh_vector_get_item(&_vector, i, &temp_handle) == ESP_OK, ESP_ERR_INVALID_STATE, NULL, "PCF8574 deinitialization failed. Vector get item fail.");
if (handle->i2c_address == temp_handle.i2c_address)
{
ZH_ERROR_CHECK(zh_vector_delete_item(&_vector, i) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Vector delete item fail.");
break;
}
}
ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_ERR_INVALID_STATE, NULL, "PCF8574 deinitialization failed. Vector get size fail.");
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 (_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(gpio_reset_pin(_interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Reset GPIO failed.");
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Free vector failed.");
vSemaphoreDelete(_interrupt_semaphore);
vTaskDelete(zh_pcf8574);
zh_pcf8574 = NULL;
vSemaphoreDelete(_interrupt_semaphore);
_interrupt_semaphore = NULL;
_interrupt_gpio = GPIO_NUM_MAX;
}
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. Free vector failed.");
}
if (handle->system != NULL)
{
heap_caps_free(handle->system);
}
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, NULL, "PCF8574 deinitialization failed. I2C remove device failed.");
handle->is_initialized = false;
for (uint8_t i = 0; i < sizeof(_i2c_matrix); ++i)
{
if (_i2c_matrix[i] == handle->i2c_address)
{
_i2c_matrix[i] = 0;
break;
}
}
heap_caps_free(*handle);
*handle = NULL;
ZH_LOGI("PCF8574 deinitialization completed successfully.");
return ESP_OK;
}
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) // -V2008
{
ZH_LOGI("PCF8574 read register started.");
ZH_ERROR_CHECK(handle != NULL && reg != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read register failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "PCF8574 read register failed. PCF8574 not initialized.");
ZH_ERROR_CHECK(_zh_pcf8574_read_register(handle, reg) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read register failed.");
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && reg != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 read register failed. Invalid argument.");
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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 read register 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 read register failed. Failed to get vector item data.");
ZH_ERROR_CHECK(_zh_pcf8574_read_register(&vector_data, reg) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read register failed. PCF8574 read register failed.");
ZH_ERROR_CHECK(zh_vector_change_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read register failed. Failed to change vector item data.");
ZH_LOGI("PCF8574 read register completed successfully.");
return ESP_OK;
}
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) // -V2008
{
ZH_LOGI("PCF8574 write register started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write register failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "PCF8574 write register failed. PCF8574 not initialized.");
ZH_ERROR_CHECK(_zh_pcf8574_write_register(handle, (reg | handle->gpio_work_mode)) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write register failed.");
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 write register failed. Invalid argument.");
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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 write register 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 write register failed. Failed to get vector item data.");
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, (reg | vector_data.gpio_work_mode)) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write register 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 register failed. Failed to change vector item data.");
ZH_LOGI("PCF8574 write register completed successfully.");
return ESP_OK;
}
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle)
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t **handle) // -V2008
{
ZH_LOGI("PCF8574 reset register started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 reset register failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "PCF8574 reset register failed. PCF8574 not initialized.");
ZH_ERROR_CHECK(_zh_pcf8574_write_register(handle, handle->gpio_work_mode) == ESP_OK, ESP_FAIL, NULL, "PCF8574 reset register failed.");
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "PCF8574 reset register failed. Invalid argument.");
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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 read register 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 reset register failed. Failed to get vector item data.");
ZH_ERROR_CHECK(_zh_pcf8574_write_register(&vector_data, vector_data.gpio_work_mode) == ESP_OK, ESP_FAIL, NULL, "PCF8574 reset register failed. PCF8574 write register failed.");
ZH_ERROR_CHECK(zh_vector_change_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 reset register failed. Failed to change vector item data.");
ZH_LOGI("PCF8574 reset register completed successfully.");
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) // -V2008
{
ZH_LOGI("PCF8574 read GPIO started.");
ZH_ERROR_CHECK(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.");
ZH_ERROR_CHECK(gpio >= ZH_PCF8574_GPIO_NUM_P0 && gpio < ZH_PCF8574_GPIO_NUM_MAX, ESP_FAIL, NULL, "PCF8574 read GPIO failed. Invalid GPIO number.")
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "PCF8574 read GPIO failed. PCF8574 not initialized.");
uint8_t gpio_temp = _gpio_matrix[gpio];
uint8_t reg_temp = 0;
ZH_ERROR_CHECK(_zh_pcf8574_read_register(handle, &reg_temp) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read GPIO failed.");
*status = ((reg_temp & gpio_temp) ? 1 : 0);
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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 read GPIO 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 read GPIO failed. Failed to get vector item data.");
ZH_ERROR_CHECK(_zh_pcf8574_read_register(&vector_data, &reg_temp) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read GPIO failed. PCF8574 read register failed.");
ZH_ERROR_CHECK(zh_vector_change_item(&_vector, (uint16_t)index, &vector_data) == ESP_OK, ESP_FAIL, NULL, "PCF8574 read GPIO failed. Failed to change vector item data.");
*status = ((reg_temp & _gpio_matrix[(uint8_t)gpio]) ? 1 : 0);
ZH_LOGI("PCF8574 read GPIO completed successfully.");
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) // -V2008
{
ZH_LOGI("PCF8574 write GPIO started.");
ZH_ERROR_CHECK(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.");
ZH_ERROR_CHECK(gpio >= ZH_PCF8574_GPIO_NUM_P0 && gpio < ZH_PCF8574_GPIO_NUM_MAX, ESP_FAIL, NULL, "PCF8574 write GPIO failed. Invalid GPIO number.")
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "PCF8574 write GPIO failed. PCF8574 not initialized.");
uint8_t gpio_temp = _gpio_matrix[gpio];
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
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 write GPIO 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 write GPIO failed. Failed to get vector item data.");
if (status == true)
{
ZH_ERROR_CHECK(_zh_pcf8574_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio_temp) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed.");
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. */
}
else
{
ZH_ERROR_CHECK(_zh_pcf8574_write_register(handle, (handle->gpio_status & ~gpio_temp) | handle->gpio_work_mode) == ESP_OK, ESP_FAIL, NULL, "PCF8574 write GPIO failed.");
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_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.");
return ESP_OK;
}
@@ -212,26 +356,18 @@ static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *con
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->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
for (uint8_t i = 0; i < sizeof(_i2c_matrix); ++i)
if (_vector != NULL)
{
ZH_ERROR_CHECK(config->i2c_address != _i2c_matrix[i], ESP_ERR_INVALID_ARG, NULL, "I2C address already present.");
_zh_pcf8574_vector_data_t vector_data = {0};
int32_t index = 0;
ZH_ERROR_CHECK(zh_vector_find_item_in_field(&_vector, &vector_data, &vector_data.i2c_address, sizeof(vector_data.i2c_address), &config->i2c_address, 0, &index) == ESP_ERR_NOT_FOUND,
ESP_ERR_INVALID_ARG, NULL, "I2C address already present.");
}
return ESP_OK;
}
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle) // -V2008
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config) // -V2008
{
uint8_t reg_temp = 0;
if (_interrupt_gpio != GPIO_NUM_MAX)
{
ZH_ERROR_CHECK(_zh_pcf8574_read_register(handle, &reg_temp) == ESP_OK, ESP_FAIL, NULL, "Failed to read expander register.");
ZH_ERROR_CHECK(zh_vector_push_back(&_vector, handle) == ESP_OK, ESP_FAIL, NULL, "Failed add item to vector.")
return ESP_OK;
}
ZH_ERROR_CHECK(zh_vector_init(&_vector, sizeof(zh_pcf8574_handle_t)) == ESP_OK, ESP_FAIL, NULL, "Failed create vector.")
ZH_ERROR_CHECK(_zh_pcf8574_read_register(handle, &reg_temp) == ESP_OK, ESP_FAIL, NULL, "Failed to read expander register.");
ZH_ERROR_CHECK(zh_vector_push_back(&_vector, handle) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed."), "Failed add item to vector.")
gpio_config_t interrupt_gpio_config = {
.intr_type = GPIO_INTR_NEGEDGE,
.mode = GPIO_MODE_INPUT,
@@ -239,19 +375,16 @@ static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config, z
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
ZH_ERROR_CHECK(gpio_config(&interrupt_gpio_config) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed."), "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);
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.");
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed."), "Failed install isr service.")
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, 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_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.");
ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Free vector failed."), "Failed add isr handler.")
{ZH_ERROR_CHECK(gpio_reset_pin(config->interrupt_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset gpio failed.")}, "Failed add isr handler.")
return ESP_OK;
}
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle)
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, _zh_pcf8574_vector_data_t *vector_data) // -V2008
{
i2c_device_config_t pcf8574_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
@@ -261,33 +394,31 @@ static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, zh
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_probe(config->i2c_handle, config->i2c_address, 1000 / portTICK_PERIOD_MS) == 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.");
handle->dev_handle = _dev_handle;
handle->gpio_work_mode = (config->p7_gpio_work_mode << 7) | (config->p6_gpio_work_mode << 6) | (config->p5_gpio_work_mode << 5) |
{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.");
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->p1_gpio_work_mode << 1) | (config->p0_gpio_work_mode << 0);
handle->gpio_status = handle->gpio_work_mode;
handle->i2c_address = config->i2c_address;
vector_data->dev_handle = _dev_handle;
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(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "Failed extender initial GPIO setup.");
uint8_t reg_temp = 0;
ZH_ERROR_CHECK(_zh_pcf8574_read_register(vector_data, &reg_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.");
return ESP_OK;
}
static esp_err_t _zh_pcf8574_resources_init(const zh_pcf8574_init_config_t *config)
static esp_err_t _zh_pcf8574_resources_init(void)
{
if (_interrupt_gpio == GPIO_NUM_MAX)
{
_interrupt_semaphore = xSemaphoreCreateBinary();
ZH_ERROR_CHECK(_interrupt_semaphore != NULL, ESP_ERR_NO_MEM, NULL, "Failed to create semaphore.")
}
return ESP_OK;
}
static esp_err_t _zh_pcf8574_task_init(const zh_pcf8574_init_config_t *config)
{
if (_interrupt_gpio == GPIO_NUM_MAX)
{
ZH_ERROR_CHECK(xTaskCreatePinnedToCore(&_zh_pcf8574_isr_processing_task, "zh_pcf8574_isr_processing_task", config->stack_size, NULL, config->task_priority, &zh_pcf8574, tskNO_AFFINITY) == pdPASS,
ESP_FAIL, NULL, "Failed to create isr processing task.")
}
return ESP_OK;
}
@@ -304,63 +435,33 @@ static void IRAM_ATTR _zh_pcf8574_isr_handler(void *arg)
};
}
static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter)
static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter) // -V2008
{
for (;;)
{
xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY);
uint16_t vector_size = 0;
if (zh_vector_get_size(&_vector, &vector_size) != ESP_OK)
{
++_stats.vector_error;
ZH_LOGE("PCF8574 isr processing failed. Failed to get vector size.", ESP_FAIL);
}
else
{
ZH_ERROR_CHECK_CONT(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ++_stats.vector_error, "PCF8574 isr processing failed. Failed to get vector size.");
for (uint16_t i = 0; i < vector_size; ++i)
{
zh_pcf8574_handle_t handle = {0};
esp_err_t err = zh_vector_get_item(&_vector, i, &handle);
if (err != ESP_OK)
{
++_stats.vector_error;
ZH_LOGE("PCF8574 isr processing failed. Failed to get vector item data.", ESP_FAIL);
continue;
}
_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_pcf8574_event_on_isr_t event = {0};
event.i2c_address = handle.i2c_address;
uint8_t old_reg = handle.gpio_status;
event.i2c_address = vector_data.i2c_address;
uint8_t old_reg = vector_data.gpio_status;
uint8_t new_reg = 0;
err = _zh_pcf8574_read_register(&handle, &new_reg);
if (err != ESP_OK)
{
ZH_LOGE("PCF8574 isr processing failed. Failed to read expander register.", err);
continue;
}
err = zh_vector_change_item(&_vector, i, &handle);
if (err != ESP_OK)
{
++_stats.vector_error;
ZH_LOGE("PCF8574 isr processing failed. Failed to change vector item data.", ESP_FAIL);
continue;
}
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_vector_change_item(&_vector, i, &vector_data) == ESP_OK, ++_stats.vector_error, "PCF8574 isr processing failed. Failed to change vector item data.");
for (uint8_t j = 0; j <= 7; ++j)
{
if ((handle.gpio_work_mode & _gpio_matrix[j]) != 0)
if ((vector_data.gpio_work_mode & _gpio_matrix[j]) != 0)
{
if ((old_reg & _gpio_matrix[j]) != (new_reg & _gpio_matrix[j]))
{
event.gpio_number = j;
event.gpio_level = new_reg & _gpio_matrix[j];
event.interrupt_time = esp_timer_get_time();
err = esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), 1000 / portTICK_PERIOD_MS);
if (err != ESP_OK)
{
++_stats.event_post_error;
ZH_LOGE("PCF8574 isr processing failed. Failed to post interrupt event.", err);
continue;
}
}
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.");
}
}
}
@@ -370,18 +471,18 @@ static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter)
vTaskDelete(NULL);
}
static esp_err_t _zh_pcf8574_read_register(zh_pcf8574_handle_t *handle, 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(handle->dev_handle, &handle->gpio_status, sizeof(handle->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), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "I2C driver error.");
*reg = handle->gpio_status;
*reg = vector_data->gpio_status;
return ESP_OK;
}
static esp_err_t _zh_pcf8574_write_register(zh_pcf8574_handle_t *handle, 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(handle->dev_handle, &reg, sizeof(reg), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_transmit(vector_data->dev_handle, &reg, sizeof(reg), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "I2C driver error.");
handle->gpio_status = reg;
vector_data->gpio_status = reg;
return ESP_OK;
}