diff --git a/boards/mbed_lpc1768/Makefile.dep b/boards/mbed_lpc1768/Makefile.dep new file mode 100644 index 0000000000..5472bf8b8d --- /dev/null +++ b/boards/mbed_lpc1768/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += saul_gpio +endif diff --git a/boards/mbed_lpc1768/Makefile.features b/boards/mbed_lpc1768/Makefile.features index 624b6ae2cb..777b500b0d 100644 --- a/boards/mbed_lpc1768/Makefile.features +++ b/boards/mbed_lpc1768/Makefile.features @@ -1,4 +1,5 @@ # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/mbed_lpc1768/board.c b/boards/mbed_lpc1768/board.c index 2ae350105d..726676f60b 100644 --- a/boards/mbed_lpc1768/board.c +++ b/boards/mbed_lpc1768/board.c @@ -14,15 +14,33 @@ * @brief Board specific implementations for the mbed LPC1768 board * * @author Hauke Petersen + * @author Bas Stottelaar * * @} */ #include "board.h" -static void leds_init(void); +#include "periph/gpio.h" + extern void SystemInit(void); +/** + * @brief Initialize the on-board LEDs. + */ +static void leds_init(void) +{ + gpio_init(LED0_PIN, GPIO_OUT); + gpio_init(LED1_PIN, GPIO_OUT); + gpio_init(LED2_PIN, GPIO_OUT); + gpio_init(LED3_PIN, GPIO_OUT); + + LED0_OFF; + LED1_OFF; + LED2_OFF; + LED3_OFF; +} + void board_init(void) { /* initialize core clocks via CMSIS function */ @@ -32,24 +50,3 @@ void board_init(void) /* initialize the boards LEDs */ leds_init(); } - -/** - * @brief Initialize the boards on-board LEDs (LED1 to LED4) - * - * The LED initialization is hard-coded in this function. As the LEDs are soldered - * onto the board they are fixed to their CPU pins. - * - * The LEDs are connected to the following pins: - * - LED1: P1.18 - * - LED2: P1.20 - * - LED3: P1.21 - * - LED4: P1.23 - */ -static void leds_init(void) -{ - /* configure LED pins as output */ - LED_PORT->FIODIR |= (LED0_MASK | LED1_MASK | LED2_MASK | LED3_MASK); - - /* clear all LEDs */ - LED_PORT->FIOCLR = (LED0_MASK | LED1_MASK | LED2_MASK | LED3_MASK); -} diff --git a/boards/mbed_lpc1768/include/board.h b/boards/mbed_lpc1768/include/board.h index 473b291a9f..8be6a66ea4 100644 --- a/boards/mbed_lpc1768/include/board.h +++ b/boards/mbed_lpc1768/include/board.h @@ -23,10 +23,9 @@ #ifndef BOARD_H #define BOARD_H -#include - -#include "bitarithm.h" #include "cpu.h" +#include "periph_conf.h" +#include "periph/gpio.h" #ifdef __cplusplus extern "C" { @@ -41,24 +40,18 @@ extern "C" { #define LED2_PIN GPIO_PIN(1, 21) #define LED3_PIN GPIO_PIN(1, 23) -#define LED_PORT (LPC_GPIO1) -#define LED0_MASK (BIT18) -#define LED1_MASK (BIT20) -#define LED2_MASK (BIT21) -#define LED3_MASK (BIT23) - -#define LED0_ON (LED_PORT->FIOSET = LED0_MASK) -#define LED0_OFF (LED_PORT->FIOCLR = LED0_MASK) -#define LED0_TOGGLE (LED_PORT->FIOPIN ^= LED0_MASK) -#define LED1_ON (LED_PORT->FIOSET = LED1_MASK) -#define LED1_OFF (LED_PORT->FIOCLR = LED1_MASK) -#define LED1_TOGGLE (LED_PORT->FIOPIN ^= LED1_MASK) -#define LED2_ON (LED_PORT->FIOSET = LED2_MASK) -#define LED2_OFF (LED_PORT->FIOCLR = LED2_MASK) -#define LED2_TOGGLE (LED_PORT->FIOPIN ^= LED2_MASK) -#define LED3_ON (LED_PORT->FIOSET = LED3_MASK) -#define LED3_OFF (LED_PORT->FIOCLR = LED3_MASK) -#define LED3_TOGGLE (LED_PORT->FIOPIN ^= LED3_MASK) +#define LED0_ON gpio_set(LED0_PIN) +#define LED0_OFF gpio_clear(LED0_PIN) +#define LED0_TOGGLE gpio_toggle(LED0_PIN) +#define LED1_ON gpio_set(LED1_PIN) +#define LED1_OFF gpio_clear(LED1_PIN) +#define LED1_TOGGLE gpio_toggle(LED1_PIN) +#define LED2_ON gpio_set(LED2_PIN) +#define LED2_OFF gpio_clear(LED2_PIN) +#define LED2_TOGGLE gpio_toggle(LED2_PIN) +#define LED3_ON gpio_set(LED3_PIN) +#define LED3_OFF gpio_clear(LED3_PIN) +#define LED3_TOGGLE gpio_toggle(LED3_PIN) /** @} */ /** diff --git a/boards/mbed_lpc1768/include/gpio_params.h b/boards/mbed_lpc1768/include/gpio_params.h new file mode 100644 index 0000000000..8f12cc7f52 --- /dev/null +++ b/boards/mbed_lpc1768/include/gpio_params.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Bas Stottelaar + * + * 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 boards_mbed_lpc1768 + * @{ + * + * @file + * @brief Board specific configuration of direct mapped GPIOs + * + * @author Bas Stottelaar + */ + +#ifndef GPIO_PARAMS_H +#define GPIO_PARAMS_H + +#include "board.h" +#include "saul/periph.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GPIO pin configuration + */ +static const saul_gpio_params_t saul_gpio_params[] = +{ + { + .name = "LED 0", + .pin = LED0_PIN, + .mode = GPIO_OUT + }, + { + .name = "LED 1", + .pin = LED1_PIN, + .mode = GPIO_OUT + }, + { + .name = "LED 2", + .pin = LED2_PIN, + .mode = GPIO_OUT + }, + { + .name = "LED 3", + .pin = LED3_PIN, + .mode = GPIO_OUT + } +}; + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_PARAMS_H */ +/** @} */ diff --git a/boards/seeeduino_arch-pro/Makefile.dep b/boards/seeeduino_arch-pro/Makefile.dep new file mode 100644 index 0000000000..5472bf8b8d --- /dev/null +++ b/boards/seeeduino_arch-pro/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += saul_gpio +endif diff --git a/boards/seeeduino_arch-pro/Makefile.features b/boards/seeeduino_arch-pro/Makefile.features index 624b6ae2cb..777b500b0d 100644 --- a/boards/seeeduino_arch-pro/Makefile.features +++ b/boards/seeeduino_arch-pro/Makefile.features @@ -1,4 +1,5 @@ # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/seeeduino_arch-pro/board.c b/boards/seeeduino_arch-pro/board.c index 61826aeb33..aa4a2412b9 100644 --- a/boards/seeeduino_arch-pro/board.c +++ b/boards/seeeduino_arch-pro/board.c @@ -21,9 +21,26 @@ #include "board.h" -static void leds_init(void); +#include "periph/gpio.h" + extern void SystemInit(void); +/** + * @brief Initialize the on-board LEDs. + */ +static void leds_init(void) +{ + gpio_init(LED0_PIN, GPIO_OUT); + gpio_init(LED1_PIN, GPIO_OUT); + gpio_init(LED2_PIN, GPIO_OUT); + gpio_init(LED3_PIN, GPIO_OUT); + + LED0_OFF; + LED1_OFF; + LED2_OFF; + LED3_OFF; +} + void board_init(void) { /* initialize core clocks via CMSIS function */ @@ -33,26 +50,3 @@ void board_init(void) /* initialize the boards LEDs */ leds_init(); } - -/** - * @brief Initialize the boards on-board LEDs (LED1 to LED4) - * - * The LED initialization is hard-coded in this function. As the LEDs are - * soldered onto the board they are fixed to their CPU pins. - * - * The LEDs are connected to the following pins: - * - LED1: P1.18 - * - LED2: P1.20 - * - LED3: P1.21 - * - LED4: P1.23 - * - * The LEDs are active-low (current-sink). - */ -static void leds_init(void) -{ - /* configure LED pins as output */ - LED_PORT->FIODIR |= (LED0_MASK | LED1_MASK | LED2_MASK | LED3_MASK); - - /* turn off all LEDs */ - LED_PORT->FIOSET = (LED0_MASK | LED1_MASK | LED2_MASK | LED3_MASK); -} diff --git a/boards/seeeduino_arch-pro/include/board.h b/boards/seeeduino_arch-pro/include/board.h index 8aa7d4bb1a..ad5ad60957 100644 --- a/boards/seeeduino_arch-pro/include/board.h +++ b/boards/seeeduino_arch-pro/include/board.h @@ -24,10 +24,9 @@ #ifndef BOARD_H #define BOARD_H -#include - -#include "bitarithm.h" #include "cpu.h" +#include "periph_conf.h" +#include "periph/gpio.h" #ifdef __cplusplus extern "C" { @@ -42,24 +41,18 @@ extern "C" { #define LED2_PIN GPIO_PIN(1, 21) #define LED3_PIN GPIO_PIN(1, 23) -#define LED_PORT (LPC_GPIO1) -#define LED0_MASK (BIT18) -#define LED1_MASK (BIT20) -#define LED2_MASK (BIT21) -#define LED3_MASK (BIT23) - -#define LED0_ON (LED_PORT->FIOCLR = LED0_MASK) -#define LED0_OFF (LED_PORT->FIOSET = LED0_MASK) -#define LED0_TOGGLE (LED_PORT->FIOPIN ^= LED0_MASK) -#define LED1_ON (LED_PORT->FIOCLR = LED1_MASK) -#define LED1_OFF (LED_PORT->FIOSET = LED1_MASK) -#define LED1_TOGGLE (LED_PORT->FIOPIN ^= LED1_MASK) -#define LED2_ON (LED_PORT->FIOCLR = LED2_MASK) -#define LED2_OFF (LED_PORT->FIOSET = LED2_MASK) -#define LED2_TOGGLE (LED_PORT->FIOPIN ^= LED2_MASK) -#define LED3_ON (LED_PORT->FIOCLR = LED3_MASK) -#define LED3_OFF (LED_PORT->FIOSET = LED3_MASK) -#define LED3_TOGGLE (LED_PORT->FIOPIN ^= LED3_MASK) +#define LED0_ON gpio_clear(LED0_PIN) +#define LED0_OFF gpio_set(LED0_PIN) +#define LED0_TOGGLE gpio_toggle(LED0_PIN) +#define LED1_ON gpio_clear(LED1_PIN) +#define LED1_OFF gpio_set(LED1_PIN) +#define LED1_TOGGLE gpio_toggle(LED1_PIN) +#define LED2_ON gpio_clear(LED2_PIN) +#define LED2_OFF gpio_set(LED2_PIN) +#define LED2_TOGGLE gpio_toggle(LED2_PIN) +#define LED3_ON gpio_clear(LED3_PIN) +#define LED3_OFF gpio_set(LED3_PIN) +#define LED3_TOGGLE gpio_toggle(LED3_PIN) /** @} */ /** diff --git a/boards/seeeduino_arch-pro/include/gpio_params.h b/boards/seeeduino_arch-pro/include/gpio_params.h new file mode 100644 index 0000000000..d599c7203f --- /dev/null +++ b/boards/seeeduino_arch-pro/include/gpio_params.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 Bas Stottelaar + * + * 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 boards_seeduino_arch-pro + * @{ + * + * @file + * @brief Board specific configuration of direct mapped GPIOs + * + * @author Bas Stottelaar + */ + +#ifndef GPIO_PARAMS_H +#define GPIO_PARAMS_H + +#include "board.h" +#include "saul/periph.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GPIO pin configuration + */ +static const saul_gpio_params_t saul_gpio_params[] = +{ + { + .name = "LED 0", + .pin = LED0_PIN, + .mode = GPIO_OUT, + .flags = SAUL_GPIO_INVERTED + }, + { + .name = "LED 1", + .pin = LED1_PIN, + .mode = GPIO_OUT, + .flags = SAUL_GPIO_INVERTED + }, + { + .name = "LED 2", + .pin = LED2_PIN, + .mode = GPIO_OUT, + .flags = SAUL_GPIO_INVERTED + }, + { + .name = "LED 3", + .pin = LED3_PIN, + .mode = GPIO_OUT, + .flags = SAUL_GPIO_INVERTED + } +}; + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_PARAMS_H */ +/** @} */ diff --git a/cpu/lpc1768/include/periph_cpu.h b/cpu/lpc1768/include/periph_cpu.h index 528a14608f..dcb3598f71 100644 --- a/cpu/lpc1768/include/periph_cpu.h +++ b/cpu/lpc1768/include/periph_cpu.h @@ -14,19 +14,54 @@ * @brief CPU specific definitions for internal peripheral handling * * @author Hauke Petersen + * @author Bas Stottelaar */ #ifndef PERIPH_CPU_H #define PERIPH_CPU_H +#include + #include "cpu.h" -#include "periph/dev_enums.h" #ifdef __cplusplus extern "C" { #endif -/* nothing to do here, yet */ +/** + * @name Override the default GPIO type + * @{ + */ +#define HAVE_GPIO_T +typedef uint8_t gpio_t; +/** @} */ + +/** + * @brief Define a custom GPIO_PIN macro for the lpc1768 + */ +#define GPIO_PIN(port, pin) (gpio_t)((port << 5) | pin) + +/** + * @name Override the default GPIO mode values + * @{ + */ +#define PIN_DIR_IN (0x00 << 0) +#define PIN_DIR_OUT (0x01 << 0) +#define PIN_MODE_PU (0x00 << 1) +#define PIN_MODE_PD (0x02 << 1) +#define PIN_MODE_NONE (0x03 << 1) +#define PIN_MODE_OD (0x01 << 3) + +#define HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN = (PIN_DIR_IN | PIN_MODE_NONE), /**< in without pull-up/down */ + GPIO_IN_PD = (PIN_DIR_IN | PIN_MODE_PD), /**< in with pull-down */ + GPIO_IN_PU = (PIN_DIR_IN | PIN_MODE_PU), /**< in with pull-up */ + GPIO_OUT = (PIN_DIR_OUT | PIN_MODE_NONE), /**< push-pull output */ + GPIO_OD = (PIN_DIR_OUT | PIN_MODE_OD), /**< open-drain output */ + GPIO_OD_PU = (PIN_DIR_OUT | PIN_MODE_OD | PIN_MODE_PU) /**< open-drain output with pull-up */ +} gpio_mode_t; +/** @} */ #ifdef __cplusplus } diff --git a/cpu/lpc1768/periph/gpio.c b/cpu/lpc1768/periph/gpio.c new file mode 100644 index 0000000000..495e8027d3 --- /dev/null +++ b/cpu/lpc1768/periph/gpio.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2017 Bas Stottelaar + * + * 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 cpu_lpc1768 + * @{ + * + * @file + * @brief Low-level GPIO driver implementation + * + * @author Bas Stottelaar + * + * @} + */ + +#include "cpu.h" +#include "periph/gpio.h" + +/** + * @brief Number of external interrupt lines. + */ +#define NUMOF_IRQS (32) + +/** + * @brief Hold one interrupt context per interrupt line + */ +static gpio_isr_ctx_t isr_ctx[NUMOF_IRQS]; + +static gpio_flank_t isr_state[2][32]; + +#define PIN_MASK (0x1f) +#define PORT_SHIFT (5U) + +static inline int _pin(gpio_t pin) +{ + return (pin & PIN_MASK); +} + +static inline int _port(gpio_t pin) +{ + return (pin >> PORT_SHIFT); +} + +static inline LPC_GPIO_TypeDef *_base(gpio_t pin) +{ + return (LPC_GPIO_TypeDef *) (LPC_GPIO_BASE + (_port(pin) * 0x20)); +} + +static inline void _configure_flank(gpio_t pin, gpio_flank_t flank) +{ + switch (flank) { + case GPIO_RISING: + if (_port(pin) == 0) { + LPC_GPIOINT->IO0IntEnF &= ~(1 << _pin(pin)); + LPC_GPIOINT->IO0IntEnR |= (1 << _pin(pin)); + } + else { + LPC_GPIOINT->IO2IntEnF &= ~(1 << _pin(pin)); + LPC_GPIOINT->IO2IntEnR |= (1 << _pin(pin)); + } + + break; + case GPIO_FALLING: + if (_port(pin) == 0) { + LPC_GPIOINT->IO0IntEnF |= (1 << _pin(pin)); + LPC_GPIOINT->IO0IntEnR &= ~(1 << _pin(pin)); + } + else { + LPC_GPIOINT->IO2IntEnF |= (1 << _pin(pin)); + LPC_GPIOINT->IO2IntEnR &= ~(1 << _pin(pin)); + } + + break; + case GPIO_BOTH: + if (_port(pin) == 0) { + LPC_GPIOINT->IO0IntEnF |= 1 << _pin(pin); + LPC_GPIOINT->IO0IntEnR |= 1 << _pin(pin); + } + else { + LPC_GPIOINT->IO2IntEnF |= 1 << _pin(pin); + LPC_GPIOINT->IO2IntEnR |= 1 << _pin(pin); + } + + break; + } +} + +int gpio_init(gpio_t pin, gpio_mode_t mode) +{ + /* check for valid pin */ + if (pin == GPIO_UNDEF) { + return -1; + } + + if (_port(pin) > 4 || _pin(pin) > 32) { + return -1; + } + + /* enable gpio peripheral */ + LPC_SC->PCONP |= (1 << 15); + + /* pin as output or input */ + LPC_GPIO_TypeDef *base = _base(pin); + + base->FIODIR &= ~(1 << _pin(pin)); + base->FIODIR |= ((mode & 0x01) << _pin(pin)); + + /* configure pin function */ + int reg = 2 * _port(pin) + (_pin(pin) / 16); + int bit = (pin % 16) * 2; + + ((uint32_t *) &LPC_PINCON->PINSEL0)[reg] &= ~(0x03 << bit); + + /* configure pull up/down */ + ((uint32_t *) &LPC_PINCON->PINMODE0)[reg] &= ~(0x03 << bit); + ((uint32_t *) &LPC_PINCON->PINMODE0)[reg] |= (((mode >> 1) & 0x03) << bit); + + /* configure open drain */ + ((uint32_t *) &LPC_PINCON->PINMODE_OD0)[_port(pin)] &= ~(1 << _pin(pin)); + ((uint32_t *) &LPC_PINCON->PINMODE_OD0)[_port(pin)] |= (((mode >> 3) & 0x01) << _pin(pin)); + + return 0; +} + +int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + /* only certain pins can be used as interrupt pins */ + if (_port(pin) != 0 && _port(pin) != 2) { + return -1; + } + + /* initialize the pin */ + int result = gpio_init(pin, mode); + + if (result != 0) { + return result; + } + + /* store interrupt callback */ + isr_ctx[_pin(pin)].cb = cb; + isr_ctx[_pin(pin)].arg = arg; + + /* need to store flank configuration for (re)enable irq */ + isr_state[_port(pin) >> 1][_pin(pin)] = flank; + + /* set flank configuration */ + _configure_flank(pin, flank); + + /* clear any pending requests and enable the interrupt */ + NVIC_ClearPendingIRQ(EINT3_IRQn); + NVIC_EnableIRQ(EINT3_IRQn); + + return 0; +} + +void gpio_irq_enable(gpio_t pin) +{ + assert(_port(pin) == 0 || _port(pin) == 2); + + _configure_flank(pin, isr_state[_port(pin) >> 1][_pin(pin)]); +} + +void gpio_irq_disable(gpio_t pin) +{ + assert(_port(pin) == 0 || _port(pin) == 2); + + if (_port(pin) == 0) { + LPC_GPIOINT->IO0IntEnF &= ~(1 << _pin(pin)); + LPC_GPIOINT->IO0IntEnR &= ~(1 << _pin(pin)); + } + else { + LPC_GPIOINT->IO2IntEnF &= ~(1 << _pin(pin)); + LPC_GPIOINT->IO2IntEnR &= ~(1 << _pin(pin)); + } +} + +int gpio_read(gpio_t pin) +{ + LPC_GPIO_TypeDef *base = _base(pin); + + return (base->FIOPIN & (1 << _pin(pin))) ? 1 : 0; +} + +void gpio_set(gpio_t pin) +{ + LPC_GPIO_TypeDef *base = _base(pin); + + base->FIOSET = (1 << _pin(pin)); +} + +void gpio_clear(gpio_t pin) +{ + LPC_GPIO_TypeDef *base = _base(pin); + + base->FIOCLR = (1 << _pin(pin)); +} + +void gpio_toggle(gpio_t pin) +{ + LPC_GPIO_TypeDef *base = _base(pin); + + base->FIOPIN ^= (1 << _pin(pin)); +} + +void gpio_write(gpio_t pin, int value) +{ + LPC_GPIO_TypeDef *base = _base(pin); + + if (value) { + base->FIOSET = (1 << _pin(pin)); + } + else { + base->FIOCLR = (1 << _pin(pin)); + } +} + +void isr_eint3(void) +{ + /* combine all interrupts */ + uint32_t status = LPC_GPIOINT->IO0IntStatF | LPC_GPIOINT->IO0IntStatR | + LPC_GPIOINT->IO2IntStatF | LPC_GPIOINT->IO2IntStatR; + + /* invoke all handlers */ + for (int i = 0; i < NUMOF_IRQS; i++) { + if (status & (1 << i)) { + isr_ctx[i].cb(isr_ctx[i].arg); + + LPC_GPIOINT->IO0IntClr |= (1 << i); + LPC_GPIOINT->IO2IntClr |= (1 << i); + } + } + + cortexm_isr_end(); +}