【问题标题】:Disabling interrupts on TX pin on Arduino Mega 2560在 Arduino Mega 2560 上禁用 TX 引脚上的中断
【发布时间】:2016-05-04 07:54:37
【问题描述】:

我会开始告诉你,关于函数 serialEvent 的引用对于 Arduino 没有很好的记录。 https://www.arduino.cc/en/Reference/SerialEvent

由于缺乏信息,我误解了此功能的工作原理。 因为我有 Arduino Mega 2560,它带有 4 个串行输入/输出,并且它们有自己的 serialEventX 函数(其中 X = {'',1,2,3})。

我已经成功与一个 ESP8266 模块通信,一旦客户端连接到它,它就会发送和接收信息。

使用serialEvent1(1,因为它连接到RX1和TX1)我希望仅在数据传入时调用serialEvent1,但实际上每当我使用Serial1.write(msg)时也会调用,所以这意味着当消息正在发送。

#define DEBUG_ESP8622 1
#include <esp8622.h>
#include <string.h>
#include "common.h"
#include <stdlib.h>
Wifi esp = Wifi(); //Here Serial1.begin(115200) happens
void setup() {
  Serial.begin(9600); //First of all SERIAL for debugging
  Serial.println("Starting");

  while(!esp.sReachable());   //Works
  Serial.println("ESP found");

  while(!esp.sSetMode(1));    //Works
  Serial.println("Mode set to Client");

  while(!esp.sConnect(WIFISSID,WIFIPASSWORD));  //Works
  Serial.println("Connected");
  Serial.print("IP:");
  Serial.println(esp.getIP());

  while(!esp.sStartServer(80));  //Works
  Serial.println("Server started");
}
void loop() {
    if(Serial.available()>0){
            int inByte=Serial.read();
            /*HERE whenever I call Serial1.write(inByte)
              serialEvent1 will be called at the end of the loop
              but actually I don't want it to
            */
            Serial1.write(inByte);
    }

}
void serialEvent(){
    return;
}
void serialEvent1(){
   Serial.println("Write or Read event?");
   while(Serial1.available()>0){
      int inByte=Serial1.read();
      //Serial.write(inByte);
   }
   //esp.onSerialEvent(); //Stores message and parses it, not relevant
   return;
}

所以现在,知道 Arduino 库是基于 AVR libc 库的,我想微控制器内部的 RX1 和 TX1 中断都通过 Arduino 库绑定到 serialEvent1。

是否可以使用库仅解除 TX1 与 serialEvent1 的绑定,并且仍然使用 Arduino 库 (Serial1.write()/read())?

我使用最简单的方法使用 Makefile 将代码上传到 Mega。选择从命令行使用 arduino 是因为到目前为止它适合我的需求,我知道 avrdude 和 avr-gcc 是从命令行编译/上传的更完整或更好的方法,如果我错了,请纠正我。

CC=arduino
upload: terminal.ino
    $(CC) terminal.ino --upload

verify: terminal.ino
    $(CC) terminal.ino --verify

如果我开始使用,我应该开始学习如何使用 avrdude 和 avr-gcc 吗? (或者可能与使用 AVR 库无关)

最后,我正在使用带有 USB 电缆的上述 Makefile,如果我使用 avrdude 和 avr-gcc 是通过 ICSP 还是仍然可以通过 USB 电缆使用?这会消除引导加载程序吗?

非常感谢

【问题讨论】:

  • serialEvent1()被调用时,有没有数据Serial1.available()? - 您可能希望将 serialEvent1() 更改为 while(Serial1.available()&gt;0){ 而不是 if(...
  • 另外,您可能还想检查 ESP8266 是否配置为 echo 将您的命令返回给您 (ATE0/ATE1)。
  • @HannoBinder,感谢您的指出,更改了它。关于 ESP8266 回显,它没有,已经用这里提到的代码检查了它:arduino.cc/en/Tutorial/MultiSerialMega
  • @HannoBinder 实际上你是对的,我正在阅读下面发布的 NeoHWSerial 代码,实际上 TX 引脚没有调用serialEvent,我没有仔细检查 ESP8266 的回声,这一次做对了ATE0 解决了这个问题。请贴出AT指令参考,我用的是这套link
  • @HannoBinder,请发布您用于 ESP8266 的 AT 命令集参考,

标签: arduino interrupt avr avr-gcc avrdude


【解决方案1】:

是的,SerialEvent 函数很愚蠢。它们与在loop 中轮询它们没有什么不同。如果您做一些耗时的事情并且没有足够快地返回loop,您仍然可能会丢失数据。解决方案是附加到 RX 中断,但内置类 HardwareSerial 不支持。

我发布了HardwareSerial 的修改版本,允许您附加到RX 中断。它被称为NeoHWSerial

因为它是一个替换,所以您必须只使用NeoSerial[#] 变量。你不能在同一个草图中使用SerialNeoSerial1。只需使用NeoSerial 而不是Serial,即使您不调用NeoSerial.attachInterrupt

void setup()
{
  NeoSerial.begin( 9600 );            // replaces `Serial.begin(9600)`
  NeoSerial.println( F("Started.") ); // ... and all your prints, too

  NeoSerial1.attachInterrupt( myRxFunction );
  NeoSerial1.begin( 9600 );

记住myRxFunction 在中断期间被调用。您必须快速处理每个字符,并且不要调用依赖于处于中断中的事物,例如print 甚至millis()。坏juju!

并确保将匹配的 IDE 版本子目录(例如 1.6.5r2)中的文件复制到您的 libraries/NeoHWSerial 子目录中。 不要将它们放入libraries/1.0.5libraries/1.6.5r2

【讨论】:

  • 事实上,标准的硬件串行也是中断驱动的。接收到的数据由 ISR 放入 RX 缓冲区,稍后在 SerialEvent 中检索。这种缓冲使您有更多时间做出反应,因为即使您的循环需要例如,您也不会丢失数据。在 SerialEvent 可以运行之前 5 个 RX 字节时间。因此,如果您不需要对传入字节做出快速/异步反应,则最好使用 Arduino 版本。
  • 我没有说标准HardwareSerial 不是中断驱动的。我说SerialEvent是一个轮询函数。 SerialEvent 不是从中断中调用的;它在您从loop 返回后调用,与仅从loop 内部轮询Serial 相比,它没有任何好处。它的名称和用法暗示它是异步调用的,但实际上并非如此。正如他所说,这让许多人感到困惑。顺便说一句,反应时间不是唯一的标准:其他库的阻塞时间会导致输入缓冲区溢出,丢失数据。在 ISR 中处理字符可能是答案。
  • OTOH,在 SerialEvent 中,您可以执行更复杂的任务,例如解析接收到的较长消息,这在 ISR 中可能会变得很尴尬。我想这取决于个人喜好,但我更喜欢异步方法,其中数据的处理与接收不同步。 - 但是我确实误解了您重新轮询的声明,认为您的意思是轮询串行端口的单个字节而不是轮询 rx 缓冲区
最近更新 更多