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:
parent
a427d630f1
commit
04ab5a74f3
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
294
cpu/atmega_common/include/gpio_ll_arch.h
Normal file
294
cpu/atmega_common/include/gpio_ll_arch.h
Normal 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 */
|
||||
/** @} */
|
@ -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
|
||||
* @{
|
||||
|
@ -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];
|
||||
|
||||
|
100
cpu/atmega_common/periph/gpio_ll.c
Normal file
100
cpu/atmega_common/periph/gpio_ll.c
Normal 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;
|
||||
}
|
||||
}
|
179
cpu/atmega_common/periph/gpio_ll_irq.c
Normal file
179
cpu/atmega_common/periph/gpio_ll_irq.c
Normal 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
|
Loading…
Reference in New Issue
Block a user