This commit is contained in:
2025-08-11 16:39:04 +03:00
parent cd67f56533
commit 125c8d8f07
4 changed files with 74 additions and 99 deletions

110
README.md
View File

@@ -9,7 +9,7 @@
## Note ## Note
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 AVR. Only PORTD3 - PORTD7 are acceptable! 2. All the INT GPIO's on the extenders must be connected to the one GPIO on AVR. Only PORTD4 - PORTD7 are acceptable!
3. The input GPIO's are always pullup to the power supply. 3. The input GPIO's are always pullup to the power supply.
## Dependencies ## Dependencies
@@ -43,17 +43,24 @@ In the application, add the component:
One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enable: One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enable:
```c ```c
#include "avr/io.h"
#include "stdio.h"
#include "zh_avr_pcf8574.h" #include "zh_avr_pcf8574.h"
#define I2C_PORT (I2C_NUM_MAX - 1) #define BAUD_RATE 9600
#define BAUD_PRESCALE (F_CPU / 16 / BAUD_RATE - 1)
#ifndef CONFIG_IDF_TARGET_ESP8266 int usart(char byte, FILE *stream)
i2c_master_bus_handle_t i2c_bus_handle = NULL; {
#endif while ((UCSR0A & (1 << UDRE0)) == 0)
{
}
UDR0 = byte;
return 0;
}
FILE uart = FDEV_SETUP_STREAM(usart, NULL, _FDEV_SETUP_WRITE);
zh_pcf8574_handle_t pcf8574_handle = {0}; zh_avr_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 print_gpio_status(const char *message, uint8_t reg) void print_gpio_status(const char *message, uint8_t reg)
{ {
@@ -66,74 +73,59 @@ void print_gpio_status(const char *message, uint8_t reg)
printf(".\n"); printf(".\n");
} }
void app_main(void) void pcf8574_example_task(void *pvParameters)
{ {
esp_log_level_set("zh_pcf8574", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. zh_avr_i2c_master_init(false);
esp_log_level_set("zh_vector", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. zh_avr_pcf8574_init_config_t pcf8574_init_config = ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT();
#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 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT,
.scl_io_num = GPIO_NUM_22, // In accordance with used chip.
.sda_io_num = GPIO_NUM_21, // In accordance with used chip.
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
#endif
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.
#endif
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;
#endif
pcf8574_init_config.i2c_address = 0x38; pcf8574_init_config.i2c_address = 0x38;
pcf8574_init_config.p0_gpio_work_mode = true; // 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 = PORTD4; // Required only if used input GPIO interrupts.
zh_pcf8574_init(&pcf8574_init_config, &pcf8574_handle); zh_avr_pcf8574_init(&pcf8574_init_config, &pcf8574_handle);
uint8_t reg = 0; uint8_t reg = 0;
zh_pcf8574_read(&pcf8574_handle, &reg); zh_avr_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg); print_gpio_status("GPIO status: ", reg);
printf("Set P7 to 1, P1 to 1 and P0 to 0.\n"); printf("Set P7 to 1, P1 to 1 and P0 to 0.\n");
zh_pcf8574_write(&pcf8574_handle, 0b10000010); // GPIO P0 will not be changed because it is operating in input mode. zh_avr_pcf8574_write(&pcf8574_handle, 0b10000010); // GPIO P0 will not be changed because it is operating in input mode.
zh_pcf8574_read(&pcf8574_handle, &reg); zh_avr_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, 0, false); // GPIO P0 will not be changed because it is operating in input mode. zh_avr_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, 0, &gpio); zh_avr_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, 1, false); zh_avr_pcf8574_write_gpio(&pcf8574_handle, 1, false);
zh_pcf8574_read_gpio(&pcf8574_handle, 1, &gpio); zh_avr_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_avr_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg); print_gpio_status("GPIO status: ", reg);
printf("Reset all GPIO.\n"); printf("Reset all GPIO.\n");
zh_pcf8574_reset(&pcf8574_handle); zh_avr_pcf8574_reset(&pcf8574_handle);
zh_pcf8574_read(&pcf8574_handle, &reg); zh_avr_pcf8574_read(&pcf8574_handle, &reg);
print_gpio_status("GPIO status: ", reg); print_gpio_status("GPIO status: ", reg);
printf("Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
vTaskDelete(NULL);
}
int main(void)
{
UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
stdout = &uart;
xTaskCreate(pcf8574_example_task, "pcf8574 example task", 124, NULL, tskIDLE_PRIORITY, NULL);
vTaskStartScheduler();
return 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_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event) // Required only if used input GPIO interrupts.
{ {
zh_pcf8574_event_on_isr_t *event = event_data;
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); 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);
printf("Interrupt Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
}
ISR(PCINT2_vect) // Required only if used input GPIO interrupts.
{
zh_avr_pcf8574_isr_handler();
} }
``` ```

