Temporizadores e multitarefa no Arduino

imagem

Hoje falaremos sobre um tópico relevante como temporizadores e multitarefa no Arduino. O motivo para escrever este artigo foram as palestras de Oleg Artamonov @olartamonov para os alunos do MIREA no âmbito da Samsung IoT Academy , ou melhor, a declaração de Oleg, citação (segunda palestra, 1:13:08):
"Há, por exemplo, tarefas para as quais a maioria dos drivers de arduino, especialmente iniciantes, pode ser quebrada, peça para eles piscarem com cinco LEDs diferentes com frequências e períodos diferentes, e para que outro período possa ser alterado individualmente para cada LED ..."

A julgar pelas declarações de Oleg, ele tem uma concepção muito errada sobre o Arduino em geral e sobre o "arduino" em particular. Piscar com cinco LEDs nos modos indicados por ele é uma tarefa absolutamente trivial para o Arduino, e para o Arduino Mega Server não é uma tarefa, mas um verdadeiro mal-entendido - a multitarefa é organizada por seus meios regulares, que controlam facilmente centenas de entidades diferentes (LEDs, servos, motores de passo etc.). d.) em tempo real.

Vamos descobrir como organizar a multitarefa no Arduino juntos e, ao mesmo tempo, ajudar os alunos do MIREA a se livrarem dos estereótipos de percepção impostos a eles em relação ao fenômeno sociocultural e tecnológico de nosso tempo chamado Arduino .

Palestras de Oleg Artamonov


Devemos dar crédito, as próprias palestras de Oleg são boas - elas fornecem muitas informações úteis e bem estruturadas sobre microcontroladores, e eu recomendaria que todos os interessados ​​nesta questão se familiarizassem com eles. A única desvantagem dessas palestras me pareceu abertamente tecno-snobbery em relação ao Arduino, que atua nelas como um "garoto chicote".

Em particular, ao longo de todas as palestras, Oleg fez declarações peremptórias sobre a inadequação do Arduino para a construção de complexos sistemas multitarefas, o que simplesmente contradiz a verdade e a prática real.

No Arduino, você pode criar incríveis sistemas multitarefa nos quais dezenas e centenas de entidades (LEDs, sensores, atuadores, servos, motores de passo, interfaces com e sem fio, etc.) trabalham simultaneamente no modo multitarefa (pseudo, naturalmente).

Não iremos longe para exemplos. Aqui está o projeto do Winter Garden (“Smart Greenhouse”) no qual as seguintes entidades trabalham em tempo real no modo multitarefa:

imagem

A topologia de um controlador nRF24 distribuído com um grande número de equipamentos conectados e em tempo real. O usuário lida apenas com a "base", o trabalho do parceiro nRF24 é completamente transparente para ele. E sim, é o Arduino.

Na "base":

- 7 servos
- 9 motores de passo
- 6 relés
- 3 sensores de umidade do solo
- 2 sensores de luz
- sensor de nível de água
- Sensor de umidade e temperatura do ar

No nRF24 da parte remota:

- 12 sensores de umidade do solo
- 12 relés
- 3 motores de passo
- 2 sensores de luz
- sensor de nível de água

Além disso, em tempo real, o próprio nRF24 opera entre as duas partes distribuídas do sistema e a interface Ethernet do servidor e o mecanismo do servidor, que fornece uma interface de usuário da web do sistema.

No total, em tempo real, no modo multitarefa, pelo menos 60 entidades estão funcionando no Mega de 8 bits (e isso não conta os muitos serviços do próprio sistema operacional AMS, com eles o número de entidades se aproximará de cem). O que obviamente não concorda com a afirmação de que "multitarefa real é impossível no Arduino e é problemático piscar mesmo com cinco LEDs".

Algumas palavras em defesa do Arduino


(Embora seja óbvio que o Arduino, como um fenômeno sociocultural e tecnológico, com um exército de milhões de fãs e muitos milhares de projetos incríveis, não precisa de proteção.)

Eu já disse várias vezes e repetirei mais uma vez que o Arduino em seu componente de software é, de fato, apenas um dos níveis possíveis de abstração (como qualquer outro) com suas próprias vantagens e desvantagens. E não há absolutamente nenhuma diferença para o usuário o que está "girando" dentro de seu pequeno pedaço de silício - o "puro" Arduino, RTOS, RIOT OS, AMS ou alguma outra abstração matemática da representação e gerenciamento dos recursos de ferro do controlador.

