【问题标题】:write timer without delay()写定时器没有延迟()
【发布时间】:2015-04-18 11:55:26
【问题描述】:

如果输入为 HIGH,我编写了这段代码来发送短信,正如您在其中看到的那样,但问题是我有 4 个输入,如果我需要做不止一个,delay()s 是致命的并且非常错误一次一件事(我使用 4 个输入)。

所以我需要将delay() 更改为millis()void loop()Send_SMS()initia() 中的其他内容。

谁能帮帮我,提前谢谢你。

const int DI = 2;
const int DT = 3;
const int DGP1 = 4;
const int DGP2 = 5;

int value1_old = 0;
int value2_old = 0;
int value3_old = 0;
int value4_old = 0;

unsigned long previousMillis = 0;
unsigned long interval=100;

#include<SoftwareSerial.h>
SoftwareSerial SIM900 (7, 8);

void setup() {
    pinMode(DI, INPUT);
    pinMode(DT, INPUT);
    pinMode(DGP1, INPUT);
    pinMode(DGP2, INPUT);

    SIM900.begin(19200);
    SIM900power();
    delay(20000);

}

void SIM900power(){
    digitalWrite(9 ,HIGH);
    delay(1000);
    digitalWrite(9 ,LOW);
    delay(5000);
}

void initia(){
    SIM900.print("AT+CMGF=1\r");
    delay(100);
    SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
    delay(100);
}

void Send_SMS(){
    SIM900.println((char)26);
    delay(100);
    SIM900.println();
    delay(100);
    SIM900power();
}

void loop() {
    int value1 = digitalRead (DI);
    int value2 = digitalRead (DT);
    int value3 = digitalRead (DGP1);
    int value4 = digitalRead (DGP2);

    if (value2 != value2_old && value2 == HIGH) {
        initia();
        SIM900.println("Station 85: Defaut electrique");
        delay(100);
        Send_SMS();
        value2_old = value2;
    }

    if (value3 != value3_old && value3 == HIGH)
    {
        initia();
        SIM900.println("Station 85: DefautGP1");
        delay(100);
        Send_SMS();
        value3_old = value3;
    }

    if (value4 != value4_old && value4 == HIGH)
    {
        initia();
        SIM900.println("Station 85:DD>1000");
        delay(100);
        Send_SMS();
        value4_old = value4;
    }
    value2_old = value2;
    value3_old = value3;
    value4_old = value4;
}

【问题讨论】:

  • 您的意思是说,通过使用延迟,您是在浪费等待事情的周期,而不是执行更多其他潜在的(时间紧迫的)任务?如果是这样,我认为您需要考虑使用中断(在 ISR 中执行时间关键任务)或使用 AVR 本身上的硬件计时器。
  • 改用millis()函数。教程示例is here.
  • 我认为我需要使用millis()代替延迟,但我不知道如何,因为我是初学者:/

标签: c timer arduino delay


【解决方案1】:

正如 mclopez 所指出的,最好提供一个好的答案,而不是只指出其他人的缺陷是什么,所以……我们开始吧。

在我看来,ISR 解决方案不是一个好的选择,因为 ISR 会阻止正常执行。使用中断唯一可行的实现是记录引脚变化,然后发送短信。

但是,在我看来,这不是一个好的解决方案。对于这个问题,我会使用状态机发送短信;因为你有状态,你可以在做其他事情的时候等待转换,比如检查按钮。

我不知道 SIM900 是如何发送 SMS 的,所以我将采用您的工作流程并在状态机中实现它。我不确定这是不是最佳解决方案,尤其是因为我认为您实际上并不需要每次都重新启动模块,但您可以稍后对其进行调整。

现在,工作流程有七个您必须执行的操作,每个操作都需要等待。我把它写在这里是为了更容易看到每个动作:

SIM900.print("AT+CMGF=1\r");
delay(100);
SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
delay(100);  
SIM900.println("Message you want");
delay(100);  
SIM900.println((char)26);
delay(100);
SIM900.println();
delay(100);
digitalWrite(9 ,HIGH);
delay(1000);
digitalWrite(9 ,LOW);
delay(5000);

所以我们有八个状态:一个空闲状态(当您等待状态机启动时,命名为 SIM_IDLE),然后是五个“SEND”状态(我将它们命名为 SIM_SEND1..5)和两个用于重置电源(命名为 SIM_POW1 和 SIM_POW2)。您通常处于空闲状态;当按下一个或多个按钮时,您切换到第一个发送,循环它们,然后重置电源并返回空闲状态。

按下的按钮仅更改状态 SIM_SEND3(当您实际发送消息时),因此每当检测到按钮按下时,就会设置一个布尔变量(在状态机执行期间也检测到按钮按下)并且仅在该状态下重置状态,在发送正确的消息后。

现在,这是实现它的代码:

const uint8_t DI = 2;
const uint8_t DT = 3;
const uint8_t DGP1 = 4;
const uint8_t DGP2 = 5;

const uint8_t SIMPOW = 9;

uint8_t value1_old = 0;
uint8_t value2_old = 0;
uint8_t value3_old = 0;
uint8_t value4_old = 0;

