feat: added all gpio support

This commit is contained in:
2025-09-03 09:56:56 +03:00
parent 8fa482771e
commit 0bb048531f
3 changed files with 60 additions and 60 deletions

116
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 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. 3. The input GPIO's are always pullup to the power supply.
## Dependencies ## 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) int usart(char byte, FILE *stream)
{ {
while ((UCSR0A & (1 << UDRE0)) == 0) while ((UCSR0A & (1 << UDRE0)) == 0)
{ {
} }
UDR0 = byte; UDR0 = byte;
return 0; return 0;
} }
FILE uart = FDEV_SETUP_STREAM(usart, NULL, _FDEV_SETUP_WRITE); 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) void print_gpio_status(const char *message, uint8_t reg)
{ {
printf("%s", message); printf("%s", message);
for (uint8_t i = 0; i < 8; ++i) for (uint8_t i = 0; i < 8; ++i)
{ {
printf("%c", (reg & 0x80) ? '1' : '0'); printf("%c", (reg & 0x80) ? '1' : '0');
reg <<= 1; reg <<= 1;
} }
printf(".\n"); printf(".\n");
} }
void pcf8574_example_task(void *pvParameters) void pcf8574_example_task(void *pvParameters)
{ {
zh_avr_i2c_master_init(false); zh_avr_i2c_master_init(false);
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_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;
zh_avr_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_avr_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_avr_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_avr_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_avr_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_avr_pcf8574_write_gpio(&pcf8574_handle, 1, false); zh_avr_pcf8574_write_gpio(&pcf8574_handle, 1, false);
zh_avr_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_avr_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_avr_pcf8574_reset(&pcf8574_handle); zh_avr_pcf8574_reset(&pcf8574_handle);
zh_avr_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)); printf("Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
vTaskDelete(NULL); vTaskDelete(NULL);
} }
int main(void) int main(void)
{ {
UBRR0H = (BAUD_PRESCALE >> 8); UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE; UBRR0L = BAUD_PRESCALE;
UCSR0B = (1 << RXEN0) | (1 << TXEN0); UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
stdout = &uart; stdout = &uart;
xTaskCreate(pcf8574_example_task, "pcf8574 example task", 124, NULL, tskIDLE_PRIORITY, NULL); xTaskCreate(pcf8574_example_task, "pcf8574 example task", 124, NULL, tskIDLE_PRIORITY, NULL);
vTaskStartScheduler(); vTaskStartScheduler();
return 0; 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. 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 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 Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
} }
// ISR(PCINT0_vect) // For AVR_PORTB. 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(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. ISR(PCINT2_vect) // For AVR_PORTD. Required only if used input GPIO interrupts.
{ {
if (zh_avr_pcf8574_isr_handler() == pdTRUE) if (zh_avr_pcf8574_isr_handler() == pdTRUE)
{ {
portYIELD(); portYIELD();
} }
} }
``` ```

View File

@@ -44,7 +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. @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

@@ -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 != 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 >= 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) if (config->interrupt_gpio != 0xFF)
{ {
ZH_ERROR_CHECK(config->interrupt_port >= AVR_PORTB && config->interrupt_port <= AVR_PORTD, AVR_ERR_INVALID_ARG); ZH_ERROR_CHECK(config->interrupt_port >= AVR_PORTB && config->interrupt_port <= AVR_PORTD, AVR_ERR_INVALID_ARG);