View File

@@ -5,6 +5,7 @@
#include "zh_avr_i2c.h" #include "zh_avr_i2c.h"
#include "zh_avr_vector.h" #include "zh_avr_vector.h"
#include "avr_err.h" #include "avr_err.h"
#include "avr_bit_defs.h"
#define ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT() \ #define ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT() \
{ \ { \

1
version.txt Normal file
View File

@@ -0,0 +1 @@
1.0.0

View File

@@ -1,16 +1,15 @@
#include "zh_avr_pcf8574.h" #include "zh_avr_pcf8574.h"
// #define GPIO_NUM_MAX 15 // Delete. static uint8_t _interrupt_gpio = 0xFF;
static uint8_t _interrupt_gpio = 0; // Check. PD3-PD7 only.
static SemaphoreHandle_t _interrupt_semaphore = NULL; static SemaphoreHandle_t _interrupt_semaphore = NULL;
static uint8_t _gpio_matrix[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; static uint8_t _gpio_matrix[8] = {AVR_BIT0, AVR_BIT1, AVR_BIT2, AVR_BIT3, AVR_BIT4, AVR_BIT5, AVR_BIT6, AVR_BIT7};
static zh_avr_vector_t _vector = {0}; static zh_avr_vector_t _vector = {0};
static avr_err_t _zh_avr_pcf8574_validate_config(const zh_avr_pcf8574_init_config_t *config); static avr_err_t _zh_avr_pcf8574_validate_config(const zh_avr_pcf8574_init_config_t *config);
static avr_err_t _zh_avr_pcf8574_configure_i2c_device(const zh_avr_pcf8574_init_config_t *config, zh_avr_pcf8574_handle_t *handle); static avr_err_t _zh_avr_pcf8574_configure_i2c_device(const zh_avr_pcf8574_init_config_t *config, zh_avr_pcf8574_handle_t *handle);
static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_config_t *config, zh_avr_pcf8574_handle_t handle); static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_config_t *config, zh_avr_pcf8574_handle_t handle);
extern void zh_avr_pcf8574_isr_handler(void);
static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter); static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter);
static avr_err_t _zh_avr_pcf8574_read_register(zh_avr_pcf8574_handle_t *handle, uint8_t *reg); static avr_err_t _zh_avr_pcf8574_read_register(zh_avr_pcf8574_handle_t *handle, uint8_t *reg);
static avr_err_t _zh_avr_pcf8574_write_register(zh_avr_pcf8574_handle_t *handle, uint8_t reg); static avr_err_t _zh_avr_pcf8574_write_register(zh_avr_pcf8574_handle_t *handle, uint8_t reg);
@@ -82,8 +81,8 @@ static avr_err_t _zh_avr_pcf8574_validate_config(const zh_avr_pcf8574_init_confi
{ {
ZH_ERROR_CHECK(config != NULL, AVR_ERR_INVALID_ARG); ZH_ERROR_CHECK(config != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK((config->i2c_address >= 0x20 && config->i2c_address <= 0x27) || (config->i2c_address >= 0x38 && config->i2c_address <= 0x3F), AVR_ERR_INVALID_ARG); ZH_ERROR_CHECK((config->i2c_address >= 0x20 && config->i2c_address <= 0x27) || (config->i2c_address >= 0x38 && config->i2c_address <= 0x3F), AVR_ERR_INVALID_ARG);
// ZH_ERROR_CHECK(config->task_priority >= 10 && config->stack_size >= 2048, AVR_ERR_INVALID_ARG, "Invalid task settings."); ZH_ERROR_CHECK(config->task_priority > tskIDLE_PRIORITY && config->stack_size >= 124, AVR_ERR_INVALID_ARG);
// ZH_ERROR_CHECK(config->interrupt_gpio >= 0 && config->interrupt_gpio <= GPIO_NUM_MAX, AVR_ERR_INVALID_ARG, "Invalid GPIO number."); ZH_ERROR_CHECK(config->interrupt_gpio == 0xFF || config->interrupt_gpio == PORTD4 || config->interrupt_gpio == PORTD5 || config->interrupt_gpio == PORTD6 || config->interrupt_gpio == PORTD7, AVR_ERR_INVALID_ARG);
return AVR_OK; return AVR_OK;
} }
@@ -116,28 +115,13 @@ static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_
ZH_ERROR_CHECK(err == AVR_OK, err); ZH_ERROR_CHECK(err == AVR_OK, err);
err = zh_avr_vector_push_back(&_vector, &handle); err = zh_avr_vector_push_back(&_vector, &handle);
ZH_ERROR_CHECK(err == AVR_OK, err); ZH_ERROR_CHECK(err == AVR_OK, err);
// gpio_config_t isr_pin_config = { DDRD &= ~(1 << _interrupt_gpio);
// .intr_type = GPIO_INTR_NEGEDGE, PORTD |= (1 << _interrupt_gpio);
// .mode = GPIO_MODE_INPUT, PCICR |= (1 << PCIE2);
// .pin_bit_mask = (1ULL << _interrupt_gpio), PCMSK2 |= (1 << _interrupt_gpio);
// .pull_down_en = GPIO_PULLDOWN_DISABLE,
// .pull_up_en = GPIO_PULLUP_ENABLE,
// };
// err = gpio_config(&isr_pin_config);
// ZH_ERROR_CHECK(err == AVR_OK, err, "GPIO configuration failed.")
// err = gpio_install_isr_service(0);
// ZH_ERROR_CHECK(err == AVR_OK, err, "Failed install isr service.")
// err = gpio_isr_handler_add(_interrupt_gpio, _zh_avr_pcf8574_isr_handler, NULL);
// ZH_ERROR_CHECK(err == AVR_OK, err, "Failed add isr handler.")
_interrupt_semaphore = xSemaphoreCreateBinary(); _interrupt_semaphore = xSemaphoreCreateBinary();
ZH_ERROR_CHECK(_interrupt_semaphore != NULL, AVR_ERR_NO_MEM); ZH_ERROR_CHECK(_interrupt_semaphore != NULL, AVR_ERR_NO_MEM);
BaseType_t x_err = xTaskCreate( BaseType_t x_err = xTaskCreate(_zh_avr_pcf8574_isr_processing_task, NULL, config->stack_size, NULL, config->task_priority, NULL);
&_zh_avr_pcf8574_isr_processing_task,
"_zh_avr_pcf8574_isr_processing_task",
config->stack_size,
NULL,
config->task_priority,
NULL);
if (x_err != pdPASS) if (x_err != pdPASS)
{ {
vSemaphoreDelete(_interrupt_semaphore); vSemaphoreDelete(_interrupt_semaphore);
@@ -146,14 +130,17 @@ static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_
return AVR_OK; return AVR_OK;
} }
ISR(PCINT2_vect) // Check. void zh_avr_pcf8574_isr_handler(void)
{ {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; if ((PIND & (1 << _interrupt_gpio)) == 0)
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{ {
portYIELD(); BaseType_t xHigherPriorityTaskWoken = pdFALSE;
}; xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD();
};
}
} }
static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter) static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter)
@@ -186,17 +173,11 @@ static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter)
{ {
event.gpio_number = j; event.gpio_number = j;
event.gpio_level = new_reg & _gpio_matrix[j]; event.gpio_level = new_reg & _gpio_matrix[j];
extern void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t * event);
zh_avr_pcf8574_event_handler(&event);
} }
} }
} }
if (event.gpio_number != 0xFF)
{
// err = esp_event_post(zh_avr_pcf8574, 0, &event, sizeof(event), portTICK_PERIOD_MS);
// if (err != AVR_OK)
// {
// zh_avr_pcf8574_LOGE_ERR("PCF8574 isr processing failed. Failed to post interrupt event.", err);
// }
}
} }
} }
vTaskDelete(NULL); vTaskDelete(NULL);