boolean value1_changed = false;
boolean value2_changed = false;
boolean value3_changed = false;
boolean value4_changed = false;

/********************************/
//         SIM STATES
#define SIM_IDLE   0
//SEND1: SIM900.print("AT+CMGF=1\r");delay(100);
#define SIM_SEND1  1
//SEND2: SIM900.println("AT + CMGS = \"+212xxxxxxx\"");delay(100);
#define SIM_SEND2  2
//SEND3: SIM900.println("Message you want");delay(100);
#define SIM_SEND3  3
//SEND4: SIM900.println((char)26);delay(100);
#define SIM_SEND4  4
//SEND5: SIM900.println();delay(100);
#define SIM_SEND5  5
//POW1: digitalWrite(SIMPOW,HIGH);delay(1000);
#define SIM_POW1   6
//POW2: digitalWrite(SIMPOW,LOW);delay(5000);
#define SIM_POW2   7
/********************************/

unsigned long previousMillis;
uint8_t currentSimState;

#include<SoftwareSerial.h>
SoftwareSerial SIM900 (7, 8);

void setup()
{
    pinMode(DI, INPUT);
    pinMode(DT, INPUT);
    pinMode(DGP1, INPUT);
    pinMode(DGP2, INPUT);
    pinMode(SIMPOW, OUTPUT);

    SIM900.begin(19200);

    digitalWrite(SIMPOW,HIGH);
    delay(1000);
    digitalWrite(SIMPOW,LOW);
    delay(25000);
    currentSimState = -1; // Force a state transition
}

void loop()
{
    uint8_t value1 = digitalRead (DI);
    uint8_t value2 = digitalRead (DT);
    uint8_t value3 = digitalRead (DGP1);
    uint8_t value4 = digitalRead (DGP2);

    unsigned long currentMillis = millis();

    if (value2 != value2_old && value2 == HIGH)
        value2_changed = true;
    if (value3 != value3_old && value3 == HIGH)
        value3_changed = true;
    if (value4 != value4_old && value4 == HIGH)
        value4_changed = true;

    value1_old = value1;
    value2_old = value2;
    value3_old = value3;
    value4_old = value4;

    // Check if a state transition is needed
    uint8_t newSimState = currentSimState;
    switch (currentSimState)
    {
    case SIM_IDLE: // Start sending if a value changed
        if ((value2_changed) || (value3_changed) || (value4_changed))
            newSimState = SIM_SEND1;
        break;
    case SIM_SEND1: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND2;
        break;
    case SIM_SEND2: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND3;
        break;
    case SIM_SEND3: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND4;
        break;
    case SIM_SEND4: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND5;
        break;
    case SIM_SEND5: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_POW1;
        break;
    case SIM_POW1: // Wait 1000 ms
        if ((currentMillis - previousMillis) >= 1000)
            newSimState = SIM_POW2;
        break;
    case SIM_POW2: // Wait 1000 ms
        if ((currentMillis - previousMillis) >= 1000)
            newSimState = SIM_IDLE;
        break;
    default:
        newSimState = SIM_IDLE;
        break;
    }

    // If there was a transition, do the appropriate action
    if (newSimState != currentSimState)
    {
        case SIM_IDLE:
            // Do nothing
            break;
        case SIM_SEND1:
            SIM900.print("AT+CMGF=1\r");
            previousMillis = millis();
            break;
        case SIM_SEND2:
            SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
            previousMillis = millis();
            break;
        case SIM_SEND3:
            if (value2_changed)
            {
                SIM900.println("Station 85: Defaut electrique");
                value2_changed = false;
            }
            else if (value3_changed)
            {
                SIM900.println("Station 85: DefautGP1");
                value2_changed = false;
            }
            else if (value4_changed)
            {
                SIM900.println("Station 85:DD>1000");
                value2_changed = false;
            }
            else
            {
                // Should never arrive here. Just in case, you
                // can either abort the SMS sending if you can
                // or send another message
            }
            previousMillis = millis();
            break;
        case SIM_SEND4:
            SIM900.println((char)26);
            previousMillis = millis();
            break;
        case SIM_SEND5:
            SIM900.println();
            previousMillis = millis();
            break;
        case SIM_POW1:
            digitalWrite(SIMPOW,HIGH);
            previousMillis = millis();
            break;
        case SIM_POW2:
            digitalWrite(SIMPOW,LOW);
            previousMillis = millis();
            break;
        }
    }

    // Advance state
    currentSimState = newSimState;
}

它可能看起来很复杂,但它确实非常简单。循环由三个“块”组成。

第一个是按钮检查。如果按下任何按钮,则设置相应的valueX_changed 标志。这是一个非常琐碎的部分,只需检查是否有任何按钮具有不同的状态,然后设置标志。

第二部分是检查状态转换。在这个 switch 语句中,程序确定状态机的状态是否需要改变。如果状态为空闲,或者如果在发送 SMS 的过程中经过了指定的时间,则按下按钮时会发生这种情况。

第三部分是当状态改变时要执行的动作。因此,如果状态发生变化,请执行状态操作,这对空闲状态没有任何意义,为 SIM_SENDx 状态发送一些内容,并为 SIM_POWx 状态更改引脚。

