Merge pull request #11122 from ibr-cm/hartung/atmega-pin-change-interrupt-pseudomodules

cpu/atmega_common: pseudomodule-based pin change interrupt implementation
This commit is contained in:
Marian Buschsieweke 2019-08-02 13:41:37 +02:00 committed by GitHub
commit e612d3d71b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 524 additions and 10 deletions

View File

@ -46,6 +46,11 @@ This should take care of everything!
We use the open `avrdude` tool to write the new code into the ATmega2560's We use the open `avrdude` tool to write the new code into the ATmega2560's
flash flash
## Pin Change Interrupts
More pins can be used for hardware interrupts using the Pin Change
Interrupt feature. See @ref boards_common_atmega for details.
## State ## State
While there is basic support in RIOT, there are still some parts missing: While there is basic support in RIOT, there are still some parts missing:
* Timer implementation needs love (ideally simulate a 32bit timer by adding * Timer implementation needs love (ideally simulate a 32bit timer by adding

View File

@ -0,0 +1,16 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_E, 0), GPIO_PIN(PORT_J, 0), GPIO_PIN(PORT_J, 1), GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF
#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_K, 0), GPIO_PIN(PORT_K, 1), GPIO_PIN(PORT_K, 2), GPIO_PIN(PORT_K, 3), GPIO_PIN(PORT_K, 4), GPIO_PIN(PORT_K, 5), GPIO_PIN(PORT_K, 6), GPIO_PIN(PORT_K, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -37,6 +37,11 @@ This should take care of everything!
We use the open `avrdude` tool to write the new code into the ATmega328p's We use the open `avrdude` tool to write the new code into the ATmega328p's
flash flash
## Pin Change Interrupts
More pins can be used for hardware interrupts using the Pin Change
Interrupt feature. See @ref boards_common_atmega for details.
##Caution ##Caution
Don't expect having a working network stack due to very limited resources. Don't expect having a working network stack due to very limited resources.
*/ */

View File

@ -0,0 +1,17 @@
/**
@defgroup boards_common_atmega ATmega common
@ingroup boards
@brief Shared files and configuration for ATmega-based boards
### Pin Change Interrupts
Pin Change Interrupts (PCINTs) can be enabled using pseudo modules. To provide
a low-memory overhead implementation, the PCINTs are grouped into **banks**.
Each banks corresponds to one PCINT on the ATmega (PCINT0, ..., PCINT3).
To enable only a specific bank, simply add `USEMODULE += atmega_pcintN` to your
Makefile. To enable all interrupts you can use `USEMODULE += atmega_pcint`.
In case you want to add a new CPU, simply provide an `atmega_pcint.h` with your
CPU and adapt your Makefile.dep and Makefile.featues files.
*/

View File

@ -173,4 +173,9 @@ the clocks for all peripherals are enabled and set to the smallest divider (high
power consumption. When the device should be put into deep sleep it is recommended to use the internal RC oscillator power consumption. When the device should be put into deep sleep it is recommended to use the internal RC oscillator
as system clock source. as system clock source.
## Pin Change Interrupts
More pins can be used for hardware interrupts using the Pin Change
Interrupt feature. See @ref boards_common_atmega for details.
*/ */

View File

@ -34,4 +34,10 @@ It will be used automatically with `make term`:
``` ```
make BOARD=mega-xplained -C examples/hello-world term make BOARD=mega-xplained -C examples/hello-world term
``` ```
### Pin Change Interrupts
More pins can be used for hardware interrupts using the Pin Change
Interrupt feature. See @ref boards_common_atmega for details.
*/ */

View File

@ -2,4 +2,10 @@
@defgroup boards_waspmote-pro Waspmote PRO v1.2 @defgroup boards_waspmote-pro Waspmote PRO v1.2
@ingroup boards @ingroup boards
@brief Support for the Waspmote PRO v1.2 board. @brief Support for the Waspmote PRO v1.2 board.
## Pin Change Interrupts
More pins can be used for hardware interrupts using the Pin Change
Interrupt feature. See @ref boards_common_atmega for details.
*/ */

View File

@ -1 +1,3 @@
-include $(RIOTCPU)/atmega_common/Makefile.features FEATURES_PROVIDED += atmega_pcint1 atmega_pcint2
include $(RIOTCPU)/atmega_common/Makefile.features

View File

@ -4,5 +4,10 @@ USEMODULE += atmega_common
RAM_LEN = 8K RAM_LEN = 8K
ROM_LEN = 128K ROM_LEN = 128K
# expand atmega_pcint for additional PCINTs of atmega1281
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2
endif
# CPU depends on the atmega common module, so include it # CPU depends on the atmega common module, so include it
include $(RIOTCPU)/atmega_common/Makefile.include include $(RIOTCPU)/atmega_common/Makefile.include

View File

@ -0,0 +1,16 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_E, 0), GPIO_PIN(PORT_J, 0), GPIO_PIN(PORT_J, 1), GPIO_PIN(PORT_J, 2), GPIO_PIN(PORT_J, 3), GPIO_PIN(PORT_J, 4), GPIO_PIN(PORT_J, 5), GPIO_PIN(PORT_J, 6)
#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_K, 0), GPIO_PIN(PORT_K, 1), GPIO_PIN(PORT_K, 2), GPIO_PIN(PORT_K, 3), GPIO_PIN(PORT_K, 4), GPIO_PIN(PORT_K, 5), GPIO_PIN(PORT_K, 6), GPIO_PIN(PORT_K, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -0,0 +1,6 @@
# additional PCINTs for atmega1284p
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2 atmega_pcint3
endif
include $(RIOTCPU)/atmega_common/Makefile.dep

View File

@ -1 +1,4 @@
-include $(RIOTCPU)/atmega_common/Makefile.features # additional PCINTs for atmega1284p
FEATURES_PROVIDED += atmega_pcint1 atmega_pcint2 atmega_pcint3
include $(RIOTCPU)/atmega_common/Makefile.features

View File

@ -4,5 +4,10 @@ USEMODULE += atmega_common
RAM_LEN = 16K RAM_LEN = 16K
ROM_LEN = 128K ROM_LEN = 128K
# expand atmega_pcint for atmega1284p
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2 atmega_pcint3
endif
# CPU depends on the atmega common module, so include it # CPU depends on the atmega common module, so include it
include $(RIOTCPU)/atmega_common/Makefile.include include $(RIOTCPU)/atmega_common/Makefile.include

View File

@ -0,0 +1,17 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_A, 0), GPIO_PIN(PORT_A, 1), GPIO_PIN(PORT_A, 2), GPIO_PIN(PORT_A, 3), GPIO_PIN(PORT_A, 4), GPIO_PIN(PORT_A, 5), GPIO_PIN(PORT_A, 6), GPIO_PIN(PORT_A, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_C, 0), GPIO_PIN(PORT_C, 1), GPIO_PIN(PORT_C, 2), GPIO_PIN(PORT_C, 3), GPIO_PIN(PORT_C, 4), GPIO_PIN(PORT_C, 5), GPIO_PIN(PORT_C, 6), GPIO_PIN(PORT_C, 7)
#define ATMEGA_PCINT_MAP_PCINT3 GPIO_PIN(PORT_D, 0), GPIO_PIN(PORT_D, 1), GPIO_PIN(PORT_D, 2), GPIO_PIN(PORT_D, 3), GPIO_PIN(PORT_D, 4), GPIO_PIN(PORT_D, 5), GPIO_PIN(PORT_D, 6), GPIO_PIN(PORT_D, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -0,0 +1,6 @@
# additional PCINTs for atmega2560
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2
endif
include $(RIOTCPU)/atmega_common/Makefile.dep

