diff --git a/boards/arduino-mega2560/Makefile.features b/boards/arduino-mega2560/Makefile.features index 675243084d23e384927a15248e183d2266e2ed59..098dfa3e535ee8e240a2adbbb7a7964bb2465bec 100644 --- a/boards/arduino-mega2560/Makefile.features +++ b/boards/arduino-mega2560/Makefile.features @@ -1,2 +1,3 @@ FEATURES_PROVIDED += periph_uart +FEATURES_PROVIDED += periph_gpio FEATURES_MCU_GROUP = avr8 diff --git a/boards/arduino-mega2560/include/periph_conf.h b/boards/arduino-mega2560/include/periph_conf.h index 22549013bc4ac991f7255ccf8f088afaf0321814..19f1d3dc1fb18117d20664e7c06eed521dc43507 100644 --- a/boards/arduino-mega2560/include/periph_conf.h +++ b/boards/arduino-mega2560/include/periph_conf.h @@ -227,12 +227,6 @@ extern "C" { #define UART3_RECEIVED_DATA (UART3_CTRL_STAT_A & (1 << UART3_RX_COMPLETE)) #define UART3_DTREG_EMPTY (UART3_CTRL_STAT_A & (1 << UART3_DATA_EMPTY)) - - -/* TODO: add defines for device agnostic implementation */ -/** @} */ - - /** * @brief ADC configuration */ @@ -261,26 +255,6 @@ extern "C" { #define I2C_0_EN 0 #define I2C_0_EN 0 -/** - * @brief GPIO configuration - */ -#define GPIO_0_EN 0 -#define GPIO_1_EN 0 -#define GPIO_2_EN 0 -#define GPIO_3_EN 0 -#define GPIO_4_EN 0 -#define GPIO_5_EN 0 -#define GPIO_6_EN 0 -#define GPIO_7_EN 0 -#define GPIO_8_EN 0 -#define GPIO_9_EN 0 -#define GPIO_10_EN 0 -#define GPIO_11_EN 0 -#define GPIO_12_EN 0 -#define GPIO_13_EN 0 -#define GPIO_14_EN 0 -#define GPIO_15_EN 0 - #ifdef __cplusplus } #endif diff --git a/cpu/atmega2560/include/periph_cpu.h b/cpu/atmega2560/include/periph_cpu.h index f4b1548294c2bbfd506dc62a4efecfcc1cdfc0e2..7d38894e90323d62b38671b587b8fa51c6b48c11 100644 --- a/cpu/atmega2560/include/periph_cpu.h +++ b/cpu/atmega2560/include/periph_cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Freie Universität Berlin + * Copyright (C) 2015 HAW Hamburg * * 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 @@ -13,19 +13,40 @@ * @file * @brief CPU specific definitions for internal peripheral handling * - * @author Hauke Petersen <hauke.peterse@fu-berlin.de> + * @author René Herthel <rene-herthel@outlook.de> */ #ifndef PERIPH_CPU_H_ #define PERIPH_CPU_H_ #include "periph/dev_enums.h" +#include <avr/io.h> #ifdef __cplusplus extern "C" { #endif -/* nothing defined here so far... */ +/** + * @brief Define a CPU specific GPIO pin generator macro + */ +#define GPIO(x, y) ((x << 4) | y) + +/** + * @brief Available ports on the ATmega2560 family + */ +enum { + PORT_A = 0, /**< port A */ + PORT_B = 1, /**< port B */ + PORT_C = 2, /**< port C */ + PORT_D = 3, /**< port D */ + PORT_E = 4, /**< port E */ + PORT_F = 5, /**< port F */ + PORT_G = 6, /**< port G */ + PORT_H = 7, /**< port H */ + PORT_J = 8, /**< port J */ + PORT_K = 9, /**< port K */ + PORT_L = 10 /**< port L */ +}; #ifdef __cplusplus } diff --git a/cpu/atmega2560/periph/Makefile b/cpu/atmega2560/periph/Makefile index 87a55ed6845fa8eeb27dfc2774608b6e8d30662c..6d1887b640099d130c5a6400e3f878f0c65aa6f1 100644 --- a/cpu/atmega2560/periph/Makefile +++ b/cpu/atmega2560/periph/Makefile @@ -1,2 +1,3 @@ MODULE = periph + include $(RIOTBASE)/Makefile.base diff --git a/cpu/atmega2560/periph/gpio.c b/cpu/atmega2560/periph/gpio.c new file mode 100644 index 0000000000000000000000000000000000000000..275e1dc7eb5e37d07129e1e963054ff8e6f14a66 --- /dev/null +++ b/cpu/atmega2560/periph/gpio.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2015 HAW Hamburg + + * + * 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 driver_periph + * @{ + * + * @file + * @brief Low-level GPIO driver implementation for ATmega2560 + * + * @author René Herthel <rene-herthel@outlook.de> + * + * @} + */ + + +#include <stdio.h> + +#include <avr/interrupt.h> + +#include "cpu.h" +#include "periph/gpio.h" +#include "periph_conf.h" + +#define GPIO_BASE_PORT_A (0x20) +#define GPIO_OFFSET_PORT_H (0xCB) +#define GPIO_OFFSET_PIN_PORT (0x02) +#define GPIO_OFFSET_PIN_PIN (0x03) +#define GPIO_EXT_INT_NUMOF (7U) + +typedef struct { + gpio_cb_t cb; + void *arg; +} gpio_state_t; + +static gpio_state_t config[GPIO_EXT_INT_NUMOF]; + +/** + * @brief Extract the pin number of the given pin + */ +static inline uint8_t _pin_num(gpio_t pin) +{ + return (pin & 0x0f); +} + +/** + * @brief Extract the port number of the given pin + */ +static inline uint8_t _port_num(gpio_t pin) +{ + return (pin >> 4) & 0x0f; +} + +/** + * @brief Generate the PORTx address of the give pin. + */ +static inline uint16_t _port_addr(gpio_t pin) +{ + uint8_t port_num = _port_num(pin); + uint16_t port_addr = port_num * GPIO_OFFSET_PIN_PIN; + + port_addr += GPIO_BASE_PORT_A; + port_addr += GPIO_OFFSET_PIN_PORT; + + if (port_num > PORT_G) { + port_addr += GPIO_OFFSET_PORT_H; + } + + return port_addr; +} + +/** + * @brief Generate the DDRx address of the given pin + */ +static inline uint8_t _ddr_addr(gpio_t pin) +{ + return (_port_addr(pin) - 0x01); +} + +/** + * @brief Generate the PINx address of the given pin. + */ +static inline uint8_t _pin_addr(gpio_t pin) +{ + return (_port_addr(pin) - 0x02); +} + +int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pullup) +{ + int res; + + if (dir == GPIO_DIR_OUT) { + _SFR_MEM8(_ddr_addr(pin)) |= (1 << _pin_num(pin)); + res = bit_is_set(_SFR_MEM8(_ddr_addr(pin)), _pin_num(pin)); + } + else { + _SFR_MEM8(_ddr_addr(pin)) &= ~(1 << _pin_num(pin)); + res = bit_is_clear(_SFR_MEM8(_ddr_addr(pin)), _pin_num(pin)); + } + + return (res == 0) ? -1 : 0; +} + +int gpio_init_int(gpio_t pin, gpio_pp_t pullup, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + uint8_t pin_num = _pin_num(pin); + + if ((_port_num(pin) == PORT_D && pin_num > 3) + || (_port_num(pin) == PORT_E && pin_num < 4)) { + return -1; + } + + if (gpio_init(pin, GPIO_DIR_IN, pullup) < 0) { + return -1; + } + + gpio_set(pin); + + /* clear global interrupt flag */ + cli(); + + EIMSK |= (1 << pin_num); + + /* configure the flank */ + switch (flank) { + case GPIO_RISING: + if (pin_num < 4) { + EICRA |= (3 << pin_num * 2); + } + else { + EICRB |= (3 << (pin_num * 2) % 4); + } + break; + case GPIO_FALLING: + if (pin_num < 4) { + EICRA |= (2 << pin_num * 2); + } + else { + EICRB |= (2 << (pin_num * 2) % 4); + } + break; + case GPIO_BOTH: + if (pin_num < 4) { + EICRA |= (1 << pin_num * 2); + } + else { + EICRB |= (1 << (pin_num * 2) % 4); + } + break; + default: + return -1; + }; + + /* set callback */ + config[pin_num].cb = cb; + config[pin_num].arg = arg; + + /* set global interrupt flag */ + sei(); + + return 0; +} + +void gpio_irq_enable(gpio_t pin) +{ + EIMSK |= (1 << _pin_num(pin)); +} + +void gpio_irq_disable(gpio_t pin) +{ + EIMSK &= ~(1 << _pin_num(pin)); +} + +int gpio_read(gpio_t pin) +{ + return (_SFR_MEM8(_pin_addr(pin)) & (1 << _pin_num(pin))); +} + +void gpio_set(gpio_t pin) +{ + _SFR_MEM8(_port_addr(pin)) |= (1 << _pin_num(pin)); +} + +void gpio_clear(gpio_t pin) +{ + _SFR_MEM8(_port_addr(pin)) &= ~(1 << _pin_num(pin)); +} + +void gpio_toggle(gpio_t pin) +{ + if (gpio_read(pin)) { + gpio_clear(pin); + } + else { + gpio_set(pin); + } +} + +void gpio_write(gpio_t pin, int value) +{ + if (value) { + gpio_set(pin); + } + else { + gpio_clear(pin); + } +} + +static inline void irq_handler(uint8_t pin_num) +{ + config[pin_num].cb(config[pin_num].arg); +} + +ISR(INT0_vect, ISR_BLOCK) +{ + irq_handler(0); /**< predefined interrupt pin */ +} + +ISR(INT1_vect, ISR_BLOCK) +{ + irq_handler(1); /**< predefined interrupt pin */ +} + +ISR(INT2_vect, ISR_BLOCK) +{ + irq_handler(2); /**< predefined interrupt pin */ +} + +ISR(INT3_vect, ISR_BLOCK) +{ + irq_handler(3); /**< predefined interrupt pin */ +} + +ISR(INT4_vect, ISR_BLOCK) +{ + irq_handler(4); /**< predefined interrupt pin */ +} + +ISR(INT5_vect, ISR_BLOCK) +{ + irq_handler(5); /**< predefined interrupt pin */ +} + +ISR(INT6_vect, ISR_BLOCK) +{ + irq_handler(6); /**< predefined interrupt pin */ +} + +ISR(INT7_vect, ISR_BLOCK) +{ + irq_handler(7); /**< predefined interrupt pin */ +}