Merge pull request #27 from foogod/issue_8

Convert headers to new *_regs.h format
This commit is contained in:
Angus Gratton 2015-08-22 17:36:37 +10:00
commit aa17221929
18 changed files with 659 additions and 565 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

@ -14,12 +14,12 @@
* the arguments aren't known at compile time (values are evaluated at * the arguments aren't known at compile time (values are evaluated at
* compile time otherwise.) * compile time otherwise.)
*/ */
uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_div_t div) uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
{ {
return _timer_freq_to_count_impl(frc, freq, div); return _timer_freq_to_count_impl(frc, freq, div);
} }
uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_div_t div) uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
{ {
return _timer_time_to_count_runtime(frc, us, div); return _timer_time_to_count_runtime(frc, us, div);
} }

View file

@ -16,6 +16,16 @@
#define BIT(X) (1<<(X)) #define BIT(X) (1<<(X))
#endif #endif
/* These macros convert values to/from bitfields specified by *_M and *_S (mask
* and shift) constants. Used primarily with ESP8266 register access.
*/
#define VAL2FIELD(fieldname, value) (((value) & fieldname##_M) << fieldname##_S)
#define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M)
#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

@ -0,0 +1,53 @@
/* esp/dport_regs.h
*
* ESP8266 DPORT0 register definitions
*
* Not compatible with ESP SDK register access code.
*/
#ifndef _ESP_DPORT_REGS_H
#define _ESP_DPORT_REGS_H
#include "esp/types.h"
#include "common_macros.h"
#define DPORT_BASE 0x3ff00000
#define DPORT (*(struct DPORT_REGS *)(DPORT_BASE))
/* DPORT registers
Control various aspects of core/peripheral interaction... Not well
documented or understood.
*/
struct DPORT_REGS {
uint32_t volatile _unknown0; // 0x00
uint32_t volatile INT_ENABLE; // 0x04
} __attribute__ (( packed ));
_Static_assert(sizeof(struct DPORT_REGS) == 0x08, "DPORT_REGS is the wrong size");
/* Details for INT_ENABLE register */
/* Set flags to enable CPU interrupts from some peripherals. Read/write.
bit 0 - Is set by RTOS SDK startup code but function is unknown.
bit 1 - INT_ENABLE_TIMER0 allows TIMER 0 (FRC1) to trigger interrupt INUM_TIMER_FRC1.
bit 2 - INT_ENABLE_TIMER1 allows TIMER 1 (FRC2) to trigger interrupt INUM_TIMER_FRC2.
Espressif calls this register "EDGE_INT_ENABLE_REG". The "edge" in
question is (I think) the interrupt line from the peripheral, as
the interrupt status bit is set. There may be a similar register
for enabling "level" interrupts instead of edge triggering
- this is unknown.
*/
#define DPORT_INT_ENABLE_TIMER0 BIT(1)
#define DPORT_INT_ENABLE_TIMER1 BIT(2)
/* Aliases for the Espressif way of referring to TIMER0 (FRC1) and TIMER1
* (FRC2).. */
#define DPORT_INT_ENABLE_FRC1 DPORT_INT_ENABLE_TIMER0
#define DPORT_INT_ENABLE_FRC2 DPORT_INT_ENABLE_TIMER1
#endif /* _ESP_DPORT_REGS_H */

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,37 +32,38 @@ 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_OE; iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
ctrl_val = GPIO_DRIVE_PUSH_PULL|GPIO_SOURCE_GPIO; ctrl_val = GPIO_CONF_PUSH_PULL;
break; break;
case GPIO_OUT_OPEN_DRAIN: case GPIO_OUT_OPEN_DRAIN:
iomux_flags = IOMUX_OE; 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_PU; 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.
* *
* If later muxing this pin to a different function, make sure to set * If later muxing this pin to a different function, make sure to set
* IOMUX_OE if necessary to enable the output buffer. * IOMUX_PIN_OUTPUT_ENABLE if necessary to enable the output buffer.
*/ */
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_OE; *gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE;
} }
/* Set output of a pin high or low. /* Set output of a pin high or low.
@ -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,130 @@
/* esp/gpio_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 "esp/types.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 ));
_Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
/* Details 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_PUSH_PULL (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_PUSH_PULL 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;
/* Details 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
/* Details 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
/* Details 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

@ -8,8 +8,8 @@
*/ */
#ifndef _ESP_IOMUX_H #ifndef _ESP_IOMUX_H
#define _ESP_IOMUX_H #define _ESP_IOMUX_H
#include <stdint.h> #include "esp/types.h"
#include "esp/registers.h" #include "esp/iomux_regs.h"
/** /**
* Convert a GPIO pin number to an iomux register index. * Convert a GPIO pin number to an iomux register index.
@ -32,11 +32,11 @@ inline static uint8_t iomux_to_gpio(const uint8_t iomux_num);
/** /**
* Directly get the IOMUX register for a particular gpio number * Directly get the IOMUX register for a particular gpio number
* *
* ie *gpio_iomux_reg(3) is equivalent to IOMUX_GP03 * ie *gpio_iomux_reg(3) is equivalent to IOMUX_GPIO3
*/ */
inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number) inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number)
{ {
return &IOMUX_REG(gpio_to_iomux(gpio_number)); return &(IOMUX.PIN[gpio_to_iomux(gpio_number)]);
} }
/** /**
@ -45,153 +45,21 @@ inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number)
* This allows you to set pins to GPIO without knowing in advance the * This allows you to set pins to GPIO without knowing in advance the
* exact register masks to use. * exact register masks to use.
* *
* flags can be any of IOMUX_OE, IOMUX_PU, IOMUX_PD, etc. Any other flags will be cleared. * flags can be any of IOMUX_PIN_OUTPUT_ENABLE, IOMUX_PIN_PULLUP, IOMUX_PIN_PULLDOWN, etc. Any other flags will be cleared.
* *
* Equivalent to a direct register operation if gpio_number is known at compile time. * Equivalent to a direct register operation if gpio_number is known at compile time.
* ie the following are equivalent: * ie the following are equivalent:
* *
* iomux_set_gpio_function(12, IOMUX_OE); * iomux_set_gpio_function(12, IOMUX_PIN_OUTPUT_ENABLE);
* IOMUX_GP12 = (IOMUX_GP12 & ~IOMUX_FUNC_MASK) | IOMUX_GP12_GPIO | IOMUX_OE; * IOMUX_GPIO12 = IOMUX_GPIO12_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE;
*/ */
inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint8_t flags) inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint32_t flags)
{ {
const uint8_t reg_idx = gpio_to_iomux(gpio_number); 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(0) : IOMUX_FUNC(3)) | flags;
const uint32_t func = (reg_idx > 11 ? IOMUX_FUNC_A : IOMUX_FUNC_D) | flags; IOMUX.PIN[reg_idx] = func | 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 /* esp_iomux_private contains implementation parts of the inline functions
declared above */ declared above */
#include "esp/iomux_private.h" #include "esp/iomux_private.h"

View file

@ -0,0 +1,151 @@
/* esp/iomux_regs.h
*
* ESP8266 IOMUX register definitions
*
* Not compatible with ESP SDK register access code.
*
* Note that IOMUX register order is _not_ the same as GPIO order. See
* esp/iomux.h for programmer-friendly IOMUX configuration options.
*/
#ifndef _ESP_IOMUX_REGS_H
#define _ESP_IOMUX_REGS_H
#include "esp/types.h"
#include "common_macros.h"
#define IOMUX_BASE 0x60000800
#define IOMUX (*(struct IOMUX_REGS *)(IOMUX_BASE))
struct IOMUX_REGS {
uint32_t volatile CONF; // 0x00
uint32_t volatile PIN[16]; // 0x04 - 0x40
} __attribute__ (( packed ));
_Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size");
/* Details for CONF register */
#define IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK BIT(8)
#define IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK BIT(9)
/* Details for PIN registers */
#define IOMUX_PIN_OUTPUT_ENABLE BIT(0)
#define IOMUX_PIN_OUTPUT_ENABLE_SLEEP BIT(1)
#define IOMUX_PIN_PULLDOWN_SLEEP BIT(2)
#define IOMUX_PIN_PULLUP_SLEEP BIT(3)
#define IOMUX_PIN_FUNC_LOW_M 0x00000003
#define IOMUX_PIN_FUNC_LOW_S 4
#define IOMUX_PIN_PULLDOWN BIT(6)
#define IOMUX_PIN_PULLUP BIT(7)
#define IOMUX_PIN_FUNC_HIGH_M 0x00000004
#define IOMUX_PIN_FUNC_HIGH_S 8
#define IOMUX_PIN_FUNC_MASK 0x00001030
/* WARNING: Macro evaluates argument twice */
#define IOMUX_FUNC(val) (VAL2FIELD(IOMUX_PIN_FUNC_LOW, val) | VAL2FIELD(IOMUX_PIN_FUNC_HIGH, val))
/* WARNING: Macro evaluates argument twice */
#define IOMUX_FUNC_VALUE(regbits) (FIELD2VAL(IOMUX_PIN_FUNC_LOW, regbits) | FIELD2VAL(IOMUX_PIN_FUNC_HIGH, regbits))
#define IOMUX_SET_FUNC(regbits, funcval) (((regbits) & ~IOMUX_PIN_FUNC_MASK) | (funcval))
#define IOMUX_GPIO0 IOMUX.PIN[12]
#define IOMUX_GPIO1 IOMUX.PIN[5]
#define IOMUX_GPIO2 IOMUX.PIN[13]
#define IOMUX_GPIO3 IOMUX.PIN[4]
#define IOMUX_GPIO4 IOMUX.PIN[14]
#define IOMUX_GPIO5 IOMUX.PIN[15]
#define IOMUX_GPIO6 IOMUX.PIN[6]
#define IOMUX_GPIO7 IOMUX.PIN[7]
#define IOMUX_GPIO8 IOMUX.PIN[8]
#define IOMUX_GPIO9 IOMUX.PIN[9]
#define IOMUX_GPIO10 IOMUX.PIN[10]
#define IOMUX_GPIO11 IOMUX.PIN[11]
#define IOMUX_GPIO12 IOMUX.PIN[0]
#define IOMUX_GPIO13 IOMUX.PIN[1]
#define IOMUX_GPIO14 IOMUX.PIN[2]
#define IOMUX_GPIO15 IOMUX.PIN[3]
#define IOMUX_GPIO0_FUNC_GPIO IOMUX_FUNC(0)
#define IOMUX_GPIO0_FUNC_SPI0_CS2 IOMUX_FUNC(1)
#define IOMUX_GPIO0_FUNC_CLOCK_OUT IOMUX_FUNC(4)
#define IOMUX_GPIO1_FUNC_UART0_TXD IOMUX_FUNC(0)
#define IOMUX_GPIO1_FUNC_SPI0_CS1 IOMUX_FUNC(1)
#define IOMUX_GPIO1_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO1_FUNC_CLOCK_RTC_BLINK IOMUX_FUNC(4)
#define IOMUX_GPIO2_FUNC_GPIO IOMUX_FUNC(0)
#define IOMUX_GPIO2_FUNC_I2SO_WS IOMUX_FUNC(1)
#define IOMUX_GPIO2_FUNC_UART1_TXD_BLINK IOMUX_FUNC(2)
#define IOMUX_GPIO2_FUNC_UART0_TXD_BLINK IOMUX_FUNC(4)
#define IOMUX_GPIO3_FUNC_UART0_RXD IOMUX_FUNC(0)
#define IOMUX_GPIO3_FUNC_I2SO_DATA IOMUX_FUNC(1)
#define IOMUX_GPIO3_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO3_FUNC_CLOCK_XTAL_BLINK IOMUX_FUNC(4)
#define IOMUX_GPIO4_FUNC_GPIO IOMUX_FUNC(0)
#define IOMUX_GPIO4_FUNC_CLOCK_XTAL IOMUX_FUNC(1)
#define IOMUX_GPIO5_FUNC_GPIO IOMUX_FUNC(0)
#define IOMUX_GPIO5_FUNC_CLOCK_RTC IOMUX_FUNC(1)
#define IOMUX_GPIO6_FUNC_SD_CLK IOMUX_FUNC(0)
#define IOMUX_GPIO6_FUNC_SPI0_CLK IOMUX_FUNC(1)
#define IOMUX_GPIO6_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO6_FUNC_UART1_CTS IOMUX_FUNC(4)
#define IOMUX_GPIO7_FUNC_SD_DATA0 IOMUX_FUNC(0)
#define IOMUX_GPIO7_FUNC_SPI0_Q_MISO IOMUX_FUNC(1)
#define IOMUX_GPIO7_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO7_FUNC_UART1_TXD IOMUX_FUNC(4)
#define IOMUX_GPIO8_FUNC_SD_DATA1 IOMUX_FUNC(0)
#define IOMUX_GPIO8_FUNC_SPI0_D_MOSI IOMUX_FUNC(1)
#define IOMUX_GPIO8_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO8_FUNC_UART1_RXD IOMUX_FUNC(4)
#define IOMUX_GPIO9_FUNC_SD_DATA2 IOMUX_FUNC(0)
#define IOMUX_GPIO9_FUNC_SPI0_HD IOMUX_FUNC(1)
#define IOMUX_GPIO9_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO9_FUNC_SPI1_HD IOMUX_FUNC(4)
#define IOMUX_GPIO10_FUNC_SD_DATA3 IOMUX_FUNC(0)
#define IOMUX_GPIO10_FUNC_SPI0_WP IOMUX_FUNC(1)
#define IOMUX_GPIO10_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO10_FUNC_SPI1_WP IOMUX_FUNC(4)
#define IOMUX_GPIO11_FUNC_SD_CMD IOMUX_FUNC(0)
#define IOMUX_GPIO11_FUNC_SPI0_CS0 IOMUX_FUNC(1)
#define IOMUX_GPIO11_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO11_FUNC_UART1_RTS IOMUX_FUNC(4)
#define IOMUX_GPIO12_FUNC_MTDI IOMUX_FUNC(0)
#define IOMUX_GPIO12_FUNC_I2SI_DATA IOMUX_FUNC(1)
#define IOMUX_GPIO12_FUNC_SPI1_Q_MISO IOMUX_FUNC(2)
#define IOMUX_GPIO12_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO12_FUNC_UART0_DTR IOMUX_FUNC(4)
#define IOMUX_GPIO13_FUNC_MTCK IOMUX_FUNC(0)
#define IOMUX_GPIO13_FUNC_I2SI_BCK IOMUX_FUNC(1)
#define IOMUX_GPIO13_FUNC_SPI1_D_MOSI IOMUX_FUNC(2)
#define IOMUX_GPIO13_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO13_FUNC_UART0_CTS IOMUX_FUNC(4)
#define IOMUX_GPIO14_FUNC_MTMS IOMUX_FUNC(0)
#define IOMUX_GPIO14_FUNC_I2SI_WS IOMUX_FUNC(1)
#define IOMUX_GPIO14_FUNC_SPI1_CLK IOMUX_FUNC(2)
#define IOMUX_GPIO14_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO14_FUNC_UART0_DSR IOMUX_FUNC(4)
#define IOMUX_GPIO15_FUNC_MTDO IOMUX_FUNC(0)
#define IOMUX_GPIO15_FUNC_I2SO_BCK IOMUX_FUNC(1)
#define IOMUX_GPIO15_FUNC_SPI1_CS0 IOMUX_FUNC(2)
#define IOMUX_GPIO15_FUNC_GPIO IOMUX_FUNC(3)
#define IOMUX_GPIO15_FUNC_UART0_RTS IOMUX_FUNC(4)
#endif /* _ESP_IOMUX_REGS_H */

View file

@ -14,295 +14,33 @@
#ifndef _ESP_REGISTERS #ifndef _ESP_REGISTERS
#define _ESP_REGISTERS #define _ESP_REGISTERS
#include "common_macros.h" #include "common_macros.h"
#include "esp/types.h"
typedef volatile uint32_t *esp_reg_t; #include "esp/iomux_regs.h"
#include "esp/gpio_regs.h"
/* Internal macro, only defined in header body */ #include "esp/timer_regs.h"
#define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET))) #include "esp/wdt_regs.h"
#include "esp/dport_regs.h"
/* Register base addresses /* Register base addresses
You shouldn't need to use these directly. You shouldn't need to use these directly.
*/ */
#define MMIO_BASE 0x60000000 #define MMIO_BASE 0x60000000
#define DPORT_BASE 0x3ff00000 //#define DPORT_BASE 0x3ff00000
#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)
#define WDT_BASE (MMIO_BASE + 0x0900) //#define WDT_BASE (MMIO_BASE + 0x0900)
#define I2C_BASE (MMIO_BASE + 0x0d00) #define I2C_BASE (MMIO_BASE + 0x0d00)
#define UART1_BASE (MMIO_BASE + 0x0F00) #define UART1_BASE (MMIO_BASE + 0x0F00)
#define RTCB_BASE (MMIO_BASE + 0x1000) #define RTCB_BASE (MMIO_BASE + 0x1000)
#define RTCS_BASE (MMIO_BASE + 0x1100) #define RTCS_BASE (MMIO_BASE + 0x1100)
#define RTCU_BASE (MMIO_BASE + 0x1200) #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) _REG(IOMUX_BASE,0x04+4*X)
#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 _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.
*
* FRC1 is a 24-bit countdown timer, triggers interrupt when reaches zero.
* FRC2 is a 32-bit countup timer, can set a variable match value to trigger an interrupt.
*
* FreeRTOS tick timer appears to come from XTensa core tick timer0,
* not either of these. FRC2 is used in the FreeRTOS SDK however. It
* is set to free-run, interrupting periodically via updates to the
* MATCH register. sdk_ets_timer_init configures FRC2 and assigns FRC2
* interrupt handler at sdk_vApplicationTickHook+0x68
*/
/* Load value for FRC1, read/write.
When TIMER_CTRL_RELOAD is cleared in TIMER_FRC1_CTRL_REG, FRC1 will
reload to TIMER_FRC1_MAX_LOAD once overflowed (unless the load
value is rewritten in the interrupt handler.)
When TIMER_CTRL_RELOAD is set in TIMER_FRC1_CTRL_REG, FRC1 will reload
from the load register value once overflowed.
*/
#define TIMER_FRC1_LOAD_REG _REG(TIMER_BASE, 0x00)
#define TIMER_FRC1_MAX_LOAD 0x7fffff
/* Current count value for FRC1, read only? */
#define TIMER_FRC1_COUNT_REG _REG(TIMER_BASE, 0x04)
/* Control register for FRC1, read/write.
See the bit definitions TIMER_CTRL_xxx lower down.
*/
#define TIMER_FRC1_CTRL_REG _REG(TIMER_BASE, 0x08)
/* Reading this register always returns the value in
* TIMER_FRC1_LOAD_REG.
*
* Writing zero to this register clears the FRC1
* interrupt status.
*/
#define TIMER_FRC1_CLEAR_INT_REG _REG(TIMER_BASE, 0x0c)
/* FRC2 load register.
*
* If TIMER_CTRL_RELOAD is cleared in TIMER_FRC2_CTRL_REG, writing to
* this register will update the FRC2 COUNT value.
*
* If TIMER_CTRL_RELOAD is set in TIMER_FRC2_CTRL_REG, the behaviour
* appears to be the same except that writing 0 to the load register
* both sets the COUNT register to 0 and disables the timer, even if
* the TIMER_CTRL_RUN bit is set.
*
* Offsets 0x34, 0x38, 0x3c all seem to read back the LOAD_REG value
* also (but have no known function.)
*/
#define TIMER_FRC2_LOAD_REG _REG(TIMER_BASE, 0x20)
/* FRC2 current count value. Read only? */
#define TIMER_FRC2_COUNT_REG _REG(TIMER_BASE, 0x24)
/* Control register for FRC2. Read/write.
See the bit definitions TIMER_CTRL_xxx lower down.
*/
#define TIMER_FRC2_CTRL_REG _REG(TIMER_BASE, 0x28)
/* Reading this value returns the current value of
* TIMER_FRC2_LOAD_REG.
*
* Writing zero to this value clears the FRC2 interrupt status.
*/
#define TIMER_FRC2_CLEAR_INT_REG _REG(TIMER_BASE, 0x2c)
/* Interrupt match value for FRC2. When COUNT == MATCH,
the interrupt fires.
*/
#define TIMER_FRC2_MATCH_REG _REG(TIMER_BASE, 0x30)
/* Timer control bits to set clock divisor values.
Divider from master 80MHz APB_CLK (unconfirmed, see esp/clocks.h).
*/
#define TIMER_CTRL_DIV_1 0
#define TIMER_CTRL_DIV_16 BIT(2)
#define TIMER_CTRL_DIV_256 BIT(3)
#define TIMER_CTRL_DIV_MASK (BIT(2)|BIT(3))
/* Set timer control bits to trigger interrupt on "edge" or "level"
*
* Observed behaviour is like this:
*
* * When TIMER_CTRL_INT_LEVEL is set, the interrupt status bit
* TIMER_CTRL_INT_STATUS remains set when the timer interrupt
* triggers, unless manually cleared by writing 0 to
* TIMER_FRCx_CLEAR_INT. While the interrupt status bit stays set
* the timer will continue to run normally, but the interrupt
* (INUM_TIMER_FRC1 or INUM_TIMER_FRC2) won't trigger again.
*
* * When TIMER_CTRL_INT_EDGE (default) is set, there's no need to
* manually write to TIMER_FRCx_CLEAR_INT. The interrupt status bit
* TIMER_CTRL_INT_STATUS automatically clears after the interrupt
* triggers, and the interrupt handler will run again
* automatically.
*
*/
#define TIMER_CTRL_INT_EDGE 0
#define TIMER_CTRL_INT_LEVEL BIT(0)
#define TIMER_CTRL_INT_MASK BIT(0)
/* Timer auto-reload bit
This bit interacts with TIMER_FRC1_LOAD_REG & TIMER_FRC2_LOAD_REG
differently, see those registers for details.
*/
#define TIMER_CTRL_RELOAD BIT(6)
/* Timer run bit */
#define TIMER_CTRL_RUN BIT(7)
/* Read-only timer interrupt status.
This bit gets set on FRC1 when interrupt fires, and cleared on a
write to TIMER_FRC1_CLEAR_INT (cleared automatically if
TIMER_CTRL_INT_LEVEL is not set).
*/
#define TIMER_CTRL_INT_STATUS BIT(8)
/* WDT register(s)
Not fully understood yet. Writing 0 here disables wdt.
See ROM functions esp_wdt_xxx
*/
#define WDT_CTRL _REG(WDT_BASE, 0x00)
/* DPORT registers
Control various aspects of core/peripheral interaction... Not well
documented or understood.
*/
/* Set flags to enable CPU interrupts from some peripherals. Read/write.
bit 0 - Is set by RTOS SDK startup code but function is unknown.
bit 1 - INT_ENABLE_FRC1 allows TIMER FRC1 to trigger interrupt INUM_TIMER_FRC1.
bit 2 - INT_ENABLE_FRC2 allows TIMER FRC2 to trigger interrupt INUM_TIMER_FRC2.
Espressif calls this register "EDGE_INT_ENABLE_REG". The "edge" in
question is (I think) the interrupt line from the peripheral, as
the interrupt status bit is set. There may be a similar register
for enabling "level" interrupts instead of edge triggering
- this is unknown.
*/
#define DP_INT_ENABLE_REG _REG(DPORT_BASE, 0x04)
/* Set to enable interrupts from TIMER FRC1 */
#define INT_ENABLE_FRC1 BIT(1)
/* Set to enable interrupts interrupts from TIMER FRC2 */
#define INT_ENABLE_FRC2 BIT(2)
#endif #endif

View file

@ -11,12 +11,12 @@
#include <stdbool.h> #include <stdbool.h>
#include <xtensa_interrupts.h> #include <xtensa_interrupts.h>
#include "esp/registers.h" #include "esp/timer_regs.h"
#include "esp/cpu.h" #include "esp/cpu.h"
typedef enum { typedef enum {
TIMER_FRC1, FRC1 = 0,
TIMER_FRC2, FRC2 = 1,
} timer_frc_t; } timer_frc_t;
/* Return current count value for timer. */ /* Return current count value for timer. */
@ -31,14 +31,8 @@ INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load);
/* Returns maximum load value for timer. */ /* Returns maximum load value for timer. */
INLINED uint32_t timer_max_load(const timer_frc_t frc); INLINED uint32_t timer_max_load(const timer_frc_t frc);
typedef enum {
TIMER_DIV1,
TIMER_DIV16,
TIMER_DIV256,
} timer_div_t;
/* Set the timer divider value */ /* Set the timer divider value */
INLINED void timer_set_divider(const timer_frc_t frc, const timer_div_t div); INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div);
/* Enable or disable timer interrupts /* Enable or disable timer interrupts
@ -62,7 +56,7 @@ INLINED bool timer_get_reload(const timer_frc_t frc);
/* Return a suitable timer divider for the specified frequency, /* Return a suitable timer divider for the specified frequency,
or -1 if none is found. or -1 if none is found.
*/ */
INLINED timer_div_t timer_freq_to_div(uint32_t freq); INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq);
/* Return the number of timer counts to achieve the specified /* Return the number of timer counts to achieve the specified
* frequency with the specified divisor. * frequency with the specified divisor.
@ -73,12 +67,12 @@ INLINED timer_div_t timer_freq_to_div(uint32_t freq);
* *
* Compile-time evaluates if all arguments are available at compile time. * Compile-time evaluates if all arguments are available at compile time.
*/ */
INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_div_t div); INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_clkdiv_t div);
/* Return a suitable timer divider for the specified duration in /* Return a suitable timer divider for the specified duration in
microseconds or -1 if none is found. microseconds or -1 if none is found.
*/ */
INLINED timer_div_t timer_time_to_div(uint32_t us); INLINED timer_clkdiv_t timer_time_to_div(uint32_t us);
/* Return the number of timer counts for the specified timer duration /* Return the number of timer counts for the specified timer duration
* in microseconds, when using the specified divisor. * in microseconds, when using the specified divisor.
@ -89,7 +83,7 @@ INLINED timer_div_t timer_time_to_div(uint32_t us);
* *
* Compile-time evaluates if all arguments are available at compile time. * Compile-time evaluates if all arguments are available at compile time.
*/ */
INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_div_t div); INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div);
/* Set a target timer interrupt frequency in Hz. /* Set a target timer interrupt frequency in Hz.

View file

@ -10,6 +10,7 @@
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "esp/dport_regs.h"
/* Timer divisor index to max frequency */ /* Timer divisor index to max frequency */
#define _FREQ_DIV1 (80*1000*1000) #define _FREQ_DIV1 (80*1000*1000)
@ -20,104 +21,90 @@ const static uint32_t IROM _TIMER_FREQS[] = { _FREQ_DIV1, _FREQ_DIV16, _FREQ_DIV
/* Timer divisor index to divisor value */ /* Timer divisor index to divisor value */
const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 }; const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 };
/* Timer divisor to mask value */
const static uint32_t IROM _TIMER_DIV_REG[] = { TIMER_CTRL_DIV_1, TIMER_CTRL_DIV_16, TIMER_CTRL_DIV_256 };
INLINED esp_reg_t _timer_ctrl_reg(const timer_frc_t frc)
{
return (frc == TIMER_FRC1) ? &TIMER_FRC1_CTRL_REG : &TIMER_FRC2_CTRL_REG;
}
INLINED uint32_t timer_get_count(const timer_frc_t frc) INLINED uint32_t timer_get_count(const timer_frc_t frc)
{ {
return (frc == TIMER_FRC1) ? TIMER_FRC1_COUNT_REG : TIMER_FRC2_COUNT_REG; return TIMER(frc).COUNT;
} }
INLINED uint32_t timer_get_load(const timer_frc_t frc) INLINED uint32_t timer_get_load(const timer_frc_t frc)
{ {
return (frc == TIMER_FRC1) ? TIMER_FRC1_LOAD_REG : TIMER_FRC2_LOAD_REG; return TIMER(frc).LOAD;
} }
INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load) INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load)
{ {
if(frc == TIMER_FRC1) TIMER(frc).LOAD = load;
TIMER_FRC1_LOAD_REG = load;
else
TIMER_FRC2_LOAD_REG = load;
} }
INLINED uint32_t timer_max_load(const timer_frc_t frc) INLINED uint32_t timer_max_load(const timer_frc_t frc)
{ {
return (frc == TIMER_FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX; return (frc == FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX;
} }
INLINED void timer_set_divider(const timer_frc_t frc, const timer_div_t div) INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div)
{ {
if(div < TIMER_DIV1 || div > TIMER_DIV256) if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
return; return;
esp_reg_t ctrl = _timer_ctrl_reg(frc); TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div);
*ctrl = (*ctrl & ~TIMER_CTRL_DIV_MASK) | (_TIMER_DIV_REG[div] & TIMER_CTRL_DIV_MASK);
} }
INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable) INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable)
{ {
const uint32_t dp_bit = (frc == TIMER_FRC1) ? INT_ENABLE_FRC1 : INT_ENABLE_FRC2; const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2;
const uint32_t int_mask = BIT((frc == TIMER_FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2); const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2);
if(enable) { if(enable) {
DP_INT_ENABLE_REG |= dp_bit; DPORT.INT_ENABLE |= dp_bit;
_xt_isr_unmask(int_mask); _xt_isr_unmask(int_mask);
} else { } else {
DP_INT_ENABLE_REG &= ~dp_bit; DPORT.INT_ENABLE &= ~dp_bit;
_xt_isr_mask(int_mask); _xt_isr_mask(int_mask);
} }
} }
INLINED void timer_set_run(const timer_frc_t frc, const bool run) INLINED void timer_set_run(const timer_frc_t frc, const bool run)
{ {
esp_reg_t ctrl = _timer_ctrl_reg(frc);
if (run) if (run)
*ctrl |= TIMER_CTRL_RUN; TIMER(frc).CTRL |= TIMER_CTRL_RUN;
else else
*ctrl &= ~TIMER_CTRL_RUN; TIMER(frc).CTRL &= ~TIMER_CTRL_RUN;
} }
INLINED bool timer_get_run(const timer_frc_t frc) INLINED bool timer_get_run(const timer_frc_t frc)
{ {
return *_timer_ctrl_reg(frc) & TIMER_CTRL_RUN; return TIMER(frc).CTRL & TIMER_CTRL_RUN;
} }
INLINED void timer_set_reload(const timer_frc_t frc, const bool reload) INLINED void timer_set_reload(const timer_frc_t frc, const bool reload)
{ {
esp_reg_t ctrl = _timer_ctrl_reg(frc);
if (reload) if (reload)
*ctrl |= TIMER_CTRL_RELOAD; TIMER(frc).CTRL |= TIMER_CTRL_RELOAD;
else else
*ctrl &= ~TIMER_CTRL_RELOAD; TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD;
} }
INLINED bool timer_get_reload(const timer_frc_t frc) INLINED bool timer_get_reload(const timer_frc_t frc)
{ {
return *_timer_ctrl_reg(frc) & TIMER_CTRL_RELOAD; return TIMER(frc).CTRL & TIMER_CTRL_RELOAD;
} }
INLINED timer_div_t timer_freq_to_div(uint32_t freq) INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq)
{ {
/* /*
try to maintain resolution without risking overflows. try to maintain resolution without risking overflows.
these values are a bit arbitrary at the moment! */ these values are a bit arbitrary at the moment! */
if(freq > 100*1000) if(freq > 100*1000)
return TIMER_DIV1; return TIMER_CLKDIV_1;
else if(freq > 100) else if(freq > 100)
return TIMER_DIV16; return TIMER_CLKDIV_16;
else else
return TIMER_DIV256; return TIMER_CLKDIV_256;
} }
/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */ /* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */
INLINED uint32_t _timer_freq_to_count_impl(const timer_frc_t frc, const uint32_t freq, const timer_div_t div) INLINED uint32_t _timer_freq_to_count_impl(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
{ {
if(div < TIMER_DIV1 || div > TIMER_DIV256) if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
return 0; /* invalid divider */ return 0; /* invalid divider */
if(freq > _TIMER_FREQS[div]) if(freq > _TIMER_FREQS[div])
@ -127,9 +114,9 @@ INLINED uint32_t _timer_freq_to_count_impl(const timer_frc_t frc, const uint32_t
return counts; return counts;
} }
uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_div_t div); uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div);
INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_div_t div) INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
{ {
if(__builtin_constant_p(frc) && __builtin_constant_p(freq) && __builtin_constant_p(div)) if(__builtin_constant_p(frc) && __builtin_constant_p(freq) && __builtin_constant_p(div))
return _timer_freq_to_count_impl(frc, freq, div); return _timer_freq_to_count_impl(frc, freq, div);
@ -137,33 +124,33 @@ INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq,
return _timer_freq_to_count_runtime(frc, freq, div); return _timer_freq_to_count_runtime(frc, freq, div);
} }
INLINED timer_div_t timer_time_to_div(uint32_t us) INLINED timer_clkdiv_t timer_time_to_div(uint32_t us)
{ {
/* /*
try to maintain resolution without risking overflows. Similar to try to maintain resolution without risking overflows. Similar to
timer_freq_to_div, these values are a bit arbitrary at the timer_freq_to_div, these values are a bit arbitrary at the
moment! */ moment! */
if(us < 1000) if(us < 1000)
return TIMER_DIV1; return TIMER_CLKDIV_1;
else if(us < 10*1000) else if(us < 10*1000)
return TIMER_DIV16; return TIMER_CLKDIV_16;
else else
return TIMER_DIV256; return TIMER_CLKDIV_256;
} }
/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */ /* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */
INLINED uint32_t _timer_time_to_count_impl(const timer_frc_t frc, uint32_t us, const timer_div_t div) INLINED uint32_t _timer_time_to_count_impl(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
{ {
if(div < TIMER_DIV1 || div > TIMER_DIV256) if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
return 0; /* invalid divider */ return 0; /* invalid divider */
const uint32_t TIMER_MAX = timer_max_load(frc); const uint32_t TIMER_MAX = timer_max_load(frc);
if(div != TIMER_DIV256) /* timer tick in MHz */ if(div != TIMER_CLKDIV_256) /* timer tick in MHz */
{ {
/* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */ /* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */
const uint32_t counts_per_us = ((div == TIMER_DIV1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000; const uint32_t counts_per_us = ((div == TIMER_CLKDIV_1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000;
if(us > TIMER_MAX/counts_per_us) if(us > TIMER_MAX/counts_per_us)
return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */ return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */
return us*counts_per_us; return us*counts_per_us;
@ -186,9 +173,9 @@ INLINED uint32_t _timer_time_to_count_impl(const timer_frc_t frc, uint32_t us, c
} }
} }
uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_div_t div); uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div);
INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_div_t div) INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
{ {
if(__builtin_constant_p(frc) && __builtin_constant_p(us) && __builtin_constant_p(div)) if(__builtin_constant_p(frc) && __builtin_constant_p(us) && __builtin_constant_p(div))
return _timer_time_to_count_impl(frc, us, div); return _timer_time_to_count_impl(frc, us, div);
@ -201,7 +188,7 @@ INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const t
INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq) INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq)
{ {
uint32_t counts = 0; uint32_t counts = 0;
timer_div_t div = timer_freq_to_div(freq); timer_clkdiv_t div = timer_freq_to_div(freq);
counts = timer_freq_to_count(frc, freq, div); counts = timer_freq_to_count(frc, freq, div);
if(counts == 0) if(counts == 0)
@ -211,7 +198,7 @@ INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq)
} }
timer_set_divider(frc, div); timer_set_divider(frc, div);
if(frc == TIMER_FRC1) if(frc == FRC1)
{ {
timer_set_load(frc, counts); timer_set_load(frc, counts);
timer_set_reload(frc, true); timer_set_reload(frc, true);
@ -219,7 +206,7 @@ INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq)
else /* FRC2 */ else /* FRC2 */
{ {
/* assume that if this overflows it'll wrap, so we'll get desired behaviour */ /* assume that if this overflows it'll wrap, so we'll get desired behaviour */
TIMER_FRC2_MATCH_REG = counts + TIMER_FRC2_COUNT_REG; TIMER(1).ALARM = counts + TIMER(1).COUNT;
} }
return true; return true;
} }
@ -239,20 +226,20 @@ INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq)
INLINED bool _timer_set_timeout_impl(const timer_frc_t frc, uint32_t us) INLINED bool _timer_set_timeout_impl(const timer_frc_t frc, uint32_t us)
{ {
uint32_t counts = 0; uint32_t counts = 0;
timer_div_t div = timer_time_to_div(us); timer_clkdiv_t div = timer_time_to_div(us);
counts = timer_time_to_count(frc, us, div); counts = timer_time_to_count(frc, us, div);
if(counts == 0) if(counts == 0)
return false; /* can't set frequency */ return false; /* can't set frequency */
timer_set_divider(frc, div); timer_set_divider(frc, div);
if(frc == TIMER_FRC1) if(frc == FRC1)
{ {
timer_set_load(frc, counts); timer_set_load(frc, counts);
} }
else /* FRC2 */ else /* FRC2 */
{ {
TIMER_FRC2_MATCH_REG = counts + TIMER_FRC2_COUNT_REG; TIMER(1).ALARM = counts + TIMER(1).COUNT;
} }
return true; return true;

View file

@ -0,0 +1,125 @@
/* esp/timer_regs.h
*
* ESP8266 Timer register definitions
*
* Not compatible with ESP SDK register access code.
*/
#ifndef _ESP_TIMER_REGS_H
#define _ESP_TIMER_REGS_H
#include "esp/types.h"
#include "common_macros.h"
#define TIMER_BASE 0x60000600
#define TIMER(i) (*(struct TIMER_REGS *)(TIMER_BASE + (i)*0x20))
#define TIMER_FRC1 TIMER(0)
#define TIMER_FRC2 TIMER(1)
/* TIMER registers
*
* ESP8266 has two hardware timer counters, FRC1 and FRC2.
*
* FRC1 is a 24-bit countdown timer, triggers interrupt when reaches zero.
* FRC2 is a 32-bit countup timer, can set a variable match value to trigger an interrupt.
*
* FreeRTOS tick timer appears to come from XTensa core tick timer0,
* not either of these. FRC2 is used in the FreeRTOS SDK however. It
* is set to free-run, interrupting periodically via updates to the
* ALARM register. sdk_ets_timer_init configures FRC2 and assigns FRC2
* interrupt handler at sdk_vApplicationTickHook+0x68
*/
struct TIMER_REGS { // FRC1 FRC2
uint32_t volatile LOAD; // 0x00 0x20
uint32_t volatile COUNT; // 0x04 0x24
uint32_t volatile CTRL; // 0x08 0x28
uint32_t volatile STATUS; // 0x0c 0x2c
uint32_t volatile ALARM; // 0x30
} __attribute__ (( packed ));
_Static_assert(sizeof(struct TIMER_REGS) == 0x14, "TIMER_REGS is the wrong size");
#define TIMER_FRC1_MAX_LOAD 0x7fffff
/* Details for LOAD registers */
/* Behavior for FRC1:
*
* When TIMER_CTRL_RELOAD is cleared in TIMER(0).CTRL, FRC1 will
* reload to its max value once underflowed (unless the load
* value is rewritten in the interrupt handler.)
*
* When TIMER_CTRL_RELOAD is set in TIMER(0).CTRL, FRC1 will reload
* from the load register value once underflowed.
*
* Behavior for FRC2:
*
* If TIMER_CTRL_RELOAD is cleared in TIMER(1).CTRL, writing to
* this register will update the FRC2 COUNT value.
*
* If TIMER_CTRL_RELOAD is set in TIMER(1).CTRL, the behaviour
* appears to be the same except that writing 0 to the load register
* both sets the COUNT register to 0 and disables the timer, even if
* the TIMER_CTRL_RUN bit is set.
*
* Offsets 0x34, 0x38, 0x3c all seem to read back the LOAD_REG value
* also (but have no known function.)
*/
/* Details for CTRL registers */
/* Observed behaviour is like this:
*
* * When TIMER_CTRL_INT_HOLD is set, the interrupt status bit
* TIMER_CTRL_INT_STATUS remains set when the timer interrupt
* triggers, unless manually cleared by writing 0 to
* TIMER(x).STATUS. While the interrupt status bit stays set
* the timer will continue to run normally, but the interrupt
* (INUM_TIMER_FRC1 or INUM_TIMER_FRC2) won't trigger again.
*
* * When TIMER_CTRL_INT_HOLD is cleared (default), there's no need to
* manually write to TIMER(x).STATUS. The interrupt status bit
* TIMER_CTRL_INT_STATUS automatically clears after the interrupt
* triggers, and the interrupt handler will run again
* automatically.
*/
/* The values for TIMER_CTRL_CLKDIV control how many CPU clock cycles amount to
* one timer clock cycle. For valid values, see the timer_clkdiv_t enum below.
*/
/* TIMER_CTRL_INT_STATUS gets set when interrupt fires, and cleared on a write
* to TIMER(x).STATUS (or cleared automatically if TIMER_CTRL_INT_HOLD is not
* set).
*/
#define TIMER_CTRL_INT_HOLD BIT(0)
#define TIMER_CTRL_CLKDIV_M 0x00000003
#define TIMER_CTRL_CLKDIV_S 2
#define TIMER_CTRL_RELOAD BIT(6)
#define TIMER_CTRL_RUN BIT(7)
#define TIMER_CTRL_INT_STATUS BIT(8)
typedef enum {
TIMER_CLKDIV_1 = 0,
TIMER_CLKDIV_16 = 1,
TIMER_CLKDIV_256 = 2,
} timer_clkdiv_t;
/* Details for STATUS registers */
/* Reading this register always returns the value in
* TIMER(x).LOAD
*
* Writing zero to this register clears the FRC1
* interrupt status.
*/
/* Details for FRC2.ALARM register */
/* Interrupt match value for FRC2. When COUNT == ALARM,
the interrupt fires.
*/
#endif /* _ESP_TIMER_REGS_H */

8
core/include/esp/types.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _ESP_TYPES_H
#define _ESP_TYPES_H
#include <stdint.h>
typedef volatile uint32_t *esp_reg_t;
#endif /* _ESP_TYPES_H */

View file

@ -0,0 +1,38 @@
/* esp/wdt_regs.h
*
* ESP8266 Watchdog Timer register definitions
*
* Not compatible with ESP SDK register access code.
*/
#ifndef _ESP_WDT_REGS_H
#define _ESP_WDT_REGS_H
#include "esp/types.h"
#include "common_macros.h"
#define WDT_BASE 0x60000900
#define WDT (*(struct WDT_REGS *)(WDT_BASE))
/* WDT register(s)
Not fully understood yet. Writing 0 to CTRL disables WDT.
See ROM functions esp_wdt_xxx
*/
struct WDT_REGS {
uint32_t volatile CTRL; // 0x00
uint32_t volatile REG1; // 0x04
uint32_t volatile REG2; // 0x08
uint32_t volatile _unused[2]; // 0x0c - 0x10
uint32_t volatile FEED; // 0x14
} __attribute__ (( packed ));
_Static_assert(sizeof(struct WDT_REGS) == 0x18, "WDT_REGS is the wrong size");
/* Writing WDT_FEED_MAGIC to WDT.FEED register "feeds the dog" and holds off
* triggering for another cycle (unconfirmed) */
#define WDT_FEED_MAGIC 0x73
#endif /* _ESP_WDT_REGS_H */

View file

@ -30,8 +30,8 @@ void blinkenTask(void *pvParameters)
/* This task uses all raw register operations to set the pins. /* This task uses all raw register operations to set the pins.
It's not fully parameterised, as the IOMUX_SET macro requires the pin number It's not fully parameterised, as the IOMUX_GPIO# macros involve a non-linear
as part of the GPxx value. mapping from GPIO to IOMUX ports.
There is no significant performance benefit to this way over the There is no significant performance benefit to this way over the
blinkenTask version, so it's probably better to use the blinkenTask blinkenTask version, so it's probably better to use the blinkenTask
@ -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_SET(GP14,GPIO,IOMUX_OE); /* 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);
} }
} }

View file

@ -26,7 +26,7 @@ void frc1_interrupt_handler(void)
void frc2_interrupt_handler(void) void frc2_interrupt_handler(void)
{ {
/* FRC2 needs the match register updated on each timer interrupt */ /* FRC2 needs the match register updated on each timer interrupt */
timer_set_frequency(TIMER_FRC2, freq_frc2); timer_set_frequency(FRC2, freq_frc2);
frc2_count++; frc2_count++;
gpio_toggle(gpio_frc2); gpio_toggle(gpio_frc2);
} }
@ -41,24 +41,24 @@ void user_init(void)
gpio_write(gpio_frc1, 1); gpio_write(gpio_frc1, 1);
/* stop both timers and mask their interrupts as a precaution */ /* stop both timers and mask their interrupts as a precaution */
timer_set_interrupts(TIMER_FRC1, false); timer_set_interrupts(FRC1, false);
timer_set_run(TIMER_FRC1, false); timer_set_run(FRC1, false);
timer_set_interrupts(TIMER_FRC2, false); timer_set_interrupts(FRC2, false);
timer_set_run(TIMER_FRC2, false); timer_set_run(FRC2, false);
/* set up ISRs */ /* set up ISRs */
_xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler); _xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler);
_xt_isr_attach(INUM_TIMER_FRC2, frc2_interrupt_handler); _xt_isr_attach(INUM_TIMER_FRC2, frc2_interrupt_handler);
/* configure timer frequencies */ /* configure timer frequencies */
timer_set_frequency(TIMER_FRC1, freq_frc1); timer_set_frequency(FRC1, freq_frc1);
timer_set_frequency(TIMER_FRC2, freq_frc2); timer_set_frequency(FRC2, freq_frc2);
/* unmask interrupts and start timers */ /* unmask interrupts and start timers */
timer_set_interrupts(TIMER_FRC1, true); timer_set_interrupts(FRC1, true);
timer_set_run(TIMER_FRC1, true); timer_set_run(FRC1, true);
timer_set_interrupts(TIMER_FRC2, true); timer_set_interrupts(FRC2, true);
timer_set_run(TIMER_FRC2, true); timer_set_run(FRC2, true);
gpio_write(gpio_frc1, 0); gpio_write(gpio_frc1, 0);
} }