View File

@ -1 +1,3 @@
-include $(RIOTCPU)/atmega_common/Makefile.features FEATURES_PROVIDED += atmega_pcint1 atmega_pcint2
include $(RIOTCPU)/atmega_common/Makefile.features

View File

@ -4,5 +4,10 @@ USEMODULE += atmega_common
RAM_LEN = 8K RAM_LEN = 8K
ROM_LEN = 256K ROM_LEN = 256K
# expand atmega_pcint with additional PCINTs for atmega2560
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2
endif
# CPU depends on the atmega common module, so include it # CPU depends on the atmega common module, so include it
include $(RIOTCPU)/atmega_common/Makefile.include include $(RIOTCPU)/atmega_common/Makefile.include

View File

@ -0,0 +1,16 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_E, 0), GPIO_PIN(PORT_J, 0), GPIO_PIN(PORT_J, 1), GPIO_PIN(PORT_J, 2), GPIO_PIN(PORT_J, 3), GPIO_PIN(PORT_J, 4), GPIO_PIN(PORT_J, 5), GPIO_PIN(PORT_J, 6)
#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_K, 0), GPIO_PIN(PORT_K, 1), GPIO_PIN(PORT_K, 2), GPIO_PIN(PORT_K, 3), GPIO_PIN(PORT_K, 4), GPIO_PIN(PORT_K, 5), GPIO_PIN(PORT_K, 6), GPIO_PIN(PORT_K, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -0,0 +1,6 @@
# additional PCINTs for atmega256rfr2
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1
endif
include $(RIOTCPU)/atmega_common/Makefile.dep

