Add esp/gpio_regs.h

This commit is contained in:
Alex Stewart 2015-08-18 22:46:25 -07:00
parent 2ffd3da71e
commit fc1a1a7d0a
6 changed files with 167 additions and 114 deletions

View file

@ -25,7 +25,7 @@
OR OR
- Implement a single function named gpio_interrupt_handler(). This - Implement a single function named gpio_interrupt_handler(). This
will need to manually check GPIO_STATUS_REG and clear any status will need to manually check GPIO.STATUS and clear any status
bits after handling interrupts. This gives you full control, but bits after handling interrupts. This gives you full control, but
you can't combine it with the first approach. you can't combine it with the first approach.
@ -67,14 +67,14 @@ const gpio_interrupt_handler_t gpio_interrupt_handlers[16] = {
void __attribute__((weak)) IRAM gpio_interrupt_handler(void) void __attribute__((weak)) IRAM gpio_interrupt_handler(void)
{ {
uint32_t status_reg = GPIO_STATUS_REG; uint32_t status_reg = GPIO.STATUS;
GPIO_STATUS_CLEAR = status_reg; GPIO.STATUS_CLEAR = status_reg;
uint8_t gpio_idx; uint8_t gpio_idx;
while((gpio_idx = __builtin_ffs(status_reg))) while((gpio_idx = __builtin_ffs(status_reg)))
{ {
gpio_idx--; gpio_idx--;
status_reg &= ~BIT(gpio_idx); status_reg &= ~BIT(gpio_idx);
if(GPIO_CTRL_REG(gpio_idx) & GPIO_INT_MASK) if(FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_idx]))
gpio_interrupt_handlers[gpio_idx](); gpio_interrupt_handlers[gpio_idx]();
} }
} }

View file

