如何处理ISR回调?

sal*_*inx 0 arduino interrupt interrupt-handling

我目前正在开发一个更大的Arduino Mega 2560项目; 涉及伺服控制和传感器读数.我正在使用超声波接近传感器(HC-SR04)和NewPing 1.8库,该库使用中断来检测传感器的回声.此外,我还阅读了温度和光强度测量.通过使用cmdMessenger3库,从HC-SR04超声波传感器接收的距离数据通过USB转发到主机.伺服系统由来自主机的消息使用标准伺服库控制.

一旦NewPing库调用ISR检测到超声回波的时间,就会开始乱七八糟.当距离数据可用时,这是NewPing库调用的函数:

void sendSonarData(uint8_t sensorId, uint16_t distance) {
  //noInterrupts();   
  cmdMsg3.sendCmdStart(kReadSonar);
  cmdMsg3.sendCmdArg(sensorId);
  cmdMsg3.sendCmdArg(distance);
  cmdMsg3.sendCmdEnd();
  //interrupts();
}
Run Code Online (Sandbox Code Playgroud)

这是另一个回调函数,它通过cmdMessenger3库将温度数据发送到主机:

void sendTemperatureData(uint8_t sensorId, uint16_t temperature) {
  //noInterrupts();   
  cmdMsg3.sendCmdStart(kReadTemperature);
  cmdMsg3.sendCmdArg(sensorId);
  cmdMsg3.sendCmdArg(temperature);
  cmdMsg3.sendCmdEnd();
  //interrupts();
}
Run Code Online (Sandbox Code Playgroud)

问题:当Arduino试图发送温度数据时,来自超声波传感器的ISR可能会跳入并将其自己的数据写入串行流; 关于发送到主机的串行数据一团糟,因为发送过程不是原子的,由多个命令组成,用于发送一条消息(sendCmdStart- > sendCmdArg- > sendCmdEnd).

这是一个例子:

  1. 读取温度sendTemperatureData(...)称为温度
  2. cmdMsg3.sendCmdStart(kReadTemperature); 叫做
  3. cmdMsg3.sendCmdArg(sensorId); 叫做
  4. 来自声纳传感器的ISR跳入
  5. sendTemperatureData(); 叫做
  6. cmdMsg3.sendCmdStart(kReadSonar); 叫做
  7. cmdMsg3.sendCmdArg(sensorId);
  8. cmdMsg3.sendCmdArg(distance);
  9. cmdMsg3.sendCmdEnd();
  10. 其余的陈述sendTemperatureData(...)被称为
  11. cmdMsg3.sendCmdArg(temperature);
  12. cmdMsg3.sendCmdEnd();
  13. 导致串口通信大乱...

然后,我试图通过使用在发送过程中阻止ISR调用noInterrupts()/interrupts(); 使发送过程有点'原子'.但这导致了许多其他问题,millis()/micros()功能不再精确,串行通信发生故障等; 所有都依赖于被禁用的计时器noInterrupts().此外,舵机的行为也很奇怪,因为PWM信号发生的时序似乎也搞砸了.

任何想法如何解决这个'并发'问题而不打破我的程序中基于中断的范例?

jas*_*per 5

你正试图在你的ISR中做很多工作; 一般来说,他们不应该做任何需要延迟的事情(任何超过1个字节的串行消息都属于该类别).更合理的实现将使ISR简单地将传感器读数存储在全局变量中,并设置主程序检查以查看是否应该发送串行消息的标志.如果来自同一传感器的第二次读取可能在前一次有机会被发送之前到达,则可能需要更高级的东西(如消息队列).