View File

@ -1,7 +1,9 @@
include $(RIOTCPU)/atmega_common/Makefile.features include $(RIOTCPU)/atmega_common/Makefile.features
# common feature are defined in atmega_common/Makefile.features # common feature are defined in atmega_common/Makefile.features
# Only add Additional features # Only add Additional features
# additional PCINT for atmega256rfr2
FEATURES_PROVIDED += atmega_pcint1
# Various other features (if any) # Various other features (if any)
FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_cpuid

View File

@ -7,5 +7,10 @@ USEMODULE += periph
RAM_LEN = 32K RAM_LEN = 32K
ROM_LEN = 256K ROM_LEN = 256K
# expand atmega_pcint for atmega256rfr2
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1
endif
# CPU depends on the atmega common module, so include it # CPU depends on the atmega common module, so include it
include $(RIOTCPU)/atmega_common/Makefile.include include $(RIOTCPU)/atmega_common/Makefile.include

View File

@ -0,0 +1,15 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_E, 0), GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -1 +1,3 @@
-include $(RIOTCPU)/atmega_common/Makefile.features FEATURES_PROVIDED += atmega_pcint1 atmega_pcint2
include $(RIOTCPU)/atmega_common/Makefile.features

View File

@ -4,5 +4,10 @@ USEMODULE += atmega_common
RAM_LEN = 2K RAM_LEN = 2K
ROM_LEN = 32K ROM_LEN = 32K
# additional PCINTs for atmega328p
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint1 atmega_pcint2
endif
# CPU depends on the atmega common module, so include it # CPU depends on the atmega common module, so include it
include $(RIOTCPU)/atmega_common/Makefile.include include $(RIOTCPU)/atmega_common/Makefile.include

View File

@ -0,0 +1,16 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#define ATMEGA_PCINT_MAP_PCINT1 GPIO_PIN(PORT_C, 0), GPIO_PIN(PORT_C, 1), GPIO_PIN(PORT_C, 2), GPIO_PIN(PORT_C, 3), GPIO_PIN(PORT_C, 4), GPIO_PIN(PORT_C, 5), GPIO_PIN(PORT_C, 6), GPIO_UNDEF
#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_D, 0), GPIO_PIN(PORT_D, 1), GPIO_PIN(PORT_D, 2), GPIO_PIN(PORT_D, 3), GPIO_PIN(PORT_D, 4), GPIO_PIN(PORT_D, 5), GPIO_PIN(PORT_D, 6), GPIO_PIN(PORT_D, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -0,0 +1,2 @@
# common Makefile.dep expand the atmega_pcint pseudo module
include $(RIOTCPU)/atmega_common/Makefile.dep

View File

@ -1 +1,2 @@
-include $(RIOTCPU)/atmega_common/Makefile.features # atmega_common provides atmega_pcint0
include $(RIOTCPU)/atmega_common/Makefile.features

View File

@ -0,0 +1,14 @@
#ifndef ATMEGA_PCINT_H
#define ATMEGA_PCINT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ATMEGA_PCINT_MAP_PCINT0 GPIO_PIN(PORT_B, 0), GPIO_PIN(PORT_B, 1), GPIO_PIN(PORT_B, 2), GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_B, 4), GPIO_PIN(PORT_B, 5), GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7)
#ifdef __cplusplus
}
#endif
#endif /* ATMEGA_PCINT_H */

View File

