Temporizador de sub milisegundos para ESP8266 en Lua

9

Estoy tratando de hacer un controlador de servomotor controlado remotamente en ESP8266 que es controlado por un servidor. El problema al que me enfrento es cómo hacer un temporizador asíncrono, como tmr.alarm(), pero en microsegundos. tmr.delay()no funciona tan bien porque detiene todo lo demás y no es tan preciso. Puede hacer que esto funcione en Arduino, pero ¿cómo implementar esto en Lua?

Raitis Bērziņš
fuente

Respuestas:

7

Creo que podría tener dificultades para obtener un retraso de microsegundos que sea preciso y no bloqueante con el ESP8266.

De acuerdo con la documentación de NodeMCU :

Si observa el app/modules/tmr.ccódigo para esta función, verá que ejecuta un nivel bajo ets_delay_us (retraso). Esta función no es parte del código NodeMCU o el SDK; en realidad es parte de la xtensa-lx106ROM de arranque, y es un ciclo de sincronización simple que sondea contra el reloj interno de la CPU. Lo hace con las interrupciones deshabilitadas, porque si están habilitadas, no hay garantía de que el retraso sea el solicitado.

tmr.delay()está realmente diseñado para usarse donde necesita tener un control de tiempo más preciso en una E / S de hardware externo (por ejemplo, levantar un pin GPIO alto por 20 μSec). No logrará ningún propósito funcional en casi todos los demás casos de uso, ya que cualquier otra actividad basada en el código del sistema se bloqueará de la ejecución; en el peor de los casos, romperá su aplicación y creará errores de tiempo de espera difíciles de diagnosticar.

Parece que las interrupciones tienen que deshabilitarse en este caso simplemente porque si ocurriera una interrupción a mitad de la demora con un intervalo corto (del orden de unos pocos microsegundos), el controlador de interrupción tomaría mucho más tiempo del que se suponía que la demora completa ser - estar.

Supongamos que desea un temporizador de 20 microsegundos y se produjo una interrupción de aproximadamente 10 μs. Si el controlador tarda más de 10 μs, ya habrá superado el retraso de 20 μs que pretendía.

Por lo tanto, podemos descartar tmr.delay()si necesita interrupciones de trabajo.

Investigué un poco más, y aparentemente el ESP8266 admite temporizadores de microsegundos a través de los cuales ets_timer_arm_new()el último parámetro es cero. NodeMCU, sin embargo, establece este valor en 1 que utiliza la precisión de milisegundos . Esta publicación parece apoyar esa idea:

Si necesita obtener el intervalo entre dos interrupciones de gpio, use la api del sistema system_get_time () para calcular el tiempo relativo. (Us) Si desea usar una api os_timer para organizar un evento del temporizador us, use system_timer_reinit al comienzo de user_init y llame a os_timer_arm_us.

Si está dispuesto a intentar editar y reconstruir el firmware, puede valer la pena intentarlo. Aunque, hubo una solicitud de función para esto , que se rechazó como:

Así que probé temporizadores de nanosegundos, y no puedo establecer intervalos de menos de 1000us (con código compilado y eliminado y en modo CPU de 160MHz obtuve algo así como 800us). ¿Es este un caso para proporcionar una nueva funcionalidad (en su mayoría inutilizable)?
- djphoenix

No factible ATM -> cierre.
- marcelstoer

Aurora0001
fuente
5

He logrado recompilar el firmware de NodeMCU con el temporizador habilitado:

  • Instale el entorno de compilación de Docker de Marcel Stör: https://hub.docker.com/r/marcelstoer/nodemcu-build/

  • cambiar archivos de firmware en su directorio de firmware (por ejemplo ./user/nodemcu-firmware)

    1. ./app/user/user_main.c

      void user_init(void)
      {
      

agregue aquí la línea: system_timer_reinit();

  1. ./sdk-overrides/osapi.h agregar por encima de la línea #include_next "osapi.h": #define USE_US_TIMER

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

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

  4. ./app/modules/tmr.c:dejar os_timer_armen int luaopen_tmr( lua_State *L ){como está, de lo contrario obtendrá un reinicio de la vigilancia en el arranque

    • recompile el firmware y actualice su ESP8266

Con la CPU funcionando a 160MHz, he logrado muestrear ADC con 8.3kHz (retraso de temporizador de 125uS). Si voy más rápido, el perro guardián entra en acción.

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

Salida:

Hora de inicio: 1

Hora de finalización: 13

Contador: 100000

100.000 lecturas en 12 seg.

kadzsol
fuente