请注意:在设置中,您添加了 20 秒延迟,不会在正常工作流程中出现。如果您想删除它,您可以从执行重置的设置中删除四行,并修改第一个开关中的default 大小写以设置newSimState = SIM_POW1; 而不是SIM_IDLE

此代码中可能存在小错误,因为我尚未对其进行测试,但它应该可以满足您的需求

【讨论】:

    【解决方案2】:

    使用定时器库https://playground.arduino.cc/Code/Timer/。正如他们所说:

    delay 方法的缺点是没有其他方法可以继续 而delay 正在发生。您无法更新显示或检查 例如按键。

    因此,您可以使用:

    而不是delay
     t.every(1000, doStuff);
    

    触发一个函数,同时离开循环执行其业务。

    希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      不完全是原始问题的答案... 但是对于那些寻找一个简单的解决方案来打印秒而不使用delay()的人:

      if(millis() % 1000 == 0) {
         Serial.println(millis());
      }
      

      很简单,不是吗?

      【讨论】:

        【解决方案4】:

        函数millis()会在值溢出时失败。正如@CPU_Terminator 所说,使用中断。有一些有用的 Arduino 库,例如 Timer1

        编辑。假设如果您的某些输入发生更改,您想要做的是每 100 毫秒发送一条短信,您可以使用这样的代码(我删除了一些对我来说似乎没有必要的延迟,如果我错了,请再次添加它们) :

        #include <SoftwareSerial.h>
        #include "TimerOne.h"
        
        const int DI = 2;
        const int DT = 3;
        const int DGP1 = 4;
        const int DGP2 = 5;
        const long interval = 100000; // in microseconds
        
        int value1 = 0;
        int value2 = 0;
        int value3 = 0;
        int value4 = 0;
        int value1_old = 0;
        int value2_old = 0;
        int value3_old = 0;
        int value4_old = 0;
        boolean changed1 = false;
        boolean changed2 = false;
        boolean changed3 = false;
        boolean changed4 = false;
        
        SoftwareSerial SIM900 (7, 8);
        
        void SIM900power(){
          digitalWrite(9, HIGH);
          delay(1000);
          digitalWrite(9, LOW);
          delay(5000);
        }
        
        void initia(){
          SIM900.print("AT+CMGF=1\r");
          SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
        }
        
        void Send_SMS(){
          SIM900.println((char)26);
          SIM900.println();
          delay(20);
          SIM900power();
        }
        
        void isr_timer(){
          if (changed2) {
            initia();
            SIM900.println("Station 85: Defaut electrique");
            Send_SMS();
            changed2 = false;
          }
        
          if (changed3) {
            initia();
            SIM900.println("Station 85: DefautGP1");
            Send_SMS();
            changed3 = false;
          }
        
          if (changed4) {
            initia();
            SIM900.println("Station 85:DD>1000");
            Send_SMS();
            changed4 = false;
          }
        }
        
        void setup() {
          pinMode(DI, INPUT);
          pinMode(DT, INPUT);
          pinMode(DGP1, INPUT);
          pinMode(DGP2, INPUT);
        
          SIM900.begin(19200);
          SIM900power();
          delay(20000);
          Timer1.initialize(interval);
          Timer1.attachInterrupt(isr_timer);
        }
        
        void loop() {
          value1 = digitalRead (DI);
          value2 = digitalRead (DT);
          value3 = digitalRead (DGP1);
          value4 = digitalRead (DGP2);
        
          if (value1 != value1_old && value1 == HIGH) changed1 = true;
          if (value2 != value2_old && value2 == HIGH) changed2 = true;
          if (value3 != value3_old && value3 == HIGH) changed3 = true;
          if (value4 != value4_old && value4 == HIGH) changed4 = true;
        
          value1_old = value1;
          value2_old = value2;
          value3_old = value3;
          value4_old = value4;
        
          // Here the rest of your code
        }
        

        这样isr_timer()函数将每0.1秒执行一次。

        【讨论】:

        • 太棒了!我得到了-1,没有人告诉我为什么。
        • 你有它,希望它有用。如果这是您期望的答案,请接受并投票:)
        • 好的,我去看看是否可以,非常感谢^^
        • 对不起,但不起作用,它无法发送短信:/,我认为millis()是解决方案,但我不知道如何使用它,因为我是初学者
        • 对于millis(),@Hans_Passant 在另一个答案中为您提供了链接。阅读。但如果这不起作用可能是因为我删除了一些需要的延迟(我真的不知道如何发送短信)。添加我删除的所有延迟,并将 interval 更改为有意义的值(一个值比 SMS 所需的总时间大一点)。
        【解决方案5】:

        您可以尝试使用 isr_timer() 函数中的代码:

        if (changed2) {
            initia();
            SIM900.println("Station 85: Defaut electrique");
            delay(100);
            Send_SMS();
            changed2 = false;
        }
        

        最好的问候

        【讨论】:

          猜你喜欢
          • 2011-07-06
          • 1970-01-01
          • 1970-01-01
          • 2021-07-01
          • 2021-12-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多