diff --git a/boards/arduino-leonardo/Makefile.dep b/boards/arduino-leonardo/Makefile.dep new file mode 100644 index 000000000000..580e800f2fcd --- /dev/null +++ b/boards/arduino-leonardo/Makefile.dep @@ -0,0 +1,3 @@ +USEMODULE += boards_common_arduino-atmega + +include $(RIOTBOARD)/common/arduino-atmega/Makefile.dep diff --git a/boards/arduino-nano/include/atmega_pcint.h b/boards/arduino-nano/include/atmega_pcint.h new file mode 100644 index 000000000000..02a80c0107c7 --- /dev/null +++ b/boards/arduino-nano/include/atmega_pcint.h @@ -0,0 +1,21 @@ +#ifndef ATMEGA_PCINT_H +#define ATMEGA_PCINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* PB5 is connected to an LED on the Arduino Nano */ +#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_UNDEF, GPIO_PIN(PORT_B, 6), GPIO_PIN(PORT_B, 7) +/* PC6 is not available on the Arduino Nano */ +#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_UNDEF, GPIO_UNDEF +/* Pin PD2 is both INT0 and PCINT18, RIOT is using it as INT0 */ +/* Pin PD3 is both INT1 and PCINT19, RIOT is using it as INT1 */ +/* Pins PD6 and PD7 are not available on the Arduino Nano */ +#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_D, 0), GPIO_PIN(PORT_D, 1), GPIO_UNDEF, GPIO_UNDEF, GPIO_PIN(PORT_D, 4), GPIO_PIN(PORT_D, 5), GPIO_UNDEF, GPIO_UNDEF + +#ifdef __cplusplus +} +#endif + +#endif /* ATMEGA_PCINT_H */ diff --git a/boards/arduino-uno/include/atmega_pcint.h b/boards/arduino-uno/include/atmega_pcint.h new file mode 100644 index 000000000000..63ac3f70d8c4 --- /dev/null +++ b/boards/arduino-uno/include/atmega_pcint.h @@ -0,0 +1,20 @@ +#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) +/* PC6 is not available on the Arduino Uno */ +#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_UNDEF, GPIO_UNDEF +/* Pin PD2 is both INT0 and PCINT18, RIOT is using it as INT0 */ +/* Pin PD3 is both INT1 and PCINT19, RIOT is using it as INT1 */ +/* Pins PD6 and PD7 are not available on the Arduino Uno */ +#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_D, 0), GPIO_PIN(PORT_D, 1), GPIO_UNDEF, GPIO_UNDEF, GPIO_PIN(PORT_D, 4), GPIO_PIN(PORT_D, 5), GPIO_UNDEF, GPIO_UNDEF + +#ifdef __cplusplus +} +#endif + +#endif /* ATMEGA_PCINT_H */ diff --git a/cpu/atmega328p/include/atmega_pcint.h b/cpu/atmega328p/include/atmega_pcint.h index 69b1d32ef889..c900b56cb09a 100644 --- a/cpu/atmega328p/include/atmega_pcint.h +++ b/cpu/atmega328p/include/atmega_pcint.h @@ -7,7 +7,9 @@ extern "C" { #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) +/* Pin PD2 is both INT0 and PCINT18, RIOT is using it as INT0 */ +/* Pin PD3 is both INT1 and PCINT19, RIOT is using it as INT1 */ +#define ATMEGA_PCINT_MAP_PCINT2 GPIO_PIN(PORT_D, 0), GPIO_PIN(PORT_D, 1), GPIO_UNDEF, GPIO_UNDEF, GPIO_PIN(PORT_D, 4), GPIO_PIN(PORT_D, 5), GPIO_PIN(PORT_D, 6), GPIO_PIN(PORT_D, 7) #ifdef __cplusplus } diff --git a/cpu/atmega32u4/include/periph_cpu.h b/cpu/atmega32u4/include/periph_cpu.h index 7bb719dfc000..603ef706255c 100644 --- a/cpu/atmega32u4/include/periph_cpu.h +++ b/cpu/atmega32u4/include/periph_cpu.h @@ -45,8 +45,7 @@ enum { #define CPU_ATMEGA_EXT_INTS { GPIO_PIN(PORT_D, 0), \ GPIO_PIN(PORT_D, 1), \ GPIO_PIN(PORT_D, 2), \ - GPIO_PIN(PORT_D, 3), \ - GPIO_PIN(PORT_E, 7) } + GPIO_PIN(PORT_D, 3) } /** * @name Defines for the I2C interface diff --git a/tests/periph_gpio_pcint/Makefile b/tests/periph_gpio_pcint/Makefile new file mode 100644 index 000000000000..972a72587645 --- /dev/null +++ b/tests/periph_gpio_pcint/Makefile @@ -0,0 +1,16 @@ +include ../Makefile.tests_common + +BOARD ?= arduino-uno + +FEATURES_REQUIRED += periph_gpio +FEATURES_REQUIRED += periph_gpio_irq +# At least one PCINT port is required +FEATURES_REQUIRED += atmega_pcint0 + +USEMODULE += atmega_pcint + +include $(RIOTBASE)/Makefile.include + +ifeq (arduino-uno,$(BOARD)) + CFLAGS += "-DTHREAD_STACKSIZE_DEFAULT=256" +endif diff --git a/tests/periph_gpio_pcint/main.c b/tests/periph_gpio_pcint/main.c new file mode 100644 index 000000000000..df37a4bf6428 --- /dev/null +++ b/tests/periph_gpio_pcint/main.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * 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 tests + * @{ + * + * @file + * @brief Test application for GPIO interrupts for the ATmega boards that + * takes PCINT into account + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include +#include + +#include "atmega_pcint.h" +#include "periph/gpio.h" +#include "periph_cpu.h" +#ifdef MODULE_BOARDS_COMMON_ARDUINO_ATMEGA +#include "arduino_board.h" + +static const gpio_t analog_map[] = { + ARDUINO_PIN_A0, + ARDUINO_PIN_A1, + ARDUINO_PIN_A2, + ARDUINO_PIN_A3, + ARDUINO_PIN_A4, + ARDUINO_PIN_A5, +#if !defined(CPU_ATMEGA328P) && !defined(CPU_ATMEGA32U4) + ARDUINO_PIN_A6, + ARDUINO_PIN_A7, +#endif /* !defined(CPU_ATMEGA328P) || !defined(CPU_ATMEGA32U4) */ +#if defined(CPU_ATMEGA2560) + ARDUINO_PIN_A8, + ARDUINO_PIN_A9, + ARDUINO_PIN_A10, + ARDUINO_PIN_A11, + ARDUINO_PIN_A12, + ARDUINO_PIN_A13, + ARDUINO_PIN_A14, + ARDUINO_PIN_A15, +#endif /* CPU_ATMEGA2560 */ +}; + +#define ANALOG_NUMOF (sizeof(analog_map) / sizeof(analog_map[0])) +#define DIGITAL_NUMOF (sizeof(arduino_pinmap) / sizeof(arduino_pinmap[0])) + +#endif /* MODULE_BOARDS_COMMON_ARDUINO_ATMEGA */ + +/* Used to represent one empty PCINT bank */ +#define EMPTY_PCINT_MAP \ + GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, \ + GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF, GPIO_UNDEF + +static const gpio_t ext_int_pins[] = CPU_ATMEGA_EXT_INTS; + +static const gpio_t pcint_pins[] = { +#ifdef ATMEGA_PCINT_MAP_PCINT0 +ATMEGA_PCINT_MAP_PCINT0 +#else +EMPTY_PCINT_MAP +#endif +, +#ifdef ATMEGA_PCINT_MAP_PCINT1 +ATMEGA_PCINT_MAP_PCINT1 +#else +EMPTY_PCINT_MAP +#endif +, +#ifdef ATMEGA_PCINT_MAP_PCINT2 +ATMEGA_PCINT_MAP_PCINT2, +#else +EMPTY_PCINT_MAP +#endif +}; + +static const gpio_t blacklist[] = { + GPIO_UNDEF, +#ifdef CPU_ATMEGA328P + GPIO_PIN(PORT_D, 0), /* RXD (UART) used for stdio */ + GPIO_PIN(PORT_D, 1), /* TXD (UART) used for stdio */ +#endif /* CPU_ATMEGA328P */ +#ifdef CPU_ATMEGA32U4 + GPIO_PIN(PORT_D, 2), /* RXD (UART) used for stdio */ + GPIO_PIN(PORT_D, 3), /* TXD (UART) used for stdio */ +#endif /* CPU_ATMEGA328P */ +}; + +/** + * Maps port number to port letter for ATmega CPUs. Not all CPUs have alls + * ports. Somehow port number 7 is H and port number 8 is J (and not I, as + * one would suspect). Port numbers are stored as the upper four bits, so + * we just define 16 letters in order not having to check for out of bound + * reads + */ +static const char port_names[16] = "ABCDEFGHJKL-----"; + +static const gpio_flank_t flanks[] = { + GPIO_FALLING, GPIO_RISING, GPIO_BOTH, +}; + +static const char *flank_names[] = { + "falling", "rising", "falling and rising", +}; + +#define GPIO_PCINT_NUMOF (sizeof(pcint_pins) / sizeof(pcint_pins[0])) +#define GPIO_EXT_INT_NUMOF (sizeof(ext_int_pins) / sizeof(ext_int_pins[0])) +#define FLANKS_NUMOF (sizeof(flanks) / sizeof(flanks[0])) +#define BLACKLIST_NUMOF (sizeof(blacklist) / sizeof(blacklist[0])) + +static short is_usable(gpio_t pin) +{ + for (uint8_t i = 0; i < BLACKLIST_NUMOF; i++) { + if (pin == blacklist[i]) { + return 0; + } + } + + return 1; +} + +static void print_pin(gpio_t pin) +{ + uint8_t port_num = (pin >> 4) & 0x0f; + uint8_t pin_num = pin & 0x0f; + printf("P%c%" PRIu8, port_names[port_num], pin_num); +#ifdef MODULE_BOARDS_COMMON_ARDUINO_ATMEGA + for (uint8_t i = 0; i < ANALOG_NUMOF; i++) { + if (pin == analog_map[i]) { + printf(" (A%" PRIu8 ")", i); + return; + } + } + for (uint8_t i = 0; i < DIGITAL_NUMOF; i++) { + if (pin == arduino_pinmap[i]) { + printf(" (D%" PRIu8 ")", i); + return; + } + } +#endif /* MODULE_BOARDS_COMMON_ARDUINO_ATMEGA */ +} + +static void ext_int_cb(void *arg) +{ + uint8_t idx = (uint8_t)(uintptr_t)arg; + gpio_t pin = ext_int_pins[idx]; + const char *flank = flank_names[idx % FLANKS_NUMOF]; + + printf("INT%" PRIu8 " on ", idx); + print_pin(pin); + printf(", flank(s) = %s\n", flank); +} + +static void pcint_cb(void *arg) +{ + uint8_t idx = (uint8_t)(uintptr_t)arg; + gpio_t pin = pcint_pins[idx]; + const char *flank = flank_names[idx % FLANKS_NUMOF]; + + printf("PCINT%" PRIu8 " on ", idx); + print_pin(pin); + printf(", flank(s) = %s\n", flank); +} + +int main(void) +{ + puts( + "GPIO interrupt test for ATmega boards\n" + "\n" + "For all pins supporting IRQs interrupts will be configured, which\n" + "are triggered on falling edge, on rising edge, and on both edges\n" + "(in turns). Pull the pins against ground one by one and check if the\n" + "console output matches your actions.\n" + "\n" + "Installing interrupts..."); + + /* first the external interrupts */ + for (uint8_t i = 0; i < GPIO_EXT_INT_NUMOF; i++) { + gpio_t pin = ext_int_pins[i]; + printf("INT%" PRIu8 " on ", i); + if (is_usable(pin)) { + const char *flank = flank_names[i % FLANKS_NUMOF]; + print_pin(pin); + printf(" with flank(s) %s: ", flank); + if (gpio_init_int(pin, GPIO_IN_PU, flanks[i % FLANKS_NUMOF], ext_int_cb, + (void *)(uintptr_t)i)) { + puts("Failed"); + } + else { + puts("OK"); + } + } + else { + print_pin(pin); + puts(": Blacklisted"); + } + } + + /* next the pin changed interrupts */ + for (uint8_t i = 0; i < GPIO_PCINT_NUMOF; i++) { + gpio_t pin = pcint_pins[i]; + printf("PCINT%" PRIu8, i); + if (is_usable(pin)) { + const char *flank = flank_names[i % FLANKS_NUMOF]; + printf(" on "); + print_pin(pin); + printf(" with flank(s) %s: ", flank); + if (gpio_init_int(pin, GPIO_IN_PU, flanks[i % FLANKS_NUMOF], + pcint_cb, (void *)(uintptr_t)i)) { + puts("Failed"); + } + else { + puts("OK"); + } + } + else { + if (pin == GPIO_UNDEF){ + puts(": Unsupported"); + } + else { + printf(" on "); + print_pin(pin); + puts(": Blacklisted"); + } + } + } + + puts("Interrupt handlers have been set up."); + + return 0; +}