Basic support for GPIOs 0-15.

Start new 'core' component for low-level parts

Progress towards #8
This commit is contained in:
Angus Gratton 2015-06-05 11:46:25 +10:00
parent b01e7a89bd
commit 2c46be9825
15 changed files with 704 additions and 14 deletions

6
core/component.mk Normal file
View file

@ -0,0 +1,6 @@
INC_DIRS += $(core_ROOT)include
# args for passing into compile rule generation
core_ROOT = $(ROOT)core/
core_SRC_DIR = $(core_ROOT)
$(eval $(call component_compile_rules,core))

20
core/esp_iomux.c Normal file
View file

@ -0,0 +1,20 @@
/* Compiler-level implementation for esp/iomux.h and esp/iomux_private.h
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#include "esp/iomux.h"
#include "common_macros.h"
/* These are non-static versions of the GPIO mapping tables in
iomux.h, so if they need to be linked only one copy is linked for
the entire program.
These are only ever linked in if the arguments to gpio_to_ionum
or ionum_to_gpio are not known at compile time.
Arrays are declared as 32-bit integers in IROM to save RAM.
*/
const IROM uint32_t GPIO_TO_IOMUX_MAP[] = _GPIO_TO_IOMUX;
const IROM uint32_t IOMUX_TO_GPIO_MAP[] = _IOMUX_TO_GPIO;

View file

@ -0,0 +1,33 @@
/* Some common compiler macros
*
* Not esp8266-specific.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#ifndef _COMMON_MACROS_H
#define _COMMON_MACROS_H
#define UNUSED __attributed((unused))
#ifndef BIT
#define BIT(X) (1<<X)
#endif
/* Use this macro to store constant values in IROM flash instead
of having them loaded into rodata (which resides in DRAM)
Unlike the ESP8266 SDK you don't need an attribute like this for
standard functions. They're stored in flash by default. But
variables need them.
Important to note: IROM flash can only be accessed via 32-bit word
aligned reads. It's up to the user of this attribute to ensure this.
*/
#define IROM __attribute__((section(".irom0"))) const
#define INLINED inline static __attribute__((always_inline)) __attribute__((unused))
#endif

84
core/include/esp/gpio.h Normal file
View file

@ -0,0 +1,84 @@
/** esp_iomux.h
*
* GPIO functions.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_GPIO_H
#include <stdbool.h>
#include "esp/registers.h"
#include "esp/iomux.h"
typedef enum {
GPIO_INPUT = 0,
GPIO_OUTPUT = IOMUX_OE,
GPIO_INPUT_PULLUP = IOMUX_PU,
} gpio_direction_t;
/* Enable GPIO on the specified pin, and set it to input/output/ with
* pullup as needed
*/
INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction)
{
iomux_set_gpio_function(gpio_num, (uint8_t)direction);
if(direction == GPIO_OUTPUT)
GPIO_DIR_SET = BIT(gpio_num);
else
GPIO_DIR_CLEAR = BIT(gpio_num);
}
/* Disable GPIO on the specified pin, and set it Hi-Z.
*
* If later muxing this pin to a different function, make sure to set
* IOMUX_OE if necessary to enable the output buffer.
*/
INLINED void gpio_disable(const uint8_t gpio_num)
{
GPIO_DIR_CLEAR = BIT(gpio_num);
*gpio_iomux_reg(gpio_num) &= ~IOMUX_OE;
}
/* Set output of a pin high or low.
*
* Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
*/
INLINED void gpio_write(const uint8_t gpio_num, const uint32_t set)
{
if(set)
GPIO_OUT_SET = BIT(gpio_num);
else
GPIO_OUT_CLEAR = BIT(gpio_num);
}
/* Toggle output of a pin
*
* Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
*/
INLINED void gpio_toggle(const uint8_t gpio_num)
{
/* Why implement like this instead of GPIO_OUT_REG ^= xxx?
Concurrency. If an interrupt or higher priority task writes to
GPIO_OUT between reading and writing, only the gpio_num pin can
get an invalid value. Prevents one task from clobbering another
task's pins, without needing to disable/enable interrupts.
*/
if(GPIO_OUT_REG & BIT(gpio_num))
GPIO_OUT_CLEAR = BIT(gpio_num);
else
GPIO_OUT_SET = BIT(gpio_num);
}
/* Read input value of a GPIO pin.
*
* If pin is set as an input, this reads the value on the pin.
* If pin is set as an output, this reads the last value written to the pin.
*/
INLINED bool gpio_read(const uint8_t gpio_num)
{
return GPIO_IN_REG & BIT(gpio_num);
}
#endif

