diff --git a/README.md b/README.md index 337f9b6..7bd915d 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,23 @@ ## Features -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. +1. I2C communication via the ESP-IDF I2C master driver. +2. Configurable GPIO direction for each pin P0 through P7. +3. Interrupt handling with GPIO level detection and event notification. +4. Event-based architecture using the esp_event framework. 5. Statistics tracking for debugging and monitoring. +6. Multiple device instances on a single I2C bus. + +## Attention + +1. Requires ESP-IDF v5.0 or later with the I2C master driver. +2. Interrupt handling runs in task context and is not IRAM-safe. +3. A single interrupt GPIO pin is shared by all initialized devices. +4. The internal task is deleted only when the last device instance is deinitialized. ## Using -In an existing project, run the following command to install the components: +In an existing project, run the following command to install the component: ```bash cd ../your_project/components @@ -23,7 +31,9 @@ In the application, add the component: #include "zh_pcf8574.h" ``` -## Example +## Examples + +### Basic Example ```c #include "zh_pcf8574.h" @@ -32,17 +42,183 @@ In the application, add the component: zh_pcf8574_handle_t *pcf8574_handle = NULL; -void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); - -void print_gpio_status(const char *message, uint8_t reg) +void app_main(void) { - printf("%s", message); - for (uint8_t i = 0; i < 8; ++i) + esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR); + esp_log_level_set("zh_vector", ESP_LOG_ERROR); + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = I2C_PORT, + .scl_io_num = GPIO_NUM_22, + .sda_io_num = GPIO_NUM_21, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t i2c_bus_handle = NULL; + i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); + zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); + config.i2c_handle = i2c_bus_handle; + config.i2c_address = 0x38; + esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle); + if (ret != ESP_OK) { - printf("%c", (reg & 0x80) ? '1' : '0'); - reg <<= 1; + printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret)); + return; } - printf(".\n"); + uint8_t reg = 0; + ret = zh_pcf8574_read(&pcf8574_handle, ®); + if (ret == ESP_OK) + { + printf("GPIO status: 0x%02X\n", reg); + } + ret = zh_pcf8574_write(&pcf8574_handle, 0b10000010); + if (ret == ESP_OK) + { + printf("Write OK\n"); + } + ret = zh_pcf8574_read(&pcf8574_handle, ®); + if (ret == ESP_OK) + { + printf("GPIO status after write: 0x%02X\n", reg); + } + ret = zh_pcf8574_deinit(&pcf8574_handle); + if (ret == ESP_OK) + { + printf("Deinit OK\n"); + } +} +``` + +--- + +### GPIO Example + +```c +#include "zh_pcf8574.h" + +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *pcf8574_handle = NULL; + +void app_main(void) +{ + esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR); + esp_log_level_set("zh_vector", ESP_LOG_ERROR); + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = I2C_PORT, + .scl_io_num = GPIO_NUM_22, + .sda_io_num = GPIO_NUM_21, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t i2c_bus_handle = NULL; + i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); + zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); + config.i2c_handle = i2c_bus_handle; + config.i2c_address = 0x38; + esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle); + if (ret != ESP_OK) + { + printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret)); + return; + } + ret = zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_HIGH); + if (ret == ESP_OK) + { + printf("P4 set high\n"); + } + bool status = false; + ret = zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &status); + if (ret == ESP_OK) + { + printf("P4 status: %d\n", status); + } + ret = zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_LOW); + if (ret == ESP_OK) + { + printf("P4 set low\n"); + } + ret = zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &status); + if (ret == ESP_OK) + { + printf("P4 status: %d\n", status); + } + zh_pcf8574_deinit(&pcf8574_handle); +} +``` + +--- + +### Reset Example + +```c +#include "zh_pcf8574.h" + +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *pcf8574_handle = NULL; + +void app_main(void) +{ + esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR); + esp_log_level_set("zh_vector", ESP_LOG_ERROR); + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = I2C_PORT, + .scl_io_num = GPIO_NUM_22, + .sda_io_num = GPIO_NUM_21, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t i2c_bus_handle = NULL; + i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); + zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); + config.i2c_handle = i2c_bus_handle; + config.i2c_address = 0x38; + esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle); + if (ret != ESP_OK) + { + printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret)); + return; + } + zh_pcf8574_write(&pcf8574_handle, 0xFF); + uint8_t reg = 0; + ret = zh_pcf8574_read(&pcf8574_handle, ®); + if (ret == ESP_OK) + { + printf("GPIO before reset: 0x%02X\n", reg); + } + ret = zh_pcf8574_reset(&pcf8574_handle); + if (ret == ESP_OK) + { + printf("Reset OK\n"); + } + ret = zh_pcf8574_read(&pcf8574_handle, ®); + if (ret == ESP_OK) + { + printf("GPIO after reset: 0x%02X\n", reg); + } + zh_pcf8574_deinit(&pcf8574_handle); +} +``` + +--- + +### Interrupt Example + +```c +#include "zh_pcf8574.h" + +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *pcf8574_handle = NULL; + +void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + zh_pcf8574_event_on_isr_t *event = event_data; + printf("Interrupt on address 0x%02X pin P%d level %d time %lld\n", + event->i2c_address, event->gpio_number, event->gpio_level, event->interrupt_time); } void app_main(void) @@ -66,44 +242,66 @@ void app_main(void) 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, ®); - print_gpio_status("GPIO status: ", reg); - printf("Set P7 to 1, P1 to 1 and P0 to 0.\n"); - zh_pcf8574_write(&pcf8574_handle, 0b10000010); - zh_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); - printf("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 P4 to 1.\n"); - zh_pcf8574_write_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, ZH_PCF8574_GPIO_HIGH); - zh_pcf8574_read_gpio(&pcf8574_handle, ZH_PCF8574_GPIO_NUM_P4, &gpio); - printf("P4 status: %d.\n", gpio); - zh_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); - printf("Reset all GPIO.\n"); - zh_pcf8574_reset(&pcf8574_handle); - zh_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); + esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle); + if (ret != ESP_OK) + { + printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret)); + return; + } for (;;) { - 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); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } +``` -void zh_pcf8574_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +--- + +### Stats Example + +```c +#include "zh_pcf8574.h" + +#define I2C_PORT (I2C_NUM_MAX - 1) + +zh_pcf8574_handle_t *pcf8574_handle = NULL; + +void app_main(void) { - 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); + esp_log_level_set("zh_pcf8574", ESP_LOG_ERROR); + esp_log_level_set("zh_vector", ESP_LOG_ERROR); + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = I2C_PORT, + .scl_io_num = GPIO_NUM_22, + .sda_io_num = GPIO_NUM_21, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t i2c_bus_handle = NULL; + i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); + zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT(); + config.i2c_handle = i2c_bus_handle; + config.i2c_address = 0x38; + esp_err_t ret = zh_pcf8574_init(&config, &pcf8574_handle); + if (ret != ESP_OK) + { + printf("PCF8574 initialization error: %s\n", esp_err_to_name(ret)); + return; + } + uint8_t reg = 0; + zh_pcf8574_read(&pcf8574_handle, ®); + zh_pcf8574_write(&pcf8574_handle, 0xAA); + zh_pcf8574_read(&pcf8574_handle, ®); + const zh_pcf8574_stats_t *stats = zh_pcf8574_get_stats(); + printf("I2C driver errors: %ld\n", stats->i2c_driver_error); + printf("Event post errors: %ld\n", stats->event_post_error); + printf("Vector errors: %ld\n", stats->vector_error); + printf("Queue overflow errors: %ld\n", stats->queue_overflow_error); + printf("Min free stack size: %ld\n", stats->min_stack_size); + zh_pcf8574_reset_stats(); + stats = zh_pcf8574_get_stats(); + printf("After reset I2C driver errors: %ld\n", stats->i2c_driver_error); + zh_pcf8574_deinit(&pcf8574_handle); } ```