diff --git a/boards/avsextrem/Makefile.features b/boards/avsextrem/Makefile.features index 2673603712..6453454450 100644 --- a/boards/avsextrem/Makefile.features +++ b/boards/avsextrem/Makefile.features @@ -1,2 +1,3 @@ +FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_rtc FEATURES_MCU_GROUP = arm7 diff --git a/boards/avsextrem/board_init.c b/boards/avsextrem/board_init.c index 4b95b7ee57..15e17fb627 100644 --- a/boards/avsextrem/board_init.c +++ b/boards/avsextrem/board_init.c @@ -78,7 +78,7 @@ void init_clks1(void) /*---------------------------------------------------------------------------*/ void bl_init_ports(void) { - SCS |= BIT0; // Set IO Ports to fast switching mode + gpio_init_ports(); /* UART0 */ PINSEL0 |= BIT4 + BIT6; // RxD0 and TxD0 diff --git a/boards/msba2/Makefile.features b/boards/msba2/Makefile.features index 8ba18f54f9..ac556ca6b4 100644 --- a/boards/msba2/Makefile.features +++ b/boards/msba2/Makefile.features @@ -1,3 +1,4 @@ +FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += cpp diff --git a/boards/msba2/board_init.c b/boards/msba2/board_init.c index 6126d6dd6b..7cf4913a78 100644 --- a/boards/msba2/board_init.c +++ b/boards/msba2/board_init.c @@ -51,7 +51,7 @@ void bl_blink(void) void bl_init_ports(void) { - SCS |= BIT0; // Set IO Ports to fast switching mode + gpio_init_ports(); /* UART0 */ PINSEL0 |= BIT4 + BIT6; // RxD0 and TxD0 diff --git a/boards/pttu/Makefile.features b/boards/pttu/Makefile.features index 2673603712..6453454450 100644 --- a/boards/pttu/Makefile.features +++ b/boards/pttu/Makefile.features @@ -1,2 +1,3 @@ +FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_rtc FEATURES_MCU_GROUP = arm7 diff --git a/boards/pttu/board_init.c b/boards/pttu/board_init.c index c672ad786d..54e1ed798f 100644 --- a/boards/pttu/board_init.c +++ b/boards/pttu/board_init.c @@ -63,7 +63,7 @@ void init_clks1(void) void bl_init_ports(void) { - SCS |= BIT0; // Set IO Ports to fast switching mode + gpio_init_ports(); /* UART0 */ PINSEL0 |= BIT4 + BIT6; // RxD0 and TxD0 diff --git a/cpu/lpc2387/Makefile.include b/cpu/lpc2387/Makefile.include index b815fc9cf6..f2e4cca5f0 100644 --- a/cpu/lpc2387/Makefile.include +++ b/cpu/lpc2387/Makefile.include @@ -4,4 +4,4 @@ include $(RIOTCPU)/arm7_common/Makefile.include export UNDEF += $(BINDIR)cpu/lpc_syscalls.o -export USEMODULE += arm7_common periph +USEMODULE += arm7_common periph bitfield diff --git a/cpu/lpc2387/include/cpu.h b/cpu/lpc2387/include/cpu.h index 5c7fcf417a..4fdb84bac9 100644 --- a/cpu/lpc2387/include/cpu.h +++ b/cpu/lpc2387/include/cpu.h @@ -29,6 +29,10 @@ extern uintptr_t __stack_start; ///< end of user stack memory space void lpc2387_pclk_scale(uint32_t source, uint32_t target, uint32_t *pclksel, uint32_t *prescale); bool install_irq(int IntNumber, void (*HandlerAddr)(void), int Priority); +#ifdef MODULE_PERIPH +void gpio_init_ports(void); +#endif + #ifdef __cplusplus } #endif diff --git a/cpu/lpc2387/include/periph_cpu.h b/cpu/lpc2387/include/periph_cpu.h new file mode 100644 index 0000000000..17c97ad374 --- /dev/null +++ b/cpu/lpc2387/include/periph_cpu.h @@ -0,0 +1,41 @@ +#ifndef PERIPH_CPU_H +#define PERIPH_CPU_H + +#include +#include "cpu.h" + +#define __IO volatile + +typedef struct { + __IO uint32_t DIR; + uint32_t _reserved[3]; + __IO uint32_t MASK; + __IO uint32_t PIN; + __IO uint32_t SET; + __IO uint32_t CLR; +} FIO_PORT_t; + +#define FIO_PORTS ((FIO_PORT_t*)FIO_BASE_ADDR) +#define PINSEL ((__IO uint32_t *)(PINSEL_BASE_ADDR)) +#define PINMODE ((__IO uint32_t *)(PINSEL_BASE_ADDR + 0x40)) + +int gpio_init_mux(unsigned pin, unsigned mux); +void gpio_init_states(void); + +#define GPIO(port, pin) (port*32 + pin) + +#define HAVE_GPIO_PP_T +typedef enum { + GPIO_PULLUP = 0, /**< enable internal pull-up resistor */ + GPIO_NOPULL = 2, /**< do not use internal pull resistors */ + GPIO_PULLDOWN = 3 /**< enable internal pull-down resistor */ +} gpio_pp_t; + +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_FALLING = 1, /**< emit interrupt on falling flank */ + GPIO_RISING = 2, /**< emit interrupt on rising flank */ + GPIO_BOTH = 3 /**< emit interrupt on both flanks */ +} gpio_flank_t; + +#endif /* PERIPH_CPU_H */ diff --git a/cpu/lpc2387/periph/gpio.c b/cpu/lpc2387/periph/gpio.c new file mode 100644 index 0000000000..b46b4f8d3b --- /dev/null +++ b/cpu/lpc2387/periph/gpio.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup lpc2387 + * @{ + * + * @file + * @brief CPU specific low-level GPIO driver implementation for the LPC2387 + * + * @author Kaspar Schleiser + * + * @} + */ + +#include +#include +#include +#include + +#include "irq.h" +#include "periph/gpio.h" +#include "bitfield.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define GPIO_NUM_ISR (16) + +static BITFIELD(_gpio_config_bitfield, GPIO_NUM_ISR); + +typedef struct { + gpio_cb_t cb; /**< callback called from GPIO interrupt */ + void *arg; /**< argument passed to the callback */ +} gpio_state_t; + +static gpio_state_t _gpio_states[GPIO_NUM_ISR]; +static BITFIELD(_gpio_rising, GPIO_NUM_ISR); +static BITFIELD(_gpio_falling, GPIO_NUM_ISR); +static uint8_t _gpio_isr_map[64]; /* only ports 0+2 can have ISRs */ + +static void _gpio_configure(gpio_t pin, unsigned rising, unsigned falling); + +void gpio_init_ports(void) { + SCS |= 0x1; /* set GPIO ports 0 and 1 to FIO mode (3.7.2) */ + memset(&_gpio_isr_map[0], 0xff, 64); +} + +static int _isr_map_entry(gpio_t pin) { + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + + /* lpc2387 can only interrupt in pins of port 0 and 2 */ + if (port && port != 2) { + return -1; + } + + if (port) { + _pin += 32; + } + + return _pin; +} + +int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pullup) +{ + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + + FIO_PORT_t *_port = &FIO_PORTS[port]; + + /* set mask */ + _port->MASK = ~(0x1<<_pin); + + /* set direction */ + _port->DIR = ~0; + + /* set pullup/pulldown **/ + PINMODE[pin>>4] |= pullup << (_pin*2); + + gpio_init_mux(pin, 0); + + return 0; +} + +int gpio_init_mux(unsigned pin, unsigned mux) { + unsigned _pin = pin & 31; + PINSEL[pin>>4] &= ~(0x1 << (_pin*2)); + return 0; +} + +int gpio_init_int(gpio_t pin, gpio_pp_t pullup, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + DEBUG("gpio_init_int(): pin %u\n", pin); + int isr_map_entry; + + /* check if interrupt is possible for this pin */ + if ((isr_map_entry = _isr_map_entry(pin)) == -1) { + DEBUG("gpio_init_int(): pin %u cannot be used to generate interrupts.\n", pin); + return -1; + } + + /* find free isr state entry */ + int _state_index = _gpio_isr_map[isr_map_entry]; + + if (_state_index == 0xff) { + _state_index = bf_get_unset(_gpio_config_bitfield, GPIO_NUM_ISR); + _gpio_isr_map[isr_map_entry] = _state_index; + DEBUG("gpio_init_int(): pin has state_index=%i isr_map_entry=%i\n", _state_index, isr_map_entry); + } + + if (_state_index == 0xff) { +#if DEVELHELP + puts("lpc2387: gpio: warning: no free gpio callback state!"); +#endif + return -1; + } + + /* store callback */ + _gpio_states[_state_index].cb = cb; + _gpio_states[_state_index].arg = arg; + + extern void GPIO_IRQHandler(void); + gpio_init(pin, GPIO_DIR_IN, pullup); + + if (flank & GPIO_FALLING) { + bf_set(_gpio_falling, _state_index); + } + else { + bf_unset(_gpio_falling, _state_index); + } + + if (flank & GPIO_RISING) { + bf_set(_gpio_rising, _state_index); + } + else { + bf_unset(_gpio_rising, _state_index); + } + + _gpio_configure(pin, flank & GPIO_RISING, flank & GPIO_FALLING); + + /* activate irq */ + INTWAKE |= GPIO0WAKE | GPIO2WAKE; /* allow GPIO to wake up from power down */ + install_irq(GPIO_INT, &GPIO_IRQHandler, IRQP_GPIO); /* install irq handler */ + + return 0; +} + +static void _gpio_configure(gpio_t pin, unsigned rising, unsigned falling) +{ + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + + /* set irq settings */ + volatile unsigned long *en_f; + volatile unsigned long *en_r; + volatile unsigned long *en_clr; + + if (!port) { + en_f = &IO0_INT_EN_F; + en_r = &IO0_INT_EN_R; + en_clr = &IO0_INT_CLR; + } + else { + en_f = &IO2_INT_EN_F; + en_r = &IO2_INT_EN_R; + en_clr = &IO2_INT_CLR; + } + + /* configure irq */ + unsigned int bit = 0x1 << _pin; + + unsigned state = disableIRQ(); + + *en_clr |= bit; /* clear interrupt */ + + if (falling) { + *en_f |= bit; /* enable falling edge */ + } + else { + *en_f &= ~bit; /* disable falling edge */ + } + + if (rising) { + *en_r |= bit; /* enable rising edge */ + } + else { + *en_r &= ~bit; /* disable rising edge */ + } + + restoreIRQ(state); +} + +void gpio_irq_enable(gpio_t pin) +{ + int isr_map_entry =_isr_map_entry(pin); + int _state_index = _gpio_isr_map[isr_map_entry]; + + if (_state_index == 0xff) { + DEBUG("gpio_irq_enable(): trying to enable unconfigured pin.\n"); + return; + } + + _gpio_configure(pin, + bf_isset(_gpio_rising, _state_index), + bf_isset(_gpio_falling, _state_index)); +} + +void gpio_irq_disable(gpio_t dev) +{ + _gpio_configure(dev, 0, 0); +} + +int gpio_read(gpio_t pin) +{ + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + FIO_PORT_t *_port = &FIO_PORTS[port]; + _port->MASK = ~(1<<_pin); + return _port->PIN != 0; +} + +void gpio_set(gpio_t pin) +{ + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + FIO_PORT_t *_port = &FIO_PORTS[port]; + _port->MASK = ~(1<<_pin); + _port->SET = ~0; +} + +void gpio_clear(gpio_t pin) +{ + unsigned _pin = pin & 31; + unsigned port = pin >> 5; + FIO_PORT_t *_port = &FIO_PORTS[port]; + _port->MASK = ~(1<<_pin); + _port->CLR = ~0; +} + +void gpio_toggle(gpio_t dev) +{ + if (gpio_read(dev)) { + gpio_clear(dev); + } + else { + gpio_set(dev); + } +} + +void gpio_write(gpio_t dev, int value) +{ + if (value) { + gpio_set(dev); + } + else { + gpio_clear(dev); + } +} + +static void test_irq(int port, unsigned long f_mask, unsigned long r_mask) +{ + /* Test each bit of rising and falling masks, if set trigger interrupt + * on corresponding device */ + unsigned bit = 0x1; + int n = 0; + while (bit) { + if ((r_mask & bit) | (f_mask & bit)) { + int _state_index = _gpio_isr_map[n + (port<<1)]; + if (_state_index != 0xff) { + _gpio_states[_state_index].cb(_gpio_states[_state_index].arg); + } + } + + bit <<= 1; + n++; + } +} + +void GPIO_IRQHandler(void) __attribute__((interrupt("IRQ"))); + +void GPIO_IRQHandler(void) +{ + if (IO_INT_STAT & BIT0) { /* interrupt(s) on PORT0 pending */ + unsigned long int_stat_f = IO0_INT_STAT_F; /* save content */ + unsigned long int_stat_r = IO0_INT_STAT_R; /* save content */ + + IO0_INT_CLR = int_stat_f; /* clear flags of fallen pins */ + IO0_INT_CLR = int_stat_r; /* clear flags of risen pins */ + + test_irq(0, int_stat_f, int_stat_r); + } + + if (IO_INT_STAT & BIT2) { /* interrupt(s) on PORT2 pending */ + unsigned long int_stat_f = IO2_INT_STAT_F; /* save content */ + unsigned long int_stat_r = IO2_INT_STAT_R; /* save content */ + + IO2_INT_CLR = int_stat_f; /* clear flags of fallen pins */ + IO2_INT_CLR = int_stat_r; /* clear flags of risen pins */ + + test_irq(2, int_stat_f, int_stat_r); + } + + VICVectAddr = 0; /* Acknowledge Interrupt */ +}