1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/atmega_common: implement periph/gpio_ll{,_irq}

Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
Co-authored-by: Alexandre Abadie <alexandre.abadie@inria.fr>
This commit is contained in:
Marian Buschsieweke 2021-12-02 21:04:40 +01:00
parent a427d630f1
commit 04ab5a74f3
No known key found for this signature in database
GPG Key ID: CB8E3238CE715A94
15 changed files with 873 additions and 37 deletions

View File

@ -56,6 +56,27 @@ enum {
GPIO_PIN(PORT_E, 6), \
GPIO_PIN(PORT_E, 7) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
if (pin_num < 4) {
return port_num == PORT_D;
}
return port_num == PORT_E;
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -53,6 +53,33 @@ enum {
GPIO_PIN(PORT_D, 3), \
GPIO_PIN(PORT_B, 2) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
if (port_num == PORT_B) {
return 2;
}
return pin_num - 2;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
switch (port_num) {
default:
return false;
case PORT_D:
return ((pin_num == 2) || (pin_num == 3));
case PORT_B:
return pin_num == 2;
}
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -54,6 +54,27 @@ enum {
GPIO_PIN(PORT_E, 6), \
GPIO_PIN(PORT_E, 7) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
if (pin_num < 4) {
return port_num == PORT_D;
}
return port_num == PORT_E;
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -58,6 +58,27 @@ enum {
GPIO_PIN(PORT_E, 6), \
GPIO_PIN(PORT_E, 7) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
if (pin_num < 4) {
return port_num == PORT_D;
}
return port_num == PORT_E;
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -54,6 +54,27 @@ enum {
GPIO_PIN(PORT_E, 6), \
GPIO_PIN(PORT_E, 7) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
if (pin_num < 4) {
return port_num == PORT_D;
}
return port_num == PORT_E;
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -49,6 +49,27 @@ enum {
#define CPU_ATMEGA_EXT_INTS { GPIO_PIN(PORT_D, 2), \
GPIO_PIN(PORT_D, 3) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num - 2;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
if (port_num == PORT_D) {
return ((pin_num == 2) || (pin_num == 3));
}
return false;
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -47,6 +47,23 @@ enum {
GPIO_PIN(PORT_D, 2), \
GPIO_PIN(PORT_D, 3) }
/**
* @brief Get the interrupt vector number of the given GPIO pin
*/
static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num)
{
(void)port_num;
return pin_num;
}
/**
* @brief Check if the given pin can be used as external interrupt
*/
static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num)
{
return ((pin_num < 4) && (port_num == PORT_D));
}
/**
* @name Defines for the I2C interface
* @{

View File

@ -19,6 +19,10 @@ config CPU_COMMON_ATMEGA
select HAS_PERIPH_EEPROM
select HAS_PERIPH_GPIO
select HAS_PERIPH_GPIO_IRQ
select HAS_PERIPH_GPIO_LL
select HAS_PERIPH_GPIO_LL_IRQ
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
select HAS_PERIPH_GPIO_LL_IRQ_UNMASK
select HAS_PERIPH_PM
select HAS_PERIPH_RTC_MS
select HAS_PERIPH_RTT_SET_COUNTER

View File

@ -9,6 +9,10 @@ FEATURES_PROVIDED += dbgpin
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_eeprom
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
FEATURES_PROVIDED += periph_gpio_ll
FEATURES_PROVIDED += periph_gpio_ll_irq
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low
FEATURES_PROVIDED += periph_gpio_ll_irq_unmask
FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_rtc_ms
FEATURES_PROVIDED += periph_rtt_set_counter

View File

@ -38,11 +38,6 @@
extern "C" {
#endif
#define ATMEGA_GPIO_BASE_PORT_A (0x20)
#define ATMEGA_GPIO_OFFSET_PORT_H (0xCB)
#define ATMEGA_GPIO_OFFSET_PIN_PORT (0x02)
#define ATMEGA_GPIO_OFFSET_PIN_PIN (0x03)
/**
* @brief Extract the pin number of the given pin
*/
@ -64,18 +59,7 @@ static inline uint8_t atmega_port_num(gpio_t pin)
*/
static inline uint16_t atmega_port_addr(gpio_t pin)
{
uint8_t port_num = atmega_port_num(pin);
uint16_t port_addr = port_num * ATMEGA_GPIO_OFFSET_PIN_PIN;
port_addr += ATMEGA_GPIO_BASE_PORT_A;
port_addr += ATMEGA_GPIO_OFFSET_PIN_PORT;
#if defined (PORTG)
if (port_num > PORT_G) {
port_addr += ATMEGA_GPIO_OFFSET_PORT_H;
}
#endif
return port_addr;
return (uintptr_t)(&atmega_gpio_port(pin)->port);
}
/**

View File

@ -0,0 +1,294 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
*
* 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 cpu_atmega_common
* @ingroup drivers_periph_gpio_ll
* @{
*
* @file
* @brief CPU specific part of the Peripheral GPIO Low-Level API
*
* @note This GPIO driver implementation supports only one pin to be
* defined as external interrupt.
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef GPIO_LL_ARCH_H
#define GPIO_LL_ARCH_H
#include <assert.h>
#include "cpu.h"
#include "irq.h"
#include "kernel_defines.h"
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
/* AVR generally requires two CPU cycles for loads from and stores to memory.
* However, there are special single cycle instructions, which however require
* the target address to be given as an 5 bit immediate. So only a 64 byte sized
* part of the data address space can be accessed this way, which is called
* the I/O memory (as (almost) only memory mapped I/O is mapped there).
*
* GPIO ports up to G are part of the I/O mapped area, but starting from port H
* the GPIO registers are only accessible via regular load and store operations
* and mapped slightly different in the data address space. For some reason,
* there is a gap between the GPIO memory regions for port A to G and H and
* above that results in special handling for GPIO ports H and above.
*
* Note that this implementation always uses the data address way to access GPIO
* registers and never the single cycle instructions. However, GCC converts the
* instructions into semantically equivalent single CPU cycle instructions
* whenever the target address is known at compile time (so can be expressed as
* immediate) and also mapped into the I/O address space. We rely on this
* optimization to claim single cycle GPIO accesses for GPIO ports below H,
* whenever the port number is known at compile time.
*/
#ifdef PORTH
#define GPIO_PORT(num) \
((num >= PORT_H) ? \
(ATMEGA_GPIO_BASE_H + ((num) - PORT_H) * ATMEGA_GPIO_SIZE) : \
(ATMEGA_GPIO_BASE_A + (num) * ATMEGA_GPIO_SIZE))
#define GPIO_PORT_NUM(port) \
(((port) >= ATMEGA_GPIO_BASE_H) ? \
(((port) - ATMEGA_GPIO_BASE_H) / ATMEGA_GPIO_SIZE + PORT_H) : \
(((port) - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE))
#else
#define GPIO_PORT(num) (ATMEGA_GPIO_BASE_A + (num) * ATMEGA_GPIO_SIZE)
#define GPIO_PORT_NUM(port) (((port) - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE)
#endif
static inline uword_t gpio_ll_read(gpio_port_t port)
{
atmega_gpio_port_t *p = (void *)port;
return p->pin;
}
static inline uword_t gpio_ll_read_output(gpio_port_t port)
{
atmega_gpio_port_t *p = (void *)port;
return p->port;
}
/**
* @brief Check if a set/clear operation with the on the given port with the
* given mask can be done in a single-cycle bit-set / bit-clear
* instruction.
*
* @param port GPIO port to set/clear bits on
* @param mask Bitmask specifying the bits to set/clear
*
* The compiler can optimize a clear/set operation if all of the following is
* true:
*
* 1. The GPIO port number is known at compile time, so that it can be used as
* immediate
* 2. The GPIO port is alphabetically smaller than H, otherwise its registers
* are not within the I/O space (and cannot fit the 5 bit immediate field)
* 3. The bitmask is known at compile time
* 4. Exactly one bit is set in the bitmask
*/
static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
{
if (IS_CT_CONSTANT(port)
#ifdef ATMEGA_GPIO_BASE_H
&& IS_CT_CONSTANT(port < ATMEGA_GPIO_BASE_H)
&& (port < ATMEGA_GPIO_BASE_H)
#endif
&& IS_CT_CONSTANT(mask)
&& IS_CT_CONSTANT(__builtin_popcount(mask) == 1)) {
return __builtin_popcount(mask) == 1;
}
return 0;
}
static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
{
atmega_gpio_port_t *p = (void *)port;
if (_can_bitwise_access(port, mask)) {
p->port |= mask;
}
else {
unsigned state = irq_disable();
p->port |= mask;
irq_restore(state);
}
}
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
{
atmega_gpio_port_t *p = (void *)port;
if (_can_bitwise_access(port, mask)) {
p->port &= ~mask;
}
else {
unsigned state = irq_disable();
p->port &= ~mask;
irq_restore(state);
}
}
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
{
atmega_gpio_port_t *p = (void *)port;
/* this is equivalent to `p->port ^= mask`, but faster and inherently
* atomically */
p->pin = mask;
}
static inline void gpio_ll_write(gpio_port_t port, uword_t value)
{
atmega_gpio_port_t *p = (void *)port;
p->port = value;
}
static inline gpio_port_t gpio_get_port(gpio_t pin)
{
return GPIO_PORT(pin >> 4);
}
static inline uint8_t gpio_get_pin_num(gpio_t pin)
{
return pin & 0x0f;
}
static inline uword_t gpio_ll_prepare_write_all_outputs(gpio_port_t port,
uword_t value)
{
atmega_gpio_port_t *p = (void *)port;
uword_t result = (gpio_ll_read_output(port) & (~p->ddr)) | value;
return result;
}
static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
uword_t value)
{
atmega_gpio_port_t *p = (void *)port;
uword_t result = gpio_ll_read_output(port);
result &= (~p->ddr) | (~mask);
result |= value;
return result;
}
static inline gpio_port_t gpio_port_pack_addr(void *addr)
{
return (gpio_port_t)addr;
}
static inline void * gpio_port_unpack_addr(gpio_port_t port)
{
if (port < RAMSTART) {
return NULL;
}
return (void *)port;
}
static inline bool is_gpio_port_num_valid(uint_fast8_t num)
{
switch (num) {
default:
return false;
#ifdef DDRA
case 0:
#endif
#ifdef DDRB
case 1:
#endif
#ifdef DDRC
case 2:
#endif
#ifdef DDRD
case 3:
#endif
#ifdef DDRE
case 4:
#endif
#ifdef DDRF
case 5:
#endif
#ifdef DDRG
case 6:
#endif
#ifdef DDRH
case 7:
#endif
#ifdef DDRI
case 8:
#endif
#ifdef DDRJ
case 9:
#endif
#ifdef DDRK
case 10:
#endif
#ifdef DDRL
case 11:
#endif
#ifdef DDRM
case 12:
#endif
#ifdef DDRN
case 13:
#endif
#ifdef DDRO
case 14:
#endif
#ifdef DDRP
case 15:
#endif
#ifdef DDRQ
case 16:
#endif
#ifdef DDRR
case 17:
#endif
#ifdef DDRS
case 18:
#endif
#ifdef DDRT
case 19:
#endif
#ifdef DDRU
case 20:
#endif
#ifdef DDRV
case 21:
#endif
#ifdef DDRW
case 22:
#endif
#ifdef DDRX
case 23:
#endif
#ifdef DDRY
case 24:
#endif
#ifdef DDRZ
case 25:
#endif
return true;
}
}
#endif /* DOXYGEN */
#ifdef __cplusplus
}
#endif
#endif /* GPIO_LL_ARCH_H */
/** @} */

View File

@ -56,6 +56,101 @@ typedef uint8_t gpio_t;
*/
#define GPIO_PIN(x, y) ((x << 4) | y)
/**
* @brief Base of the GPIO registers as memory address
*
* Must be identical to the address of `PINA` provided by avr/io.h
*/
#define ATMEGA_GPIO_BASE_A (0x20)
/**
* @brief Base of the GPIO port G register as memory address
*
* Must be identical to the address of `PING` provided by avr/io.h
*/
#define ATMEGA_GPIO_BASE_G (ATMEGA_GPIO_BASE_A + ATMEGA_GPIO_SIZE * ('G' - 'A'))
/**
* @brief Base of the GPIO registers of the second memory region (port >= H)
*
* Must be identical to the address of `PINH` provided by avr/io.h
*/
#define ATMEGA_GPIO_BASE_H (0x100)
/**
* @brief sizeof(atmega_gpio_port_t), but preprocessor friendly
*/
#define ATMEGA_GPIO_SIZE (0x03)
#if defined(DOXYGEN)
/**
* @brief Number of external interrupt vectors
*/
#define GPIO_EXT_INT_NUMOF <CPU_SPECIFIC>
#elif defined(INT7_vect)
#define GPIO_EXT_INT_NUMOF (8U)
#elif defined(INT6_vect)
#define GPIO_EXT_INT_NUMOF (7U)
#elif defined(INT5_vect)
#define GPIO_EXT_INT_NUMOF (6U)
#elif defined(INT4_vect)
#define GPIO_EXT_INT_NUMOF (5U)
#elif defined(INT3_vect)
#define GPIO_EXT_INT_NUMOF (4U)
#elif defined(INT2_vect)
#define GPIO_EXT_INT_NUMOF (3U)
#else
#define GPIO_EXT_INT_NUMOF (2U)
#endif
/**
* @brief Structure describing the memory layout of the registers of a
* GPIO port on ATmega MCUs.
*/
typedef struct {
/**
* @brief Toggle bits in the port register
* @note The bits in the port register will be also toggled for inputs.
* This can be both a footgun as well as an efficient way to toggle
* the pull up resistor on inputs
*
* Referred to as "Input Pins Address" in the datasheet.
*/
volatile uint8_t pin;
/**
* @brief Configure pins as output (1) or input (0) using the Data
* Direction Register
*/
volatile uint8_t ddr;
/**
* @brief Read/write the state of GPIO pins using the Port Data Register
*
* @note When in input mode (see @ref atmega_gpio_port_t::ddr) writing a
* 1 will enable the pull up resistor, writing a 0 will put the
* pin in floating mode.
*/
volatile uint8_t port;
} atmega_gpio_port_t;
/**
* @brief Get the GPIO PORT registers of the given GPIO PORT
* @param[in] port_num Number of the port to get the registers of
* @return Pointer to the registers controlling the given GPIO PORT
*/
static inline atmega_gpio_port_t *atmega_gpio_port(uint8_t port_num)
{
static const uintptr_t base_addr = (uintptr_t)ATMEGA_GPIO_BASE_A;
uintptr_t res = base_addr + port_num * sizeof(atmega_gpio_port_t);
/* GPIO ports up to (including) G are mapped in the I/O address space,
* port H and higher (if present) are mapped in a different contiguous
* region afterwards (e.g. 0x100 for ATmega2560). */
#ifdef PORTH
if (port_num > 'G'-'A') {
static const uintptr_t offset = ATMEGA_GPIO_BASE_H - ATMEGA_GPIO_BASE_G;
res += offset;
}
#endif
return (atmega_gpio_port_t *)res;
}
#ifndef DOXYGEN
/**
* @brief Override the GPIO flanks
@ -76,6 +171,53 @@ typedef enum {
/** @} */
#endif /* ndef DOXYGEN */
#ifndef DOXYGEN /* BEGIN: GPIO LL overwrites */
#define HAVE_GPIO_SLEW_T
typedef enum {
GPIO_SLEW_SLOWEST = 0,
GPIO_SLEW_SLOW = 0,
GPIO_SLEW_FAST = 0,
GPIO_SLEW_FASTEST = 0,
} gpio_slew_t;
#define HAVE_GPIO_PULL_STRENGTH_T
typedef enum {
GPIO_PULL_WEAKEST = 0,
GPIO_PULL_WEAK = 0,
GPIO_PULL_STRONG = 0,
GPIO_PULL_STRONGEST = 0
} gpio_pull_strength_t;
#define HAVE_GPIO_DRIVE_STRENGTH_T
typedef enum {
GPIO_DRIVE_WEAKEST = 0,
GPIO_DRIVE_WEAK = 0,
GPIO_DRIVE_STRONG = 0,
GPIO_DRIVE_STRONGEST = 0
} gpio_drive_strength_t;
#define HAVE_GPIO_IRQ_TRIG_T
typedef enum {
GPIO_TRIGGER_LEVEL_LOW = 0x00,
GPIO_TRIGGER_EDGE_BOTH = 0x01,
GPIO_TRIGGER_EDGE_FALLING = 0x02,
GPIO_TRIGGER_EDGE_RISING = 0x03,
GPIO_TRIGGER_LEVEL_HIGH = 0xff, /**< not supported */
} gpio_irq_trig_t;
#define HAVE_GPIO_PULL_T
typedef enum {
GPIO_FLOATING = 0,
GPIO_PULL_UP = 1,
GPIO_PULL_DOWN = 0xfe, /*< not supported */
GPIO_PULL_KEEP = 0xff, /*< not supported */
} gpio_pull_t;
#define HAVE_GPIO_LL_PREPARE_WRITE_ALL_PINS
#define HAVE_GPIO_LL_PREPARE_WRITE
#endif /* END: GPIO LL overwrites */
/**
* @brief Use some common SPI functions
* @{

View File

@ -41,26 +41,6 @@
#include "debug.h"
#ifdef MODULE_PERIPH_GPIO_IRQ
/*
* @brief Define GPIO interruptions for an specific atmega CPU, by default
* 2 (for small atmega CPUs)
*/
#if defined(INT7_vect)
#define GPIO_EXT_INT_NUMOF (8U)
#elif defined(INT6_vect)
#define GPIO_EXT_INT_NUMOF (7U)
#elif defined(INT5_vect)
#define GPIO_EXT_INT_NUMOF (6U)
#elif defined(INT4_vect)
#define GPIO_EXT_INT_NUMOF (5U)
#elif defined(INT3_vect)
#define GPIO_EXT_INT_NUMOF (4U)
#elif defined(INT2_vect)
#define GPIO_EXT_INT_NUMOF (3U)
#else
#define GPIO_EXT_INT_NUMOF (2U)
#endif
static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF];

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2015 HAW Hamburg
* 2016 INRIA
* 2021 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 cpu_atmega_common
* @ingroup drivers_periph_gpio_ll
* @{
*
* @file
* @brief Peripheral GPIO Low-Level API implementation for the ATmega GPIO peripheral
*
* @author René Herthel <rene-herthel@outlook.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @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>
*
* @}
*/
#include <errno.h>
#include <string.h>
#include "cpu.h"
#include "irq.h"
#include "periph/gpio_ll.h"
static void _set_dir(gpio_port_t port, uint8_t pin, bool output)
{
atmega_gpio_port_t *p = (void *)port;
if (output) {
p->ddr |= 1U << pin;
}
else {
p-> ddr &= ~(1U << pin);
}
}
static bool _is_output(gpio_port_t port, uint8_t pin)
{
atmega_gpio_port_t *p = (void *)port;
return (p->ddr >> pin) & 1U;
}
static void _set_pull_config(gpio_port_t port, uint8_t pin, gpio_pull_t pull)
{
atmega_gpio_port_t *p = (void *)port;
p->port |= pull << pin;
}
int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf)
{
if ((conf->pull > GPIO_PULL_UP)
|| (conf->state == GPIO_OUTPUT_OPEN_DRAIN)
|| (conf->state == GPIO_OUTPUT_OPEN_SOURCE)) {
return -ENOTSUP;
}
unsigned state = irq_disable();
if (conf->initial_value) {
gpio_ll_set(port, 1UL << pin);
}
else {
gpio_ll_clear(port, 1UL << pin);
}
_set_dir(port, pin, conf->state == GPIO_OUTPUT_PUSH_PULL);
if (conf->state == GPIO_INPUT) {
_set_pull_config(port, pin, conf->pull);
}
irq_restore(state);
return 0;
}
void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin)
{
assert(dest);
memset(dest, 0, sizeof(*dest));
/* E.g. the schematics in figure 14-5 in the ATmega328P datasheet shows that
* a Schmitt Trigger is always connected before the digital input signal.
* Let's assume this is also true for all other ATmegas */
dest->schmitt_trigger = true;
if (_is_output(port, pin)) {
dest->state = GPIO_OUTPUT_PUSH_PULL;
dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1U;
}
else {
dest->state = GPIO_INPUT;
dest->pull = (gpio_ll_read_output(port) >> pin) & 1U;
dest->initial_value = (gpio_ll_read(port) >> pin) & 1U;
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2015 HAW Hamburg
* 2016 INRIA
* 2022 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 cpu_atmega_common
* @ingroup drivers_periph_gpio_ll_irq
* @{
*
* @file
* @brief IRQ implementation of the GPIO Low-Level API for ATmega
*
* @author René Herthel <rene-herthel@outlook.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @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>
*
* @}
*/
#include <avr/interrupt.h>
#include <errno.h>
#include "cpu.h"
#include "irq.h"
#include "periph/gpio_ll_irq.h"
#include "periph_conf.h"
#include "periph_cpu.h"
#define ENABLE_DEBUG 0
#include "debug.h"
struct isr_ctx {
gpio_ll_cb_t cb;
void *arg;
};
static struct isr_ctx isr_ctx[GPIO_EXT_INT_NUMOF];
static void clear_pending_irqs(uint8_t exti)
{
EIFR |= 1 << exti;
}
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)
{
uint8_t exti = atmega_pin2exti(GPIO_PORT_NUM(port), pin);
EIMSK &= ~(1 << exti);
}
void gpio_ll_irq_unmask(gpio_port_t port, uint8_t pin)
{
uint8_t exti = atmega_pin2exti(GPIO_PORT_NUM(port), pin);
EIMSK |= 1 << exti;
}
void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin)
{
uint8_t exti = atmega_pin2exti(GPIO_PORT_NUM(port), pin);
clear_pending_irqs(exti);
EIMSK |= 1 << exti;
}
static void set_trigger(uint8_t exti, gpio_irq_trig_t trig)
{
exti <<= 1;
volatile uint8_t *eicr = &EICRA;
#ifdef EICRB
if (exti >= 8) {
eicr = & EICRB;
exti -= 8;
}
#endif
/* being a bit more verbose here to avoid two read-modify-write cycles,
* as the compiler won't optimize access to volatile memory */
uint8_t tmp = *eicr;
tmp &= ~(0x3 << exti);
tmp |= trig << exti;
*eicr = tmp;
}
int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig,
gpio_ll_cb_t cb, void *arg)
{
int port_num = GPIO_PORT_NUM(port);
assert((trig != GPIO_TRIGGER_LEVEL_HIGH) && cb);
if (!atmega_has_pin_exti(port_num, pin)) {
return -ENOTSUP;
}
uint8_t exti = atmega_pin2exti(port_num, pin);
unsigned irq_state = irq_disable();
/* set callback */
isr_ctx[exti].cb = cb;
isr_ctx[exti].arg = arg;
/* setup IRQ */
set_trigger(exti, trig);
clear_pending_irqs(exti);
EIMSK |= 1 << exti;
irq_restore(irq_state);
return 0;
}
static void isr_exti(uint8_t exti)
{
avr8_enter_isr();
isr_ctx[exti].cb(isr_ctx[exti].arg);
avr8_exit_isr();
}
#ifdef INT0_vect
ISR(INT0_vect, ISR_BLOCK)
{
isr_exti(0);
}
#endif
#ifdef INT1_vect
ISR(INT1_vect, ISR_BLOCK)
{
isr_exti(1);
}
#endif
#ifdef INT2_vect
ISR(INT2_vect, ISR_BLOCK)
{
isr_exti(2);
}
#endif
#ifdef INT3_vect
ISR(INT3_vect, ISR_BLOCK)
{
isr_exti(3);
}
#endif
#ifdef INT4_vect
ISR(INT4_vect, ISR_BLOCK)
{
isr_exti(4);
}
#endif
#ifdef INT5_vect
ISR(INT5_vect, ISR_BLOCK)
{
isr_exti(5);
}
#endif
#ifdef INT6_vect
ISR(INT6_vect, ISR_BLOCK)
{
isr_exti(6);
}
#endif
#ifdef INT7_vect
ISR(INT7_vect, ISR_BLOCK)
{
isr_exti(7);
}
#endif