1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp8266/periph/gpio.c
iosabi 52107b2416 esp8266: Support UART1 and other UART0 pins.
The esp8266 CPU has actually two hardware UART peripherals. UART0 is
used by the boot ROM for flashing and serial output during boot,
typically at a baudrate of 74880 bps until the bootloader or application
sets the more standard 115200 baudrate. This UART0 device has two
possible pins for TXD, GPIO1 and GPIO2, which are both set to TXD by the
boot ROM. esp8266 modules will typically have GPIO1 labeled as the TX
pin, but it is possible to use GPIO2 for that purpose even while
flashing the device with esptool.py.

The second device, UART1, also has two options for TXD, GPIO2 and GPIO7,
and only one option for RXD, GPIO8. However, GPIO7 and GPIO8 are used
by the flash internally so those options are not very useful unless
maybe while running from IRAM with the flash disabled, for example for
a debugger over UART1.

This patch allows boards to override UART{0,1}_{R,T}XD in their
periph_conf.h to configure the uart selection. Defining UART1_TX will
make the UART_DEV(1) device available.

Tested with:

```CFLAGS='-DUART1_TXD=GPIO2' make -C tests/periph_uart BOARD=esp8266-esp-12x flash term```

* Connected one USB-UART to the standard GPIO1 and GPIO3 for flashing
  and console. After flashing we see the manual test output at 115200
  bps

* Connected a second USB-UART with RX to GPIO2 running at 74880.

Then run on the first console:
```
> init 1 74880
> send 1 hello
```

The word "hello" appears on the second UART connection.

Note that GPIO2 is used during boot for UART0's TX until the application
or bootloader set it to a regular GPIO, so some boot ROM messages at
74880 bps are visible. After running `init 1 74880` it is set to UART1's
TX.
2021-05-02 12:27:27 +00:00

313 lines
8.6 KiB
C

