Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions boards/arduino-leonardo/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
USEMODULE += boards_common_arduino-atmega

include $(RIOTBOARD)/common/arduino-atmega/Makefile.dep
21 changes: 21 additions & 0 deletions boards/arduino-nano/include/atmega_pcint.h
Original file line number Diff line number Diff line change
@@ -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 */
20 changes: 20 additions & 0 deletions boards/arduino-uno/include/atmega_pcint.h
Original file line number Diff line number Diff line change
@@ -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 */
4 changes: 3 additions & 1 deletion cpu/atmega328p/include/atmega_pcint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 1 addition & 2 deletions cpu/atmega32u4/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions tests/periph_gpio_pcint/Makefile
Original file line number Diff line number Diff line change
@@ -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
242 changes: 242 additions & 0 deletions tests/periph_gpio_pcint/main.c
Original file line number Diff line number Diff line change
@@ -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 <marian.buschsieweke@ovgu.de>
*
* @}
*/

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#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;
}