@ -23,7 +23,8 @@
#define VAL2FIELD(fieldname, value) (((value) & fieldname##_M) << fieldname##_S) #define VAL2FIELD(fieldname, value) (((value) & fieldname##_M) << fieldname##_S)
#define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M) #define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M)
#define SETFIELD(regbits, fieldname, value) (((regbits) & ~(fieldname##_M << fieldname##_S)) | VAL2FIELD(fieldname, value)) #define FIELD_MASK(fieldname) (fieldname##_M << fieldname##_S)
#define SET_FIELD(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD(fieldname, value))
/* Use this macro to store constant values in IROM flash instead /* Use this macro to store constant values in IROM flash instead
of having them loaded into rodata (which resides in DRAM) of having them loaded into rodata (which resides in DRAM)

View file

@ -9,7 +9,7 @@
#ifndef _ESP_GPIO_H #ifndef _ESP_GPIO_H
#define _ESP_GPIO_H #define _ESP_GPIO_H
#include <stdbool.h> #include <stdbool.h>
#include "esp/registers.h" #include "esp/gpio_regs.h"
#include "esp/iomux.h" #include "esp/iomux.h"
#include "esp/cpu.h" #include "esp/cpu.h"
#include "xtensa_interrupts.h" #include "xtensa_interrupts.h"
@ -32,26 +32,27 @@ INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t directio
switch(direction) { switch(direction) {
case GPIO_INPUT: case GPIO_INPUT:
iomux_flags = 0; iomux_flags = 0;
ctrl_val = GPIO_SOURCE_GPIO; ctrl_val = 0;
break; break;
case GPIO_OUTPUT: case GPIO_OUTPUT:
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
ctrl_val = GPIO_DRIVE_PUSH_PULL|GPIO_SOURCE_GPIO; ctrl_val = GPIO_CONF_DRIVER_ENABLE;
break; break;
case GPIO_OUT_OPEN_DRAIN: case GPIO_OUT_OPEN_DRAIN:
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
ctrl_val = GPIO_DRIVE_OPEN_DRAIN|GPIO_SOURCE_GPIO; ctrl_val = 0;
break; break;
case GPIO_INPUT_PULLUP: case GPIO_INPUT_PULLUP:
iomux_flags = IOMUX_PIN_PULLUP; iomux_flags = IOMUX_PIN_PULLUP;
ctrl_val = GPIO_SOURCE_GPIO; ctrl_val = 0;
break;
} }
iomux_set_gpio_function(gpio_num, iomux_flags); iomux_set_gpio_function(gpio_num, iomux_flags);
GPIO_CTRL_REG(gpio_num) = (GPIO_CTRL_REG(gpio_num)&GPIO_INT_MASK) | ctrl_val; GPIO.CONF[gpio_num] = (GPIO.CONF[gpio_num] & FIELD_MASK(GPIO_CONF_INTTYPE)) | ctrl_val;
if(direction == GPIO_OUTPUT) if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE)
GPIO_DIR_SET = BIT(gpio_num); GPIO.ENABLE_OUT_SET = BIT(gpio_num);
else else
GPIO_DIR_CLEAR = BIT(gpio_num); GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
} }
/* Disable GPIO on the specified pin, and set it Hi-Z. /* Disable GPIO on the specified pin, and set it Hi-Z.
@ -61,7 +62,7 @@ INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t directio
*/ */
INLINED void gpio_disable(const uint8_t gpio_num) INLINED void gpio_disable(const uint8_t gpio_num)
{ {
GPIO_DIR_CLEAR = BIT(gpio_num); GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
*gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE; *gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE;
} }
@ -72,9 +73,9 @@ INLINED void gpio_disable(const uint8_t gpio_num)
INLINED void gpio_write(const uint8_t gpio_num, const bool set) INLINED void gpio_write(const uint8_t gpio_num, const bool set)
{ {
if(set) if(set)
GPIO_OUT_SET = BIT(gpio_num); GPIO.OUT_SET = BIT(gpio_num);
else else
GPIO_OUT_CLEAR = BIT(gpio_num); GPIO.OUT_CLEAR = BIT(gpio_num);
} }
/* Toggle output of a pin /* Toggle output of a pin
@ -89,10 +90,10 @@ INLINED void gpio_toggle(const uint8_t gpio_num)
get an invalid value. Prevents one task from clobbering another get an invalid value. Prevents one task from clobbering another
task's pins, without needing to disable/enable interrupts. task's pins, without needing to disable/enable interrupts.
*/ */
if(GPIO_OUT_REG & BIT(gpio_num)) if(GPIO.OUT & BIT(gpio_num))
GPIO_OUT_CLEAR = BIT(gpio_num); GPIO.OUT_CLEAR = BIT(gpio_num);
else else
GPIO_OUT_SET = BIT(gpio_num); GPIO.OUT_SET = BIT(gpio_num);
} }
/* Read input value of a GPIO pin. /* Read input value of a GPIO pin.
@ -102,38 +103,28 @@ INLINED void gpio_toggle(const uint8_t gpio_num)
*/ */
INLINED bool gpio_read(const uint8_t gpio_num) INLINED bool gpio_read(const uint8_t gpio_num)
{ {
return GPIO_IN_REG & BIT(gpio_num); return GPIO.IN & BIT(gpio_num);
} }
typedef enum {
INT_NONE = 0,
INT_RISING = GPIO_INT_RISING,
INT_FALLING = GPIO_INT_FALLING,
INT_CHANGE = GPIO_INT_CHANGE,
INT_LOW = GPIO_INT_LOW,
INT_HIGH = GPIO_INT_HIGH,
} gpio_interrupt_t;
extern void gpio_interrupt_handler(void); extern void gpio_interrupt_handler(void);
/* Set the interrupt type for a given pin /* Set the interrupt type for a given pin
* *
* If int_type is not INT_NONE, the gpio_interrupt_handler will be attached and unmasked. * If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be attached and unmasked.
*/ */
INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_interrupt_t int_type) INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type)
{ {
GPIO_CTRL_REG(gpio_num) = (GPIO_CTRL_REG(gpio_num)&~GPIO_INT_MASK) GPIO.CONF[gpio_num] = SET_FIELD(GPIO.CONF[gpio_num], GPIO_CONF_INTTYPE, int_type);
| (int_type & GPIO_INT_MASK); if(int_type != GPIO_INTTYPE_NONE) {
if(int_type != INT_NONE) {
_xt_isr_attach(INUM_GPIO, gpio_interrupt_handler); _xt_isr_attach(INUM_GPIO, gpio_interrupt_handler);
_xt_isr_unmask(1<<INUM_GPIO); _xt_isr_unmask(1<<INUM_GPIO);
} }
} }
/* Return the interrupt type set for a pin */ /* Return the interrupt type set for a pin */
INLINED gpio_interrupt_t gpio_get_interrupt(const uint8_t gpio_num) INLINED gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num)
{ {
return (gpio_interrupt_t)(GPIO_CTRL_REG(gpio_num) & GPIO_INT_MASK); return FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]);
} }
#endif #endif

