Temporizadores y multitarea en Arduino

imagen

Hoy hablaremos sobre un tema tan relevante como los temporizadores y la multitarea en Arduino. La razón para escribir este artículo fueron las conferencias de Oleg Artamonov @olartamonov para los estudiantes de MIREA dentro de la Academia Samsung IoT , o más bien, la declaración de Oleg, cita (segunda conferencia, 1:13:08):
"Hay, por ejemplo, tareas para las que la mayoría de los controladores arduino, especialmente los principiantes, pueden interrumpirse, pedirles que parpadeen con cinco LED diferentes con diferentes frecuencias y períodos, y para que se pueda cambiar otro período individualmente para cada LED ..."

A juzgar por las declaraciones de Oleg, tiene una idea muy errónea sobre Arduino en general y sobre "arduino" en particular. El parpadeo con cinco LED en los modos indicados por él es una tarea absolutamente trivial para Arduino, y para Arduino Mega Server no es una tarea en absoluto, sino un verdadero malentendido: la multitarea se organiza por sus medios regulares, que controlan fácilmente cientos de entidades diferentes (LED, servos, motores paso a paso, etc.). d.) en tiempo real.

Veamos cómo organizar juntos la multitarea en Arduino y, al mismo tiempo, ayudar a los estudiantes de MIREA a deshacerse de los estereotipos de percepción que se les imponen en relación con el fenómeno sociocultural y tecnológico de nuestro tiempo llamado Arduino .

Conferencias de Oleg Artamonov


Debemos rendir homenaje, las conferencias de Oleg son buenas: brindan mucha información útil y bien estructurada sobre los microcontroladores y recomendaría que todos los interesados ​​en este tema se familiaricen con ellos. El único inconveniente de estas conferencias me pareció abiertamente tecno-esnobismo con respecto a Arduino, que actúa en ellas como un "niño látigo".

En particular, a lo largo de todas las conferencias, Oleg hizo declaraciones perentorias sobre la inadecuación de Arduino para construir sistemas complejos de tareas múltiples, lo que simplemente contradice la verdad y la práctica real.

En Arduino, puede crear sorprendentes sistemas multitarea en los que docenas y cientos de entidades (LED, sensores, actuadores, servos, motores paso a paso, interfaces inalámbricas y cableadas, etc.) trabajan simultáneamente en modo multitarea (pseudo, naturalmente).

No iremos lejos por ejemplos. Aquí está el proyecto del Winter Garden ("Smart Greenhouse") en el que las siguientes entidades trabajan en tiempo real en modo multitarea:

imagen

La topología de un controlador nRF24 distribuido con una gran cantidad de equipos conectados y en tiempo real. El usuario solo se ocupa de la "base", el trabajo del socio nRF24 es completamente transparente para él. Y sí, este es Arduino.

En la "base":

- 7 servos
- 9 motores paso a paso
- 6 relés
- 3 sensores de humedad del suelo
- 2 sensores de luz
- sensor de nivel de agua
- Sensor de humedad y temperatura del aire

En nRF24 de la parte remota:

- 12 sensores de humedad del suelo
- 12 relés
- 3 motores paso a paso
- 2 sensores de luz
- sensor de nivel de agua

Además, en tiempo real, el nRF24 funciona entre las dos partes distribuidas del sistema y la interfaz Ethernet del servidor y el motor del servidor, que proporciona una interfaz de usuario web del sistema.

En total, en tiempo real, en modo multitarea, al menos 60 entidades están funcionando en el Mega de 8 bits (y esto sin contar los muchos servicios del sistema operativo AMS en sí, con ellos el número de entidades se acercará a cien). Lo que obviamente no está de acuerdo con la afirmación de que "la multitarea real es imposible en el Arduino y es problemático parpadear incluso con cinco LED en él".

Unas palabras en defensa de Arduino


(Aunque es obvio que Arduino como un fenómeno sociocultural y tecnológico con un ejército multimillonario de fanáticos y muchos miles de proyectos increíbles no necesita protección).

Lo he dicho muchas veces y repetiré nuevamente que Arduino en su componente de software es, de hecho, solo uno de los posibles niveles de abstracción (como cualquier otro) con sus propias ventajas y desventajas. Y no hay absolutamente ninguna diferencia para el usuario de lo que está "girando" dentro de su pequeña pieza de silicio: el "puro" Arduino, RTOS, RIOT OS, AMS o alguna otra abstracción matemática de la representación y gestión de los recursos de hierro del controlador.

Es importante que el usuario resuelva sus problemas: que el controlador riegue las plantas, encienda la luz, controle las cortinas, etc. Y el problema principal no está en las herramientas utilizadas en el desarrollo, sino en la capacidad de usarlas y, curiosamente, en la imaginación y la visión de ingeniería del propio desarrollador.

