我正在尝试在由服务器控制的ESP8266上制作一个远程控制的伺服电机控制器。
我面临的问题是如何制作一个异步计时器,如tmr.alarm(),但要以微秒为单位。 tmr.delay()不能很好地工作,因为它会停止所有其他操作并且不够精确。
您可以在Arduino上使用它,但是如何在Lua中实现呢?

#1 楼

我认为您可能会难以获得与ESP8266一致且无阻塞的微秒延迟。
根据NodeMCU文档:

如果您查看此功能的app/modules/tmr.c代码, ,那么您会看到它执行了一个低级别的ets_delay_us(delay)。该功能不是NodeMCU代码或SDK的一部分;它实际上是xtensa-lx106引导ROM的一部分,并且是一个简单的定时循环,可对内部CPU时钟进行轮询。它在禁用中断的情况下执行此操作,因为如果启用了中断,则不能保证延迟将按要求进行。
tmr.delay()确实适用于需要在外部硬件上进行更精确的时序控制的场合/ O(例如,将GPIO引脚提升为20 µSec)。在几乎所有其他用例中,它都不会实现任何功能目的,因为任何其他基于系统代码的活动都将被阻止执行。在最坏的情况下,它将破坏您的应用程序并创建难以诊断的超时错误。

在这种情况下似乎必须禁用中断,这仅仅是因为如果中断确​​实发生在中间延迟中且间隔很短(大约几微秒),中断处理程序将花费比整个延迟本来要更长的时间。
假设您需要一个20毫秒的计时器,而中断发生的时间约为10μs 。如果处理程序花费的时间超过10μs,那么您将已经超过了预期的20μs延迟。
因此,如果您确实需要中断工作,我们可以排除tmr.delay()
我做了更多的挖掘工作,显然ESP8266确实通过ets_timer_arm_new()支持微秒计时器,其中最后一个参数为零。但是,NodeMCU将此值设置为1(使用毫秒精度)。这篇文章似乎支持该想法:

如果需要获取两次gpio中断之间的间隔,请使用系统api system_get_time()来计算相对时间。(us)
如果要使用os_timer api安排美国计时器事件,请在user_init的开头使用system_timer_reinit并调用os_timer_arm_us。

如果您想尝试编辑和重建固件,可能是值得一试。尽管对此有一个功能要求,但被拒绝为:

所以我测试了纳秒计时器,并且无法建立小于1000us的间隔(使用已编译和剥离的代码,并且在160MHz CPU模式下,我得到了一些东西)例如800us)。这是提供新的(几乎不可用)功能的情况吗?-djphoenix
不可行的ATM->关闭。-marcelstoer


#2 楼

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


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

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



./app/user/user_main.c

void user_init(void)
{





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


./sdk-overrides/osapi.h
在行#include_next "osapi.h": #define USE_US_TIMER上方添加
./app/modules/tmr.c-> static int tmr_start(lua_State* L){
os_timer_arm -> os_timer_arm_us
./app/modules/tmr.c-> static int tmr_interval(lua_State* L){
os_timer_arm -> os_timer_arm_us

./app/modules/tmr.c:保留os_timer_arm in int luaopen_tmr( lua_State *L ){ ,否则在启动时会重置看门狗


重新编译固件,并在运行CPU的同时刷新ESP8266



在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。