雏雁-Arduino部分(四)非阻塞延时

先说阻塞延时。上一篇里我们写过delay(1000)这个函数,这个函数的作用是让程序暂停执行1000ms,也就是1s。我们说他暗示阻塞延时也就意味着在执行这条语句的时候整个程序被暂停,只有1s后才恢复。
我们设计的功能中,lcd的显示是耗时的,如果随这程序快速刷新,会导致lcd显示不完整。
至于DHT11这位这更是个重量级,网上的教程一般会告诉你两次读数最好间隔2s,不然他蚌埠住。。。
所以说我们需要给这两位设计延时。如果使用阻塞延时那么会变成这样:

lcd清屏–>dth读数–>lcd打印------2s后------>lcd清屏–>dth读数…

好像还好是吧,但别忘了我们还得实现开关。如果说你在两秒内按下开关,那么相当于没按,因为程序休眠呢。

所以为了避免每次按开关都要拼一波手速和血压,我们需要给三套逻辑三套独立的延时设计。
这里面我们引入函数millis(),这个函数的作用是返回程序从开机到现在的毫秒数。相当于记录一个时间点。

我们设定全局变量

1
2
3
unsigned long buttontime = 0;
unsigned long dhttime = 0;
unsigned long lcdtime = 0;

分别记录按钮,dht,lcd(实际上只有第二行,因为温湿度那行确定字符数了,不会出问题)的时间点。
在循环函数中我们通过判断当前时间与上一个记录时间差确定延时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void loop() {

if (millis() - dhttime > 2000) {
//read dht11
float t = dht.readTemperature();
float h = dht.readHumidity();

//这里我们读完数重新记录时间点,下一次执行到这个if语句时就会判断与这次的时间间隔,只有大于两秒才会进入读数
dhttime = millis();

// print dht11 data on lcd...
}

if (digitalRead(PIN_ACON) == LOW && millis() - buttontime > 1000) {

//这里我们记录按下按钮的时间点,同理,为了实现就算长按也只能发送一次串口消息
buttontime = millis();

// AC ON
if (lastdata == 0) {
Serial.println(key2operation[0]);
lcd.setCursor(0, 1);
lcd.print("AC ON");

//这个是为了记录我们在lcd上打印指令的时间,与下一个if语句配合,使指令显示1s
lcdtime = millis();
lastdata = 1;
}
}

if (millis() - lcdtime > 1000) {
//距离打印指令已经过去1s,清除第2行内容
lcdclearline(1);
lcdtime = millis();
}

}

当然想理解延时怎么起的作用,要认定loop函数执行的非常快,每行代码也执行的非常快,整体执行下来会是这样:
dht读取超过2s了吗?

  • 是:读取dht,记录时间点
  • 否:不读取dht

1s内按钮按过了吗?

  • 按过了:不管了
  • 没按过:读取按钮,记录时间点

lcd打印指令超过1s了吗?

  • 是:清除第2行内容,记录时间点
  • 否:留着

可以发现我们三个操作都是用if语句控制的,不会相互干扰。我们在等待dht读取冷却的时候,可以随时按开关,指令显示1s后也一定会清除。