diff --git a/boards/common/arduino-atmega/include/board_common.h b/boards/common/arduino-atmega/include/board_common.h index bc78749d9997..21c72ff36a2e 100644 --- a/boards/common/arduino-atmega/include/board_common.h +++ b/boards/common/arduino-atmega/include/board_common.h @@ -70,6 +70,7 @@ extern "C" { PCMSK2 |= (1 << PCINT23); \ } while (0) #define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT2_vect +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT2_vect_num #define AVR_CONTEXT_SWAP_TRIGGER PORTD ^= (1 << PD7) #endif @@ -80,6 +81,7 @@ extern "C" { PCMSK1 |= (1 << PCINT15); \ } while (0) #define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT1_vect +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT1_vect_num #define AVR_CONTEXT_SWAP_TRIGGER PORTJ ^= (1 << PJ6) #endif diff --git a/boards/waspmote-pro/include/board.h b/boards/waspmote-pro/include/board.h index b29350f99959..6c93b4dad2af 100644 --- a/boards/waspmote-pro/include/board.h +++ b/boards/waspmote-pro/include/board.h @@ -157,6 +157,7 @@ extern "C" { } while (0) /** @cond INTERNAL */ #define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT0_vect +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM PCINT0_vect_num #define AVR_CONTEXT_SWAP_TRIGGER PORTB ^= (1 << PB5) /** @endcond */ diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index e74db5d68cd7..11297d5786ad 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -1,7 +1,8 @@ /* * Copyright (C) 2015 HAW Hamburg * 2016 INRIA - + * 2017 TU Braunschweig, IBR + * * * 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 @@ -19,6 +20,7 @@ * @author René Herthel * @author Francisco Acosta * @author Laurent Navet + * @author Robert Hartung * * @} */ @@ -31,6 +33,7 @@ #include "cpu.h" #include "periph/gpio.h" #include "periph_conf.h" +#include "board.h" #define GPIO_BASE_PORT_A (0x20) #define GPIO_OFFSET_PORT_H (0xCB) @@ -57,7 +60,28 @@ #define GPIO_EXT_INT_NUMOF (2U) #endif +#ifdef AVR_USE_PCINT +#if defined(PCINT3_vect) +#define GPIO_PC_INT_NUMOF (32U) +#elif defined(PCINT2_vect) +#define GPIO_PC_INT_NUMOF (24U) +#elif defined(PCINT1_vect) +#define GPIO_PC_INT_NUMOF (16U) +#elif defined(PCINT0_vect) +#define GPIO_PC_INT_NUMOF (8U) +#endif +#endif + +/* holds the callback and argument for regular interrupts */ static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF]; +#ifdef AVR_USE_PCINT +/* holds the callback and argument for pin change interrupts */ +static gpio_isr_ctx_t pcint[GPIO_PC_INT_NUMOF]; +/* stores the configured flank for the respective pcint */ +static gpio_flank_t pcint_flank[GPIO_PC_INT_NUMOF]; +/* stores the last state of each port */ +static uint8_t pcint_state[GPIO_PC_INT_NUMOF / 8]; +#endif /** * @brief Extract the pin number of the given pin @@ -137,13 +161,61 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, if ((_port_num(pin) == PORT_D && pin_num > 3) #if defined (PORTE) - || (_port_num(pin) == PORT_E && pin_num < 4) - || (_port_num(pin) != PORT_D && _port_num(pin) != PORT_E) -#elif defined(CPU_ATMEGA328P) - || (pin_num < 2) || (_port_num(pin) != PORT_D) + || (_port_num(pin) == PORT_E && pin_num < 4) + || (_port_num(pin) != PORT_D && _port_num(pin) != PORT_E) +#elif defined(CPU_ATMEGA328P) /* INT0: PD2, INT1: PD3 */ + || (pin_num < 2) || (_port_num(pin) != PORT_D) +#elif defined(CPU_ATMEGA1284P) /* INT0: PD2, INT1: PD3, INT2: PB2 */ + || ((pin_num < 2) && (_port_num(pin) == PORT_D)) + || ((pin_num < 2) && (_port_num(pin) == PORT_B)) + || ((pin_num > 2) && (_port_num(pin) == PORT_B)) + || (_port_num(pin) == PORT_C) + || (_port_num(pin) == PORT_A) +#endif + || ((mode != GPIO_IN) && (mode != GPIO_IN_PU))) { + + /* If pin change interrupts are enabled, enable mask and interrupt */ + #ifdef GPIO_PC_INT_NUMOF + gpio_init(pin, mode); + cli(); + switch (_port_num(pin)) { + case 0: + PCMSK0 |= (1 << pin_num); + PCICR |= (1 << PCIE0); + break; + case 1: + PCMSK1 |= (1 << pin_num); + PCICR |= (1 << PCIE1); + break; +#ifdef PCIE2 + case 2: + PCMSK2 |= (1 << pin_num); + PCICR |= (1 << PCIE2); + break; +#endif +#ifdef PCIE3 + case 3: + PCMSK3 |= (1 << pin_num); + PCICR |= (1 << PCIE3); + break; #endif - || ((mode != GPIO_IN) && (mode != GPIO_IN_PU))) { + default: + return -1; + break; + } + /* set configuration */ + int pcint_num = _port_num(pin) * 8 + pin_num; + pcint[pcint_num].cb = cb; + pcint[pcint_num].arg = arg; + pcint_flank[pcint_num] = flank; + /* store current value of the port */ + pcint_state[_port_num(pin)] = (_SFR_MEM8(_pin_addr( GPIO_PIN( _port_num(pin), pin_num ) ))); + /* enable global interrupt flag */ + sei(); + return 0; + #else /* if no pin change interrupts are used */ return -1; + #endif /* GPIO_PC_INT_NUMOF */ } gpio_init(pin, mode); @@ -237,6 +309,75 @@ void gpio_write(gpio_t pin, int value) } } +#ifdef GPIO_PC_INT_NUMOF +/* inline function that is used by the PCINT ISR */ +static inline void pcint_handler(uint8_t port_num, volatile uint8_t *mask_reg) +{ + uint8_t pin_num = 0; + /* calculate changed bits */ + uint8_t state = _SFR_MEM8(_pin_addr(GPIO_PIN(port_num, 1))); + /* get pins that changed */ + uint8_t change = pcint_state[port_num] ^ state; + + /* apply mask to change */ + change &= *mask_reg; + /* loop through all changed pins with enabled pcint */ + while (change > 0) { + /* check if this pin is enabled & has changed */ + if (change & 0x1) { + uint8_t pin_mask = (1 << pin_num); + gpio_flank_t flank = pcint_flank[ port_num * 8 + pin_num ]; + /* trigger only on correct flank */ + if (flank == GPIO_BOTH || ((state & pin_mask) && flank == GPIO_RISING) || (!(state & pin_mask) && flank == GPIO_FALLING)) { + /* finally execute callback routine */ + __enter_isr(); + pcint[port_num * 8 + pin_num].cb( pcint[port_num * 8 + pin_num].arg ); + __exit_isr(); + } + } + change = change >> 1; + pin_num++; + } + + /* store current state */ + pcint_state[port_num] = state; +} + +#ifndef AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +#error gpio.c requires the definition of AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +#endif + +/* + * PCINT0 is always defined, if GPIO_PC_INT_NUMOF is defined + */ +#if PCINT0_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +ISR(PCINT0_vect, ISR_BLOCK) { + pcint_handler(0, &PCMSK0); +} +#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */ +#if defined(PCINT1_vect) +#if PCINT1_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +ISR(PCINT1_vect, ISR_BLOCK) { + pcint_handler(1, &PCMSK1); +} +#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */ +#endif /* PCINT1_vect */ +#if defined(PCINT2_vect) +#if PCINT2_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +ISR(PCINT2_vect, ISR_BLOCK) { + pcint_handler(2, &PCMSK2); +} +#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */ +#endif /* PCINT2_vect */ +#if defined(PCINT3_vect) +#if PCINT3_vect_num != AVR_CONTEXT_SWAP_INTERRUPT_VECT_NUM +ISR(PCINT3_vect, ISR_BLOCK) { + pcint_handler(3, &PCMSK3); +} +#endif /* AVR_CONTEXT_SWAP_INTERRUPT_VECT */ +#endif /* PCINT3_vect */ +#endif /* GPIO_PC_INT_NUMOF */ + static inline void irq_handler(uint8_t pin_num) { __enter_isr(); @@ -244,54 +385,46 @@ static inline void irq_handler(uint8_t pin_num) __exit_isr(); } -ISR(INT0_vect, ISR_BLOCK) -{ +ISR(INT0_vect, ISR_BLOCK){ irq_handler(0); /**< predefined interrupt pin */ } -ISR(INT1_vect, ISR_BLOCK) -{ +ISR(INT1_vect, ISR_BLOCK){ irq_handler(1); /**< predefined interrupt pin */ } #if defined(INT2_vect) -ISR(INT2_vect, ISR_BLOCK) -{ +ISR(INT2_vect, ISR_BLOCK){ irq_handler(2); /**< predefined interrupt pin */ } #endif #if defined(INT3_vect) -ISR(INT3_vect, ISR_BLOCK) -{ +ISR(INT3_vect, ISR_BLOCK){ irq_handler(3); /**< predefined interrupt pin */ } #endif #if defined(INT4_vect) -ISR(INT4_vect, ISR_BLOCK) -{ +ISR(INT4_vect, ISR_BLOCK){ irq_handler(4); /**< predefined interrupt pin */ } #endif #if defined(INT5_vect) -ISR(INT5_vect, ISR_BLOCK) -{ +ISR(INT5_vect, ISR_BLOCK){ irq_handler(5); /**< predefined interrupt pin */ } #endif #if defined(INT6_vect) -ISR(INT6_vect, ISR_BLOCK) -{ +ISR(INT6_vect, ISR_BLOCK){ irq_handler(6); /**< predefined interrupt pin */ } #endif #if defined(INT7_vect) -ISR(INT7_vect, ISR_BLOCK) -{ +ISR(INT7_vect, ISR_BLOCK){ irq_handler(7); /**< predefined interrupt pin */ } #endif