10 Commits

7 changed files with 275 additions and 309 deletions

View File

@@ -1,6 +1 @@
if(${IDF_TARGET} STREQUAL esp8266) idf_component_register(SRCS "zh_pcf8574.c" INCLUDE_DIRS "include" REQUIRES driver esp_event zh_vector)
set(requires driver zh_vector)
else()
set(requires driver esp_event zh_vector)
endif()
idf_component_register(SRCS "zh_pcf8574.c" INCLUDE_DIRS "include" REQUIRES ${requires})

View File

@@ -1,9 +1,8 @@
# ESP32 ESP-IDF and ESP8266 RTOS SDK component for PCF8574(A) 8-bit I/O expander # ESP32 ESP-IDF component for PCF8574(A) 8-bit I/O expander
## Tested on ## Tested on
1. [ESP8266 RTOS_SDK v3.4](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/index.html#) 1. [ESP32 ESP-IDF v5.5.1](https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32/index.html)
2. [ESP32 ESP-IDF v5.4](https://docs.espressif.com/projects/esp-idf/en/release-v5.4/esp32/index.html)
## Features ## Features
@@ -15,11 +14,11 @@
1. Enable interrupt support only if input GPIO's are used. 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. 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. They must be connected to ground to trigger an interrupt. 3. The input GPIO's are always pullup to the power supply.
## Dependencies ## Dependencies
1. [zh_vector](http://git.zh.com.ru/alexey.zholtikov/zh_vector) 1. [zh_vector](http://git.zh.com.ru/esp_components/zh_vector)
## Using ## Using
@@ -27,8 +26,8 @@ In an existing project, run the following command to install the components:
```text ```text
cd ../your_project/components cd ../your_project/components
git clone http://git.zh.com.ru/alexey.zholtikov/zh_pcf8574 git clone http://git.zh.com.ru/esp_components/zh_pcf8574
git clone http://git.zh.com.ru/alexey.zholtikov/zh_vector git clone http://git.zh.com.ru/esp_components/zh_vector
``` ```
In the application, add the component: In the application, add the component:
@@ -46,10 +45,7 @@ One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enab
#define I2C_PORT (I2C_NUM_MAX - 1) #define I2C_PORT (I2C_NUM_MAX - 1)
#ifndef CONFIG_IDF_TARGET_ESP8266 i2c_master_bus_handle_t i2c_bus_handle = NULL;
i2c_master_bus_handle_t i2c_bus_handle = {0};
#endif
zh_pcf8574_handle_t pcf8574_handle = {0}; 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); // Required only if used input GPIO interrupts. void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); // Required only if used input GPIO interrupts.
@@ -67,44 +63,24 @@ void print_gpio_status(const char *message, uint8_t reg)
void app_main(void) void app_main(void)
{ {
esp_log_level_set("zh_pcf8574", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR);
esp_log_level_set("zh_vector", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. esp_log_level_set("zh_vector", ESP_LOG_ERROR);
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_4, // In accordance with used chip.
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = GPIO_NUM_5, // In accordance with used chip.
.scl_pullup_en = GPIO_PULLUP_ENABLE,
};
i2c_driver_install(I2C_PORT, i2c_config.mode);
i2c_param_config(I2C_PORT, &i2c_config);
#else
i2c_master_bus_config_t i2c_bus_config = { i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT, .clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT, .i2c_port = I2C_PORT,
.scl_io_num = GPIO_NUM_22, // In accordance with used chip. .scl_io_num = GPIO_NUM_22,
.sda_io_num = GPIO_NUM_21, // In accordance with used chip. .sda_io_num = GPIO_NUM_21,
.glitch_ignore_cnt = 7, .glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true, .flags.enable_internal_pullup = true,
}; };
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
#endif esp_event_loop_create_default(); // Required only if used input GPIO interrupts.
esp_event_loop_create_default(); // Required only if used input GPIO interrupts.
#ifdef CONFIG_IDF_TARGET_ESP8266
esp_event_handler_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL); // Required only if used input GPIO interrupts.
#else
esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL); // Required only if used input GPIO interrupts. esp_event_handler_instance_register(ZH_PCF8574, ESP_EVENT_ANY_ID, &zh_pcf8574_event_handler, NULL, NULL); // Required only if used input GPIO interrupts.
#endif
zh_pcf8574_init_config_t pcf8574_init_config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); zh_pcf8574_init_config_t pcf8574_init_config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
#ifdef CONFIG_IDF_TARGET_ESP8266
pcf8574_init_config.i2c_port = I2C_PORT;
#else
pcf8574_init_config.i2c_handle = i2c_bus_handle; pcf8574_init_config.i2c_handle = i2c_bus_handle;
#endif
pcf8574_init_config.i2c_address = 0x38; pcf8574_init_config.i2c_address = 0x38;
pcf8574_init_config.p0_gpio_work_mode = EXP_GPIO_INPUT; // Required only for input GPIO. pcf8574_init_config.p0_gpio_work_mode = true; // Required only for input GPIO.
pcf8574_init_config.interrupt_gpio = GPIO_NUM_14; // Required only if used input GPIO interrupts. pcf8574_init_config.interrupt_gpio = GPIO_NUM_14; // Required only if used input GPIO interrupts.
zh_pcf8574_init(&pcf8574_init_config, &pcf8574_handle); zh_pcf8574_init(&pcf8574_init_config, &pcf8574_handle);
uint8_t reg = 0; uint8_t reg = 0;
zh_pcf8574_read(&pcf8574_handle, &reg); zh_pcf8574_read(&pcf8574_handle, &reg);
@@ -114,13 +90,13 @@ void app_main(void)
zh_pcf8574_read(&pcf8574_handle, &reg); zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg); print_gpio_status("GPIO status: ", reg);
printf("Sets P0 to 0.\n"); printf("Sets P0 to 0.\n");
zh_pcf8574_write_gpio(&pcf8574_handle, EXP_GPIO_NUM_P0, 0); // GPIO P0 will not be changed because it is operating in input mode. zh_pcf8574_write_gpio(&pcf8574_handle, 0, false); // GPIO P0 will not be changed because it is operating in input mode.
bool gpio = 0; bool gpio = 0;
zh_pcf8574_read_gpio(&pcf8574_handle, EXP_GPIO_NUM_P0, &gpio); zh_pcf8574_read_gpio(&pcf8574_handle, 0, &gpio);
printf("P0 status: %d.\n", gpio); printf("P0 status: %d.\n", gpio);
printf("Set P1 to 0.\n"); printf("Set P1 to 0.\n");
zh_pcf8574_write_gpio(&pcf8574_handle, EXP_GPIO_NUM_P1, 0); zh_pcf8574_write_gpio(&pcf8574_handle, 1, false);
zh_pcf8574_read_gpio(&pcf8574_handle, EXP_GPIO_NUM_P1, &gpio); zh_pcf8574_read_gpio(&pcf8574_handle, 1, &gpio);
printf("P1 status: %d.\n", gpio); printf("P1 status: %d.\n", gpio);
zh_pcf8574_read(&pcf8574_handle, &reg); zh_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg); print_gpio_status("GPIO status: ", reg);
@@ -133,6 +109,6 @@ void app_main(void)
void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) // Required only if used input GPIO interrupts. void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) // Required only if used input GPIO interrupts.
{ {
zh_pcf8574_event_on_isr_t *event = event_data; zh_pcf8574_event_on_isr_t *event = event_data;
printf("Interrupt happened on device address 0x%02X on GPIO number %d.\n", event->i2c_address, event->gpio_number); printf("Interrupt happened on device address 0x%02X on GPIO number %d at level %d.\n", event->i2c_address, event->gpio_number, event->gpio_level);
} }
``` ```