@ -0,0 +1,24 @@
# expand atmega_pcint module
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint0
endif
# feature for atmega_pcint0 module
ifneq (,$(filter atmega_pcint0,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint0
endif
# feature for atmega_pcint1 module
ifneq (,$(filter atmega_pcint1,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint1
endif
# feature for atmega_pcint2 module
ifneq (,$(filter atmega_pcint2,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint2
endif
# feature for atmega_pcint3 module
ifneq (,$(filter atmega_pcint3,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint3
endif

View File

@ -1,2 +1,4 @@
FEATURES_PROVIDED += periph_eeprom FEATURES_PROVIDED += periph_eeprom
FEATURES_PROVIDED += periph_pm FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += atmega_pcint0

View File

@ -6,4 +6,34 @@ export INCLUDES += -I$(RIOTCPU)/atmega_common/include \
# avr libc needs some RIOT-specific support code # avr libc needs some RIOT-specific support code
USEMODULE += avr_libc_extra USEMODULE += avr_libc_extra
PSEUDOMODULES += atmega_pcint
PSEUDOMODULES += atmega_pcint%
# expand atmega_pcint module
# atmega16u4 only features atmega_pcint0, therefore additional pseudomodules
# are activated in each CPU's Makefile.include
ifneq (,$(filter atmega_pcint,$(USEMODULE)))
USEMODULE += atmega_pcint0
endif
# feature for atmega_pcint0 module
ifneq (,$(filter atmega_pcint0,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint0
endif
# feature for atmega_pcint1 module
ifneq (,$(filter atmega_pcint1,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint1
endif
# feature for atmega_pcint2 module
ifneq (,$(filter atmega_pcint2,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint2
endif
# feature for atmega_pcint3 module
ifneq (,$(filter atmega_pcint3,$(USEMODULE)))
FEATURES_REQUIRED += atmega_pcint3
endif
include $(RIOTMAKE)/arch/atmega.inc.mk include $(RIOTMAKE)/arch/atmega.inc.mk

View File

@ -19,6 +19,9 @@
* @author René Herthel <rene-herthel@outlook.de> * @author René Herthel <rene-herthel@outlook.de>
* @author Francisco Acosta <francisco.acosta@inria.fr> * @author Francisco Acosta <francisco.acosta@inria.fr>
* @author Laurent Navet <laurent.navet@gmail.com> * @author Laurent Navet <laurent.navet@gmail.com>
* @author Robert Hartung <hartung@ibr.cs.tu-bs.de>
* @author Torben Petersen <petersen@ibr.cs.tu-bs.de>
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* *
* @} * @}
*/ */
@ -28,10 +31,14 @@
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include "cpu.h" #include "cpu.h"
#include "board.h"
#include "periph/gpio.h" #include "periph/gpio.h"
#include "periph_conf.h" #include "periph_conf.h"
#include "periph_cpu.h" #include "periph_cpu.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define GPIO_BASE_PORT_A (0x20) #define GPIO_BASE_PORT_A (0x20)
#define GPIO_OFFSET_PORT_H (0xCB) #define GPIO_OFFSET_PORT_H (0xCB)
#define GPIO_OFFSET_PIN_PORT (0x02) #define GPIO_OFFSET_PIN_PORT (0x02)
@ -42,6 +49,7 @@
* @brief Define GPIO interruptions for an specific atmega CPU, by default * @brief Define GPIO interruptions for an specific atmega CPU, by default
* 2 (for small atmega CPUs) * 2 (for small atmega CPUs)
*/ */
#if defined(INT7_vect) #if defined(INT7_vect)
#define GPIO_EXT_INT_NUMOF (8U) #define GPIO_EXT_INT_NUMOF (8U)
#elif defined(INT6_vect) #elif defined(INT6_vect)
@ -59,7 +67,105 @@
#endif #endif
static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF]; static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF];
#endif /* MODULE_PERIPH_GPIO_IRQ */
/**
* @brief detects ammount of possible PCINTs
*/
#if defined(MODULE_ATMEGA_PCINT0) || defined(MODULE_ATMEGA_PCINT1) || \
defined(MODULE_ATMEGA_PCINT2) || defined(MODULE_ATMEGA_PCINT3)
#include "atmega_pcint.h"
/**
* @brief check which pcints should be enabled!
*/
#ifdef MODULE_ATMEGA_PCINT0
#ifndef ATMEGA_PCINT_MAP_PCINT0
#error \
Either mapping for pin change interrupt bank 0 is missing or not supported by the MCU
#else
#define PCINT0_IDX (0)
#define _COUNTER0 (1)
#endif /* ATMEGA_PCINT_MAP_PCINT0 */
#else
#define _COUNTER0 (0)
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
#ifndef ATMEGA_PCINT_MAP_PCINT1
#error \
Either mapping for pin change interrupt bank 1 is missing or not supported by the MCU
#else
#define PCINT1_IDX _COUNTER0
#define _COUNTER1 (_COUNTER0 + 1)
#endif /* ATMEGA_PCINT_MAP_PCINT1 */
#else
#define _COUNTER1 _COUNTER0
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
#ifndef ATMEGA_PCINT_MAP_PCINT2
#error \
Either mapping for pin change interrupt bank 2 is missing or not supported by the MCU
#else
#define PCINT2_IDX _COUNTER1
#define _COUNTER2 (_COUNTER1 + 1)
#endif /* ATMEGA_PCINT_MAP_PCINT2 */
#else
#define _COUNTER2 _COUNTER1
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
#ifndef ATMEGA_PCINT_MAP_PCINT3
#error \
Either mapping for pin change interrupt bank 3 is missing or not supported by the MCU
#else
#define PCINT3_IDX _COUNTER2
#define _COUNTER3 (_COUNTER2 + 1)
#endif /* ATMEGA_PCINT_MAP_PCINT3 */
#else
#define _COUNTER3 _COUNTER2
#endif /* MODULE_ATMEGA_PCINT3 */
#define PCINT_NUM_BANKS (_COUNTER3)
/**
* @brief stores the last pcint state of each port
*/
static uint8_t pcint_state[PCINT_NUM_BANKS];
/**
* @brief stores all cb and args for all defined pcint.
*/
typedef struct {
gpio_flank_t flank; /**< type of interrupt the flank should be triggered on */
gpio_cb_t cb; /**< interrupt callback */
void *arg; /**< optional argument */
} gpio_isr_ctx_pcint_t;
/**
* @brief
*/
static const gpio_t pcint_mapping[] = {
#ifdef PCINT0_IDX
ATMEGA_PCINT_MAP_PCINT0,
#endif /* PCINT0_IDX */
#ifdef PCINT1_IDX
ATMEGA_PCINT_MAP_PCINT1,
#endif /* PCINT1_IDX */
#ifdef PCINT2_IDX
ATMEGA_PCINT_MAP_PCINT2,
#endif /* PCINT2_IDX */
#ifdef PCINT3_IDX
ATMEGA_PCINT_MAP_PCINT3,
#endif /* PCINT3_IDX */
};
/**
* @brief
*/
static gpio_isr_ctx_pcint_t pcint_config[8 * PCINT_NUM_BANKS];
#endif /* MODULE_ATMEGA_PCINTn */
#endif /* MODULE_PERIPH_GPIO_IRQ */
/** /**
* @brief Extract the pin number of the given pin * @brief Extract the pin number of the given pin
@ -115,6 +221,7 @@ static inline uint16_t _pin_addr(gpio_t pin)
int gpio_init(gpio_t pin, gpio_mode_t mode) int gpio_init(gpio_t pin, gpio_mode_t mode)
{ {
uint8_t pin_mask = (1 << _pin_num(pin)); uint8_t pin_mask = (1 << _pin_num(pin));
switch (mode) { switch (mode) {
case GPIO_OUT: case GPIO_OUT:
_SFR_MEM8(_ddr_addr(pin)) |= pin_mask; _SFR_MEM8(_ddr_addr(pin)) |= pin_mask;
@ -195,8 +302,82 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
return -1; return -1;
} }
/* not a valid interrupt pin */ /* not a valid interrupt pin. Set as pcint instead if pcints are enabled */
if (int_num < 0) { if (int_num < 0) {
/* If pin change interrupts are enabled, enable mask and interrupt */
#ifdef PCINT_NUM_BANKS
int8_t offset = -1;
uint8_t pin_num = _pin_num(pin);
for (unsigned i = 0;
i < sizeof(pcint_mapping) / sizeof(pcint_mapping[0]); i++) {
if (pin != GPIO_UNDEF && pin == pcint_mapping[i]) {
offset = i;
break;
}
}
/* if pcint was not found: return -1 */
if (offset < 0) {
return offset;
}
uint8_t bank = offset / 8;
uint8_t bank_idx = offset % 8;
DEBUG("PCINT enabled for bank %u offset %u\n",
(unsigned)bank, (unsigned)offset);
/* save configuration for pin change interrupt */
pcint_config[offset].flank = flank;
pcint_config[offset].arg = arg;
pcint_config[offset].cb = cb;
/* init gpio */
gpio_init(pin, mode);
/* configure pcint */
cli();
switch (bank) {
#ifdef PCINT0_IDX
case PCINT0_IDX:
PCMSK0 |= (1 << bank_idx);
PCICR |= (1 << PCIE0);
break;
#endif /* PCINT0_IDX */
#ifdef PCINT1_IDX
case PCINT1_IDX:
PCMSK1 |= (1 << bank_idx);
PCICR |= (1 << PCIE1);
break;
#endif /* PCINT1_IDX */
#ifdef PCINT2_IDX
case PCINT2_IDX:
PCMSK2 |= (1 << bank_idx);
PCICR |= (1 << PCIE2);
break;
#endif /* PCINT2_IDX */
#ifdef PCINT3_IDX
case PCINT3_IDX:
PCMSK3 |= (1 << pin_num);
PCICR |= (1 << PCIE3);
break;
#endif /* PCINT3_IDX */
default:
return -1;
break;
}
/* As ports are mixed in a bank (e.g. PCINT0), we can only save a single bit here! */
uint8_t port_value = (_SFR_MEM8(_pin_addr( pin )));
uint8_t pin_mask = (1 << pin_num);
uint8_t pin_value = ((port_value & pin_mask) != 0);
if (pin_value) {
pcint_state[bank] |= pin_mask;
}
else {
pcint_state[bank] &= ~pin_mask;
}
sei();
return 0;
#endif /* GPIO_PC_INT_NUMOF */
return -1; return -1;
} }
@ -219,12 +400,12 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
EICRA &= ~(0x3 << (int_num * 2)); EICRA &= ~(0x3 << (int_num * 2));
EICRA |= (flank << (int_num * 2)); EICRA |= (flank << (int_num * 2));
} }
#if defined(EICRB) #if defined(EICRB)
else { else {
EICRB &= ~(0x3 << ((int_num % 4) * 2)); EICRB &= ~(0x3 << ((int_num % 4) * 2));
EICRB |= (flank << ((int_num % 4) * 2)); EICRB |= (flank << ((int_num % 4) * 2));
} }
#endif #endif
/* set callback */ /* set callback */
config[int_num].cb = cb; config[int_num].cb = cb;
@ -254,6 +435,72 @@ static inline void irq_handler(uint8_t int_num)
__exit_isr(); __exit_isr();
} }
#ifdef PCINT_NUM_BANKS
/* inline function that is used by the PCINT ISR */
static inline void pcint_handler(uint8_t bank, uint8_t enabled_pcints)
{
__enter_isr();
/* Find right item */
uint8_t idx = 0;
while (enabled_pcints > 0) {
/* check if this pin is enabled & has changed */
if (enabled_pcints & 0x1) {
/* get pin from mapping (assumes 8 entries per bank!) */
gpio_t pin = pcint_mapping[bank * 8 + idx];
/* re-construct mask from pin */
uint8_t pin_mask = (1 << (_pin_num(pin)));
uint8_t idx_mask = (1 << idx);
uint8_t port_value = (_SFR_MEM8(_pin_addr( pin )));
uint8_t pin_value = ((port_value & pin_mask) != 0);
uint8_t old_state = ((pcint_state[bank] & idx_mask) != 0);
gpio_isr_ctx_pcint_t *conf = &pcint_config[bank * 8 + idx];
if (old_state != pin_value) {
pcint_state[bank] ^= idx_mask;
if ((conf->flank == GPIO_BOTH ||
(pin_value && conf->flank == GPIO_RISING) ||
(!pin_value && conf->flank == GPIO_FALLING))) {
/* execute callback routine */
conf->cb(conf->arg);
}
}
}
enabled_pcints = enabled_pcints >> 1;
idx++;
}
__exit_isr();
}
#if defined(PCINT0_IDX)
ISR(PCINT0_vect, ISR_BLOCK)
{
pcint_handler(PCINT0_IDX, PCMSK0);
}
#endif /* PCINT0_IDX */
#if defined(PCINT1_IDX)
ISR(PCINT1_vect, ISR_BLOCK)
{
pcint_handler(PCINT1_IDX, PCMSK1);
}
#endif /* PCINT1_IDX */
#if defined(PCINT2_IDX)
ISR(PCINT2_vect, ISR_BLOCK)
{
pcint_handler(PCINT2_IDX, PCMSK2);
}
#endif /* PCINT2_IDX */
#if defined(PCINT3_IDX)
ISR(PCINT3_vect, ISR_BLOCK)
{
pcint_handler(PCINT3_IDX, PCMSK3);
}
#endif /* PCINT3_IDX */
#endif /* GPIO_PC_INT_NUMOF */
ISR(INT0_vect, ISR_BLOCK) ISR(INT0_vect, ISR_BLOCK)
{ {
irq_handler(0); /**< predefined interrupt pin */ irq_handler(0); /**< predefined interrupt pin */