2010-08-11 249 views
1

虽然问题出在C++端,但Arduino Nano和C++之间的串行端口通信存在问题。基本上我想从Arduino发送整数(或长,...)到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的,哪些地方是在COM端口发送8889。命令ReadFile将'88'保存为szBuff。在下一次迭代'89 \ n'被保存到sZBuff。基本上我想避免后处理sZBuff来连接'88'和'89 \ n'。

有人吗? 谢谢!

+0

也许你曾在此格式它可能帮助? – 2010-08-11 02:29:31

+0

我在格式化文本时没有问题,但我不确定您指的是什么。代码,“解释”还是两者? PS:注意引号的解释。我不确定我是否很好地解释了这个问题。 – sergi 2010-08-11 02:51:13

+0

只要使用原始API,就无法避免它。带有实现ReadLine()的串口封装器的类库当然可用。 – 2010-08-11 02:58:21

回答

1

由于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); 
     } 
    } 
} 
1

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

基本上,你会有两个指针。一个到缓冲区,另一个到缓冲区中数据的结尾。所以当你的程序启动时,两个指针都是一样的。现在,你读了前两个字节。您将数据结束指针增加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 - 

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

+0

谢谢你的回答。 这两个系统(我现在正在使用的那个和你的解决方案)的问题是,在这两种情况下,我将不得不继续读'char',直到我读完行。然后把所有的char放在一起来重新创建初始的'int'。我确信,虽然看起来我错了,但有一种类似于Matlab fscanf(h,'%d“)的解决方案已经解决了这个问题,尽管我还没有发现类似的东西。在Matlab代码中,这是通过终止符来完成的,我基本上想知道是否我错过了函数的某些属性。 – sergi 2010-08-11 03:02:00

+0

噢,我明白了,这基本上是fscanf的实现方式,但Windows不提供据我所知,任何可以做到的功能。 – 2010-08-11 03:29:43