¿Hay alguna manera de que varias partes del programa se ejecuten juntas sin hacer varias cosas en el mismo bloque de código?
Un hilo esperando un dispositivo externo mientras parpadea un LED en otro hilo.
arduino-uno
threads
Bja
fuente
fuente
Respuestas:
No hay soporte multiproceso, ni multiproceso, en el Arduino. Sin embargo, puede hacer algo cercano a múltiples hilos con algún software.
Quiere ver Protothreads :
Por supuesto, hay un ejemplo de Arduino aquí con código de ejemplo . Esta pregunta SO también podría ser útil.
ArduinoThread también es bueno.
fuente
Los Arduino basados en AVR no admiten subprocesos (hardware), no estoy familiarizado con los Arduino basados en ARM. Una forma de evitar esta limitación es el uso de interrupciones, especialmente interrupciones temporizadas. Puede programar un temporizador para interrumpir la rutina principal cada tantos microsegundos, para ejecutar otra rutina específica.
http://arduino.cc/en/Reference/Interrupts
fuente
Es posible realizar subprocesos múltiples del lado del software en el Uno. El subproceso de nivel de hardware no es compatible.
Para lograr el subprocesamiento múltiple, requerirá la implementación de un planificador básico y mantener un proceso o una lista de tareas para rastrear las diferentes tareas que deben ejecutarse.
La estructura de un planificador no preventivo muy simple sería como:
Aquí,
tasklist
puede haber una matriz de punteros de función.Con cada función de la forma:
Cada función puede realizar una tarea separada, como
function1
realizar manipulaciones de LED yfunction2
hacer cálculos flotantes. Será responsabilidad de cada tarea (función) cumplir con el tiempo asignado.Con suerte, esto debería ser suficiente para comenzar.
fuente
Según la descripción de sus requisitos:
Parece que podría usar una interrupción de Arduino para el primer "hilo" (de hecho, preferiría llamarlo "tarea").
Las interrupciones de Arduino pueden llamar a una función (su código) en función de un evento externo (nivel de voltaje o cambio de nivel en un pin de entrada digital), que activará su función de inmediato.
Sin embargo, un punto importante a tener en cuenta con las interrupciones es que la función llamada debe ser lo más rápida posible (por lo general, no debe haber ninguna
delay()
llamada ni ninguna otra API de la que dependadelay()
).Si tiene una tarea larga para activar en el desencadenante de eventos externos, podría utilizar un programador cooperativo y agregarle una nueva tarea desde su función de interrupción.
Un segundo punto importante sobre las interrupciones es que su número es limitado (por ejemplo, solo 2 en UNO). Entonces, si comienza a tener más eventos externos, necesitará implementar algún tipo de multiplexación de todas las entradas en una sola, y que su función de interrupción determine qué entrada multiplexada fue el disparador real.
fuente
Una solución simple es usar un Programador . Hay varias implementaciones. Esto describe brevemente uno que está disponible para placas basadas en AVR y SAM. Básicamente, una sola llamada comenzará una tarea; "bosquejo dentro de un bosquejo".
Scheduler.start () agregará una nueva tarea que ejecutará taskSetup una vez y luego llamará repetidamente taskLoop tal como funciona el boceto Arduino. La tarea tiene su propia pila. El tamaño de la pila es un parámetro opcional. El tamaño predeterminado de la pila es de 128 bytes.
Para permitir el cambio de contexto, las tareas deben llamar a yield () o delay () . También hay una macro de soporte para esperar una condición.
La macro es azúcar sintáctica para lo siguiente:
Await también se puede usar para sincronizar tareas. A continuación se muestra un fragmento de ejemplo:
Para más detalles ver los ejemplos . Hay ejemplos de múltiples parpadeos de LED para eliminar el botón de rebote y un shell simple con lectura de línea de comando sin bloqueo. Se pueden usar plantillas y espacios de nombres para ayudar a estructurar y reducir el código fuente. El siguiente bosquejo muestra cómo usar las funciones de plantilla para el parpadeo múltiple. Es suficiente con 64 bytes para la pila.
También hay un punto de referencia para dar una idea del rendimiento, es decir, el tiempo para comenzar la tarea, el cambio de contexto, etc.
Por último, hay algunas clases de soporte para la sincronización y comunicación a nivel de tarea; Cola y semáforo .
fuente
De un encantamiento anterior de este foro, la siguiente pregunta / respuesta fue trasladada a Ingeniería Eléctrica. Tiene un código arduino de muestra para parpadear un LED usando una interrupción del temporizador mientras usa el bucle principal para hacer IO en serie.
https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091
Volver a publicar:
Las interrupciones son una forma común de hacer las cosas mientras sucede algo más. En el ejemplo a continuación, el LED parpadea sin usar
delay()
. Siempre que seTimer1
dispara,isrBlinker()
se llama a la rutina de servicio de interrupción (ISR) . Enciende / apaga el LED.Para mostrar que otras cosas pueden suceder simultáneamente,
loop()
escribe repetidamente foo / bar en el puerto serie independientemente del parpadeo del LED.Esta es una demostración muy simple. Los ISR pueden ser mucho más complejos y pueden ser activados por temporizadores y eventos externos (pines). Muchas de las bibliotecas comunes se implementan mediante ISR.
fuente
También llegué a este tema mientras implementaba una pantalla LED de matriz.
En una palabra, puede construir un planificador de sondeo utilizando la función millis () y la interrupción del temporizador en Arduino.
Sugiero los siguientes artículos de Bill Earl:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
fuente
También puedes probar mi biblioteca ThreadHandler
https://bitbucket.org/adamb3_14/threadhandler/src/master/
Utiliza un programador de interrupción para permitir el cambio de contexto sin transmitir en yield () o delay ().
Creé la biblioteca porque necesitaba tres subprocesos y dos para ejecutarlos en un momento preciso, sin importar lo que estuvieran haciendo los demás. El primer hilo manejó la comunicación en serie. El segundo estaba ejecutando un filtro de Kalman usando la multiplicación de matriz flotante con la biblioteca Eigen. Y el tercero era un hilo de bucle de control de corriente rápido que tenía que poder interrumpir los cálculos de la matriz.
Cómo funciona
Cada hilo cíclico tiene una prioridad y un punto. Si un subproceso, con mayor prioridad que el subproceso actual en ejecución, alcanza su próximo tiempo de ejecución, el programador pausará el subproceso actual y cambiará al de mayor prioridad. Una vez que el subproceso de alta prioridad completa su ejecución, el planificador vuelve al subproceso anterior.
Reglas de programación
El esquema de programación de la biblioteca ThreadHandler es el siguiente:
Cómo utilizar
Los hilos se pueden crear a través de la herencia de c ++
O a través de createThread y una función lambda
Los objetos de subproceso se conectan automáticamente al ThreadHandler cuando se crean.
Para iniciar la ejecución de los objetos de hilo creados, llame a:
fuente
Y aquí hay otra biblioteca multitarea cooperativa de microprocesador: PQRST: una cola prioritaria para ejecutar tareas simples.
En este modelo, un subproceso se implementa como una subclase de a
Task
, que está programado para algún tiempo futuro (y posiblemente reprogramado a intervalos regulares si, como es común, en suLoopTask
lugar se subclasifica ). Elrun()
método del objeto se llama cuando la tarea se vence. Elrun()
método realiza el trabajo debido y luego regresa (este es el bit cooperativo); normalmente mantendrá algún tipo de máquina de estado para administrar sus acciones en invocaciones sucesivas (un ejemplo trivial es lalight_on_p_
variable en el ejemplo a continuación). Requiere un ligero replanteamiento de cómo organizar su código, pero ha demostrado ser muy flexible y robusto en un uso bastante intensivo.Es agnóstico acerca de las unidades de tiempo, por lo que es tan feliz correr en unidades de
millis()
comomicros()
, o cualquier otro tic que sea conveniente.Aquí está el programa 'blink' implementado usando esta biblioteca. Esto muestra solo una tarea en ejecución: otras tareas normalmente se crearían y comenzarían dentro
setup()
.fuente
run()
que se llama al método, no se interrumpe, por lo que tiene la responsabilidad de finalizarlo razonablemente de inmediato. Normalmente, sin embargo, hará su trabajo y luego se reprogramará (posiblemente de forma automática, en el caso de una subclase deLoopTask
) para algún tiempo futuro. Un patrón común es que la tarea mantenga alguna máquina de estado interna (un ejemplo trivial es ellight_on_p_
estado anterior) para que se comporte adecuadamente cuando sea el próximo vencimiento.run()
. Esto está en contraste con los hilos cooperativos, que pueden producir la CPU, por ejemplo, llamandoyield()
odelay()
. O subprocesos preventivos, que se pueden programar en cualquier momento. Siento que la distinción es importante, ya que he visto que muchas personas que vienen por aquí buscando hilos lo hacen porque prefieren escribir código de bloqueo en lugar de máquinas de estado. El bloqueo de hilos reales que producen la CPU está bien. Bloquear tareas RtC no lo es.