199
core/include/esp/iomux.h Normal file
View file

@ -0,0 +1,199 @@
/** esp/iomux.h
*
* Configuration of iomux registers.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_IOMUX_H
#define _ESP_IOMUX_H
#include <stdint.h>
#include "esp/registers.h"
/**
* Convert a GPIO pin number to an iomux register index.
*
* This function should evaluate to a constant if the gpio_number is
* known at compile time, or return the result from a lookup table if not.
*
*/
inline static uint8_t gpio_to_iomux(const uint8_t gpio_number);
/**
* Convert an iomux register index to a GPIO pin number.
*
* This function should evaluate to a constant if the iomux_num is
* known at compile time, or return the result from a lookup table if not.
*
*/
inline static uint8_t iomux_to_gpio(const uint8_t iomux_num);
/**
* Directly get the IOMUX register for a particular gpio number
*
* ie *gpio_iomux_reg(3) is equivalent to IOMUX_GP03
*/
inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number)
{
return &IOMUX_REG(gpio_to_iomux(gpio_number));
}
/**
* Set a pin to the GPIO function.
*
* This allows you to set pins to GPIO without knowing in advance the
* exact register masks to use.
*
* flags can be any of IOMUX_OE, IOMUX_PU, IOMUX_PD, etc. Any other flags will be cleared.
*
* Equivalent to a direct register operation if gpio_number is known at compile time.
* ie the following are equivalent:
*
* iomux_set_gpio_function(12, IOMUX_OE);
* IOMUX_GP12 = (IOMUX_GP12 & ~IOMUX_FUNC_MASK) | IOMUX_GP12_GPIO | IOMUX_OE;
*/
inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint8_t flags)
{
const uint8_t reg_idx = gpio_to_iomux(gpio_number);
const esp_reg_t reg = &IOMUX_REG(reg_idx);
const uint32_t func = (reg_idx > 11 ? IOMUX_FUNC_A : IOMUX_FUNC_D) | flags;
const uint32_t val = *reg & ~(IOMUX_FUNC_MASK | IOMUX_FLAG_MASK);
*reg = val | func;
}
/**
* Set an IOMUX register directly
*
* Shortcut for
* IOMUX_GPxx = (IOMUX_GPxx & ~IOMUX_FUNC_MASK) | IOMUX_GPxx_func
*
* instead call
* IOMUX_SET_FN(GPxx, func);
* can also do
* IOMUX_SET_FN(GP12, GPIO)|IOMUX_OE;
* ... to set the OE flag if it was previously cleared.
*
* but a better option is:
* IOMUX_SET(GP12, GPIO, IOMUX_OE);
* ...which clears any other flags at the same time.
*/
#define IOMUX_SET_FN(GP,FN) IOMUX_##GP = ((IOMUX_##GP & ~IOMUX_FUNC_MASK) | IOMUX_##GP##_##FN)
#define IOMUX_SET(GP,FN,FLAGS) IOMUX_##GP = ((IOMUX_##GP & ~(IOMUX_FUNC_MASK|IOMUX_FLAG_MASK)) | IOMUX_##GP##_##FN|FLAGS)
/* IOMUX register index 0, GPIO 12 */
#define IOMUX_GP12 IOMUX_REG(0)
#define IOMUX_GP12_MTDI IOMUX_FUNC_A
#define IOMUX_GP12_I2S_DIN IOMUX_FUNC_B
#define IOMUX_GP12_HSPI_MISO IOMUX_FUNC_C
#define IOMUX_GP12_GPIO IOMUX_FUNC_D
#define IOMUX_GP12_UART0_DTR IOMUX_FUNC_E
/* IOMUX register index 1, GPIO 13 */
#define IOMUX_GP13 IOMUX_REG(1)
#define IOMUX_GP13_MTCK IOMUX_FUNC_A
#define IOMUX_GP13_I2SI_BCK IOMUX_FUNC_B
#define IOMUX_GP13_HSPI_MOSI IOMUX_FUNC_C
#define IOMUX_GP13_GPIO IOMUX_FUNC_D
#define IOMUX_GP13_UART0_CTS IOMUX_FUNC_E
/* IOMUX register index 2, GPIO 14 */
#define IOMUX_GP14 IOMUX_REG(2)
#define IOMUX_GP14_MTMS IOMUX_FUNC_A
#define IOMUX_GP14_I2SI_WS IOMUX_FUNC_B
#define IOMUX_GP14_HSPI_CLK IOMUX_FUNC_C
#define IOMUX_GP14_GPIO IOMUX_FUNC_D
#define IOMUX_GP14_UART0_DSR IOMUX_FUNC_E
/* IOMUX register index 3, GPIO 15 */
#define IOMUX_GP15 IOMUX_REG(3)
#define IOMUX_GP15_MTDO IOMUX_FUNC_A
#define IOMUX_GP15_I2SO_BCK IOMUX_FUNC_B
#define IOMUX_GP15_HSPI_CS0 IOMUX_FUNC_C
#define IOMUX_GP15_GPIO IOMUX_FUNC_D
#define IOMUX_GP15_UART0_RTS IOMUX_FUNC_E
/* IOMUX register index 4, GPIO 3 */
#define IOMUX_GP03 IOMUX_REG(4)
#define IOMUX_GP03_UART0_RX IOMUX_FUNC_A
#define IOMUX_GP03_I2SO_DATA IOMUX_FUNC_B
#define IOMUX_GP03_GPIO IOMUX_FUNC_D
#define IOMUX_GP03_CLK_XTAL_BK IOMUX_FUNC_E
/* IOMUX register index 5, GPIO 1 */
#define IOMUX_GP01 IOMUX_REG(5)
#define IOMUX_GP01_UART0_TX IOMUX_FUNC_A
#define IOMUX_GP01_SPICS1 IOMUX_FUNC_B
#define IOMUX_GP01_GPIO IOMUX_FUNC_D
#define IOMUX_GP01_CLK_RTC_BK IOMUX_FUNC_E
/* IOMUX register index 6, GPIO 6 */
#define IOMUX_GP06 IOMUX_REG(6)
#define IOMUX_GP06_SD_CLK IOMUX_FUNC_A
#define IOMUX_GP06_SP_ICLK IOMUX_FUNC_B
#define IOMUX_GP06_GPIO IOMUX_FUNC_D
#define IOMUX_GP06_UART1_CTS IOMUX_FUNC_E
/* IOMUX register index 7, GPIO 7 */
#define IOMUX_GP07 IOMUX_REG(7)
#define IOMUX_GP07_SD_DATA0 IOMUX_FUNC_A
#define IOMUX_GP07_SPIQ_MISO IOMUX_FUNC_B
#define IOMUX_GP07_GPIO IOMUX_FUNC_D
#define IOMUX_GP07_UART1_TX IOMUX_FUNC_E
/* IOMUX register index 8, GPIO 8 */
#define IOMUX_GP08 IOMUX_REG(8)
#define IOMUX_GP08_SD_DATA1 IOMUX_FUNC_A
#define IOMUX_GP08_SPID_MOSI IOMUX_FUNC_B
#define IOMUX_GP08_GPIO IOMUX_FUNC_D
#define IOMUX_GP08_UART1_RX IOMUX_FUNC_E
/* IOMUX register index 9, GPIO 9 */
#define IOMUX_GP09 IOMUX_REG(9)
#define IOMUX_GP09_SD_DATA2 IOMUX_FUNC_A
#define IOMUX_GP09_SPI_HD IOMUX_FUNC_B
#define IOMUX_GP09_GPIO IOMUX_FUNC_D
#define IOMUX_GP09_UFNC_HSPIHD IOMUX_FUNC_E
/* IOMUX register index 10, GPIO 10 */
#define IOMUX_GP10 IOMUX_REG(10)
#define IOMUX_GP10_SD_DATA3 IOMUX_FUNC_A
#define IOMUX_GP10_SPI_WP IOMUX_FUNC_B
#define IOMUX_GP10_GPIO IOMUX_FUNC_D
#define IOMUX_GP10_HSPIWP IOMUX_FUNC_E
/* IOMUX register index 11, GPIO 11 */
#define IOMUX_GP11 IOMUX_REG(11)
#define IOMUX_GP11_SD_CMD IOMUX_FUNC_A
#define IOMUX_GP11_SPI_CS0 IOMUX_FUNC_B
#define IOMUX_GP11_GPIO IOMUX_FUNC_D
#define IOMUX_GP11_UART1_RTS IOMUX_FUNC_E
/* IOMUX register index 12, GPIO 0 */
#define IOMUX_GP00 IOMUX_REG(12)
#define IOMUX_GP00_GPIO IOMUX_FUNC_A
#define IOMUX_GP00_SPI_CS2 IOMUX_FUNC_B
#define IOMUX_GP00_CLK_OUT IOMUX_FUNC_E
/* IOMUX register index 13, GPIO 2 */
#define IOMUX_GP02 IOMUX_REG(13)
#define IOMUX_GP02_GPIO IOMUX_FUNC_A
#define IOMUX_GP02_I2SO_WS IOMUX_FUNC_B
#define IOMUX_GP02_UART1_TX IOMUX_FUNC_C
#define IOMUX_GP02_UART0_TX IOMUX_FUNC_E
/* IOMUX register index 14, GPIO 4 */
#define IOMUX_GP04 IOMUX_REG(14)
#define IOMUX_GP04_GPIO4 IOMUX_FUNC_A
#define IOMUX_GP04_CLK_XTAL IOMUX_FUNC_B
/* IOMUX register index 15, GPIO 5 */
#define IOMUX_GP05 IOMUX_REG(15)
#define IOMUX_GP05_GPIO5 IOMUX_FUNC_A
#define IOMUX_GP05_CLK_RTC IOMUX_FUNC_B
/* esp_iomux_private contains implementation parts of the inline functions
declared above */
#include "esp/iomux_private.h"
#endif

