Lua 中 ESP8266 的亚毫秒计时器

物联网 ESP8266
2021-06-18 01:28:38

我正在尝试在由服务器控制的 ESP8266 上制作远程控制的伺服电机控制器。我面临的问题是如何制作一个异步计时器,比如tmr.alarm(),但以微秒为单位。tmr.delay()效果不是很好,因为它会停止其他一切并且不太准确。你可以让它在 Arduino 上工作,但是如何在 Lua 中实现它呢?

2个回答

我认为您可能很难通过 ESP8266获得既准确无阻塞的微秒延迟

根据NodeMCU 文档

如果您查看app/modules/tmr.c此函数代码,您将看到它执行了一个低级别的 ets_delay_us(delay)。此函数不是 NodeMCU 代码或 SDK 的一部分;它实际上是xtensa-lx106引导 ROM 的一部分,是一个简单的定时循环,它轮询内部 CPU 时钟。它在禁用中断的情况下执行此操作,因为如果启用它们,则无法保证延迟将符合要求。

tmr.delay()真正用于需要对外部硬件 I/O 进行更精确定时控制的地方(例如,将 GPIO 引脚提升 20 微秒)。在几乎所有其他用例中,它都不会实现任何功能目的,因为任何其他基于系统代码的活动都将被阻止执行;在最坏的情况下,它会破坏您的应用程序并产生难以诊断的超时错误。

似乎在这种情况下必须禁用中断,因为如果中断确​​实发生在中间延迟时间很短(大约几微秒),则中断处理程序将占用比整个延迟更多的时间成为。

假设您想要一个 20 微秒的计时器,并且在大约 10 微秒时发生中断。如果处理程序花费的时间超过 10 μs,则您将已经超过了预期的 20 μs 延迟。

因此,我们可以排除tmr.delay()您是否确实需要中断工作。

我做了更多的挖掘,显然 ESP8266 确实支持微秒计时器ets_timer_arm_new(),因为最后一个参数为零。但是,NodeMCU将此值设置为 1,使用毫秒精度这篇文章似乎支持这个想法:

如果需要获取两次gpio中断的时间间隔,可以使用system api system_get_time()来计算相对时间。并调用 os_timer_arm_us。

如果您愿意尝试编辑和重建固件,可能值得一试。虽然,有一个功能请求,但被拒绝了:

所以我测试了纳秒计时器,并且不能建立小于 1000us 的间隔(使用编译和剥离的代码,在 160MHz CPU 模式下我得到类似 800us 的时间)。这是提供新(大部分不可用)功能的案例吗?
- djphoenix

不可行的 ATM -> 关闭。
-马塞尔斯托

我已经设法在启用我们的计时器的情况下重新编译 NodeMCU 固件:

  • 安装 Marcel Stör 的 docker 构建环境:https ://hub.docker.com/r/marcelstoer/nodemcu-build/

  • 更改固件目录中的固件文件(例如./user/nodemcu-firmware

    1. ./app/user/user_main.c

      void user_init(void)
      {
      

在此处添加以下行: system_timer_reinit();

  1. ./sdk-overrides/osapi.h 添加在线上方 #include_next "osapi.h": #define USE_US_TIMER

  2. ./app/modules/tmr.c->static int tmr_start(lua_State* L){ 改变:os_timer_arm->os_timer_arm_us

  3. ./app/modules/tmr.c->static int tmr_interval(lua_State* L){ 改变:os_timer_arm->os_timer_arm_us

  4. ./app/modules/tmr.c:离开os_timer_armint luaopen_tmr( lua_State *L ){原样,否则你将时可以获得看门狗复位启动

    • 重新编译固件,刷入你的 ESP8266

当 CPU 以 160MHz 运行时,我设法以 8.3kHz(125uS 的定时器延迟)对 ADC 进行采样。如果我走得更快,看门狗就会启动。

代码:

    local mytimer2 = tmr.create()
    local count = 0
    local count2 = 0
    local adc_read = adc.read
    mytimer2:register(125, 1, function (t2) 
        count = count + 1; count2 = count2 + 1
        local adc_v = adc_read(0) 
        if (count2 == 500) then 
            count2 = 0
        end
        if count == 100000 then
            mytimer2:stop()
            print("Time at end: "..tmr.time())
            print("Counter: "..count)
        end
    end)
    print("Time at start: "..tmr.time())
    mytimer2:start()

输出:

开始时间:1

结束时间:13

计数器:100000

12 秒内读取 100.000 次。