Compare commits
	
		
			7 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3fa55b8fa2 | |||
| 9a6d82fb64 | |||
| e6eb45dd9b | |||
| 6b8b8c3314 | |||
| 196398ac6f | |||
| dfebb15213 | |||
| 8c6460a1d1 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
.DS_Store
 | 
			
		||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							@@ -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 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.
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
@@ -78,8 +78,9 @@ void pcf8574_example_task(void *pvParameters)
 | 
			
		||||
    zh_avr_i2c_master_init(false);
 | 
			
		||||
    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_gpio = PORTD4;  // Required only if used input GPIO interrupts.
 | 
			
		||||
    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;
 | 
			
		||||
    zh_avr_pcf8574_read(&pcf8574_handle, ®);
 | 
			
		||||
@@ -118,14 +119,19 @@ int main(void)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event) // Required only if used input GPIO interrupts.
 | 
			
		||||
void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event) // Do not delete! Leave blank if interrupts are not used.
 | 
			
		||||
{
 | 
			
		||||
    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.
 | 
			
		||||
// 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -30,7 +33,7 @@ extern "C"
 | 
			
		||||
    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 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.
 | 
			
		||||
        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.
 | 
			
		||||
@@ -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.
 | 
			
		||||
@@ -133,7 +137,7 @@ extern "C"
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief PCF8574 ISR handler.
 | 
			
		||||
     */
 | 
			
		||||
    void zh_avr_pcf8574_isr_handler(void);
 | 
			
		||||
    BaseType_t zh_avr_pcf8574_isr_handler(void);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
1.0.0
 | 
			
		||||
1.3.2
 | 
			
		||||
							
								
								
									
										122
									
								
								zh_avr_pcf8574.c
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								zh_avr_pcf8574.c
									
									
									
									
									
								
							@@ -1,8 +1,11 @@
 | 
			
		||||
#include "zh_avr_pcf8574.h"
 | 
			
		||||
 | 
			
		||||
TaskHandle_t zh_avr_pcf8574 = NULL;
 | 
			
		||||
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};
 | 
			
		||||
volatile static bool _interrupt_on = false;
 | 
			
		||||
 | 
			
		||||
static zh_avr_vector_t _vector = {0};
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +61,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, ®_temp);
 | 
			
		||||
    *status = ((reg_temp & gpio_temp) ? 1 : 0);
 | 
			
		||||
@@ -68,7 +71,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);
 | 
			
		||||
@@ -80,8 +83,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->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 == PORTD4 || config->interrupt_gpio == PORTD5 || config->interrupt_gpio == PORTD6 || config->interrupt_gpio == PORTD7, 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 >= 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,13 +122,32 @@ 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);
 | 
			
		||||
    DDRD &= ~(1 << _interrupt_gpio);
 | 
			
		||||
    PORTD |= (1 << _interrupt_gpio);
 | 
			
		||||
    PCICR |= (1 << PCIE2);
 | 
			
		||||
    PCMSK2 |= (1 << _interrupt_gpio);
 | 
			
		||||
    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);
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        vSemaphoreDelete(_interrupt_semaphore);
 | 
			
		||||
@@ -129,17 +156,72 @@ static avr_err_t _zh_avr_pcf8574_configure_interrupts(const zh_avr_pcf8574_init_
 | 
			
		||||
    return AVR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void zh_avr_pcf8574_isr_handler(void)
 | 
			
		||||
BaseType_t zh_avr_pcf8574_isr_handler(void)
 | 
			
		||||
{
 | 
			
		||||
    if ((PIND & (1 << _interrupt_gpio)) == 0)
 | 
			
		||||
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 | 
			
		||||
    switch (_interrupt_port)
 | 
			
		||||
    {
 | 
			
		||||
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 | 
			
		||||
        xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
 | 
			
		||||
        if (xHigherPriorityTaskWoken == pdTRUE)
 | 
			
		||||
    case AVR_PORTB:
 | 
			
		||||
        if ((PINB & (1 << _interrupt_gpio)) == (1 << _interrupt_gpio))
 | 
			
		||||
        {
 | 
			
		||||
            portYIELD();
 | 
			
		||||
        };
 | 
			
		||||
            if (_interrupt_on == false)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = true;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if ((PINB & (1 << _interrupt_gpio)) == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (_interrupt_on == true)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = false;
 | 
			
		||||
                xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case AVR_PORTC:
 | 
			
		||||
        if ((PINC & (1 << _interrupt_gpio)) == (1 << _interrupt_gpio))
 | 
			
		||||
        {
 | 
			
		||||
            if (_interrupt_on == false)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = true;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if ((PINC & (1 << _interrupt_gpio)) == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (_interrupt_on == true)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = false;
 | 
			
		||||
                xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case AVR_PORTD:
 | 
			
		||||
        if ((PIND & (1 << _interrupt_gpio)) == (1 << _interrupt_gpio))
 | 
			
		||||
        {
 | 
			
		||||
            if (_interrupt_on == false)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = true;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if ((PIND & (1 << _interrupt_gpio)) == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (_interrupt_on == true)
 | 
			
		||||
            {
 | 
			
		||||
                _interrupt_on = false;
 | 
			
		||||
                xSemaphoreGiveFromISR(_interrupt_semaphore, &xHigherPriorityTaskWoken);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return xHigherPriorityTaskWoken;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _zh_avr_pcf8574_isr_processing_task(void *pvParameter)
 | 
			
		||||
@@ -166,12 +248,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);
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user