From 91047f23ee91e4da5103c1d106af73ee56c5e058 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Sun, 29 Dec 2013 22:15:33 +0100 Subject: [PATCH] GPIO interrupts configuration and handling. --- drivers/gpio.c | 171 ++++++++++++++++++++++++++++++++++++++++- include/drivers/gpio.h | 11 +++ 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/drivers/gpio.c b/drivers/gpio.c index 351cd44..e5ba6c4 100644 --- a/drivers/gpio.c +++ b/drivers/gpio.c @@ -198,15 +198,184 @@ void set_gpio_pins(void) io_config_clk_off(); } -/* Handlers */ + +static void (*gpio_calbacks_port0[PORT0_NB_PINS]) (uint32_t); +static void (*gpio_calbacks_port1[PORT1_NB_PINS]) (uint32_t); +static void (*gpio_calbacks_port2[PORT2_NB_PINS]) (uint32_t); + + +int set_gpio_callback(void (*callback) (uint32_t), uint8_t port, uint8_t pin, uint8_t sense) +{ + struct lpc_gpio* gpio_port = NULL; + uint32_t irq = 0; + + /* Register the callback */ + /* FIXME : we should check that there were none registered for the selected pin */ + switch (port) { + case 0: + if (pin >= PORT0_NB_PINS) + return -EINVAL; + gpio_calbacks_port0[pin] = callback; + gpio_port = LPC_GPIO_0; + irq = PIO_0_IRQ; + break; + case 1: + if (pin >= PORT1_NB_PINS) + return -EINVAL; + gpio_calbacks_port1[pin] = callback; + gpio_port = LPC_GPIO_1; + irq = PIO_1_IRQ; + break; + case 2: + if (pin >= PORT2_NB_PINS) + return -EINVAL; + gpio_calbacks_port2[pin] = callback; + gpio_port = LPC_GPIO_2; + irq = PIO_2_IRQ; + break; + default: + return -EINVAL; + } + + /* Configure the pin as interrupt source */ + gpio_port->data_dir &= ~(1 << pin); /* Input */ + config_gpio(port, pin, (LPC_IO_FUNC_ALT(0) | LPC_IO_DIGITAL)); + switch (sense) { + case EDGES_BOTH: + gpio_port->int_sense &= ~(1 << pin); + gpio_port->int_both_edges |= (1 << pin); + break; + case EDGE_RISING: + gpio_port->int_sense &= ~(1 << pin); + gpio_port->int_both_edges &= ~(1 << pin); + gpio_port->int_event |= (1 << pin); + break; + case EDGE_FALLING: + gpio_port->int_sense &= ~(1 << pin); + gpio_port->int_both_edges &= ~(1 << pin); + gpio_port->int_event &= ~(1 << pin); + break; + case LEVEL_LOW: + gpio_port->int_sense |= (1 << pin); + gpio_port->int_both_edges &= ~(1 << pin); + gpio_port->int_event &= ~(1 << pin); + break; + case LEVEL_HIGH: + gpio_port->int_sense |= (1 << pin); + gpio_port->int_both_edges &= ~(1 << pin); + gpio_port->int_event |= (1 << pin); + break; + default: /* Not handled, do not activate the interrupt */ + return -EINVAL; + } + gpio_port->int_enable |= (1 << pin); + NVIC_EnableIRQ(irq); + return 0; +} +void remove_gpio_callback(uint8_t port, uint8_t pin) +{ + struct lpc_gpio* gpio_port = NULL; + uint32_t irq = 0; + + /* Remove the handler */ + switch (port) { + case 0: + if (pin >= PORT0_NB_PINS) + return; + gpio_calbacks_port0[pin] = NULL; + gpio_port = LPC_GPIO_0; + irq = PIO_0_IRQ; + break; + case 1: + if (pin >= PORT1_NB_PINS) + return; + gpio_calbacks_port1[pin] = NULL; + gpio_port = LPC_GPIO_1; + irq = PIO_1_IRQ; + break; + case 2: + if (pin >= PORT2_NB_PINS) + return; + gpio_calbacks_port2[pin] = NULL; + gpio_port = LPC_GPIO_2; + irq = PIO_2_IRQ; + break; + default: + return; + } + /* And disable the interrupt */ + gpio_port->int_enable &= ~(1 << pin); + if (gpio_port->int_enable == 0) { + NVIC_DisableIRQ(irq); + } +} + + +/* Interrupt Handlers */ +/* Those handlers are far from the most effective if used without concertation + * with the people doing the electronic design. + * Use them if you place the signals generating interrupts on low numbered pins + */ +#include "drivers/serial.h" void PIO_0_Handler(void) { + struct lpc_gpio* gpio0 = LPC_GPIO_0; + uint32_t status = gpio0->masked_int_status; + uint32_t i = 0; + + /* Call interrupt handlers */ + while (status) { + if (status & 1) { + /* Is there an handler for this one ? */ + if (gpio_calbacks_port0[i] != NULL) { + gpio_calbacks_port0[i](i); + } + /* Clear edge detection logic */ + gpio0->int_clear |= (1 << i); + } + status >>= 1; + i++; + } } void PIO_1_Handler(void) { + struct lpc_gpio* gpio1 = LPC_GPIO_1; + uint32_t status = gpio1->masked_int_status; + uint32_t i = 0; + + /* Call interrupt handlers */ + while (status) { + if (status & 1) { + /* Is there an handler for this one ? */ + if (gpio_calbacks_port1[i] != NULL) { + gpio_calbacks_port1[i](i); + } + /* Clear pending interrupt */ + gpio1->int_clear |= (1 << i); + } + status >>= 1; + i++; + } } void PIO_2_Handler(void) { + struct lpc_gpio* gpio2 = LPC_GPIO_2; + uint32_t status = gpio2->masked_int_status; + uint32_t i = 0; + + /* Call interrupt handlers */ + while (status) { + if (status & 1) { + /* Is there an handler for this one ? */ + if (gpio_calbacks_port2[i] != NULL) { + gpio_calbacks_port2[i](i); + } + /* Clear pending interrupt */ + gpio2->int_clear |= (1 << i); + } + status >>= 1; + i++; + } } diff --git a/include/drivers/gpio.h b/include/drivers/gpio.h index cafd106..33a3cca 100644 --- a/include/drivers/gpio.h +++ b/include/drivers/gpio.h @@ -31,6 +31,17 @@ void config_gpio(uint8_t port, uint8_t pin, uint32_t mode); +enum gpio_interrupt_senses { + EDGES_BOTH = 0, + EDGE_FALLING, + EDGE_RISING, + LEVEL_HIGH, + LEVEL_LOW, +}; + +int set_gpio_callback(void (*callback) (uint32_t), uint8_t port, uint8_t pin, uint8_t sense); +void remove_gpio_callback(uint8_t port, uint8_t pin); + void gpio_on(void); void gpio_off(void); -- 2.43.0