Arduino上的计时器和多任务

图片

今天,我们将讨论与Arduino上的计时器和多任务相关的话题。 撰写本文的原因是Oleg Artamonov @olartamonov 在三星物联网学院的框架MIREA学生提供的讲座,或者更确切地说,是Oleg的声明,引用(第2讲,1:13:08):
“例如,有些arduino驾驶员(尤其是初学者)可能会被打破,要求他们使用具有不同频率和周期的五个不同的LED闪烁,以便每个LED可以单独更改另一个周期...”

从奥列格(Oleg)的陈述来看,他对Arduino尤其是对“ arduino”有一个非常误解。 以Arduino指示的模式以五个LED闪烁是绝对微不足道的任务,对于Arduino Mega Server来说这根本不是一项任务,而是一个真正的误解-多任务是通过其常规方式组织的,它可以轻松控制数百个不同的实体(LED,伺服器,步进电机等)。 d。)实时。

让我们弄清楚如何在Arduino上一起组织多任务,同时帮助MIREA学生摆脱对他们与当今时代称为Arduino的社会文化和技术现象有关的刻板印象。

Oleg Artamonov的演讲


我们必须致敬,Oleg的演讲本身很好-它们提供了许多有关微控制器的有用且结构良好的信息,我建议对这个问题感兴趣的每个人都熟悉它们。 在我看来,这些讲座的唯一缺点似乎是关于Arduino的公开技术吹嘘,它在Arduino中充当“鞭打男孩”的角色。

特别是,在所有讲座中,Oleg都强行声明了Arduino不适合构建复杂的多任务系统,这与事实和实际做法相矛盾。

在Arduino上,您可以创建令人惊叹的多任务系统,其中数十个实体(LED,传感器,执行器,伺服器,步进电机,无线和有线接口等)在(伪自然)多任务模式下同时工作。

我们不会走很多例子。 这是冬季花园 (“智能温室”)的项目,其中以下实体以多任务模式实时工作:

图片

具有大量连接的实时设备的分布式nRF24控制器的拓扑。 用户只处理“基础”,nRF24合作伙伴的工作对他是完全透明的。 而且,是的,这是Arduino。

在“基地”:

-7个伺服器
-9个步进电机
-6个继电器
-3个土壤湿度传感器
-2个光传感器
-水位传感器
-湿度和空气温度传感器

在远程部分的nRF24上:

-12个土壤湿度传感器
-12个继电器
-3个步进电机
-2个光传感器
-水位传感器

此外,nRF24本身可以实时在系统的两个分布式部分之间运行,并在服务器和服务器引擎的以太网接口之间运行,从而提供系统的Web用户界面。

总体上,在多任务处理模式下,实时至少有60个实体在8位Mega上运行(这还不包括AMS操作系统本身的许多服务,实体的数量将接近100)。 这显然与“在Arduino上无法实现真正​​的多任务处理,即使上面有五个LED指示灯闪烁也存在问题”的说法不符。

捍卫Arduino的几句话


(尽管很明显,Arduino是一种社会文化和技术现象,拥有数百万粉丝和数以千计的惊人项目,但并不需要保护。)

我已经说过很多遍了,并且会再次重申,事实上,Arduino在其软件组件中只是具有自身优缺点的可能抽象级别之一(就像其他任何抽象级别一样)。 而且,对于用户来说,在他的小片硅片中“旋转”什么是绝对没有区别的-纯粹的Arduino,RTOS,RIOT OS,AMS或控制器铁资源的表示和管理的其他一些数学抽象。

对于用户来说,解决他的问题很重要-对于控制器给植物浇水,开灯,控制窗帘等。主要问题不在于开发中使用的工具,而在于使用它们的能力以及开发人员本人的想象力和工程视野。

如何运作?


就其本身而言,微控制器上的多任务可以以不同的方式组织,在这种情况下,我们将讨论最简单的一种-进程轮流进行控制,并在使用其时间片后自愿放弃。 当然,这种方法并非没有明显的缺点,但是,正如他们所说,实践是一种真理的标准,并且已经在真实条件下证明了自己:在标准Arduino Mega Server发行版和AMS Pro的许多项目中都使用了这种方法。 这些系统可以24/7全天候工作,并且已经在许多个月的无故障运行中确认了正常运行时间。

图片

这表明分布式nRF24系统的大约一百个实体是实时独立管理的。 请注意最后两个CPU指示器-即使在8位Mega上,处理器负载也为零(即系统完全空闲)。

关于计时器的一些知识


为了组织复杂系统的控制,仅在过程之间依次转移控制是不够的,并且随着自动控制转移到AMS,使用了各种类型的计时器:循环,具有给定重复次数(批处理)的循环,单个,随机,混合等。所有这些都是有组织的Arduino的本机方式,不使用中断或直接对微控制器计时器进行编程(但是,中断是系统“出于其预期目的”使用的)。

这又直接与以下说法背道而驰:“将有足够的熨斗计时器来容纳3个LED,从那时起,建筑学家将遇到问题。” 不要开始。 我们可以使用任何数量的任何类型的计时器。 而且,如果需要的话,我们可以使自己变得更加新颖和随意。

主要情况


这种多任务组织的主要情况是创建所谓的“非阻塞”代码,即不使用delay()函数的代码,该函数只是将程序暂停给定时间。

实时性


可以将所描述的用于实现多任务的方法描述为“软实时”,系统中的典型延迟时间为10毫秒(但峰值延迟可能更长,并且没有标准化)。 这对该解决方案的应用范围施加了众所周知的限制,但是对于大多数“家庭”任务(不仅是),它非常适合,请参见上面的示例。

如果您需要更严格的实时控制,则需要对特定任务的代码进行特殊优化,重新架构,或者在极端情况下为特定功能分配单独的控制器。 例如,突出显示用于智能LED灯条的单独的效果控制器

这是对Arduino尤其是AMS中多任务工作的一般理论描述,现在我们转向考虑实际示例。

循环计时器



图片

考虑最简单的循环计时器的实现。 这些是计时器(在AMS“周期”的术语中),它们以某些预定义的时间间隔激活,并用于激活循环过程。

通常,最好以编程方式将计时器设计为对象,但是在Arduino Mega Server的标准交付中,这些计时器被实现为功能,因此,对于初学者而言,我们将在这种假设中考虑它们。

使用循环计时器非常简单:只需将需要定期执行的代码放在if语句的括号之间。 如果需要使用其他触发间隔,则只需使用所需的变量而不是cycle1s。 您可以根据需要设置任意多个周期-即使在8位Mega上,系统也可以轻松地处理数百个此类计时器(当然,只有您需要记住被调用的代码没有阻塞)。

  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/zh-CN413779/


All Articles