diff --git a/core/esp_gpio_interrupts.c b/core/esp_gpio_interrupts.c index 99c7640..ef0a47d 100644 --- a/core/esp_gpio_interrupts.c +++ b/core/esp_gpio_interrupts.c @@ -25,7 +25,7 @@ OR - 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 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) { - uint32_t status_reg = GPIO_STATUS_REG; - GPIO_STATUS_CLEAR = status_reg; + uint32_t status_reg = GPIO.STATUS; + GPIO.STATUS_CLEAR = status_reg; uint8_t gpio_idx; while((gpio_idx = __builtin_ffs(status_reg))) { 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](); } } diff --git a/core/include/common_macros.h b/core/include/common_macros.h index 1ed2e22..669203a 100644 --- a/core/include/common_macros.h +++ b/core/include/common_macros.h @@ -23,7 +23,8 @@ #define VAL2FIELD(fieldname, value) (((value) & fieldname##_M) << fieldname##_S) #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 of having them loaded into rodata (which resides in DRAM) diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h index c4bca56..ab4b241 100644 --- a/core/include/esp/gpio.h +++ b/core/include/esp/gpio.h @@ -9,7 +9,7 @@ #ifndef _ESP_GPIO_H #define _ESP_GPIO_H #include -#include "esp/registers.h" +#include "esp/gpio_regs.h" #include "esp/iomux.h" #include "esp/cpu.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) { case GPIO_INPUT: iomux_flags = 0; - ctrl_val = GPIO_SOURCE_GPIO; + ctrl_val = 0; break; case GPIO_OUTPUT: iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; - ctrl_val = GPIO_DRIVE_PUSH_PULL|GPIO_SOURCE_GPIO; + ctrl_val = GPIO_CONF_DRIVER_ENABLE; break; case GPIO_OUT_OPEN_DRAIN: iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; - ctrl_val = GPIO_DRIVE_OPEN_DRAIN|GPIO_SOURCE_GPIO; + ctrl_val = 0; break; case GPIO_INPUT_PULLUP: iomux_flags = IOMUX_PIN_PULLUP; - ctrl_val = GPIO_SOURCE_GPIO; + ctrl_val = 0; + break; } iomux_set_gpio_function(gpio_num, iomux_flags); - GPIO_CTRL_REG(gpio_num) = (GPIO_CTRL_REG(gpio_num)&GPIO_INT_MASK) | ctrl_val; - if(direction == GPIO_OUTPUT) - GPIO_DIR_SET = BIT(gpio_num); + GPIO.CONF[gpio_num] = (GPIO.CONF[gpio_num] & FIELD_MASK(GPIO_CONF_INTTYPE)) | ctrl_val; + if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE) + GPIO.ENABLE_OUT_SET = BIT(gpio_num); 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. @@ -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) { - GPIO_DIR_CLEAR = BIT(gpio_num); + GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num); *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) { if(set) - GPIO_OUT_SET = BIT(gpio_num); + GPIO.OUT_SET = BIT(gpio_num); else - GPIO_OUT_CLEAR = BIT(gpio_num); + GPIO.OUT_CLEAR = BIT(gpio_num); } /* 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 task's pins, without needing to disable/enable interrupts. */ - if(GPIO_OUT_REG & BIT(gpio_num)) - GPIO_OUT_CLEAR = BIT(gpio_num); + if(GPIO.OUT & BIT(gpio_num)) + GPIO.OUT_CLEAR = BIT(gpio_num); else - GPIO_OUT_SET = BIT(gpio_num); + GPIO.OUT_SET = BIT(gpio_num); } /* 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) { - 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); /* 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) - | (int_type & GPIO_INT_MASK); - if(int_type != INT_NONE) { + GPIO.CONF[gpio_num] = SET_FIELD(GPIO.CONF[gpio_num], GPIO_CONF_INTTYPE, int_type); + if(int_type != GPIO_INTTYPE_NONE) { _xt_isr_attach(INUM_GPIO, gpio_interrupt_handler); _xt_isr_unmask(1< +#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 */ diff --git a/core/include/esp/registers.h b/core/include/esp/registers.h index ae8f169..5b8a785 100644 --- a/core/include/esp/registers.h +++ b/core/include/esp/registers.h @@ -17,6 +17,7 @@ #include "esp/types.h" #include "esp/iomux_regs.h" +#include "esp/gpio_regs.h" /* Internal macro, only defined in header body */ #define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET))) @@ -31,7 +32,7 @@ #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 GPIO0_BASE (MMIO_BASE + 0x0300) #define TIMER_BASE (MMIO_BASE + 0x0600) #define RTC_BASE (MMIO_BASE + 0x0700) //#define IOMUX_BASE (MMIO_BASE + 0x0800) @@ -42,79 +43,6 @@ #define RTCS_BASE (MMIO_BASE + 0x1100) #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 * * ESP8266 has two hardware(?) timer counters, FRC1 and FRC2. diff --git a/examples/blink/blink.c b/examples/blink/blink.c index 8f1b4bf..52d69a5 100644 --- a/examples/blink/blink.c +++ b/examples/blink/blink.c @@ -41,12 +41,12 @@ void blinkenTask(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' */ while(1) { - GPIO_OUT_SET = BIT(gpio); + GPIO.OUT_SET = BIT(gpio); vTaskDelay(1000 / portTICK_RATE_MS); - GPIO_OUT_CLEAR = BIT(gpio); + GPIO.OUT_CLEAR = BIT(gpio); vTaskDelay(1000 / portTICK_RATE_MS); } }