Added esp/timer_regs.h and esp/dport_regs.h
This commit is contained in:
parent
3cc5d1fa86
commit
eaa090e267
6 changed files with 234 additions and 227 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
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 */
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "esp/iomux_regs.h"
|
#include "esp/iomux_regs.h"
|
||||||
#include "esp/gpio_regs.h"
|
#include "esp/gpio_regs.h"
|
||||||
|
#include "esp/timer_regs.h"
|
||||||
|
#include "esp/dport_regs.h"
|
||||||
|
|
||||||
/* Internal macro, only defined in header body */
|
/* Internal macro, only defined in header body */
|
||||||
#define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET)))
|
#define _REG(BASE, OFFSET) (*(esp_reg_t)((BASE)+(OFFSET)))
|
||||||
|
@ -27,13 +29,13 @@
|
||||||
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)
|
||||||
|
@ -43,135 +45,6 @@
|
||||||
#define RTCS_BASE (MMIO_BASE + 0x1100)
|
#define RTCS_BASE (MMIO_BASE + 0x1100)
|
||||||
#define RTCU_BASE (MMIO_BASE + 0x1200)
|
#define RTCU_BASE (MMIO_BASE + 0x1200)
|
||||||
|
|
||||||
/* 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)
|
/* WDT register(s)
|
||||||
|
|
||||||
Not fully understood yet. Writing 0 here disables wdt.
|
Not fully understood yet. Writing 0 here disables wdt.
|
||||||
|
@ -180,29 +53,4 @@
|
||||||
*/
|
*/
|
||||||
#define WDT_CTRL _REG(WDT_BASE, 0x00)
|
#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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
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 */
|
Loading…
Reference in a new issue