4 Commits

5 changed files with 79 additions and 23 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

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 PORTD4 - 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. 3. The input GPIO's are always pullup to the power supply.
## Dependencies ## 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(); zh_avr_pcf8574_init_config_t pcf8574_init_config = ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT();
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_port = AVR_PORTD; // Required only if used input GPIO interrupts.
pcf8574_init_config.interrupt_gpio = PORTD4; // 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); zh_avr_pcf8574_init(&pcf8574_init_config, &pcf8574_handle);
uint8_t reg = 0; 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)); 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 "FreeRTOS.h"
#include "semphr.h" #include "semphr.h"
#include "avr/pgmspace.h"
#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_port.h"
#include "avr_bit_defs.h" #include "avr_bit_defs.h"
#define ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT() \ #define ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT() \
@@ -20,7 +22,8 @@
.p5_gpio_work_mode = 0, \ .p5_gpio_work_mode = 0, \
.p6_gpio_work_mode = 0, \ .p6_gpio_work_mode = 0, \
.p7_gpio_work_mode = 0, \ .p7_gpio_work_mode = 0, \
.interrupt_gpio = 0xFF} .interrupt_gpio = 0xFF, \
.interrupt_port = 0}
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@@ -30,7 +33,7 @@ extern "C"
typedef struct // Structure for initial initialization of PCF8574 expander. 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 configMAX_PRIORITIES. uint8_t task_priority; // Task priority for the PCF8574 expander isr processing. @note It is not recommended to set a value less than configMAX_PRIORITIES.
uint8_t stack_size; // Stack size for task for the PCF8574 expander isr processing processing. @note The minimum size is 124 byte. uint8_t stack_size; // Stack size for task for the PCF8574 expander isr processing processing.
uint8_t i2c_address; // Expander I2C address. uint8_t i2c_address; // Expander I2C address.
bool p0_gpio_work_mode; // Expander GPIO PO work mode. True for input, false for output. bool p0_gpio_work_mode; // Expander GPIO PO work mode. True for input, false for output.
bool p1_gpio_work_mode; // Expander GPIO P1 work mode. True for input, false for output. bool p1_gpio_work_mode; // Expander GPIO P1 work mode. True for input, false for output.
@@ -41,6 +44,7 @@ extern "C"
bool p6_gpio_work_mode; // Expander GPIO P6 work mode. True for input, false for output. 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. 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_gpio; // Interrupt GPIO. @attention Must be same for all PCF8574 expanders.
uint8_t interrupt_port; // Interrupt port.
} zh_avr_pcf8574_init_config_t; } zh_avr_pcf8574_init_config_t;
typedef struct // PCF8574 expander handle. typedef struct // PCF8574 expander handle.

View File

@@ -1 +1 @@
1.1.0 1.3.1

View File

@@ -1,8 +1,10 @@
#include "zh_avr_pcf8574.h" #include "zh_avr_pcf8574.h"
TaskHandle_t zh_avr_pcf8574 = NULL;
static uint8_t _interrupt_gpio = 0xFF; static uint8_t _interrupt_gpio = 0xFF;
static uint8_t _interrupt_port = 0;
static SemaphoreHandle_t _interrupt_semaphore = NULL; 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}; static zh_avr_vector_t _vector = {0};
@@ -58,7 +60,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) 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); 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; uint8_t reg_temp = 0;
avr_err_t err = _zh_avr_pcf8574_read_register(handle, &reg_temp); avr_err_t err = _zh_avr_pcf8574_read_register(handle, &reg_temp);
*status = ((reg_temp & gpio_temp) ? 1 : 0); *status = ((reg_temp & gpio_temp) ? 1 : 0);
@@ -68,7 +70,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) 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); 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) if (status == true)
{ {
return _zh_avr_pcf8574_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio_temp); return _zh_avr_pcf8574_write_register(handle, handle->gpio_status | handle->gpio_work_mode | gpio_temp);
@@ -80,8 +82,13 @@ 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 > tskIDLE_PRIORITY && config->stack_size >= 124, AVR_ERR_INVALID_ARG); ZH_ERROR_CHECK(config->task_priority > tskIDLE_PRIORITY && config->stack_size >= configMINIMAL_STACK_SIZE, AVR_ERR_INVALID_ARG);
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); 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; return AVR_OK;
} }
@@ -114,13 +121,32 @@ 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);
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); DDRD &= ~(1 << _interrupt_gpio);
PORTD |= (1 << _interrupt_gpio); PORTD |= (1 << _interrupt_gpio);
PCICR |= (1 << PCIE2); PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << _interrupt_gpio); PCMSK2 |= (1 << _interrupt_gpio);
break;
default:
break;
}
_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(_zh_avr_pcf8574_isr_processing_task, NULL, config->stack_size, NULL, config->task_priority, NULL); BaseType_t x_err = xTaskCreate(_zh_avr_pcf8574_isr_processing_task, "zh_avr_pcf8574", config->stack_size, NULL, config->task_priority, &zh_avr_pcf8574);
if (x_err != pdPASS) if (x_err != pdPASS)
{ {
vSemaphoreDelete(_interrupt_semaphore); vSemaphoreDelete(_interrupt_semaphore);
@@ -132,10 +158,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 zh_avr_pcf8574_isr_handler(void)
{ {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; 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) if ((PIND & (1 << _interrupt_gpio)) == 0)
{ {
xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken); xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
} }
break;
default:
break;
}
return xHigherPriorityTaskWoken; return xHigherPriorityTaskWoken;
} }
@@ -163,12 +208,12 @@ static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter)
} }
for (uint8_t j = 0; j <= 7; ++j) 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_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); extern void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t * event);
zh_avr_pcf8574_event_handler(&event); zh_avr_pcf8574_event_handler(&event);
} }