View file

@ -0,0 +1,47 @@
/** esp/iomux_private.h
*
* Private implementation parts of iomux registers. In headers to
* allow compile-time optimisations.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
/* Mapping from register index to GPIO and from GPIO index to register
number. DO NOT USE THESE IN YOUR CODE, call gpio_to_iomux(xxx) or
iomux_to_gpio(xxx) instead.
*/
#ifndef _IOMUX_PRIVATE
#define _IOMUX_PRIVATE
#include "common_macros.h"
#define _IOMUX_TO_GPIO { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 }
#define _GPIO_TO_IOMUX { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 }
extern const IROM uint32_t GPIO_TO_IOMUX_MAP[];
extern const IROM uint32_t IOMUX_TO_GPIO_MAP[];
INLINED uint8_t gpio_to_iomux(const uint8_t gpio_number)
{
if(__builtin_constant_p(gpio_number)) {
static const uint8_t _regs[] = _GPIO_TO_IOMUX;
return _regs[gpio_number];
} else {
return GPIO_TO_IOMUX_MAP[gpio_number];
}
}
INLINED uint8_t iomux_to_gpio(const uint8_t iomux_number)
{
if(__builtin_constant_p(iomux_number)) {
static const uint8_t _regs[] = _IOMUX_TO_GPIO;
return _regs[iomux_number];
} else {
return IOMUX_TO_GPIO_MAP[iomux_number];
}
}
#endif