/*
* Copyright (C) 2019 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_esp8266
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ESP8266
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include <stdbool.h>
#include "log.h"
#include "periph/gpio.h" /* RIOT gpio.h */
#include "esp8266/eagle_soc.h"
#include "esp8266/gpio_register.h"
#include "rom/ets_sys.h"
#include "sdk/ets.h"
#include "esp/gpio_regs.h"
#include "esp/iomux_regs.h"
#include "esp/rtc_regs.h"
#include "esp_common.h"
#include "gpio_arch.h"
#include "irq_arch.h"
#include "syscalls.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/*
* IOMUX to GPIO mapping
* source https://www.espressif.com/sites/default/files/documentation/0d-esp8266_pin_list_release_15-11-2014.xlsx
*/
const uint8_t _gpio_to_iomux[] = { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 };
const uint8_t _iomux_to_gpio[] = { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 };
gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] =
{
_GPIO, /* gpio0 */
/* UART0 is initialized by the boot ROM, this only reflects our selection so
* other devices don't step on it. */
UART0_TXD == GPIO1 ? _UART : _GPIO, /* gpio1 */
UART0_TXD == GPIO2 ? _UART : _GPIO, /* gpio2 */
UART0_RXD == GPIO3 ? _UART : _GPIO, /* gpio3 */
_GPIO, /* gpio4 */
_GPIO, /* gpio5 */
_SPIF, /* gpio6 SPI flash CLK */
_SPIF, /* gpio7 SPI flash MISO */
_SPIF, /* gpio8 SPI flash MOSI */
#if defined(FLASH_MODE_QIO) || defined(FLASH_MODE_QOUT)
_SPIF, /* gpio9 SPI flash HD (qio/qout flash mode) */
_SPIF, /* gpio10 SPI flash WP (qio/qout flash mode) */
#else
_GPIO, /* gpio9 can be used as GPIO if not needed for flash */
_GPIO, /* gpio10 can be used as GPIO if not needed for flash */
#endif
_SPIF, /* gpio11 SPI flash CS */
_GPIO, /* gpio12 */
_GPIO, /* gpio13 */
_GPIO, /* gpio14 */
_GPIO, /* gpio15 */
_GPIO /* gpio16 */
};
/* String representation of usage types */
const char* _gpio_pin_usage_str[] =
{
"GPIO", "I2C", "PWM", "SPI", "SPI Flash", "UART", "N/A"
};
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
DEBUG("%s: %d %d\n", __func__, pin, mode);
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
/* check whether pin can be used as GPIO or is used in anyway else */
switch (_gpio_pin_usage[pin]) {
case _I2C: LOG_ERROR("GPIO%d is used as I2C signal.\n", pin); return -1;
case _PWM: LOG_ERROR("GPIO%d is used as PWM output.\n", pin); return -1;
case _SPI: LOG_ERROR("GPIO%d is used as SPI interface.\n", pin); return -1;
case _SPIF: LOG_ERROR("GPIO%d is used as SPI flash.\n", pin); return -1;
case _UART: LOG_ERROR("GPIO%d is used as UART interface.\n", pin); return -1;
default: break;
}
/* GPIO16 requires separate handling */
if (pin == GPIO16) {
RTC.GPIO_CFG[3] = (RTC.GPIO_CFG[3] & 0xffffffbc) | BIT(0);
RTC.GPIO_CONF = RTC.GPIO_CONF & ~RTC_GPIO_CONF_OUT_ENABLE;
switch (mode) {
case GPIO_OUT:
RTC.GPIO_ENABLE = RTC.GPIO_OUT | RTC_GPIO_CONF_OUT_ENABLE;
break;
case GPIO_OD:
LOG_ERROR("GPIO mode GPIO_OD is not supported for GPIO16.\n");
return -1;
case GPIO_OD_PU:
LOG_ERROR("GPIO mode GPIO_OD_PU is not supported for GPIO16.\n");
return -1;
case GPIO_IN:
RTC.GPIO_ENABLE = RTC.GPIO_OUT & ~RTC_GPIO_CONF_OUT_ENABLE;
RTC.GPIO_CFG[3] &= ~RTC_GPIO_CFG3_PIN_PULLUP;
break;
case GPIO_IN_PU:
LOG_ERROR("GPIO mode GPIO_IN_PU is not supported for GPIO16.\n");
return -1;
case GPIO_IN_PD:
LOG_ERROR("GPIO mode GPIO_IN_PD is not supported for GPIO16.\n");
return -1;
default:
LOG_ERROR("Invalid GPIO mode for GPIO%d.\n", pin);
return -1;
}
return 0;
}
uint8_t iomux = _gpio_to_iomux [pin];
uint32_t iomux_conf = (iomux > 11) ? IOMUX_FUNC(0) : IOMUX_FUNC(3);
switch (mode) {
case GPIO_OUT: iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE;
iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
GPIO.CONF[pin] &= ~GPIO_CONF_OPEN_DRAIN;
GPIO.ENABLE_OUT_SET = BIT(pin);
break;
case GPIO_OD_PU: iomux_conf |= IOMUX_PIN_PULLUP;
iomux_conf |= IOMUX_PIN_PULLUP_SLEEP;
case GPIO_OD: iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE;
iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
GPIO.CONF[pin] |= GPIO_CONF_OPEN_DRAIN;
GPIO.ENABLE_OUT_SET = BIT(pin);
break;
case GPIO_IN_PU: iomux_conf |= IOMUX_PIN_PULLUP;
iomux_conf |= IOMUX_PIN_PULLUP_SLEEP;
case GPIO_IN: GPIO.CONF[pin] |= GPIO_CONF_OPEN_DRAIN;
GPIO.ENABLE_OUT_CLEAR = BIT(pin);
break;
case GPIO_IN_PD: LOG_ERROR("GPIO mode GPIO_IN_PD is not supported.\n");
return -1;
default: LOG_ERROR("Invalid GPIO mode for GPIO%d.\n", pin);
}
IOMUX.PIN[iomux] = iomux_conf;
return 0;
}
#ifdef MODULE_PERIPH_GPIO_IRQ
static gpio_isr_ctx_t gpio_isr_ctx_table [GPIO_PIN_NUMOF] = { };
static bool gpio_int_enabled_table [GPIO_PIN_NUMOF] = { };
void IRAM gpio_int_handler (void* arg)
{
irq_isr_enter();
for (int i = 0; i < GPIO_PIN_NUMOF; i++) {
uint32_t mask = BIT(i);
if (GPIO.STATUS & mask) {
GPIO.STATUS_CLEAR = mask;
if (gpio_int_enabled_table[i] && (GPIO.CONF[i] & GPIO_PIN_INT_TYPE_MASK)) {
gpio_isr_ctx_table[i].cb (gpio_isr_ctx_table[i].arg);
}
}
mask = mask << 1;
}
irq_isr_exit();
}
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
if (gpio_init(pin, mode)) {
return -1;
}
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
LOG_ERROR("GPIO16 cannot generate interrupts.\n");
return -1;
}
gpio_isr_ctx_table[pin].cb = cb;
gpio_isr_ctx_table[pin].arg = arg;
GPIO.CONF[pin] = SET_FIELD(GPIO.CONF[pin], GPIO_CONF_INTTYPE, flank);
if (flank != GPIO_NONE) {
gpio_int_enabled_table [pin] = (gpio_isr_ctx_table[pin].cb != NULL);
ets_isr_attach (ETS_GPIO_INUM, gpio_int_handler, 0);
ets_isr_unmask ((1 << ETS_GPIO_INUM));
}
return 0;
}
void gpio_irq_enable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = true;
}
void gpio_irq_disable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = false;
}
#endif /* MODULE_PERIPH_GPIO_IRQ */
int gpio_read (gpio_t pin)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
return RTC.GPIO_IN & BIT(0);
}
return (GPIO.IN & BIT(pin)) ? 1 : 0;
}
void gpio_write (gpio_t pin, int value)
{
DEBUG("%s: %d %d\n", __func__, pin, value);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
RTC.GPIO_OUT = (RTC.GPIO_OUT & ~BIT(0)) | (value ? 1 : 0);
return;
}
if (value) {
GPIO.OUT_SET = BIT(pin) & GPIO_OUT_PIN_MASK;
}
else {
GPIO.OUT_CLEAR = BIT(pin) & GPIO_OUT_PIN_MASK;
}
}
void gpio_set (gpio_t pin)
{
gpio_write (pin, 1);
}
void gpio_clear (gpio_t pin)
{
gpio_write (pin, 0);
}
void gpio_toggle (gpio_t pin)
{
DEBUG("%s: %d\n", __func__, pin);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
RTC.GPIO_OUT = (RTC.GPIO_OUT & ~BIT(0)) | ((RTC.GPIO_IN & BIT(0)) ? 0 : 1);
return;
}
GPIO.OUT ^= BIT(pin);
}
int gpio_set_pin_usage(gpio_t pin, gpio_pin_usage_t usage)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
_gpio_pin_usage [pin] = usage;
return 0;
}
gpio_pin_usage_t gpio_get_pin_usage (gpio_t pin)
{
return (pin < GPIO_PIN_NUMOF) ? _gpio_pin_usage[pin] : _NOT_EXIST;
}
const char* gpio_get_pin_usage_str(gpio_t pin)
{
return _gpio_pin_usage_str[_gpio_pin_usage[((pin < GPIO_PIN_NUMOF) ? pin : _NOT_EXIST)]];
}