View file

@ -0,0 +1,133 @@
/* esp/iomux_regs.h
*
* ESP8266 GPIO register definitions
*
* Not compatible with ESP SDK register access code.
*/
#ifndef _ESP_GPIO_REGS_H
#define _ESP_GPIO_REGS_H
#include <stdint.h>
#include "common_macros.h"
#define GPIO_BASE 0x60000300
#define GPIO (*(struct GPIO_REGS *)(GPIO_BASE))
/** GPIO output registers GPIO.OUT, GPIO.OUT_SET, GPIO.OUT_CLEAR:
*
* _SET and _CLEAR write-only registers set and clear bits in the main register,
* respectively.
*
* i.e.
* GPIO.OUT_SET = BIT(3);
* and
* GPIO.OUT |= BIT(3);
*
* ... are equivalent, but the former uses fewer CPU cycles.
*
* ENABLE_OUT / ENABLE_OUT_SET / ENABLE_OUT_CLEAR:
*
* Determine whether the corresponding GPIO has its output enabled or not.
* When clear, GPIO can function as an input. When set, GPIO will drive its
* output (and IN register will simply reflect the output state).
*
* (_SET/_CLEAR function similarly to OUT registers)
*
* STATUS / STATUS_SET / STATUS_CLEAR:
*
* Indicates which GPIOs have triggered an interrupt. Interrupt status should
* be reset by writing to STATUS or STATUS_CLEAR.
*
* (_SET/_CLEAR function similarly to OUT registers)
*/
struct GPIO_REGS {
uint32_t volatile OUT; // 0x00
uint32_t volatile OUT_SET; // 0x04
uint32_t volatile OUT_CLEAR; // 0x08
uint32_t volatile ENABLE_OUT; // 0x0c
uint32_t volatile ENABLE_OUT_SET; // 0x10
uint32_t volatile ENABLE_OUT_CLEAR; // 0x14
uint32_t volatile IN; // 0x18
uint32_t volatile STATUS; // 0x1c
uint32_t volatile STATUS_SET; // 0x20
uint32_t volatile STATUS_CLEAR; // 0x24
uint32_t volatile CONF[16]; // 0x28 - 0x64
uint32_t volatile PWM; // 0x68
uint32_t volatile RTC_CALIB; // 0x6c
uint32_t volatile RTC_CALIB_RESULT; // 0x70
} __attribute__ (( packed ));
/* Double-check the structure size to make sure the compiler hasn't done
* something strange (or somebody typoed in the struct definition, etc)
*/
_Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
/* Bit mapping for CONF[i] registers */
/* GPIO.CONF[i] control the pin behavior for the corresponding GPIO in/output.
*
* GPIO_CONF_CONFIG (multi-value)
* FIXME: Unclear what these do. Need to find a better name.
*
* GPIO_CONF_WAKEUP_ENABLE (boolean)
* Can an interrupt contion on this pin wake the processor from a sleep
* state?
*
* GPIO_CONF_INTTYPE (multi-value)
* Under what conditions this GPIO input should generate an interrupt.
* (see gpio_inttype_t enum below for values)
*
* GPIO_CONF_DRIVER_ENABLE (boolean)
* When set, a high output state will pull the pin up to +Vcc (3.3V). When
* cleared, output functions in "open drain" mode (low state will pull down
* to ground, but high state allows output to "float").
*
* GPIO_CONF_SOURCE_PWM (boolean)
* When set, GPIO pin output will be connected to the sigma-delta PWM
* generator (controlled by the GPIO.PWM register). When cleared, pin
* output will function as a normal GPIO output (controlled by the
* GPIO.OUT* registers).
*/
#define GPIO_CONF_CONFIG_M 0x00000003
#define GPIO_CONF_CONFIG_S 11
#define GPIO_CONF_WAKEUP_ENABLE BIT(10)
#define GPIO_CONF_INTTYPE_M 0x00000007
#define GPIO_CONF_INTTYPE_S 7
#define GPIO_CONF_DRIVER_ENABLE BIT(2)
#define GPIO_CONF_SOURCE_PWM BIT(0)
/* Valid values for the GPIO_CONF_INTTYPE field */
typedef enum {
GPIO_INTTYPE_NONE = 0,
GPIO_INTTYPE_EDGE_POS = 1,
GPIO_INTTYPE_EDGE_NEG = 2,
GPIO_INTTYPE_EDGE_ANY = 3,
GPIO_INTTYPE_LEVEL_LOW = 4,
GPIO_INTTYPE_LEVEL_HIGH = 5,
} gpio_inttype_t;
/* Bit mapping for PWM register */
#define GPIO_PWM_ENABLE BIT(16)
#define GPIO_PWM_PRESCALER_M 0x000000ff
#define GPIO_PWM_PRESCALER_S 8
#define GPIO_PWM_TARGET_M 0x000000ff
#define GPIO_PWM_TARGET_S 0
/* Bit mapping for RTC_CALIB register */
#define GPIO_RTC_CALIB_START BIT(31)
#define GPIO_RTC_CALIB_PERIOD_M 0x000003ff
#define GPIO_RTC_CALIB_PERIOD_S 0
/* Bit mapping for RTC_CALIB_RESULT register */
#define GPIO_RTC_CALIB_RESULT_READY BIT(31)
#define GPIO_RTC_CALIB_RESULT_READY_REAL BIT(30)
#define GPIO_RTC_CALIB_RESULT_VALUE_M 0x000fffff
#define GPIO_RTC_CALIB_RESULT_VALUE_S 0
#endif /* _ESP_GPIO_REGS_H */