View file

@ -0,0 +1,116 @@
/* esp/registers.h
*
* ESP8266 register addresses and bitmasks.
*
* Not compatible with ESP SDK register access code.
*
* Based on register map documentation:
* https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_REGISTERS
#define _ESP_REGISTERS
#include "common_macros.h"
typedef volatile uint32_t *esp_reg_t;
/* Register base addresses
You shouldn't need to use these directly.
*/
#define MMIO_BASE 0x60000000
#define UART0_BASE (MMIO_BASE + 0)
#define SPI1_BASE (MMIO_BASE + 0x0100)
#define SPI_BASE (MMIO_BASE + 0x0200)
#define GPIO0_BASE (MMIO_BASE + 0x0300)
#define TIMER_BASE (MMIO_BASE + 0x0600)
#define RTC_BASE (MMIO_BASE + 0x0700)
#define IOMUX_BASE (MMIO_BASE + 0x0800)
#define WDT_BASE (MMIO_BASE + 0x0900)
#define I2C_BASE (MMIO_BASE + 0x0d00)
#define UART1_BASE (MMIO_BASE + 0x0F00)
#define RTCB_BASE (MMIO_BASE + 0x1000)
#define RTCS_BASE (MMIO_BASE + 0x1100)
#define RTCU_BASE (MMIO_BASE + 0x1200)
/*
* iomux registers, apply to pin functions.
*
* Note that IOMUX register order is _not_ the same as GPIO order. See
* esp_iomux.h for programmer-friendly IOMUX configuration options
*/
#define IOMUX_REG(X) *(esp_reg_t)(IOMUX_BASE+4*(X+1))
#define IOMUX_OE BIT(0) /* iomux Output enable bit */
#define IOMUX_OE_SLEEP BIT(1) /* iomux Output during sleep bit */
#define IOMUX_PD BIT(6) /* iomux soft pulldown bit */
#define IOMUX_PD_SLEEP BIT(2) /* iomux soft pulldown during sleep bit */
#define IOMUX_PU BIT(7) /* iomux soft pullup bit */
#define IOMUX_PU_SLEEP BIT(3) /* iomux soft pullup during sleep bit */
#define IOMUX_FLAG_WAKE_MASK (IOMUX_OE|IOMUX_PD|IOMUX_PU)
#define IOMUX_FLAG_SLEEP_MASK (IOMUX_OE_SLEEP|IOMUX_PD_SLEEP|IOMUX_PU_SLEEP)
#define IOMUX_FLAG_MASK (IOMUX_FLAG_WAKE_MASK|IOMUX_FLAG_SLEEP_MASK)
#define IOMUX_FUNC_MASK (BIT(4)|BIT(5)|BIT(12))
/* All pins have FUNC_A on reset (unconfirmed) */
#define IOMUX_FUNC_A (0)
#define IOMUX_FUNC_B BIT(4)
#define IOMUX_FUNC_C BIT(5)
#define IOMUX_FUNC_D BIT(4)|BIT(5)
#define IOMUX_FUNC_E BIT(12)
/*
* Based on descriptions by mamalala at https://github.com/esp8266/esp8266-wiki/wiki/gpio-registers
*/
/** GPIO OUTPUT registers GPIO_OUT_REG, GPIO_OUT_SET, GPIO_OUT_CLEAR
*
* Registers for pin outputs.
*
* _SET and _CLEAR write-only registers set and clear bits in _REG,
* respectively.
*
* ie
* GPIO_OUT_REG |= BIT(3);
* and
* GPIO_OUT_SET = BIT(3);
*
* ... are equivalent, but latter uses less CPU cycles.
*/
#define GPIO_OUT_REG *(esp_reg_t)(GPIO0_BASE)
#define GPIO_OUT_SET *(esp_reg_t)(GPIO0_BASE+0x04)
#define GPIO_OUT_CLEAR *(esp_reg_t)(GPIO0_BASE+0x08)
/* GPIO DIR registers GPIO_DIR_REG, GPIO_DIR_SET, GPIO_DIR_CLEAR
*
* Set bit in DIR register for output pins. Writing to _SET and _CLEAR
* registers set and clear bits in _REG, respectively.
*/
#define GPIO_DIR_REG *(esp_reg_t)(GPIO0_BASE+0x0C)
#define GPIO_DIR_SET *(esp_reg_t)(GPIO0_BASE+0x10)
#define GPIO_DIR_CLEAR *(esp_reg_t)(GPIO0_BASE+0x14)
/* GPIO IN register GPIO_IN_REG
*
* Reads current input values.
*/
#define GPIO_IN_REG *(esp_reg_t)(GPIO0_BASE+0x18)
/* WDT register(s)
Not fully understood yet. Writing 0 here disables wdt.
See ROM functions esp_wdt_xxx
*/
#define WDT_CTRL *(esp_reg_t)(WDT_BASE)
#endif

19
core/include/esp8266.h Normal file
View file

@ -0,0 +1,19 @@
/* esp8266.h
*
* ESP-specific SoC-level addresses, macros, etc.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#include <stdint.h>
#ifndef _ESP8266_H
#define _ESP8266_H
#include "common_macros.h"
#include "esp/registers.h"
#include "esp/iomux.h"
#include "esp/gpio.h"
#endif