É importante para o usuário resolver seus problemas - para o controlador regar as plantas, acender a luz, controlar as cortinas etc. E o principal problema não está nas ferramentas usadas no desenvolvimento, mas na capacidade de usá-las e, brega, na imaginação e visão de engenharia do próprio desenvolvedor.

Como isso funciona?


Por si só, a multitarefa em microcontroladores pode ser organizada de maneiras diferentes; neste caso, falaremos sobre o mais simples - os processos se revezam no controle e o distribuem voluntariamente após o uso do intervalo de tempo. Esse método, é claro, não tem desvantagens óbvias, mas, como se costuma dizer, a prática é um critério de verdade e se provou em condições reais: é usado tanto nas distribuições padrão do Mega Server Arduino quanto em muitos projetos no AMS Pro . E esses sistemas funcionam 24/7 e confirmaram o tempo de atividade em muitos meses de operação sem problemas.

imagem

Esta é uma indicação de cerca de cem entidades de um sistema nRF24 distribuído, gerenciado de forma independente em tempo real. Preste atenção nos dois últimos indicadores da CPU - mesmo no Mega de 8 bits, a carga do processador é zero (ou seja, o sistema é totalmente gratuito).

Um pouco sobre temporizadores


Para organizar o controle de sistemas complexos, não basta apenas transferir o controle entre os processos e, juntamente com a transferência automática do controle para o AMS, vários tipos de temporizadores são usados: cíclico, cíclico com um determinado número de repetições (lote), simples, aleatório, misto, etc. Tudo isso é organizado meios nativos do Arduino e não usa interrupções ou programação direta de temporizadores de microcontroladores (mas as interrupções, é claro, são usadas pelo sistema "para a finalidade pretendida").

O que novamente contradiz diretamente a afirmação: "Haverá temporizadores de ferro suficientes para 3 LEDs; a partir de então, os arduinistas terão problemas". Não comece. Qualquer tipo de temporizador está disponível para nós em qualquer quantidade. E, se desejado, podemos nos tornar mais novos e arbitrariamente exóticos.

Caso principal


O principal caso desse tipo de organização multitarefa é criar o chamado código "não bloqueador", ou seja, código que não usa a função delay (), que simplesmente pausa o programa por um determinado período.

Tempo real


O método descrito para implementar a multitarefa pode ser descrito como "tempo real", o tempo de atraso típico no sistema é de 10 ms (mas os atrasos de pico podem ser muito mais longos e não padronizados). Isso impõe limitações bem conhecidas ao espectro de aplicação desta solução, mas para a maioria das tarefas "domésticas" (e não apenas) é perfeitamente adequado, veja o exemplo acima.

Se você precisar controlar em tempo real mais rigoroso, isso exige otimização especial do código para uma tarefa específica, reestruturação da arquitetura ou, em casos muito extremos, alocação de um controlador separado para funções específicas. Como exemplo, destacando um controlador de efeitos separado para uma faixa de LED inteligente.

Esta é uma descrição teórica geral do trabalho de multitarefa no Arduino em geral e na AMS em particular, agora passamos à consideração de exemplos práticos.

Temporizadores de ciclo



imagem

Considere a implementação dos cronômetros cíclicos mais simples. Estes são temporizadores (na terminologia dos “ciclos” do AMS), que são ativados em determinados intervalos de tempo predefinidos e são utilizados para ativar processos cíclicos.

Em geral, é melhor projetar programaticamente os cronômetros como objetos, mas na entrega padrão do Mega Server do Arduino, esses cronômetros são implementados como funções; portanto, para começar, os consideraremos nesta hipóstase.

O uso de temporizadores cíclicos é muito simples: basta colocar o código que precisa ser executado periodicamente entre os colchetes da instrução if. Se você precisar usar um intervalo de disparo diferente, basta usar a variável desejada em vez de cycle1s. Você pode fazer quantos ciclos diferentes quiser - um sistema mesmo em um Mega de 8 bits pode lidar facilmente com literalmente centenas desses temporizadores (apenas, é claro, é preciso lembrar que o código chamado não 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/pt413779/


All Articles