View File

148
include/zh_pcf8574.h Normal file → Executable file
View File

@@ -1,22 +1,33 @@
/**
* @file zh_pcf8574.h
*/
#pragma once #pragma once
#include "esp_log.h" #include "esp_log.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#ifdef CONFIG_IDF_TARGET_ESP8266
#include "driver/i2c.h"
#else
#include "driver/i2c_master.h" #include "driver/i2c_master.h"
#endif
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_event.h" #include "esp_event.h"
#include "zh_vector.h" #include "zh_vector.h"
/**
* @brief PCF8574 expander initial default values.
*/
#define ZH_PCF8574_INIT_CONFIG_DEFAULT() \ #define ZH_PCF8574_INIT_CONFIG_DEFAULT() \
{ \ { \
.task_priority = 10, \ .task_priority = 10, \
.stack_size = 2048, \ .stack_size = 2048, \
.i2c_address = 0xFF, \ .i2c_address = 0xFF, \
.p0_gpio_work_mode = 0, \
.p1_gpio_work_mode = 0, \
.p2_gpio_work_mode = 0, \
.p3_gpio_work_mode = 0, \
.p4_gpio_work_mode = 0, \
.p5_gpio_work_mode = 0, \
.p6_gpio_work_mode = 0, \
.p7_gpio_work_mode = 0, \
.interrupt_gpio = GPIO_NUM_MAX} .interrupt_gpio = GPIO_NUM_MAX}
#ifdef __cplusplus #ifdef __cplusplus
@@ -24,63 +35,51 @@ extern "C"
{ {
#endif #endif
typedef enum // Enumeration of possible GPIO work mode. /**
* @brief Structure for initial initialization of PCF8574 expander.
*/
typedef struct
{ {
EXP_GPIO_OUTPUT, uint8_t task_priority; /*!< Task priority for the PCF8574 expander isr processing. @note It is not recommended to set a value less than 10. */
EXP_GPIO_INPUT uint16_t stack_size; /*!< Stack size for task for the PCF8574 expander isr processing processing. @note The minimum size is 2048 bytes. */
} zh_pcf8574_gpio_work_mode_t; uint8_t i2c_address; /*!< Expander I2C address. */
bool p0_gpio_work_mode; /*!< Expander GPIO PO work mode. True for input, false for output. */
typedef enum // Enumeration of possible GPIO's number. bool p1_gpio_work_mode; /*!< Expander GPIO P1 work mode. True for input, false for output. */
{ bool p2_gpio_work_mode; /*!< Expander GPIO P2 work mode. True for input, false for output. */
EXP_GPIO_NUM_P0 = 0x01, bool p3_gpio_work_mode; /*!< Expander GPIO P3 work mode. True for input, false for output. */
EXP_GPIO_NUM_P1 = 0x02, bool p4_gpio_work_mode; /*!< Expander GPIO P4 work mode. True for input, false for output. */
EXP_GPIO_NUM_P2 = 0x04, bool p5_gpio_work_mode; /*!< Expander GPIO P5 work mode. True for input, false for output. */
EXP_GPIO_NUM_P3 = 0x08, bool p6_gpio_work_mode; /*!< Expander GPIO P6 work mode. True for input, false for output. */
EXP_GPIO_NUM_P4 = 0x10, bool p7_gpio_work_mode; /*!< Expander GPIO P7 work mode. True for input, false for output. */
EXP_GPIO_NUM_P5 = 0x20, uint8_t interrupt_gpio; /*!< Interrupt GPIO. @attention Must be same for all PCF8574 expanders. */
EXP_GPIO_NUM_P6 = 0x40, i2c_master_bus_handle_t i2c_handle; /*!< Unique I2C bus handle. @attention Must be same for all PCF8574 expanders. */
EXP_GPIO_NUM_P7 = 0x80
} zh_pcf8574_gpio_number_t;
typedef struct // Structure for initial initialization of PCF8574 expander.
{
uint8_t task_priority; // Task priority for the PCF8574 expander isr processing. @note It is not recommended to set a value less than 10.
uint16_t stack_size; // Stack size for task for the PCF8574 expander isr processing processing. @note The minimum size is 2048 bytes.
uint8_t i2c_address; // Expander I2C address.
zh_pcf8574_gpio_work_mode_t p0_gpio_work_mode; // Expander GPIO PO work mode.
zh_pcf8574_gpio_work_mode_t p1_gpio_work_mode; // Expander GPIO P1 work mode.
zh_pcf8574_gpio_work_mode_t p2_gpio_work_mode; // Expander GPIO P2 work mode.
zh_pcf8574_gpio_work_mode_t p3_gpio_work_mode; // Expander GPIO P3 work mode.
zh_pcf8574_gpio_work_mode_t p4_gpio_work_mode; // Expander GPIO P4 work mode.
zh_pcf8574_gpio_work_mode_t p5_gpio_work_mode; // Expander GPIO P5 work mode.
zh_pcf8574_gpio_work_mode_t p6_gpio_work_mode; // Expander GPIO P6 work mode.
zh_pcf8574_gpio_work_mode_t p7_gpio_work_mode; // Expander GPIO P7 work mode.
gpio_num_t interrupt_gpio; // Interrupt GPIO. @attention Must be same for all PCF8574 expanders.
bool i2c_port; // I2C port. @attention Must be same for all PCF8574 expanders.
#ifndef CONFIG_IDF_TARGET_ESP8266
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle. @attention Must be same for all PCF8574 expanders.
#endif
} zh_pcf8574_init_config_t; } zh_pcf8574_init_config_t;
typedef struct // PCF8574 expander handle. /**
* @brief PCF8574 expander handle.
*/
typedef struct
{ {
uint8_t i2c_address; // Expander I2C address. uint8_t i2c_address; /*!< Expander I2C address. */
uint8_t gpio_work_mode; // Expander GPIO's work mode. uint8_t gpio_work_mode; /*!< Expander GPIO's work mode. */
uint8_t gpio_status; // Expander GPIO's status. uint8_t gpio_status; /*!< Expander GPIO's status. */
bool is_initialized; // Expander initialization flag. bool is_initialized; /*!< Expander initialization flag. */
bool i2c_port; // I2C port. i2c_master_dev_handle_t dev_handle; /*!< Unique I2C device handle. */
#ifndef CONFIG_IDF_TARGET_ESP8266 void *system; /*!< System pointer for use in another components. */
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle.
i2c_master_dev_handle_t dev_handle; // Unique I2C device handle.
#endif
} zh_pcf8574_handle_t; } zh_pcf8574_handle_t;
ESP_EVENT_DECLARE_BASE(ZH_PCF8574); ESP_EVENT_DECLARE_BASE(ZH_PCF8574);
typedef struct // Structure for sending data to the event handler when cause an interrupt. @note Should be used with ZH_PCF8574W event base. /**
* @brief Structure for sending data to the event handler when cause an interrupt.
*
* @note Should be used with ZH_PCF8574 event base.
*/
typedef struct
{ {
uint8_t i2c_address; // The i2c address of PCF8574 expander that caused the interrupt. uint8_t i2c_address; /*!< The i2c address of PCF8574 expander that caused the interrupt. */
uint8_t gpio_number; // The GPIO that caused the interrupt. uint8_t gpio_number; /*!< The GPIO that caused the interrupt. */
bool gpio_level; /*!< The GPIO level that caused the interrupt. */
} zh_pcf8574_event_on_isr_t; } zh_pcf8574_event_on_isr_t;
/** /**
@@ -95,12 +94,7 @@ extern "C"
* *
* @code zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT() @endcode * @code zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT() @endcode
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if initialization was success
* - ESP_ERR_INVALID_ARG if parameter error or invalid i2c adress
* - ESP_ERR_NOT_FOUND if expander not connected or not responded
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
* - ESP_FAIL if internal error
*/ */
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);
@@ -112,11 +106,7 @@ extern "C"
* *
* @note For input GPIO's status will be 1 (HIGH) always. * @note For input GPIO's status will be 1 (HIGH) always.
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if read was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/ */
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);
@@ -128,11 +118,7 @@ extern "C"
* *
* @attention Only the GPIO outputs are affected. * @attention Only the GPIO outputs are affected.
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if write was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/ */
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);
@@ -141,11 +127,7 @@ extern "C"
* *
* @param[in] handle Pointer to unique PCF8574 handle. * @param[in] handle Pointer to unique PCF8574 handle.
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if reset was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/ */
esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle); esp_err_t zh_pcf8574_reset(zh_pcf8574_handle_t *handle);
@@ -154,34 +136,26 @@ extern "C"
* *
* @param[in] handle Pointer to unique PCF8574 handle. * @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number. * @param[in] gpio GPIO number.
* @param[out] status Pointer to GPIO status. * @param[out] status Pointer to GPIO status (true - HIGH, false - LOW).
* *
* @note For input GPIO's status will be 1 (HIGH) always. * @note For input GPIO's status will be 1 (HIGH) always.
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if read was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/ */
esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool *status); esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t *handle, uint8_t gpio, bool *status);
/** /**
* @brief Set PCF8574 GPIO status. * @brief Set PCF8574 GPIO status.
* *
* @param[in] handle Pointer to unique PCF8574 handle. * @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] gpio GPIO number. * @param[in] gpio GPIO number.
* @param[in] status Pointer to GPIO status. * @param[in] status GPIO status (true - HIGH, false - LOW).
* *
* @attention Only the GPIO output is affected. * @attention Only the GPIO output is affected.
* *
* @return * @return ESP_OK if success or an error code otherwise.
* - ESP_OK if write was success
* - ESP_ERR_INVALID_ARG if parameter error
* - ESP_ERR_NOT_FOUND if expander is not initialized
* - ESP_ERR_INVALID_RESPONSE if I2C driver error
*/ */
esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t *handle, zh_pcf8574_gpio_number_t gpio, bool status); esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t *handle, uint8_t gpio, bool status);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -1 +1 @@
1.0.0 2.0.0