View file

@ -17,6 +17,7 @@
#include "esp/types.h" #include "esp/types.h"
#include "esp/iomux_regs.h" #include "esp/iomux_regs.h"
#include "esp/gpio_regs.h"
/* Internal macro, only defined in header body */ /* Internal macro, only defined in header body */
#define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET))) #define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET)))
@ -31,7 +32,7 @@
#define UART0_BASE (MMIO_BASE + 0) #define UART0_BASE (MMIO_BASE + 0)
#define SPI1_BASE (MMIO_BASE + 0x0100) #define SPI1_BASE (MMIO_BASE + 0x0100)
#define SPI_BASE (MMIO_BASE + 0x0200) #define SPI_BASE (MMIO_BASE + 0x0200)
#define GPIO0_BASE (MMIO_BASE + 0x0300) //#define GPIO0_BASE (MMIO_BASE + 0x0300)
#define TIMER_BASE (MMIO_BASE + 0x0600) #define TIMER_BASE (MMIO_BASE + 0x0600)
#define RTC_BASE (MMIO_BASE + 0x0700) #define RTC_BASE (MMIO_BASE + 0x0700)
//#define IOMUX_BASE (MMIO_BASE + 0x0800) //#define IOMUX_BASE (MMIO_BASE + 0x0800)
@ -42,79 +43,6 @@
#define RTCS_BASE (MMIO_BASE + 0x1100) #define RTCS_BASE (MMIO_BASE + 0x1100)
#define RTCU_BASE (MMIO_BASE + 0x1200) #define RTCU_BASE (MMIO_BASE + 0x1200)
/*
* 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 _REG(GPIO0_BASE, 0x00)
#define GPIO_OUT_SET _REG(GPIO0_BASE, 0x04)
#define GPIO_OUT_CLEAR _REG(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 _REG(GPIO0_BASE, 0x0C)
#define GPIO_DIR_SET _REG(GPIO0_BASE, 0x10)
#define GPIO_DIR_CLEAR _REG(GPIO0_BASE, 0x14)
/* GPIO IN register GPIO_IN_REG
*
* Reads current input values.
*/
#define GPIO_IN_REG _REG(GPIO0_BASE, 0x18)
/* GPIO interrupt 'status' flag
Bit set if interrupt has fired (see below for interrupt config
registers.
Lower 16 bits only are used.
*/
#define GPIO_STATUS_REG _REG(GPIO0_BASE,0x1c)
#define GPIO_STATUS_SET _REG(GPIO0_BASE,0x20)
#define GPIO_STATUS_CLEAR _REG(GPIO0_BASE,0x24)
#define GPIO_STATUS_MASK 0x0000FFFFL
/* GPIO pin control registers for GPIOs 0-15
*
*/
#define GPIO_CTRL_REG(GPNUM) _REG(GPIO0_BASE, 0x28+(GPNUM*4))
#define GPIO_SOURCE_GPIO 0
#define GPIO_SOURCE_DAC BIT(0) /* "Sigma-Delta" */
#define GPIO_SOURCE_MASK BIT(0
#define GPIO_DRIVE_PUSH_PULL 0
#define GPIO_DRIVE_OPEN_DRAIN BIT(2)
#define GPIO_DRIVE_MASK BIT(2)
#define GPIO_INT_NONE 0
#define GPIO_INT_RISING BIT(7)
#define GPIO_INT_FALLING BIT(8)
#define GPIO_INT_CHANGE (BIT(7)|BIT(8))
#define GPIO_INT_LOW BIT(9)
#define GPIO_INT_HIGH (BIT(7)|BIT(9))
#define GPIO_INT_MASK (BIT(7)|BIT(8)|BIT(9))
/* TIMER registers /* TIMER registers
* *
* ESP8266 has two hardware(?) timer counters, FRC1 and FRC2. * ESP8266 has two hardware(?) timer counters, FRC1 and FRC2.

View file

@ -41,12 +41,12 @@ void blinkenTask(void *pvParameters)
*/ */
void blinkenRegisterTask(void *pvParameters) void blinkenRegisterTask(void *pvParameters)
{ {
GPIO_DIR_SET = BIT(gpio); GPIO.ENABLE_OUT_SET = BIT(gpio);
IOMUX_GPIO14 = IOMUX_GPIO14_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE; /* change this line if you change 'gpio' */ IOMUX_GPIO14 = IOMUX_GPIO14_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE; /* change this line if you change 'gpio' */
while(1) { while(1) {
GPIO_OUT_SET = BIT(gpio); GPIO.OUT_SET = BIT(gpio);
vTaskDelay(1000 / portTICK_RATE_MS); vTaskDelay(1000 / portTICK_RATE_MS);
GPIO_OUT_CLEAR = BIT(gpio); GPIO.OUT_CLEAR = BIT(gpio);
vTaskDelay(1000 / portTICK_RATE_MS); vTaskDelay(1000 / portTICK_RATE_MS);
} }
} }