Temporizador de sub milissegundos para ESP8266 em Lua


9

Estou tentando criar um servomotor controlado remotamente no ESP8266, que é controlado por um servidor. O problema que estou enfrentando é como criar um timer assíncrono tmr.alarm(), mas em microssegundos. tmr.delay()não funciona tão bem, porque interrompe todo o resto e não é tão preciso. Você pode fazer isso funcionar no Arduino, mas como implementar isso no Lua?

Respostas:


7

Eu acho que você pode ter dificuldade em obter um atraso de microssegundo que seja preciso e não bloqueie o ESP8266.

De acordo com a documentação do NodeMCU :

Se você olhar o app/modules/tmr.ccódigo para esta função, verá que ele executa um ets_delay_us de baixo nível (atraso). Esta função não faz parte do código NodeMCU ou do SDK; na verdade, faz parte da xtensa-lx106ROM de inicialização e é um loop de tempo simples que controla o relógio interno da CPU. Isso é feito com as interrupções desativadas, porque, se estiverem ativadas, não há garantia de que o atraso será conforme solicitado.

tmr.delay()é realmente destinado a ser usado onde você precisa ter um controle de tempo mais preciso em uma E / S de hardware externa (por exemplo, elevando um pino GPIO alto por 20 μSec). Ele não alcançará nenhum objetivo funcional em praticamente todos os outros casos de uso, pois qualquer outra atividade baseada em código do sistema será impedida de executar; na pior das hipóteses, ele interromperá o aplicativo e criará erros de tempo limite difíceis de diagnosticar.

Parece que as interrupções precisam ser desativadas nesse caso, simplesmente porque, se uma interrupção ocorrer no meio do atraso com um intervalo curto (da ordem de alguns microssegundos), o manipulador de interrupções levaria muito mais tempo do que o atraso inteiro deveria ser estar.

Digamos que você desejasse um cronômetro por 20 microssegundos e ocorreu uma interrupção em cerca de 10 μs. Se o manipulador demorar mais de 10 μs, você já terá passado o atraso de 20 μs pretendido.

Portanto, podemos descartar tmr.delay()se você precisa interromper o trabalho.

Fiz um pouco mais de escavação e, aparentemente, o ESP8266 suporta temporizadores de microssegundos, nos ets_timer_arm_new()quais o último parâmetro é zero. O NodeMCU, no entanto, define esse valor como 1, que usa a precisão de milissegundos . Este post parece apoiar essa ideia:

Se você precisar obter o intervalo entre duas interrupções do gpio, use o sistema api system_get_time () para calcular o tempo relativo. (Us) Se você deseja usar um os_timer api para organizar um evento nos timer, use system_timer_reinit no início de user_init e chame os_timer_arm_us.

Se você estiver disposto a tentar editar e reconstruir o firmware, pode valer a pena tentar. No entanto, houve uma solicitação de recurso para isso , que foi recusada como:

Então testei temporizadores de nanossegundos e não consigo estabelecer intervalos menores que 1000us (com código compilado e despojado e no modo de CPU de 160MHz obtive algo como 800us). É este o caso de fornecer novas funcionalidades (principalmente inutilizáveis)?
- djphoenix

ATM inviável -> fechamento.
- marcelstoer


5

Consegui recompilar o firmware do NodeMCU com o timer ativado:

  • Instale o ambiente de construção de janela de encaixe do Marcel Stör: https://hub.docker.com/r/marcelstoer/nodemcu-build/

  • alterar arquivos de firmware em seu diretório de firmware (por exemplo ./user/nodemcu-firmware)

    1. ./app/user/user_main.c

      void user_init(void)
      {

adicione aqui a linha: system_timer_reinit();

  1. ./sdk-overrides/osapi.h adicione acima da linha #include_next "osapi.h": #define USE_US_TIMER

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

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

  4. ./app/modules/tmr.c:deixar os_timer_armem int luaopen_tmr( lua_State *L ){como está, caso contrário você terá uma redefinição de cão de guarda sobre start-up

    • recompile o firmware e atualize seu ESP8266

Com a CPU rodando a 160MHz, eu consegui amostrar o ADC com 8.3kHz (atraso do temporizador de 125uS). Se eu for mais rápido, o cão de guarda entra em ação.

Código:

    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()

Resultado:

Hora no início: 1

Tempo no fim: 13

Contador: 100000

100.000 leituras em 12 s.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.