关于loop( ) 阻塞和非阻塞探究

一、SIR的补充

在上几篇博客中,有朋友私信问我,在ticker函数程序和中断服务程序(ISR)中写 物联网请求报错。怎么回事,在此解释。控制台如下

1.1解释

在使用 Ticker 函数和中断服务程序(ISR)时,如果在 ISR 中进行物联网请求可能会导致一些问题,因为 ISR 中需要尽量保持简洁和快速执行,而物联网请求可能涉及到网络通信,执行时间较长,容易引起不可预期的问题,比如中断嵌套、堆栈溢出等。

通常来说,在 ISR 中不应该执行耗时的操作,包括网络通信、文件操作等。如果需要在 ISR 中进行某些操作,可以考虑通过设置标志位的方式,在主循环中检查该标志位并执行相应的操作。

1.2说明

因此在实现网络请求时,请将请求程序写入loop()主循环内,简单的硬件信号输入输出程序则可以放入 多任务处理 ticker和中断执行ISR中。如下

#include <Ticker.h>

Ticker ticker;
Ticker ticker1;

void setup() {

    attachInterrupt(digitalPinToInterrupt(keyPin), handleInterrupt, FALLING);  // 设置外部中断
    
  ticker.attach_ms(10, controlLED);  // 10微秒执行一次

 // ------

  // 设置定时器,在1秒后发送设备信息
  ticker1.once(1, startedInstruction);
}




// 1.外部中断处理函数
ICACHE_RAM_ATTR void handleInterrupt() {
  flag = !flag;  // 切换 LED 状态
  //不能在 ticker 和中断中写网络请求
}

// 2.控制 LED 状态
void controlLED() {
  digitalWrite(ledPin, flag ? HIGH : LOW);  // 根据标志控制 LED 亮灭
}

//3.ticker检测启动说明
void startedInstruction() {
  Serial.println("启动说明");
  String tag = String("/LoveMomServer/jb82IWj8q")
               + "?dIdmail=" + String(DEVICE_ID) + "&netWork="
               + netWork + "&deviceName=" + deviceName + "&localIp="
               + localIp + "&deviceMac=" + deviceMac;
  Serial.println(tag);
}

二、loop也有阻塞的时候

2.1案例

网络请求写在loop中,有的请求是10秒请求一次,有的请求是20秒、甚至是5秒、1秒。这么多网络请求都写在loop函数内,请求和休眠时间各不一样。

尤其是对硬件监听digitalRead(keyPin) == LOW是毫秒级的不间歇的。强行加delay(1000),会导致程序阻塞的。

2.2阻塞情况

void loop() {
  if (sendDeviceInfoRequestCount < 3) {
    sendDeviceInfo();
    sendDeviceInfoRequestCount++;
  } else {
    Serial.println("------");
  }

  if (digitalRead(keyPin) == LOW) {
    sendChangeLEDRequest();
    while (!digitalRead(keyPin))
      ;  //按键释放时候退出while循环,防止按键按下多次触发
  }

  delay(10000);

  httpRequest();

  delay(10000);
}

在 loop() 函数中,当执行 delay(10000); 的时候,代码会暂停执行10秒钟。如果在这10秒钟内按下了按钮,digitalRead(keyPin) == LOW 将会成立,从而触发 sendChangeLEDRequest()。然而,由于 delay(10000); 正在执行,代码会一直停留在这个延迟函数中,直到时间到了才会继续执行后面的代码。因此,在延迟期间按下按钮是不会触发 sendChangeLEDRequest() 的。

2.3非阻塞逐个定时

void loop() {
  unsigned long currentMillis = millis();

  if (sendDeviceInfoRequestCount < 1) {
    sendDeviceInfo();
    sendDeviceInfoRequestCount++;
  } else {
    //Serial.println("eeeeee");
  }

  if (digitalRead(keyPin) == LOW) {
    sendChangeLEDRequest();
    while (!digitalRead(keyPin))
      ;  //等待按键释放
  }

  if (currentMillis - previousMillis >= interval) {
    httpRequest();
    previousMillis = currentMillis;  // 重置计时器
  }
}

millis() 函数是 Arduino 编程中常用的一个函数,用于获取从 Arduino 开始运行以来经过的毫秒数。它返回一个 unsigned long 类型的值,表示自 Arduino 开始运行以来经过的毫秒数。

在 Arduino 程序中,通常需要进行时间相关的操作,比如控制执行某些任务的时间间隔、实现定时功能等。millis() 函数可以帮助你实现这些功能,而不必使用阻塞延迟函数 delay(),从而使得 Arduino 在等待某些事件发生的同时可以执行其他任务。

相关推荐

  1. Verilog 14: 阻塞阻塞赋值的异同

    2024-04-02 10:28:07       26 阅读
  2. TCP Server工具类,BIO阻塞阻塞NIO

    2024-04-02 10:28:07       26 阅读
  3. 高级IO——阻塞IOselect

    2024-04-02 10:28:07       16 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-02 10:28:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-02 10:28:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-02 10:28:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-02 10:28:07       18 阅读

热门阅读

  1. 正则表达式 (regex) 简介和基本用法

    2024-04-02 10:28:07       10 阅读
  2. 常见故障排查和优化

    2024-04-02 10:28:07       11 阅读
  3. Spark面试整理-Spark如何处理大数据

    2024-04-02 10:28:07       13 阅读
  4. C++4.2

    2024-04-02 10:28:07       13 阅读
  5. 深入理解 C++ Lambda 表达式

    2024-04-02 10:28:07       14 阅读
  6. 【DevOps工具篇】安装 LDAP 管理 GUI PhpLdapAdmin

    2024-04-02 10:28:07       14 阅读
  7. Docker:使用MinIO搭建对象存储平台

    2024-04-02 10:28:07       14 阅读
  8. 在k8s中部署高可用程序实践和资源治理

    2024-04-02 10:28:07       14 阅读
  9. 【数据结构】二叉树

    2024-04-02 10:28:07       13 阅读
  10. Unity 读写Excel打包后无法运行可能的解决方案

    2024-04-02 10:28:07       17 阅读
  11. ubuntu 20.04 SD 卡分区类型 msdos 改为 GPT 的方法

    2024-04-02 10:28:07       15 阅读
  12. LeetCode热题Hot100 - 最长回文子串

    2024-04-02 10:28:07       17 阅读