Merge pull request #27 from foogod/issue_8
Convert headers to new *_regs.h format
This commit is contained in:
commit
aa17221929
18 changed files with 659 additions and 565 deletions
|
@ -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]();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
* the arguments aren't known at compile time (values are evaluated at
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
#define BIT(X) (1<<(X))
|
||||
#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
|
||||
of having them loaded into rodata (which resides in DRAM)
|
||||
|
||||
|
|
53
core/include/esp/dport_regs.h
Normal file
53
core/include/esp/dport_regs.h
Normal 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 */
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef _ESP_GPIO_H
|
||||
#define _ESP_GPIO_H
|
||||
#include <stdbool.h>
|
||||
#include "esp/registers.h"
|
||||
#include "esp/gpio_regs.h"
|
||||
#include "esp/iomux.h"
|
||||
#include "esp/cpu.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) {
|
||||
case GPIO_INPUT:
|
||||
iomux_flags = 0;
|
||||
ctrl_val = GPIO_SOURCE_GPIO;
|
||||
ctrl_val = 0;
|
||||
break;
|
||||
case GPIO_OUTPUT:
|
||||
iomux_flags = IOMUX_OE;
|
||||
ctrl_val = GPIO_DRIVE_PUSH_PULL|GPIO_SOURCE_GPIO;
|
||||
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
|
||||
ctrl_val = GPIO_CONF_PUSH_PULL;
|
||||
break;
|
||||
case GPIO_OUT_OPEN_DRAIN:
|
||||
iomux_flags = IOMUX_OE;
|
||||
ctrl_val = GPIO_DRIVE_OPEN_DRAIN|GPIO_SOURCE_GPIO;
|
||||
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
|
||||
ctrl_val = 0;
|
||||
break;
|
||||
case GPIO_INPUT_PULLUP:
|
||||
iomux_flags = IOMUX_PU;
|
||||
ctrl_val = GPIO_SOURCE_GPIO;
|
||||
iomux_flags = IOMUX_PIN_PULLUP;
|
||||
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.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
GPIO_DIR_CLEAR = BIT(gpio_num);
|
||||
*gpio_iomux_reg(gpio_num) &= ~IOMUX_OE;
|
||||
GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
|
||||
*gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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<<INUM_GPIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
|
130
core/include/esp/gpio_regs.h
Normal file
130
core/include/esp/gpio_regs.h
Normal 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 */
|
|
@ -8,8 +8,8 @@
|
|||
*/
|
||||
#ifndef _ESP_IOMUX_H
|
||||
#define _ESP_IOMUX_H
|
||||
#include <stdint.h>
|
||||
#include "esp/registers.h"
|
||||
#include "esp/types.h"
|
||||
#include "esp/iomux_regs.h"
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
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
|
||||
* 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.
|
||||
* ie the following are equivalent:
|
||||
*
|
||||
* iomux_set_gpio_function(12, IOMUX_OE);
|
||||
* IOMUX_GP12 = (IOMUX_GP12 & ~IOMUX_FUNC_MASK) | IOMUX_GP12_GPIO | IOMUX_OE;
|
||||
* iomux_set_gpio_function(12, IOMUX_PIN_OUTPUT_ENABLE);
|
||||
* 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 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;
|
||||
const uint32_t func = (reg_idx > 11 ? IOMUX_FUNC(0) : IOMUX_FUNC(3)) | flags;
|
||||
IOMUX.PIN[reg_idx] = func | flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"
|
||||
|
|
151
core/include/esp/iomux_regs.h
Normal file
151
core/include/esp/iomux_regs.h
Normal 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 */
|
|
@ -14,295 +14,33 @@
|
|||
#ifndef _ESP_REGISTERS
|
||||
#define _ESP_REGISTERS
|
||||
#include "common_macros.h"
|
||||
#include "esp/types.h"
|
||||
|
||||
typedef volatile uint32_t *esp_reg_t;
|
||||
|
||||
/* Internal macro, only defined in header body */
|
||||
#define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET)))
|
||||
#include "esp/iomux_regs.h"
|
||||
#include "esp/gpio_regs.h"
|
||||
#include "esp/timer_regs.h"
|
||||
#include "esp/wdt_regs.h"
|
||||
#include "esp/dport_regs.h"
|
||||
|
||||
/* Register base addresses
|
||||
|
||||
You shouldn't need to use these directly.
|
||||
*/
|
||||
#define MMIO_BASE 0x60000000
|
||||
#define DPORT_BASE 0x3ff00000
|
||||
//#define DPORT_BASE 0x3ff00000
|
||||
|
||||
#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 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 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) _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
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <xtensa_interrupts.h>
|
||||
#include "esp/registers.h"
|
||||
#include "esp/timer_regs.h"
|
||||
#include "esp/cpu.h"
|
||||
|
||||
typedef enum {
|
||||
TIMER_FRC1,
|
||||
TIMER_FRC2,
|
||||
FRC1 = 0,
|
||||
FRC2 = 1,
|
||||
} timer_frc_t;
|
||||
|
||||
/* 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. */
|
||||
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 */
|
||||
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
|
||||
|
||||
|
@ -62,7 +56,7 @@ INLINED bool timer_get_reload(const timer_frc_t frc);
|
|||
/* Return a suitable timer divider for the specified frequency,
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp/dport_regs.h"
|
||||
|
||||
/* Timer divisor index to max frequency */
|
||||
#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 */
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(frc == TIMER_FRC1)
|
||||
TIMER_FRC1_LOAD_REG = load;
|
||||
else
|
||||
TIMER_FRC2_LOAD_REG = load;
|
||||
TIMER(frc).LOAD = load;
|
||||
}
|
||||
|
||||
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;
|
||||
esp_reg_t ctrl = _timer_ctrl_reg(frc);
|
||||
*ctrl = (*ctrl & ~TIMER_CTRL_DIV_MASK) | (_TIMER_DIV_REG[div] & TIMER_CTRL_DIV_MASK);
|
||||
TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div);
|
||||
}
|
||||
|
||||
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 int_mask = BIT((frc == TIMER_FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2);
|
||||
const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2;
|
||||
const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2);
|
||||
if(enable) {
|
||||
DP_INT_ENABLE_REG |= dp_bit;
|
||||
DPORT.INT_ENABLE |= dp_bit;
|
||||
_xt_isr_unmask(int_mask);
|
||||
} else {
|
||||
DP_INT_ENABLE_REG &= ~dp_bit;
|
||||
DPORT.INT_ENABLE &= ~dp_bit;
|
||||
_xt_isr_mask(int_mask);
|
||||
}
|
||||
}
|
||||
|
||||
INLINED void timer_set_run(const timer_frc_t frc, const bool run)
|
||||
{
|
||||
esp_reg_t ctrl = _timer_ctrl_reg(frc);
|
||||
if (run)
|
||||
*ctrl |= TIMER_CTRL_RUN;
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RUN;
|
||||
else
|
||||
*ctrl &= ~TIMER_CTRL_RUN;
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RUN;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
esp_reg_t ctrl = _timer_ctrl_reg(frc);
|
||||
if (reload)
|
||||
*ctrl |= TIMER_CTRL_RELOAD;
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RELOAD;
|
||||
else
|
||||
*ctrl &= ~TIMER_CTRL_RELOAD;
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD;
|
||||
}
|
||||
|
||||
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.
|
||||
these values are a bit arbitrary at the moment! */
|
||||
if(freq > 100*1000)
|
||||
return TIMER_DIV1;
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(freq > 100)
|
||||
return TIMER_DIV16;
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_DIV256;
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
timer_freq_to_div, these values are a bit arbitrary at the
|
||||
moment! */
|
||||
if(us < 1000)
|
||||
return TIMER_DIV1;
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(us < 10*1000)
|
||||
return TIMER_DIV16;
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_DIV256;
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
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 */
|
||||
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)
|
||||
return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */
|
||||
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))
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
if(frc == TIMER_FRC1)
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
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 */
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
if(counts == 0)
|
||||
return false; /* can't set frequency */
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == TIMER_FRC1)
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
TIMER_FRC2_MATCH_REG = counts + TIMER_FRC2_COUNT_REG;
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
125
core/include/esp/timer_regs.h
Normal file
125
core/include/esp/timer_regs.h
Normal 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
8
core/include/esp/types.h
Normal 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 */
|
38
core/include/esp/wdt_regs.h
Normal file
38
core/include/esp/wdt_regs.h
Normal 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 */
|
|
@ -30,8 +30,8 @@ void blinkenTask(void *pvParameters)
|
|||
|
||||
/* 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
|
||||
as part of the GPxx value.
|
||||
It's not fully parameterised, as the IOMUX_GPIO# macros involve a non-linear
|
||||
mapping from GPIO to IOMUX ports.
|
||||
|
||||
There is no significant performance benefit to this way over the
|
||||
blinkenTask version, so it's probably better to use the blinkenTask
|
||||
|
@ -41,12 +41,12 @@ void blinkenTask(void *pvParameters)
|
|||
*/
|
||||
void blinkenRegisterTask(void *pvParameters)
|
||||
{
|
||||
GPIO_DIR_SET = BIT(gpio);
|
||||
IOMUX_SET(GP14,GPIO,IOMUX_OE); /* change this line if you change '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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ void frc1_interrupt_handler(void)
|
|||
void frc2_interrupt_handler(void)
|
||||
{
|
||||
/* 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++;
|
||||
gpio_toggle(gpio_frc2);
|
||||
}
|
||||
|
@ -41,24 +41,24 @@ void user_init(void)
|
|||
gpio_write(gpio_frc1, 1);
|
||||
|
||||
/* stop both timers and mask their interrupts as a precaution */
|
||||
timer_set_interrupts(TIMER_FRC1, false);
|
||||
timer_set_run(TIMER_FRC1, false);
|
||||
timer_set_interrupts(TIMER_FRC2, false);
|
||||
timer_set_run(TIMER_FRC2, false);
|
||||
timer_set_interrupts(FRC1, false);
|
||||
timer_set_run(FRC1, false);
|
||||
timer_set_interrupts(FRC2, false);
|
||||
timer_set_run(FRC2, false);
|
||||
|
||||
/* set up ISRs */
|
||||
_xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler);
|
||||
_xt_isr_attach(INUM_TIMER_FRC2, frc2_interrupt_handler);
|
||||
|
||||
/* configure timer frequencies */
|
||||
timer_set_frequency(TIMER_FRC1, freq_frc1);
|
||||
timer_set_frequency(TIMER_FRC2, freq_frc2);
|
||||
timer_set_frequency(FRC1, freq_frc1);
|
||||
timer_set_frequency(FRC2, freq_frc2);
|
||||
|
||||
/* unmask interrupts and start timers */
|
||||
timer_set_interrupts(TIMER_FRC1, true);
|
||||
timer_set_run(TIMER_FRC1, true);
|
||||
timer_set_interrupts(TIMER_FRC2, true);
|
||||
timer_set_run(TIMER_FRC2, true);
|
||||
timer_set_interrupts(FRC1, true);
|
||||
timer_set_run(FRC1, true);
|
||||
timer_set_interrupts(FRC2, true);
|
||||
timer_set_run(FRC2, true);
|
||||
|
||||
gpio_write(gpio_frc1, 0);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
/* pin config */
|
||||
const int gpio = 0; /* gpio 0 usually has "PROGRAM" button attached */
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "esp8266.h"
|
||||
#include "common_macros.h"
|
||||
|
||||
#define DUMP_SZ 0x10 /* number of regs not size of buffer */
|
||||
|
||||
IRAM void dump_frc1_seq(void)
|
||||
{
|
||||
uint32_t f1_a = TIMER_FRC1_COUNT_REG;
|
||||
uint32_t f1_b = TIMER_FRC1_COUNT_REG;
|
||||
uint32_t f1_c = TIMER_FRC1_COUNT_REG;
|
||||
uint32_t f1_a = TIMER(0).COUNT;
|
||||
uint32_t f1_b = TIMER(0).COUNT;
|
||||
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 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)
|
||||
*
|
||||
*/
|
||||
uint32_t f2_a = TIMER_FRC2_COUNT_REG;
|
||||
uint32_t f2_b = TIMER_FRC2_COUNT_REG;
|
||||
uint32_t f2_c = TIMER_FRC2_COUNT_REG;
|
||||
uint32_t f2_a = TIMER(1).COUNT;
|
||||
uint32_t f2_b = TIMER(1).COUNT;
|
||||
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 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)
|
||||
{
|
||||
frc1_handler_call_count++;
|
||||
frc1_last_count_val = TIMER_FRC1_COUNT_REG;
|
||||
//TIMER_FRC1_LOAD_REG = 0x300000;
|
||||
//TIMER_FRC1_CLEAR_INT = 0;
|
||||
frc1_last_count_val = TIMER(0).COUNT;
|
||||
//TIMER(0).LOAD = 0x300000;
|
||||
//TIMER(0).STATUS = 0;
|
||||
//TIMER_FRC1_MATCH_REG = frc1_last_count_val + 0x100000;
|
||||
}
|
||||
|
||||
void frc2_handler(void)
|
||||
{
|
||||
frc2_handler_call_count++;
|
||||
frc2_last_count_val = TIMER_FRC2_COUNT_REG;
|
||||
TIMER_FRC2_MATCH_REG = frc2_last_count_val + 0x100000;
|
||||
//TIMER_FRC2_LOAD_REG = 0;
|
||||
//TIMER_FRC2_LOAD_REG = 0x2000000;
|
||||
//TIMER_FRC2_CLEAR_INT_REG = 0;
|
||||
frc2_last_count_val = TIMER(1).COUNT;
|
||||
TIMER(1).ALARM = frc2_last_count_val + 0x100000;
|
||||
//TIMER(1).LOAD = 0;
|
||||
//TIMER(1).LOAD = 0x2000000;
|
||||
//TIMER(1).STATUS = 0;
|
||||
}
|
||||
|
||||
void user_init(void)
|
||||
|
@ -120,19 +121,19 @@ void user_init(void)
|
|||
sdk_uart_div_modify(0, UART_CLK_FREQ / 115200);
|
||||
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_FRC1_LOAD_REG = 0x200000;
|
||||
TIMER(0).CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_256) | TIMER_CTRL_RELOAD;
|
||||
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_unmask(1<<INUM_TIMER_FRC1);
|
||||
_xt_isr_attach(INUM_TIMER_FRC2, frc2_handler);
|
||||
_xt_isr_unmask(1<<INUM_TIMER_FRC2);
|
||||
|
||||
TIMER_FRC1_CTRL_REG |= TIMER_CTRL_RUN;
|
||||
TIMER_FRC2_CTRL_REG |= TIMER_CTRL_RUN;
|
||||
TIMER(0).CTRL |= TIMER_CTRL_RUN;
|
||||
TIMER(1).CTRL |= TIMER_CTRL_RUN;
|
||||
|
||||
dump_timer_regs("timer regs during user_init");
|
||||
dump_timer_regs("#2 timer regs during user_init");
|
||||
|
|
Loading…
Reference in a new issue