View file

@ -15,7 +15,7 @@
/* pin config */ /* pin config */
const int gpio = 0; /* gpio 0 usually has "PROGRAM" button attached */ const int gpio = 0; /* gpio 0 usually has "PROGRAM" button attached */
const int active = 0; /* active == 0 for active low */ const int active = 0; /* active == 0 for active low */
const gpio_interrupt_t int_type = INT_FALLING; const gpio_inttype_t int_type = GPIO_INTTYPE_EDGE_NEG;
#define GPIO_HANDLER gpio00_interrupt_handler #define GPIO_HANDLER gpio00_interrupt_handler

View file

@ -11,14 +11,15 @@
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "esp8266.h" #include "esp8266.h"
#include "common_macros.h"
#define DUMP_SZ 0x10 /* number of regs not size of buffer */ #define DUMP_SZ 0x10 /* number of regs not size of buffer */
IRAM void dump_frc1_seq(void) IRAM void dump_frc1_seq(void)
{ {
uint32_t f1_a = TIMER_FRC1_COUNT_REG; uint32_t f1_a = TIMER(0).COUNT;
uint32_t f1_b = TIMER_FRC1_COUNT_REG; uint32_t f1_b = TIMER(0).COUNT;
uint32_t f1_c = TIMER_FRC1_COUNT_REG; uint32_t f1_c = TIMER(0).COUNT;
printf("FRC1 sequence 0x%08lx 0x%08lx 0x%08lx\r\n", f1_a, f1_b, f1_c); printf("FRC1 sequence 0x%08lx 0x%08lx 0x%08lx\r\n", f1_a, f1_b, f1_c);
printf("FRC1 deltas %ld %ld \r\n", f1_b-f1_a, f1_c-f1_b); printf("FRC1 deltas %ld %ld \r\n", f1_b-f1_a, f1_c-f1_b);
} }
@ -33,9 +34,9 @@ IRAM void dump_frc2_seq(void)
* /16 = 0 or 1 (usually 1) * /16 = 0 or 1 (usually 1)
* *
*/ */
uint32_t f2_a = TIMER_FRC2_COUNT_REG; uint32_t f2_a = TIMER(1).COUNT;
uint32_t f2_b = TIMER_FRC2_COUNT_REG; uint32_t f2_b = TIMER(1).COUNT;
uint32_t f2_c = TIMER_FRC2_COUNT_REG; uint32_t f2_c = TIMER(1).COUNT;
printf("FRC2 sequence 0x%08lx 0x%08lx 0x%08lx\r\n", f2_a, f2_b, f2_c); printf("FRC2 sequence 0x%08lx 0x%08lx 0x%08lx\r\n", f2_a, f2_b, f2_c);
printf("FRC2 deltas %ld %ld \r\n", f2_b-f2_a, f2_c-f2_b); printf("FRC2 deltas %ld %ld \r\n", f2_b-f2_a, f2_c-f2_b);
} }
@ -99,20 +100,20 @@ void timerRegTask(void *pvParameters)
IRAM void frc1_handler(void) IRAM void frc1_handler(void)
{ {
frc1_handler_call_count++; frc1_handler_call_count++;
frc1_last_count_val = TIMER_FRC1_COUNT_REG; frc1_last_count_val = TIMER(0).COUNT;
//TIMER_FRC1_LOAD_REG = 0x300000; //TIMER(0).LOAD = 0x300000;
//TIMER_FRC1_CLEAR_INT = 0; //TIMER(0).STATUS = 0;
//TIMER_FRC1_MATCH_REG = frc1_last_count_val + 0x100000; //TIMER_FRC1_MATCH_REG = frc1_last_count_val + 0x100000;
} }
void frc2_handler(void) void frc2_handler(void)
{ {
frc2_handler_call_count++; frc2_handler_call_count++;
frc2_last_count_val = TIMER_FRC2_COUNT_REG; frc2_last_count_val = TIMER(1).COUNT;
TIMER_FRC2_MATCH_REG = frc2_last_count_val + 0x100000; TIMER(1).ALARM = frc2_last_count_val + 0x100000;
//TIMER_FRC2_LOAD_REG = 0; //TIMER(1).LOAD = 0;
//TIMER_FRC2_LOAD_REG = 0x2000000; //TIMER(1).LOAD = 0x2000000;
//TIMER_FRC2_CLEAR_INT_REG = 0; //TIMER(1).STATUS = 0;
} }
void user_init(void) void user_init(void)
@ -120,19 +121,19 @@ void user_init(void)
sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); sdk_uart_div_modify(0, UART_CLK_FREQ / 115200);
xTaskCreate(timerRegTask, (signed char *)"timerRegTask", 1024, NULL, 2, NULL); xTaskCreate(timerRegTask, (signed char *)"timerRegTask", 1024, NULL, 2, NULL);
TIMER_FRC1_CTRL_REG = TIMER_CTRL_DIV_256|TIMER_CTRL_INT_EDGE|TIMER_CTRL_RELOAD; TIMER(0).CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_256) | TIMER_CTRL_RELOAD;
TIMER_FRC1_LOAD_REG = 0x200000; TIMER(0).LOAD = 0x200000;
TIMER_FRC2_CTRL_REG = TIMER_CTRL_DIV_256|TIMER_CTRL_INT_EDGE; TIMER(1).LOAD = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_256);
DP_INT_ENABLE_REG |= INT_ENABLE_FRC1|INT_ENABLE_FRC2; DPORT.INT_ENABLE |= DPORT_INT_ENABLE_TIMER0 | DPORT_INT_ENABLE_TIMER1;
_xt_isr_attach(INUM_TIMER_FRC1, frc1_handler); _xt_isr_attach(INUM_TIMER_FRC1, frc1_handler);
_xt_isr_unmask(1<<INUM_TIMER_FRC1); _xt_isr_unmask(1<<INUM_TIMER_FRC1);
_xt_isr_attach(INUM_TIMER_FRC2, frc2_handler); _xt_isr_attach(INUM_TIMER_FRC2, frc2_handler);
_xt_isr_unmask(1<<INUM_TIMER_FRC2); _xt_isr_unmask(1<<INUM_TIMER_FRC2);
TIMER_FRC1_CTRL_REG |= TIMER_CTRL_RUN; TIMER(0).CTRL |= TIMER_CTRL_RUN;
TIMER_FRC2_CTRL_REG |= TIMER_CTRL_RUN; TIMER(1).CTRL |= TIMER_CTRL_RUN;
dump_timer_regs("timer regs during user_init"); dump_timer_regs("timer regs during user_init");
dump_timer_regs("#2 timer regs during user_init"); dump_timer_regs("#2 timer regs during user_init");