1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/atmega_common/periph/gpio.c
Marian Buschsieweke c2c2cc8592
drivers/periph_gpio: let gpio_read() return bool
Since https://github.com/RIOT-OS/RIOT/pull/20935 gpio_write()
uses a `bool` instead of an `int`. This does the same treatment for
`gpio_read()`.

This does indeed add an instruction to `gpio_read()` implementations.
However, users caring about an instruction more are better served with
`gpio_ll_read()` anyway. And `gpio_read() == 1` is often seen in
newcomer's code, which would now work as expected.
2024-10-23 13:24:09 +02:00

457 lines
12 KiB
C

/*
* Copyright (C) 2015 HAW Hamburg
* 2016 INRIA
* 2023 Hugues Larrive
* 2023 Gerson Fernando Budke
*
* 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
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ATmega family
*
* @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>
* @author Hugues Larrive <hugues.larrive@pm.me>
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <stdio.h>
#include <avr/interrupt.h>
#include "cpu.h"
#include "irq.h"
#include "board.h"
#include "periph/gpio.h"
#include "periph_conf.h"
#include "periph_cpu.h"
#include "atmega_gpio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifdef MODULE_PERIPH_GPIO_IRQ
static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF];
/* Detects amount of possible PCINTs */
#if defined(MODULE_ATMEGA_PCINT0) || defined(MODULE_ATMEGA_PCINT1) || \
defined(MODULE_ATMEGA_PCINT2) || defined(MODULE_ATMEGA_PCINT3)
#include "atmega_pcint.h"
#define ENABLE_PCINT
/* Check which pcints should be enabled */
#if defined(MODULE_ATMEGA_PCINT0) && !defined(ATMEGA_PCINT_MAP_PCINT0)
# error "Either mapping for pin change interrupt bank 0 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT1) && !defined(ATMEGA_PCINT_MAP_PCINT1)
# error "Either mapping for pin change interrupt bank 1 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT2) && !defined(ATMEGA_PCINT_MAP_PCINT2)
# error "Either mapping for pin change interrupt bank 2 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT3) && !defined(ATMEGA_PCINT_MAP_PCINT3)
# error "Either mapping for pin change interrupt bank 3 is missing or not supported by the MCU"
#endif
/**
* @brief Use anonymous enum as for addressing the @ref pcint_state
*/
enum {
#ifdef MODULE_ATMEGA_PCINT0
PCINT0_IDX, /**< Index of PCINT0, if used */
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
PCINT1_IDX, /**< Index of PCINT1, if used */
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
PCINT2_IDX, /**< Index of PCINT2, if used */
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
PCINT3_IDX, /**< Index of PCINT3, if used */
#endif /* MODULE_ATMEGA_PCINT3 */
PCINT_NUM_BANKS /**< Number of PCINT banks used */
};
/**
* @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 MODULE_ATMEGA_PCINT0
ATMEGA_PCINT_MAP_PCINT0,
#endif /* PCINT0_IDX */
#ifdef MODULE_ATMEGA_PCINT1
ATMEGA_PCINT_MAP_PCINT1,
#endif /* PCINT1_IDX */
#ifdef MODULE_ATMEGA_PCINT2
ATMEGA_PCINT_MAP_PCINT2,
#endif /* PCINT2_IDX */
#ifdef MODULE_ATMEGA_PCINT3
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 */
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
uint8_t pin_mask = (1 << atmega_pin_num(pin));
switch (mode) {
case GPIO_OUT:
_SFR_MEM8(atmega_ddr_addr(pin)) |= pin_mask;
break;
case GPIO_IN:
_SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask;
_SFR_MEM8(atmega_port_addr(pin)) &= ~pin_mask;
break;
case GPIO_IN_PU:
_SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask;
_SFR_MEM8(atmega_port_addr(pin)) |= pin_mask;
break;
default:
return -1;
}
return 0;
}
bool gpio_read(gpio_t pin)
{
return (_SFR_MEM8(atmega_pin_addr(pin)) & (1 << atmega_pin_num(pin)));
}
void gpio_set(gpio_t pin)
{
_SFR_MEM8(atmega_port_addr(pin)) |= (1 << atmega_pin_num(pin));
}
void gpio_clear(gpio_t pin)
{
_SFR_MEM8(atmega_port_addr(pin)) &= ~(1 << atmega_pin_num(pin));
}
void gpio_toggle(gpio_t pin)
{
if (gpio_read(pin)) {
gpio_clear(pin);
}
else {
gpio_set(pin);
}
}
void gpio_write(gpio_t pin, bool value)
{
if (value) {
gpio_set(pin);
}
else {
gpio_clear(pin);
}
}
#ifdef MODULE_PERIPH_GPIO_IRQ
static inline int8_t _int_num(gpio_t pin)
{
uint8_t num;
const gpio_t ext_ints[GPIO_EXT_INT_NUMOF] = CPU_ATMEGA_EXT_INTS;
/* find pin in ext_ints array to get the interrupt number */
for (num = 0; num < GPIO_EXT_INT_NUMOF; num++) {
if (pin == ext_ints[num]) {
return num;
}
}
return -1;
}
#ifdef ENABLE_PCINT
static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode,
gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
int8_t offset = -1;
uint8_t pin_num = atmega_pin_num(pin);
for (unsigned i = 0; i < ARRAY_SIZE(pcint_mapping); 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 MODULE_ATMEGA_PCINT0
case PCINT0_IDX:
PCMSK0 |= (1 << bank_idx);
PCICR |= (1 << PCIE0);
break;
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
case PCINT1_IDX:
PCMSK1 |= (1 << bank_idx);
PCICR |= (1 << PCIE1);
break;
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
case PCINT2_IDX:
PCMSK2 |= (1 << bank_idx);
PCICR |= (1 << PCIE2);
break;
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
case PCINT3_IDX:
PCMSK3 |= (1 << bank_idx);
PCICR |= (1 << PCIE3);
break;
#endif /* MODULE_ATMEGA_PCINT3 */
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(atmega_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 /* ENABLE_PCINT */
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
int8_t int_num = _int_num(pin);
/* mode not supported */
if ((mode != GPIO_IN) && (mode != GPIO_IN_PU)) {
return -1;
}
/* not a valid interrupt pin. Set as pcint instead if pcints are enabled */
if (int_num < 0) {
#ifdef ENABLE_PCINT
/* If pin change interrupts are enabled, enable mask and interrupt */
return pcint_init_int(pin, mode, flank, cb, arg);
#else
return -1;
#endif /* ENABLE_PCINT */
}
/* flank not supported */
if (flank > GPIO_RISING) {
return -1;
}
gpio_init(pin, mode);
/* clear global interrupt flag */
cli();
/* enable interrupt number int_num */
#if defined(EIFR) && defined(EIMSK)
EIFR |= (1 << int_num);
EIMSK |= (1 << int_num);
#elif defined(GIFR) && defined(GICR)
GIFR |= (1 << (INTF0 + int_num));
GICR |= (1 << (INT0 + int_num));
#endif
/* apply flank to interrupt number int_num */
#if defined(EICRB)
if (int_num >= 4) {
EICRB &= ~(0x3 << ((int_num % 4) * 2));
EICRB |= (flank << ((int_num % 4) * 2));
}
else
#endif
{
#if defined(EICRA)
EICRA &= ~(0x3 << (int_num * 2));
EICRA |= (flank << (int_num * 2));
#elif defined(MCUCR)
MCUCR &= ~(0x3 << (int_num * 2));
MCUCR |= (flank << (int_num * 2));
#endif
}
/* set callback */
config[int_num].cb = cb;
config[int_num].arg = arg;
/* set global interrupt flag */
sei();
return 0;
}
void gpio_irq_enable(gpio_t pin)
{
#if defined(EIFR) && defined(EIMSK)
EIFR |= (1 << _int_num(pin));
EIMSK |= (1 << _int_num(pin));
#elif defined(GIFR) && defined(GICR)
GIFR |= (1 << (INTF0 + _int_num(pin)));
GICR |= (1 << (INT0 + _int_num(pin)));
#endif
}
void gpio_irq_disable(gpio_t pin)
{
#if defined(EIMSK)
EIMSK &= ~(1 << _int_num(pin));
#elif defined(GICR)
GICR &= ~(1 << (INT0 + _int_num(pin)));
#endif
}
static inline void irq_handler(uint8_t int_num)
{
config[int_num].cb(config[int_num].arg);
}
#ifdef ENABLE_PCINT
/* inline function that is used by the PCINT ISR */
static inline void pcint_handler(uint8_t bank, uint8_t enabled_pcints)
{
/* 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 << (atmega_pin_num(pin)));
uint8_t idx_mask = (1 << idx);
uint8_t port_value = (_SFR_MEM8(atmega_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++;
}
}
#ifdef MODULE_ATMEGA_PCINT0
AVR8_ISR(PCINT0_vect, pcint_handler, PCINT0_IDX, PCMSK0);
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
AVR8_ISR(PCINT1_vect, pcint_handler, PCINT1_IDX, PCMSK1);
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
AVR8_ISR(PCINT2_vect, pcint_handler, PCINT2_IDX, PCMSK2);
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
AVR8_ISR(PCINT3_vect, pcint_handler, PCINT3_IDX, PCMSK3);
#endif /* MODULE_ATMEGA_PCINT3 */
#endif /* ENABLE_PCINT */
AVR8_ISR(INT0_vect, irq_handler, 0); /**< predefined interrupt pin */
AVR8_ISR(INT1_vect, irq_handler, 1); /**< predefined interrupt pin */
#if defined(INT2_vect)
AVR8_ISR(INT2_vect, irq_handler, 2); /**< predefined interrupt pin */
#endif
#if defined(INT3_vect)
AVR8_ISR(INT3_vect, irq_handler, 3); /**< predefined interrupt pin */
#endif
#if defined(INT4_vect)
AVR8_ISR(INT4_vect, irq_handler, 4); /**< predefined interrupt pin */
#endif
#if defined(INT5_vect)
AVR8_ISR(INT5_vect, irq_handler, 5); /**< predefined interrupt pin */
#endif
#if defined(INT6_vect)
AVR8_ISR(INT6_vect, irq_handler, 6); /**< predefined interrupt pin */
#endif
#if defined(INT7_vect)
AVR8_ISR(INT7_vect, irq_handler, 7); /**< predefined interrupt pin */
#endif
#endif /* MODULE_PERIPH_GPIO_IRQ */