【问题标题】:Serial Port communication with Arduino and C++与 Arduino 和 C++ 的串行端口通信
【发布时间】:2010-08-11 02:27:05
【问题描述】:

我遇到了 Arduino Nano 和 C++ 之间的串行端口通信问题,即使问题出在 C++ 端。基本上我想将整数(或长整数,...)从 Arduino 发送到要处理的 C++ 程序。

首先我做了一个测试,使用 Matlab 将信息从 Arduino 发送到计算机。 Arduino 代码非常简单:

int i = 0;

void setup() {

   // start serial port at 9600 bps:
   Serial.begin(9600);
   establishContact(); 
}

void loop() {
  Serial.println(i);
  i=i+1;  
  delay(10);
}

void establishContact() {
   while (Serial.available() <= 0) {
     Serial.println('A', BYTE);
     delay(10);
   }
}

Matlab 方面也很简单:

clc;
clear all;
numSec=2;
t=[];
v=[];

s1 = serial('COM3');    % define serial port
s1.BaudRate=9600;               % define baud rate
set(s1, 'terminator', 'LF');    % define the terminator for println
fopen(s1);

try                             % use try catch to ensure fclose
                                % signal the arduino to start collection
    w=fscanf(s1,'%s');              % must define the input % d or %s, etc.
    if (w=='A')
        display(['Collecting data']);
        fprintf(s1,'%s\n','A');     % establishContact just wants 
                                    % something in the buffer
    end

    i=0;
    t0=tic;
    while (toc(t0)<=numSec)
        i=i+1;
        t(i)=toc(t0);
        t(i)=t(i)-t(1);
        v(i)=fscanf(s1,'%d');     
    end

    fclose(s1);
    plot(t,v,'*r')   

catch me
    fclose(s1);                
end       

我的目标是,使用 C++,执行与在 Matlab 中使用 fscanf(s1, '%d') 相同的操作。

这是我正在使用的当前代码(C++ 代码):

void main()
{
 HANDLE hSerial;
 hSerial = CreateFile(TEXT("COM3"), 
   GENERIC_READ | GENERIC_WRITE, 
   0,
   NULL, 
   OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL,//FILE_FLAG_OVERLAPPED, 
   NULL);



if ( hSerial == INVALID_HANDLE_VALUE)
 {
  printf("Error initializing handler");
 } 
 else 
 {

  // Set the parameters of the handler to the serial port.
  DCB dcb = {0};

  dcb.DCBlength = sizeof(dcb);

  if ( !GetCommState(hSerial, &dcb) )
  {
   printf("Error setting parameters");
  }

  FillMemory(&dcb, sizeof(dcb), 0);
  dcb.BaudRate = CBR_9600;
  dcb.ByteSize = 8;
  dcb.StopBits = ONESTOPBIT;
  dcb.Parity = NOPARITY;

  if ( !SetCommState(hSerial, &dcb) )
  {
   // error setting serial port state.
  }

  // Tell the program not to wait for data to show up
  COMMTIMEOUTS timeouts = {0};

  timeouts.ReadIntervalTimeout = 0;//20;
  timeouts.ReadTotalTimeoutConstant = 0;//20;
  timeouts.ReadTotalTimeoutMultiplier = 0;//50;
  timeouts.WriteTotalTimeoutConstant = 0;//100;
  timeouts.WriteTotalTimeoutMultiplier = 0;//100;

  if ( !SetCommTimeouts(hSerial, &timeouts) )
  {
   printf("Error setting the timeouts");

  }

  char szBuff[5] = "";
  DWORD dwBytesRead = 0;
  int i = 0;
  char test[] = "B\n";
  int maxSamples = 10;
  DWORD dwCommStatus;

  WriteFile(hSerial, test, 2, &dwBytesRead, NULL);

  SetCommMask(hSerial,EV_RXCHAR);

  while (i < maxSamples)
  {
   WaitCommEvent (hSerial, &dwCommStatus, 0);

   if (dwCommStatus & EV_RXCHAR) 
   {
    memset(szBuff,0,sizeof(szBuff));
    ReadFile(hSerial, LPVOID(szBuff), 4, &dwBytesRead, NULL);

    cout<<szBuff;
    printf(" - %d - \n", atoi(szBuff));
   }
   i++;     
  }

  scanf("%d", &i);

  CloseHandle(hSerial);
 }
    }

我的代码目标类似于num = ReadSerialCOM(hSerial, "%d");

我当前的 C++ 代码从缓冲区读取信息,但没有可接受的行尾,这意味着我的数字(整数)被截断了。

例如:

我从 Arduino 发送 8889,将其放置在 COM 端口中。命令ReadFile 将“88”保存到szBuff。在下一次迭代中,“89\n”被保存到sZBuff。基本上我想避免对sZBuff 进行后处理以连接'88'和'89\n'。

有人吗? 谢谢!

