mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
dd585f9e9d
- `gpio_ll_toggle()` now is race-free - avoid using a look up table but branch to the two different registers in the `gpio_ll*()` functions - in most cases the GPIO port is a compile time constant and the dead branch is eliminated by the optimizer, making this vastly more efficient - some MCUs do only have a single port, in which case `GPIO_PORT_NUM(port)` is known to return `0` even if `port` is not known, resulting in one of the branch being eliminated as dead branch no matter what - in case it really is unknown at compile time which port to work on, the branch can still be implemented efficiently by the compiler e.g. using a conditional move; likely more efficient than fetching a value from the look up table.
158 lines
3.8 KiB
C
158 lines
3.8 KiB
C
/*
|
|
* Copyright (C) 2021 Gunar Schorcht
|
|
*
|
|
* 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_esp32
|
|
* @ingroup drivers_periph_gpio_ll_irq
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief IRQ implementation of the GPIO Low-Level API for ESP32
|
|
*
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
* @}
|
|
*/
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "log.h"
|
|
#include "periph/gpio_ll_irq.h"
|
|
|
|
#include "esp/common_macros.h"
|
|
#include "esp_intr_alloc.h"
|
|
#include "hal/gpio_hal.h"
|
|
#include "hal/gpio_types.h"
|
|
#include "rom/ets_sys.h"
|
|
|
|
#if __xtensa__
|
|
#include "xtensa/xtensa_api.h"
|
|
#endif
|
|
|
|
#include "esp_idf_api/gpio.h"
|
|
|
|
#include "irq_arch.h"
|
|
|
|
#if MODULE_PERIPH_GPIO_IRQ
|
|
/* variables that have to be used together with periph/gpio_irq */
|
|
extern bool gpio_int_enabled_table[];
|
|
extern bool gpio_isr_service_installed;
|
|
#else
|
|
bool gpio_int_enabled_table[GPIO_PIN_NUMOF] = { };
|
|
bool gpio_isr_service_installed = false;
|
|
#endif
|
|
|
|
extern void IRAM gpio_int_handler(void* arg);
|
|
|
|
int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig,
|
|
gpio_ll_cb_t cb, void *arg)
|
|
{
|
|
assert(is_gpio_port_num_valid(port));
|
|
assert(arg);
|
|
assert(pin < GPIO_PORT_PIN_NUMOF(port));
|
|
|
|
unsigned state = irq_disable();
|
|
|
|
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
|
|
|
DEBUG("%s gpio=%u port=%u pin=%u trig=%d cb=%p arg=%p\n",
|
|
__func__, gpio, GPIO_PORT_NUM(port), pin, trig, cb, arg);
|
|
|
|
/* install GPIO ISR of ESP-IDF if not yet done */
|
|
if (!gpio_isr_service_installed &&
|
|
esp_idf_gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1) != ESP_OK) {
|
|
return -1;
|
|
}
|
|
gpio_isr_service_installed = true;
|
|
|
|
/* set the interrupt type for the pin */
|
|
gpio_int_type_t type;
|
|
switch (trig) {
|
|
case GPIO_TRIGGER_EDGE_FALLING:
|
|
type = GPIO_INTR_NEGEDGE;
|
|
break;
|
|
case GPIO_TRIGGER_EDGE_RISING:
|
|
type = GPIO_INTR_POSEDGE;
|
|
break;
|
|
case GPIO_TRIGGER_EDGE_BOTH:
|
|
type = GPIO_INTR_ANYEDGE;
|
|
break;
|
|
case GPIO_TRIGGER_LEVEL_HIGH:
|
|
type = GPIO_INTR_HIGH_LEVEL;
|
|
break;
|
|
case GPIO_TRIGGER_LEVEL_LOW:
|
|
type = GPIO_INTR_LOW_LEVEL;
|
|
break;
|
|
default:
|
|
type = GPIO_INTR_DISABLE;
|
|
}
|
|
if (esp_idf_gpio_set_intr_type(gpio, type) != ESP_OK) {
|
|
return -1;
|
|
}
|
|
|
|
/* add interrupt handler for the pin */
|
|
if (esp_idf_gpio_isr_handler_add(gpio, cb, arg) != ESP_OK) {
|
|
return -1;
|
|
}
|
|
|
|
/* unmask and clear pending interrupts for the pin */
|
|
gpio_ll_irq_unmask_and_clear(port, pin);
|
|
|
|
irq_restore(state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)
|
|
{
|
|
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
|
|
|
DEBUG("%s gpio=%u port=%u pin=%u\n",
|
|
__func__, gpio, GPIO_PORT_NUM(port), pin);
|
|
|
|
if (esp_idf_gpio_intr_disable(gpio) == ESP_OK) {
|
|
gpio_int_enabled_table[gpio] = false;
|
|
}
|
|
}
|
|
|
|
void gpio_ll_irq_unmask(gpio_port_t port, uint8_t pin)
|
|
{
|
|
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
|
|
|
DEBUG("%s gpio=%u port=%u pin=%u\n",
|
|
__func__, gpio, port, pin);
|
|
|
|
if (esp_idf_gpio_intr_enable(gpio) == ESP_OK) {
|
|
gpio_int_enabled_table[gpio] = true;
|
|
}
|
|
}
|
|
|
|
void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin)
|
|
{
|
|
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
|
|
|
DEBUG("%s gpio=%u port=%u pin=%u\n",
|
|
__func__, gpio, GPIO_PORT_NUM(port), pin);
|
|
|
|
volatile uint32_t *status_w1tc = (uint32_t *)GPIO_STATUS_W1TC_REG;
|
|
#if GPIO_PORT_NUMOF > 1
|
|
if (GPIO_PORT_NUM(port) != 0) {
|
|
status_w1tc = (uint32_t *)GPIO_STATUS1_W1TC_REG;
|
|
}
|
|
#endif
|
|
|
|
*status_w1tc = BIT(pin);
|
|
|
|
if (esp_idf_gpio_intr_enable(gpio) == ESP_OK) {
|
|
gpio_int_enabled_table[gpio] = true;
|
|
}
|
|
}
|