如何使用boost :: asio正确编写Win32应用程序以通过串行连接进行通信

时间:2018-11-12 17:36:49

标签: c++ boost arduino serial-port

我正在开发Windows应用程序,该应用程序必须通过其串行端口与Arduino通信(输入和输出)。出于可移植性原因,我正在使用boost :: asio,我想继续使用它。发生的情况是,第一次运行应用程序时,它运行良好,但是如果第二次运行,则不再有来自Arduino的数据,并且应用程序卡在了读取操作上。恢复的唯一方法是从计算机上拔下并重新连接Arduino USB电缆。

此行为是Windows特定的。相同的代码可以在Linux上完美运行。

编译器是Visual Studio 2017 Community Edition。

以下是重现此问题的示例代码:

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <vector>


int main() {
  boost::asio::serial_port port(ioctx, "COM3"); // "/dev/ttyACM0" on Linux

  port.set_option(boost::asio::serial_port::baud_rate(9600));
  port.set_option(boost::asio::serial_port::character_size(8));
  port.set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));
  port.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));
  port.set_option(boost::asio::serial_port::flow_control(boost::asio::serial_port::flow_control::none));
  char c = 'e';
  auto const s = boost::asio::write(port, boost::asio::buffer(&c, 1));
  std::cout << "sent " << s << " bytes" << std::endl;
  boost::asio::streambuf response;
  boost::asio::read_until(port, response, "\r\n");
  std::istream response_stream(&response);
  std::string line;
  std::getline(response_stream, line);
  std::cout << line << std::endl;

  port.close(); // last-ditch effort to get it working
}

这是Arduino草图(从Arduino网站获得):

int incomingByte = 0;   // for incoming serial data                         

void setup() {
  Serial.begin(9600);   // opens serial port, sets data rate to 9600bps
}

void loop() {
  // send data only when you receive data:                            
  if (Serial.available() > 0) {
    // read the incoming byte:                                  
    incomingByte = Serial.read();                               

    // say what you got:
    Serial.print("I received: ");                               
    Serial.println(incomingByte, DEC);                          
  }
}

有没有办法恢复连接的正确状态?我想念什么吗?

2 个答案:

答案 0 :(得分:0)

USB串行适配器可能存在设备驱动程序错误和硬件问题。您必须拔出并重新插入设备才能使它重新工作,这一事实表明设备驱动程序存在错误。

  • 寻找更新的驱动程序。它可能是Prolific或FTDI芯片组,请确保从芯片制造商那里获得驱动程序。参见ProlificFTDI
  • 如果是与流控制有关的硬件问题,则可以将DTR,DSR和CD引脚连接在一起,并将USB适配器的RS-232连接器上的RTS和CTS引脚连接在一起。尽管没有在软件中设置流控制,但我已经看到了需要USB适配器的地方。

答案 1 :(得分:0)

学到了几件事之后,下面是解决方法:

  1. Arduino使用其USB通信来烧录草图和在PC上来回传输数据。引导顺序可以预见,在2秒钟内(对于Arduino新版本和标准引导加载程序),与引导加载程序的通信将处于活动状态。在那之后,草图被执行。
  2. Windows API允许通过SetCommState函数一次设置所有连接参数,并以与GetCommState类似的方式检索它们。这是set_option函数用于设置参数的方法,但是碰巧连续多次调用GetCommState-SetCommState会大大减慢该过程(可能是通过重置Arduino多次)。

我结束了编写以下功能:

    #include <Windows.h>
    #include <chrono>

    void init_arduino(boost::asio::serial_port& port, std::chrono::milliseconds const& sleep = 2000)
    {
      DCB dcbSerialParams = { 0 };
      GetCommState(port.native_handle(), &dcbSerialParams);
      // this is the optimal way to set the whole serial port configuration
      // just in one shot.
      dcbSerialParams.BaudRate = CBR_9600;
      dcbSerialParams.ByteSize = 8;
      dcbSerialParams.StopBits = ONESTOPBIT;
      dcbSerialParams.Parity = NOPARITY;
      //Setting the DTR to Control_Enable ensures that the Arduino is properly
      //reset upon establishing a connection
      dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
      SetCommState(port.native_handle(), &dcbSerialParams);
      PurgeComm(port.native_handle(), PURGE_RXCLEAR | PURGE_TXCLEAR);

      // Wait for Arduino to boot the sketch
      Sleep(sleep.count());
    }

并用它替换问题示例中的port.set_option(行。

我还将流控制设置为DTR_CONTROL_ENABLE而不是原始的none,以便在连接时重置Arduino。