Como funciona


Por sí solo, la multitarea en microcontroladores se puede organizar de diferentes maneras, en este caso hablaremos sobre el más simple: los procesos toman el control y lo regalan voluntariamente después de usar su segmento de tiempo. Este método, por supuesto, no carece de inconvenientes obvios, pero, como dicen, la práctica es un criterio de verdad y se ha demostrado en condiciones reales: se utiliza tanto en distribuciones estándar de Arduino Mega Server como en muchos proyectos en AMS Pro . Y estos sistemas funcionan las 24 horas, los 7 días de la semana y han confirmado el tiempo de actividad en muchos meses de funcionamiento sin problemas.

imagen

Esta es una indicación de aproximadamente un centenar de entidades de un sistema nRF24 distribuido, gestionado de forma independiente en tiempo real. Preste atención a los últimos dos indicadores de CPU: incluso en el Mega de 8 bits, la carga del procesador es cero (es decir, el sistema es completamente libre).

Un poco sobre temporizadores


Para organizar el control de sistemas complejos, no es suficiente simplemente transferir el control entre procesos y, junto con la transferencia automática de control a AMS, se utilizan varios tipos de temporizadores: cíclicos, cíclicos con un número determinado de repeticiones (lote), únicos, aleatorios, mixtos, etc. Todo esto está organizado significa nativo de Arduino y no utiliza interrupciones o programación directa de temporizadores de microcontroladores (pero las interrupciones, por supuesto, son utilizadas por el sistema "para su propósito previsto").

Lo que nuevamente contradice directamente la afirmación: "Habrá suficientes temporizadores de hierro para 3 LED, a partir de entonces, los arduinistas tendrán problemas". No empieces Cualquier tipo de temporizador está disponible para nosotros en cualquier cantidad. Y, si lo desea, podemos hacernos más nuevos y arbitrariamente exóticos.

Caso principal


El caso principal de este tipo de organización multitarea es crear el llamado código "sin bloqueo", es decir, el código que no utiliza la función delay (), que simplemente detiene el programa durante un tiempo determinado.

Tiempo real


El método descrito para implementar la multitarea se puede describir como "tiempo real suave", el tiempo de retraso típico en el sistema es de 10 ms (pero los retrasos máximos pueden ser mucho más largos y no estandarizados). Esto impone limitaciones bien conocidas en el espectro de aplicación de esta solución, pero para la mayoría de las tareas "domésticas" (y no solo) es perfectamente adecuada, consulte el ejemplo anterior.

Si necesita control en tiempo real más estricto, esto requiere una optimización especial del código para una tarea específica, reestructurar la arquitectura o, en casos muy extremos, asignar un controlador separado para funciones específicas. Como ejemplo, destacando un controlador de efectos separado para una tira de LED inteligente.

Esta es una descripción teórica general del trabajo de multitarea en Arduino en general y en AMS en particular, ahora pasamos a la consideración de ejemplos prácticos.

Temporizadores de ciclo



imagen

Considere la implementación de los temporizadores cíclicos más simples. Estos son temporizadores (en la terminología de los "ciclos" de AMS), que se activan en ciertos intervalos de tiempo predefinidos y se utilizan para activar procesos cíclicos.

En general, es mejor diseñar temporizadores mediante programación como objetos, pero en la entrega estándar de Arduino Mega Server estos temporizadores se implementan como funciones, por lo tanto, para empezar, los consideraremos en esta hipóstasis.

El uso de temporizadores cíclicos es muy simple: simplemente coloque el código que debe ejecutarse periódicamente entre los corchetes de la instrucción if. Si necesita usar un intervalo de activación diferente, simplemente use la variable deseada en lugar de cycle1s. Puede realizar tantos ciclos diferentes como desee, incluso en un sistema Mega de 8 bits, puede manejar literalmente cientos de tales temporizadores (solo, por supuesto, debe recordar que el código llamado no está bloqueando).

  if (cycle1s) {
    // ,   , ,  
  }

. :

// Cycles
bool cycle1s  = false;
bool cycle5s  = false;
bool cycle20s = false;
bool cycle30s = false;
bool cycle1m  = false;
bool cycle3m  = false;
bool cycle5m  = false;

.

«Timers»:

/*
  Module Timers
  part of Arduino Mega Server project
*/

// Cycles
unsigned long timeSec;
unsigned long timer1s;
unsigned long timer5s;
unsigned long timer20s;
unsigned long timer30s;
unsigned long timer1m;
unsigned long timer3m;
unsigned long timer5m;