365
zh_pcf8574.c Normal file → Executable file
View File

@@ -2,122 +2,206 @@
static const char *TAG = "zh_pcf8574"; static const char *TAG = "zh_pcf8574";
static gpio_num_t _interrupt_gpio = GPIO_NUM_MAX; #define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__)
static SemaphoreHandle_t _interrupt_semaphore = {0}; #define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__)
#define ZH_ERROR_CHECK(cond, err, cleanup, msg, ...) \
if (!(cond)) \
{ \
ZH_LOGE(msg, err, ##__VA_ARGS__); \
cleanup; \
return err; \
}
static uint8_t _interrupt_gpio = GPIO_NUM_MAX;
static SemaphoreHandle_t _interrupt_semaphore = NULL;
static const uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
static zh_vector_t _vector = {0}; static zh_vector_t _vector = {0};
static void _zh_isr_handler(void *arg); static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *config);
static void _zh_isr_processing_task(void *pvParameter); 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_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg); 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_write_register(zh_pcf8574_handle_t *handle, uint8_t reg); static esp_err_t _zh_pcf8574_resources_init(const zh_pcf8574_init_config_t *config);
static esp_err_t _zh_pcf8574_task_init(const zh_pcf8574_init_config_t *config);
static void _zh_pcf8574_isr_handler(void *arg);
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);
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) esp_err_t zh_pcf8574_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle)
{ {
ESP_LOGI(TAG, "PCF8574 initialization begin."); ZH_LOGI("PCF8574 initialization started.");
if (config == NULL || handle == NULL || config->i2c_address == 0xFF) ZH_ERROR_CHECK(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.");
esp_err_t err = _zh_pcf8574_validate_config(config);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 initialization failed. Initial configuration check failed.");
err = _zh_pcf8574_i2c_init(config, handle);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 initialization failed. Failed to add I2C device.");
err = _zh_pcf8574_write_register(handle, handle->gpio_work_mode);
ZH_ERROR_CHECK(err == ESP_OK, err, i2c_master_bus_rm_device(handle->dev_handle), "PCF8574 initialization failed. Failed extender initial GPIO setup.");
if (config->interrupt_gpio < GPIO_NUM_MAX && handle->gpio_work_mode != 0)
{ {
ESP_LOGE(TAG, "PCF8574 initialization fail. Invalid argument."); err = _zh_pcf8574_gpio_init(config, handle);
return ESP_ERR_INVALID_ARG; ZH_ERROR_CHECK(err == ESP_OK, err, i2c_master_bus_rm_device(handle->dev_handle), "PCF8574 initialization failed. Interrupt GPIO initialization failed.");
err = _zh_pcf8574_resources_init(config);
ZH_ERROR_CHECK(err == ESP_OK, err, i2c_master_bus_rm_device(handle->dev_handle); gpio_isr_handler_remove(config->interrupt_gpio); gpio_uninstall_isr_service();
gpio_reset_pin(config->interrupt_gpio); zh_vector_free(&_vector), "PCF8574 initialization failed. Resources initialization failed.");
err = _zh_pcf8574_task_init(config);
ZH_ERROR_CHECK(err == ESP_OK, err, i2c_master_bus_rm_device(handle->dev_handle); gpio_isr_handler_remove(config->interrupt_gpio); gpio_uninstall_isr_service();
gpio_reset_pin(config->interrupt_gpio); zh_vector_free(&_vector); vSemaphoreDelete(_interrupt_semaphore), "PCF8574 initialization failed. Task initialization failed.");
} }
handle->i2c_address = config->i2c_address;
handle->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_port = config->i2c_port;
#ifndef CONFIG_IDF_TARGET_ESP8266
handle->i2c_handle = config->i2c_handle;
i2c_device_config_t pcf8574_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = handle->i2c_address,
.scl_speed_hz = 100000,
};
i2c_master_bus_add_device(handle->i2c_handle, &pcf8574_config, &handle->dev_handle);
if (i2c_master_probe(handle->i2c_handle, handle->i2c_address, 1000 / portTICK_PERIOD_MS) != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 initialization fail. Expander not connected or not responded.");
return ESP_ERR_NOT_FOUND;
}
#endif
handle->is_initialized = true; handle->is_initialized = true;
if (_zh_write_register(handle, handle->gpio_work_mode) != ESP_OK) ZH_LOGI("PCF8574 initialization completed successfully.");
{
ESP_LOGE(TAG, "PCF8574 initialization fail. I2C driver error at line %d.", __LINE__);
handle->is_initialized = false;
return ESP_ERR_INVALID_RESPONSE;
}
if (_interrupt_gpio == GPIO_NUM_MAX && config->interrupt_gpio != GPIO_NUM_MAX)
{
zh_vector_init(&_vector, sizeof(zh_pcf8574_handle_t));
_interrupt_gpio = config->interrupt_gpio;
gpio_config_t isr_pin_config = {0};
isr_pin_config.intr_type = GPIO_INTR_NEGEDGE;
isr_pin_config.mode = GPIO_MODE_INPUT;
isr_pin_config.pin_bit_mask = (1ULL << _interrupt_gpio);
isr_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
isr_pin_config.pull_up_en = GPIO_PULLUP_ENABLE;
if (gpio_config(&isr_pin_config) != ESP_OK)
{
ESP_LOGW(TAG, "PCF8574 initialization warning. GPIO driver error at line %d.", __LINE__);
}
else
{
_interrupt_semaphore = xSemaphoreCreateBinary();
if (xTaskCreatePinnedToCore(&_zh_isr_processing_task, "_zh_isr_processing_task", config->stack_size, NULL, config->task_priority, NULL, tskNO_AFFINITY) != pdPASS)
{
ESP_LOGE(TAG, "PCF8574 initialization warning. Internal error at line %d.", __LINE__);
return ESP_FAIL;
}
gpio_install_isr_service(0);
gpio_isr_handler_add(_interrupt_gpio, _zh_isr_handler, NULL);
}
}
if (_interrupt_gpio != GPIO_NUM_MAX && handle->gpio_work_mode != 0)
{
zh_vector_push_back(&_vector, handle);
}
ESP_LOGI(TAG, "PCF8574 initialization success.");
return ESP_OK; 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)
{ {
return _zh_read_register(handle, reg); 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.");
esp_err_t err = _zh_pcf8574_read_register(handle, reg);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 read register failed.");
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)
{ {
return _zh_write_register(handle, (reg | handle->gpio_work_mode)); 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.");
esp_err_t err = _zh_pcf8574_write_register(handle, (reg | handle->gpio_work_mode));
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 write register failed.");
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)
{ {
return _zh_write_register(handle, handle->gpio_work_mode); 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.");
esp_err_t err = _zh_pcf8574_write_register(handle, handle->gpio_work_mode);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 reset register failed.");
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_number_t gpio, bool *status) esp_err_t zh_pcf8574_read_gpio(zh_pcf8574_handle_t *handle, uint8_t gpio, bool *status)
{ {
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(gpio <= 7, 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; uint8_t reg_temp = 0;
esp_err_t err = _zh_read_register(handle, &reg_temp); esp_err_t err = _zh_pcf8574_read_register(handle, &reg_temp);
*status = ((reg_temp & gpio) ? 1 : 0); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 read GPIO failed.");
return err; *status = ((reg_temp & gpio_temp) ? 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_number_t gpio, bool status) esp_err_t zh_pcf8574_write_gpio(zh_pcf8574_handle_t *handle, uint8_t gpio, bool status)
{ {
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(gpio <= 7, 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];
esp_err_t err = ESP_OK;
if (status == true) if (status == true)
{ {
return _zh_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio); err = _zh_pcf8574_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio_temp);
} }
else else
{ {
return _zh_write_register(handle, (handle->gpio_status ^ gpio) | handle->gpio_work_mode); err = _zh_pcf8574_write_register(handle, (handle->gpio_status ^ gpio_temp) | handle->gpio_work_mode);
} }
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCF8574 write GPIO failed.");
ZH_LOGI("PCF8574 write GPIO completed successfully.");
return ESP_OK;
} }
void IRAM_ATTR _zh_isr_handler(void *arg) static esp_err_t _zh_pcf8574_validate_config(const zh_pcf8574_init_config_t *config)
{
ZH_ERROR_CHECK(config != NULL, ESP_ERR_INVALID_ARG, NULL, "Initial config is NULL.");
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->task_priority >= 10 && config->stack_size >= 2048, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
ZH_ERROR_CHECK(config->interrupt_gpio >= GPIO_NUM_0 && 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.");
return ESP_OK;
}
static esp_err_t _zh_pcf8574_gpio_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle)
{
if (_interrupt_gpio != GPIO_NUM_MAX)
{
esp_err_t err = zh_vector_push_back(&_vector, handle);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Failed add item to vector.")
return ESP_OK;
}
esp_err_t err = zh_vector_init(&_vector, sizeof(zh_pcf8574_handle_t));
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Failed create vector.")
err = zh_vector_push_back(&_vector, handle);
ZH_ERROR_CHECK(err == ESP_OK, err, zh_vector_free(&_vector), "Failed add item to vector.")
gpio_config_t interrupt_gpio_config = {
.intr_type = GPIO_INTR_NEGEDGE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << config->interrupt_gpio),
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
err = gpio_config(&interrupt_gpio_config);
ZH_ERROR_CHECK(err == ESP_OK, err, zh_vector_free(&_vector), "GPIO configuration failed.")
err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_reset_pin(config->interrupt_gpio); zh_vector_free(&_vector), "Failed install isr service.")
err = gpio_isr_handler_add(config->interrupt_gpio, _zh_pcf8574_isr_handler, NULL);
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_uninstall_isr_service(); gpio_reset_pin(config->interrupt_gpio); zh_vector_free(&_vector), "Failed add isr handler.")
_interrupt_gpio = config->interrupt_gpio;
return ESP_OK;
}
static esp_err_t _zh_pcf8574_i2c_init(const zh_pcf8574_init_config_t *config, zh_pcf8574_handle_t *handle)
{
i2c_device_config_t pcf8574_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = config->i2c_address,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t _dev_handle = NULL;
esp_err_t err = i2c_master_bus_add_device(config->i2c_handle, &pcf8574_config, &_dev_handle);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL;, "Failed to add I2C device.");
err = i2c_master_probe(config->i2c_handle, config->i2c_address, 1000 / portTICK_PERIOD_MS);
ZH_ERROR_CHECK(err == ESP_OK, err, i2c_master_bus_rm_device(_dev_handle), "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) |
(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;
return ESP_OK;
}
static esp_err_t _zh_pcf8574_resources_init(const zh_pcf8574_init_config_t *config)
{
_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)
{
BaseType_t err = xTaskCreatePinnedToCore(&_zh_pcf8574_isr_processing_task, "zh_pcf8574_isr_processing_task", config->stack_size, NULL, config->task_priority, NULL, tskNO_AFFINITY);
ZH_ERROR_CHECK(err == pdPASS, err, NULL, "Failed to create isr processing task.")
return ESP_OK;
}
static void IRAM_ATTR _zh_pcf8574_isr_handler(void *arg)
{ {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken); xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
@@ -127,130 +211,67 @@ void IRAM_ATTR _zh_isr_handler(void *arg)
}; };
} }
void _zh_isr_processing_task(void *pvParameter) static void IRAM_ATTR _zh_pcf8574_isr_processing_task(void *pvParameter)
{ {
for (;;) for (;;)
{ {
xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY); xSemaphoreTake(_interrupt_semaphore, portMAX_DELAY);
ESP_LOGI(TAG, "PCF8574 isr processing begin.");
for (uint16_t i = 0; i < zh_vector_get_size(&_vector); ++i) for (uint16_t i = 0; i < zh_vector_get_size(&_vector); ++i)
{ {
zh_pcf8574_handle_t *handle = zh_vector_get_item(&_vector, i); zh_pcf8574_handle_t *handle = zh_vector_get_item(&_vector, i);
if (handle == NULL)
{
ZH_LOGE("PCF8574 isr processing failed. Failed to get vector item data.", ESP_FAIL);
continue;
}
zh_pcf8574_event_on_isr_t event = {0}; zh_pcf8574_event_on_isr_t event = {0};
event.i2c_address = handle->i2c_address; event.i2c_address = handle->i2c_address;
event.gpio_number = 0xFF; event.gpio_number = 0xFF;
uint8_t reg_temp = 0; uint8_t old_reg = handle->gpio_status;
_zh_read_register(handle, &reg_temp); uint8_t new_reg = 0;
if (((handle->gpio_work_mode & EXP_GPIO_NUM_P0) != 0) && ((reg_temp & EXP_GPIO_NUM_P0) == 0)) esp_err_t err = _zh_pcf8574_read_register(handle, &new_reg);
if (err != ESP_OK)
{ {
event.gpio_number = 0; ZH_LOGE("PCF8574 isr processing failed. Failed to read expander register.", err);
continue;
} }
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P1) != 0) && ((reg_temp & EXP_GPIO_NUM_P1) == 0)) for (uint8_t j = 0; j <= 7; ++j)
{ {
event.gpio_number = 1; if ((handle->gpio_work_mode & _gpio_matrix[j]) != 0)
} {
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P2) != 0) && ((reg_temp & EXP_GPIO_NUM_P2) == 0)) if ((old_reg & _gpio_matrix[j]) != (new_reg & _gpio_matrix[j]))
{ {
event.gpio_number = 2; event.gpio_number = j;
} event.gpio_level = new_reg & _gpio_matrix[j];
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P3) != 0) && ((reg_temp & EXP_GPIO_NUM_P3) == 0)) }
{ }
event.gpio_number = 3;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P4) != 0) && ((reg_temp & EXP_GPIO_NUM_P4) == 0))
{
event.gpio_number = 4;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P5) != 0) && ((reg_temp & EXP_GPIO_NUM_P5) == 0))
{
event.gpio_number = 5;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P6) != 0) && ((reg_temp & EXP_GPIO_NUM_P6) == 0))
{
event.gpio_number = 6;
}
else if (((handle->gpio_work_mode & EXP_GPIO_NUM_P7) != 0) && ((reg_temp & EXP_GPIO_NUM_P7) == 0))
{
event.gpio_number = 7;
} }
if (event.gpio_number != 0xFF) if (event.gpio_number != 0xFF)
{ {
if (esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), portTICK_PERIOD_MS) != ESP_OK) err = esp_event_post(ZH_PCF8574, 0, &event, sizeof(event), 1000 / portTICK_PERIOD_MS);
if (err != ESP_OK)
{ {
ESP_LOGE(TAG, "PCF8574 isr processing task internal error at line %d.", __LINE__); ZH_LOGE("PCF8574 isr processing failed. Failed to post interrupt event.", err);
continue;
} }
} }
} }
ESP_LOGI(TAG, "PCF8574 isr processing success.");
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }
esp_err_t _zh_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg) static esp_err_t _zh_pcf8574_read_register(zh_pcf8574_handle_t *handle, uint8_t *reg)
{ {
ESP_LOGI(TAG, "PCF8574 read begin."); esp_err_t err = i2c_master_receive(handle->dev_handle, &handle->gpio_status, sizeof(handle->gpio_status), 1000 / portTICK_PERIOD_MS);
if (handle == NULL) ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "I2C driver error.");
{
ESP_LOGE(TAG, "PCF8574 read fail. Invalid argument.");
return ESP_ERR_INVALID_ARG;
}
if (handle->is_initialized == false)
{
ESP_LOGE(TAG, "PCF8574 read fail. PCF8574 not initialized.");
return ESP_ERR_NOT_FOUND;
}
esp_err_t err = ESP_OK;
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_cmd_handle_t i2c_cmd_handle = i2c_cmd_link_create();
i2c_master_start(i2c_cmd_handle);
i2c_master_write_byte(i2c_cmd_handle, handle->i2c_address << 1 | I2C_MASTER_READ, true);
i2c_master_read_byte(i2c_cmd_handle, &handle->gpio_status, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd_handle);
err = i2c_master_cmd_begin(handle->i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd_handle);
#else
err = i2c_master_receive(handle->dev_handle, &handle->gpio_status, sizeof(handle->gpio_status), 1000 / portTICK_PERIOD_MS);
#endif
if (err != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 read fail. I2C driver error at line %d.", __LINE__);
return ESP_ERR_INVALID_RESPONSE;
}
*reg = handle->gpio_status; *reg = handle->gpio_status;
ESP_LOGI(TAG, "PCF8574 read success.");
return ESP_OK; return ESP_OK;
} }
esp_err_t _zh_write_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)
{ {
ESP_LOGI(TAG, "PCF8574 write begin."); esp_err_t err = i2c_master_transmit(handle->dev_handle, &reg, sizeof(reg), 1000 / portTICK_PERIOD_MS);
if (handle == NULL) ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "I2C driver error.");
{
ESP_LOGE(TAG, "PCF8574 write fail. Invalid argument.");
return ESP_ERR_INVALID_ARG;
}
if (handle->is_initialized == false)
{
ESP_LOGE(TAG, "PCF8574 write fail. PCF8574 not initialized.");
return ESP_ERR_NOT_FOUND;
}
esp_err_t err = ESP_OK;
#ifdef CONFIG_IDF_TARGET_ESP8266
i2c_cmd_handle_t i2c_cmd_handle = i2c_cmd_link_create();
i2c_master_start(i2c_cmd_handle);
i2c_master_write_byte(i2c_cmd_handle, handle->i2c_address << 1 | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd_handle, reg, true);
i2c_master_stop(i2c_cmd_handle);
err = i2c_master_cmd_begin(handle->i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd_handle);
#else
err = i2c_master_transmit(handle->dev_handle, &reg, sizeof(reg), 1000 / portTICK_PERIOD_MS);
#endif
if (err != ESP_OK)
{
ESP_LOGE(TAG, "PCF8574 write fail. I2C driver error at line %d.", __LINE__);
return ESP_ERR_INVALID_RESPONSE;
}
handle->gpio_status = reg; handle->gpio_status = reg;
ESP_LOGI(TAG, "PCF8574 write success.");
return ESP_OK; return ESP_OK;
} }