Use this forum to chat about hardware specific topics for the ESP8266 (peripherals, memory, clocks, JTAG, programming)

User avatar
By 3pei
#62760
3pei wrote:
dkinzer wrote:
Atlantis wrote:Can anyone explain to me how to set up hardware timer, to be executed after passing given amount of time, ranging from few hundreds of microseconds, to few milliseconds?
You can get intervals of microsecond resolution using os_timer_arm_us() as described in the SDK documentation. The basic idea is that you specify a callback function and then arm the timer. The callback function will be invoked not sooner than the specified time (but it could be later). There may be (probably will be) jitter with this setup, meaning that the invocations won't necessarily be evenly spaced.

Another technique is to configure RTC Timer1 to generate an interrupt at a desired rate. The resolution with this technique is 200nS (using the divide-by-1 prescaler). The code below shows how to do this. I haven't been able to get the NMI capability to work but Espressif support folk ensure me that it works. If you use the regular interrupt (i.e. *not* NMI) there may be some jitter. Even if you do use NMI there may still be jitter but I would expect it to be less.
Code: Select all// definitions for RTC Timer1
#define TIMER1_DIVIDE_BY_1              0x0000
#define TIMER1_DIVIDE_BY_16             0x0004
#define TIMER1_DIVIDE_BY_256            0x0008

#define TIMER1_AUTO_LOAD                0x0040
#define TIMER1_ENABLE_TIMER             0x0080
#define TIMER1_FLAGS_MASK               0x00cc

#define TIMER1_NMI                      0x8000

#define TIMER1_COUNT_MASK               0x007fffff        // 23 bit timer

void ICACHE_FLASH_ATTR
timer1Start(uint32_t ticks, uint16_t flags, void (*handler)(void))
{
    RTC_REG_WRITE(FRC1_LOAD_ADDRESS, ticks & TIMER1_COUNT_MASK);
    RTC_REG_WRITE(FRC1_CTRL_ADDRESS, (flags & TIMER1_FLAGS_MASK) | TIMER1_ENABLE_TIMER);
    RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK);
    if (handler != NULL)
    {
        if (flags & TIMER1_NMI)
            ETS_FRC_TIMER1_NMI_INTR_ATTACH(handler);
        else
            ETS_FRC_TIMER1_INTR_ATTACH((void (*)(void *))handler, NULL);
        TM1_EDGE_INT_ENABLE();
        ETS_FRC1_INTR_ENABLE();
    }
}

void ICACHE_FLASH_ATTR
timer1Stop(void)
{
    ETS_FRC1_INTR_DISABLE();
    TM1_EDGE_INT_DISABLE();
    RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
}

It is important to know that RTC Timer1 is also used by the PWM routines - these two uses are mutually exclusive.


Can anybody help to confirm that we can get the 200nS resolution?

I did the same thing, but when I set the interval less than 10us, the interrupt handler is not repeated as it has been set, or some calls are missed. The handler code doesn't have much thing todo, it just have some simple calculation and a GPIO ouput. I have tried DIVIDED_BY_1 but it makes nothing different.


OK, answer it myself.

One gpio read + one gpio status reg read + one gpio status clearing cost 50 cycles, and other instructions used by SDK cost about 100 cycles before entering the interrupt handler. these totally cost about 150 cycles ~= 1875ns. And to achieve this result, we need use the a clock source divided by 1.

that' all we can get. Maybe we could do better by replace vectors at lower level, which I haven't try yet.