void timersInit() {
  unsigned long uptimeSec = millis() / 1000;
  timeSec  = uptimeSec;
  timer1s  = uptimeSec;  
  timer5s  = uptimeSec; 
  timer20s = uptimeSec;
  timer30s = uptimeSec;
  timer1m  = uptimeSec;
  timer3m  = uptimeSec;
  timer5m  = uptimeSec;
}

void timersWorks() {
  timeSec = millis() / 1000;
    if (timeSec - timer1s  >=  1)  {
                                    timer1s  = timeSec; cycle1s  = true;
    if (timeSec - timer5s  >=  5)  {timer5s  = timeSec; cycle5s  = true;}
    if (timeSec - timer20s >= 20)  {timer20s = timeSec; cycle20s = true;}
    if (timeSec - timer30s >= 30)  {timer30s = timeSec; cycle30s = true;}
    if (timeSec - timer1m  >= 60)  {timer1m  = timeSec; cycle1m  = true;}
    if (timeSec - timer3m  >= 180) {timer3m  = timeSec; cycle3m  = true;}
    if (timeSec - timer5m  >= 300) {timer5m  = timeSec; cycle5m  = true;}
  }
}

void eraseCycles() {
  cycle1s  = false;
  cycle5s  = false;
  cycle20s = false;
  cycle30s = false;
  cycle1m  = false;
  cycle3m  = false;
  cycle5m  = false;
}

. - , . , , .

void loop() {
  timersWorks();

  //   

  eraseCycles();
}


, , . myCycle.

, :

/*
  myCycle Library
  part of Arduino Mega Server project
*/

#ifndef _MYCYCLE_H
#define _MYCYCLE_H

#define MS_500       500
#define MS_01S      1000
#define MS_02S      2000
#define MS_05S      5000
#define MS_10S     10000
#define MS_13S     13000
#define MS_17S     17000
#define MS_20S     20000
#define MS_30S     30000
#define MS_01M     60000
#define MS_03M    180000
#define MS_05M    300000
#define MS_01H   3600000
#define MS_06H  21600000
#define MS_12H  43200000
#define MS_01D  86400000

class myCycle {
  private:
    bool          _go;
    bool          _active;
    unsigned long _start;
    unsigned long _period;
  
  public:
    myCycle(unsigned long per, bool act);
    void reInit(unsigned long per, bool act);
    void reStart();
    bool check();
    bool go();
    void clear();
    
    // active
    bool active();
    void setActive(bool act);
    // period
    unsigned long period();
    void setPeriod(unsigned long per);
}; // class myCycle

#endif // _MYCYCLE_H

:

/*
  myCycle Library
  part of Arduino Mega Server project
*/

#include "myCycle.h"
#include <Arduino.h>

myCycle::myCycle(unsigned long per, bool act) {
  _go     = false;
  _active = act;
  _period = per;
  _start  = millis();
}

// Methods

void myCycle::reInit(unsigned long per, bool act) {
  _go     = false;
  _active = act;
  _period = per;
  _start  = millis();
}

void myCycle::reStart() {
  _start = millis();
}

bool myCycle::check() {
  if (millis() - _start >= _period) {
    _start = millis();
    if (_active) {
      _go = true;
    }
  }
  return _go;
}

bool myCycle::go() {
  return _go;
}

void myCycle::clear() {
  _go = false;
}

// Active

bool myCycle::active() {
  return _active;
}

void myCycle::setActive(bool act) {
  _active = act;
}

// Period

unsigned long myCycle::period() {
  return _period;
}

void myCycle::setPeriod(unsigned long per) {
  _period = per;
}

«» : « ».

:

:

#include "myCycle.h"

:

// Cycles
myCycle cycle500(MS_500, true);
myCycle cycle2s(MS_02S, true);
myCycle cycle5s(MS_05S, true);

:

void timersWorks() {
  cycle500.check();
  cycle2s.check();
  cycle5s.check();
}

void eraseCycles() {
  cycle500.clear();
  cycle2s.clear();
  cycle5s.clear();
}

:

void loop() {
  timersWorks();

  //   

  if (cycle5s.go()) {
    Serial.println(F("cycle5s!"));
  }

  eraseCycles();
}

, , . , / , , , .

Arduino



image

, — . , . , AMS .

()


, . , 3 nRF24. 3 .

/ . .


«», - - .


- , , .

, , . , , , 20 , «», «» , . , «» .

, . — Arduino Mega Server .


, , / , « » . .

— , 8- .


, , Arduino - — — , .

, Arduino Mega Server .


, , Arduino , , , Arduino () .

, , .

P.S.
, 0.17 Arduino Mega Server Arduino Mega, Due, 101 M0, . , , .

Source: https://habr.com/ru/post/es413779/


All Articles