【问题讨论】:

  • 也许如果你在格式化它可能会有所帮助?
  • 我对更多文本的格式没有问题,但我不确定你指的是什么。代码,“解释”或两者兼而有之? PS:注意解释中的引号。我不确定我是否能很好地解释问题。
  • 只要使用原始 API,就无法避免。具有实现 ReadLine() 的串行端口包装器的类库当然是可用的。

标签: c++ serial-port arduino


【解决方案1】:

如果我正确理解您的问题,避免“后处理”的一种方法是将传递给 ReadFile 的指针移动到可用数据的末尾,因此 ReadFile 调用将附加到缓冲区,而不是覆盖。

基本上,您将有两个指针。一个到缓冲区,另一个到缓冲区中数据的末尾。所以当你的程序启动时,两个指针都是一样的。现在,您读取了前 2 个字节。您将数据结尾指针增加 2。您进行另一次读取,但不是 szBuff,而是将指针传递给先前读取的数据的结尾。您读取接下来的三个字节,您在szBuff 中有完整的条目。

如果您需要等到收到某个分隔符来标记条目的结尾,您可以搜索接收到的数据。如果它不存在,则继续阅读,直到找到为止。如果它在那里,您可以返回。

// Fill the buffer with 0
char szBuff[256] = {0};
// We have no data in the buffer, so the end of data points to the beginning 
// of the buffer.
char* szEndOfData = szBuff; 
while (i < maxSamples)
{
    WaitCommEvent (hSerial, &dwCommStatus, 0);

    if (dwCommStatus & EV_RXCHAR) 
    {
        // Append up to 4 bytes from the serial port to the buffer
        ReadFile(hSerial, LPVOID(szEndOfData), 4, &dwBytesRead, NULL);
        // Increment the end of data pointer, so it points to the end of the
        // data available in the buffer.
        szEndOfData += dwBytesRead;

        cout<<szBuff;
        printf(" - %d - \n", atoi(szBuff));
    }
    i++;     
}

// Output, assuming what you mentioned happens:
// - 88 -
// - 8889 -

如果您可以接受这种方法,则需要做更多的工作。例如,您必须确保不会溢出缓冲区。当您从缓冲区中删除数据时,您必须将删除段之后的所有数据移动到开头,并修复数据指针的结尾。或者,您可以使用循环缓冲区。

【讨论】:

  • 感谢您的回答。两个系统(我现在使用的那个和你的解决方案)的问题是,在这两种情况下,我都必须继续阅读char,直到我读完一行。然后将所有字符放在一起重新创建初始int。我确信,尽管看起来我弄错了,有一个类似于 Matlab fscanf(h, '%d") 的解决方案已经解决了这个问题,即使我没有找到任何类似的东西。在 Matlab 代码中,这是使用终止符完成的,我基本上想知道我是否遗漏了所用函数的某些属性。
  • 哦,我明白了。这基本上就是 fscanf 之类的实现方式,但据我所知,Windows 没有提供任何可以做到这一点的函数。
【解决方案2】:

正如Hans Passantdauphic 所指出的,这似乎不是我问题的通用解决方案。不过,我正在编写我试图避免的代码,以防有人发现它有用或遇到与我相同的问题:

int i = 0;  
DWORD dwBytesRead = 0;
DWORD dwCommStatus = 0;
char szBuff[2] = "";                
int maxRead = 20;   
int sizeNum = 1;    
int *num    = (int*)malloc(maxRead*sizeof(int)); 
char *currNum;
char *pastNum;

// Write something into the Serial Port to start receive 
// information from the Arduino
WriteFile(hSerial, (LPCVOID)"A\0", 1, &dwBytesRead, NULL);    
SetCommMask(hSerial, EV_RXCHAR);

// Start reading from the Serial Port
while ( i < maxRead )
{
    WaitCommEvent (hSerial, &dwCommStatus, 0);

    if (dwCommStatus & EV_RXCHAR) // if a char is received in the serial port
    {
        ReadFile(hSerial, LPVOID(szBuff), 1, &dwBytesRead, NULL);

        if ( szBuff[0] > 47 && szBuff[0] < 58 )
        {
            sizeNum++;
            if (sizeNum ==2)
            {
                currNum = (char*)malloc(sizeNum*sizeof(char));
                strcpy(currNum, szBuff);
            } else
            {
                if (pastNum != NULL)
                    free(pastNum);
                pastNum = currNum;
                currNum = (char*)malloc(sizeNum*sizeof(char));
                strcpy(currNum, pastNum);
                strcpy(currNum+(sizeNum-2)*sizeof(char), szBuff);
            }

            cout << szBuff<<endl;   
        } else if (szBuff[0] == '\n' && sizeNum > 1) // end of number
        {
            num[i] = atoi(currNum);
            i++;                    

            sizeNum = 1;
            if (currNum!=NULL)
                free(currNum);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多