From 0bb048531fcbe57d1e3ae42896c77004b2b888d0 Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Wed, 3 Sep 2025 09:56:56 +0300 Subject: [PATCH] feat: added all gpio support --- README.md | 116 +++++++++++++++++++-------------------- include/zh_avr_pcf8574.h | 2 +- zh_avr_pcf8574.c | 2 +- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 2cccbbc..9770837 100644 --- a/README.md +++ b/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 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 @@ -52,11 +52,11 @@ One expander on bus. All GPIO's as output (except P0 - input). Interrupt is enab int usart(char byte, FILE *stream) { - while ((UCSR0A & (1 << UDRE0)) == 0) - { - } - UDR0 = byte; - return 0; + while ((UCSR0A & (1 << UDRE0)) == 0) + { + } + UDR0 = byte; + return 0; } FILE uart = FDEV_SETUP_STREAM(usart, NULL, _FDEV_SETUP_WRITE); @@ -64,74 +64,74 @@ zh_avr_pcf8574_handle_t pcf8574_handle = {0}; void print_gpio_status(const char *message, uint8_t reg) { - printf("%s", message); - for (uint8_t i = 0; i < 8; ++i) - { - printf("%c", (reg & 0x80) ? '1' : '0'); - reg <<= 1; - } - printf(".\n"); + printf("%s", message); + for (uint8_t i = 0; i < 8; ++i) + { + printf("%c", (reg & 0x80) ? '1' : '0'); + reg <<= 1; + } + printf(".\n"); } 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_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, ®); - print_gpio_status("GPIO status: ", reg); - printf("Set P7 to 1, P1 to 1 and P0 to 0.\n"); - zh_avr_pcf8574_write(&pcf8574_handle, 0b10000010); // GPIO P0 will not be changed because it is operating in input mode. - zh_avr_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); - printf("Sets P0 to 0.\n"); - 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; - zh_avr_pcf8574_read_gpio(&pcf8574_handle, 0, &gpio); - printf("P0 status: %d.\n", gpio); - printf("Set P1 to 0.\n"); - zh_avr_pcf8574_write_gpio(&pcf8574_handle, 1, false); - zh_avr_pcf8574_read_gpio(&pcf8574_handle, 1, &gpio); - printf("P1 status: %d.\n", gpio); - zh_avr_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); - printf("Reset all GPIO.\n"); - zh_avr_pcf8574_reset(&pcf8574_handle); - zh_avr_pcf8574_read(&pcf8574_handle, ®); - print_gpio_status("GPIO status: ", reg); - printf("Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL)); - vTaskDelete(NULL); + 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_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, ®); + print_gpio_status("GPIO status: ", reg); + printf("Set P7 to 1, P1 to 1 and P0 to 0.\n"); + zh_avr_pcf8574_write(&pcf8574_handle, 0b10000010); // GPIO P0 will not be changed because it is operating in input mode. + zh_avr_pcf8574_read(&pcf8574_handle, ®); + print_gpio_status("GPIO status: ", reg); + printf("Sets P0 to 0.\n"); + 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; + zh_avr_pcf8574_read_gpio(&pcf8574_handle, 0, &gpio); + printf("P0 status: %d.\n", gpio); + printf("Set P1 to 0.\n"); + zh_avr_pcf8574_write_gpio(&pcf8574_handle, 1, false); + zh_avr_pcf8574_read_gpio(&pcf8574_handle, 1, &gpio); + printf("P1 status: %d.\n", gpio); + zh_avr_pcf8574_read(&pcf8574_handle, ®); + print_gpio_status("GPIO status: ", reg); + printf("Reset all GPIO.\n"); + zh_avr_pcf8574_reset(&pcf8574_handle); + zh_avr_pcf8574_read(&pcf8574_handle, ®); + 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; + 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_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)); + 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(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. { - if (zh_avr_pcf8574_isr_handler() == pdTRUE) - { - portYIELD(); - } + if (zh_avr_pcf8574_isr_handler() == pdTRUE) + { + portYIELD(); + } } ``` diff --git a/include/zh_avr_pcf8574.h b/include/zh_avr_pcf8574.h index 9a8680d..bfe2941 100644 --- a/include/zh_avr_pcf8574.h +++ b/include/zh_avr_pcf8574.h @@ -44,7 +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. @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. diff --git a/zh_avr_pcf8574.c b/zh_avr_pcf8574.c index c6d51e5..c3d2bb6 100644 --- a/zh_avr_pcf8574.c +++ b/zh_avr_pcf8574.c @@ -82,7 +82,7 @@ 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);