feat: added all gpio support

This commit is contained in:
2025-09-03 09:58:12 +03:00
parent 196398ac6f
commit 6b8b8c3314
4 changed files with 74 additions and 20 deletions

View File

@@ -9,7 +9,7 @@
## 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 AVR. Only PORTD0 - PORTD7 are acceptable!
2. All the INT GPIO's on the extenders must be connected to the one GPIO on AVR.
3. The input GPIO's are always pullup to the power supply.
## Dependencies
@@ -79,6 +79,7 @@ void pcf8574_example_task(void *pvParameters)
zh_avr_pcf8574_init_config_t pcf8574_init_config = ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT();
pcf8574_init_config.i2c_address = 0x38;
pcf8574_init_config.p0_gpio_work_mode = true; // Required only for input GPIO.
pcf8574_init_config.interrupt_port = AVR_PORTD; // Required only if used input GPIO interrupts.
pcf8574_init_config.interrupt_gpio = PORTD4; // Required only if used input GPIO interrupts.
zh_avr_pcf8574_init(&pcf8574_init_config, &pcf8574_handle);
uint8_t reg = 0;
@@ -124,8 +125,13 @@ void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event) // Do no
printf("Interrupt Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
}
ISR(PCINT2_vect) // Required only if used input GPIO interrupts.
// ISR(PCINT0_vect) // For AVR_PORTB. Required only if used input GPIO interrupts.
// ISR(PCINT1_vect) // For AVR_PORTC. Required only if used input GPIO interrupts.
ISR(PCINT2_vect) // For AVR_PORTD. Required only if used input GPIO interrupts.
{
zh_avr_pcf8574_isr_handler();
if (zh_avr_pcf8574_isr_handler() == pdTRUE)
{
portYIELD();
}
}
```

View File

@@ -2,9 +2,11 @@
#include "FreeRTOS.h"
#include "semphr.h"
#include "avr/pgmspace.h"
#include "zh_avr_i2c.h"
#include "zh_avr_vector.h"
#include "avr_err.h"
#include "avr_port.h"
#include "avr_bit_defs.h"
#define ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT() \
@@ -20,7 +22,8 @@
.p5_gpio_work_mode = 0, \
.p6_gpio_work_mode = 0, \
.p7_gpio_work_mode = 0, \
.interrupt_gpio = 0xFF}
.interrupt_gpio = 0xFF, \
.interrupt_port = 0}
#ifdef __cplusplus
extern "C"
@@ -41,6 +44,7 @@ extern "C"
bool p6_gpio_work_mode; // Expander GPIO P6 work mode. True for input, false for output.
bool p7_gpio_work_mode; // Expander GPIO P7 work mode. True for input, false for output.
uint8_t interrupt_gpio; // Interrupt GPIO. @attention Must be same for all PCF8574 expanders.
uint8_t interrupt_port; // Interrupt port.
} zh_avr_pcf8574_init_config_t;
typedef struct // PCF8574 expander handle.

View File

@@ -1 +1 @@
1.2.0
1.3.0

View File

@@ -1,8 +1,9 @@
#include "zh_avr_pcf8574.h"
static uint8_t _interrupt_gpio = 0xFF;
static uint8_t _interrupt_port = 0;
static SemaphoreHandle_t _interrupt_semaphore = NULL;
static uint8_t _gpio_matrix[8] = {AVR_BIT0, AVR_BIT1, AVR_BIT2, AVR_BIT3, AVR_BIT4, AVR_BIT5, AVR_BIT6, AVR_BIT7};
static const uint8_t _gpio_matrix[8] PROGMEM = {AVR_BIT0, AVR_BIT1, AVR_BIT2, AVR_BIT3, AVR_BIT4, AVR_BIT5, AVR_BIT6, AVR_BIT7};
static zh_avr_vector_t _vector = {0};
@@ -58,7 +59,7 @@ avr_err_t zh_avr_pcf8574_reset(zh_avr_pcf8574_handle_t *handle)
avr_err_t zh_avr_pcf8574_read_gpio(zh_avr_pcf8574_handle_t *handle, uint8_t gpio, bool *status)
{
ZH_ERROR_CHECK(gpio <= 7, AVR_FAIL);
uint8_t gpio_temp = _gpio_matrix[gpio];
uint8_t gpio_temp = pgm_read_byte(&_gpio_matrix[gpio]);
uint8_t reg_temp = 0;
avr_err_t err = _zh_avr_pcf8574_read_register(handle, &reg_temp);
*status = ((reg_temp & gpio_temp) ? 1 : 0);
@@ -68,7 +69,7 @@ avr_err_t zh_avr_pcf8574_read_gpio(zh_avr_pcf8574_handle_t *handle, uint8_t gpio
avr_err_t zh_avr_pcf8574_write_gpio(zh_avr_pcf8574_handle_t *handle, uint8_t gpio, bool status)
{
ZH_ERROR_CHECK(gpio <= 7, AVR_FAIL);
uint8_t gpio_temp = _gpio_matrix[gpio];
uint8_t gpio_temp = pgm_read_byte(&_gpio_matrix[gpio]);
if (status == true)
{
return _zh_avr_pcf8574_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio_temp);
@@ -81,7 +82,12 @@ 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->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 > tskIDLE_PRIORITY && config->stack_size >= 124, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->interrupt_gpio == 0xFF || (config->interrupt_gpio >= PORTD0 && config->interrupt_gpio <= PORTD7), AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->interrupt_gpio == 0xFF || (config->interrupt_gpio >= 0 && config->interrupt_gpio <= 7), AVR_ERR_INVALID_ARG);
if (config->interrupt_gpio != 0xFF)
{
ZH_ERROR_CHECK(config->interrupt_port >= AVR_PORTB && config->interrupt_port <= AVR_PORTD, AVR_ERR_INVALID_ARG);
_interrupt_port = config->interrupt_port;
}
return AVR_OK;
}
@@ -114,10 +120,29 @@ static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_
ZH_ERROR_CHECK(err == AVR_OK, err);
err = zh_avr_vector_push_back(&_vector, &handle);
ZH_ERROR_CHECK(err == AVR_OK, err);
switch (_interrupt_port)
{
case AVR_PORTB:
DDRB &= ~(1 << _interrupt_gpio);
PORTB |= (1 << _interrupt_gpio);
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << _interrupt_gpio);
break;
case AVR_PORTC:
DDRC &= ~(1 << _interrupt_gpio);
PORTC |= (1 << _interrupt_gpio);
PCICR |= (1 << PCIE1);
PCMSK1 |= (1 << _interrupt_gpio);
break;
case AVR_PORTD:
DDRD &= ~(1 << _interrupt_gpio);
PORTD |= (1 << _interrupt_gpio);
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << _interrupt_gpio);
break;
default:
break;
}
_interrupt_semaphore = xSemaphoreCreateBinary();
ZH_ERROR_CHECK(_interrupt_semaphore != NULL, AVR_ERR_NO_MEM);
BaseType_t x_err = xTaskCreate(_zh_avr_pcf8574_isr_processing_task, NULL, config->stack_size, NULL, config->task_priority, NULL);
@@ -132,10 +157,29 @@ static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_
BaseType_t zh_avr_pcf8574_isr_handler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
switch (_interrupt_port)
{
case AVR_PORTB:
if ((PINB & (1 << _interrupt_gpio)) == 0)
{
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
}
break;
case AVR_PORTC:
if ((PINC & (1 << _interrupt_gpio)) == 0)
{
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
}
break;
case AVR_PORTD:
if ((PIND & (1 << _interrupt_gpio)) == 0)
{
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
}
break;
default:
break;
}
return xHigherPriorityTaskWoken;
}
@@ -163,12 +207,12 @@ static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter)
}
for (uint8_t j = 0; j <= 7; ++j)
{
if ((handle->gpio_work_mode & _gpio_matrix[j]) != 0)
if ((handle->gpio_work_mode & pgm_read_byte(&_gpio_matrix[j])) != 0)
{
if ((old_reg & _gpio_matrix[j]) != (new_reg & _gpio_matrix[j]))
if ((old_reg & pgm_read_byte(&_gpio_matrix[j])) != (new_reg & pgm_read_byte(&_gpio_matrix[j])))
{
event.gpio_number = j;
event.gpio_level = new_reg & _gpio_matrix[j];
event.gpio_level = new_reg & pgm_read_byte(&_gpio_matrix[j]);
extern void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t * event);
zh_avr_pcf8574_event_handler(&event);
}