2013-02-05 39 views
2

我寫了一些C++代碼通過串口與我的arduino進行通信。它只是試圖使用正弦和餘弦在兩臺伺服電機上進行振盪,但它正在跳過數據。我不知道爲什麼會發生這種情況。我正在使用termios.h作爲序列的東西。 C++的輸出結果類似於「V180H90」,即垂直180,水平90.我使用fstream和usleep()發送數據並且它正在工作,但是我想使用比通過某些任意數字延遲更好的方法。串口在發送數據時爲什麼跳過數據?

感謝您的任何幫助或指導。

我的Arduino代碼

#include <Servo.h> 
typedef enum { NONE, GOT_V, GOT_H } states; 
states state = NONE; 
Servo pan; 
Servo tilt; 
int laser = 11; 
unsigned int currentValue; 

int v_pan = 0; 
int v_tilt = 0; 

void setup() 
{ 
    pan.attach(10); 
    tilt.attach(9); 

    Serial.begin(9600); 
    state = NONE; 
} 

void processVertical(const unsigned int value) 
{ 
    Serial.print("Vertical = "); 
    Serial.println(value); 
    int result = 1300 + (value - 90) * 2; 
    //Serial.println(result); 
    tilt.writeMicroseconds(result); 
} 

void processHorizontal(const unsigned int value) 
{ 
    Serial.print("Horizontal = "); 
    Serial.println(value); 
    int result = 1500 + (value - 180) * 1; 
    //Serial.println(result); 
    pan.writeMicroseconds(result); 
} 

void handlePreviousState() 
{ 
    switch(state) 
    { 
    case GOT_V: 
     processVertical(currentValue); 
     break; 
    case GOT_H: 
     processHorizontal(currentValue); 
     break; 
    } 
    currentValue = 0; 
} 

void processIncomingByte (const byte c) 
{ 
    if (isdigit(c)) 
    { 
    currentValue *=10; 
    currentValue += c - '0'; 
    } 
    else 
    { 
    handlePreviousState(); 

    switch (c) 
    { 
     case 'V': 
     state = GOT_V; 
     break; 
     case 'H': 
     state = GOT_H; 
     break; 
     default: 
     state = NONE; 
     break; 
    } 
    } 
} 

void loop() 
{ 
    if(Serial.available() > 0) 
    { 
    processIncomingByte(Serial.read()); 
    } 
    digitalWrite(laser, HIGH); 
} 

//check out writeMicroseconds 

我的C++代碼

// Program for sending data to serial 

#include <iostream> 
#include <sstream> 
#include <string> 
#include <termios.h> 
#include <fcntl.h> 
#include <math.h> 

using namespace std; 

//open serial port 
int openPort(string path) 
{ 
    int fd; //file descriptor for port 
    fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); 
    if (fd == -1) 
    cerr << "Cannot open port" << endl; 
    else 
    fcntl(fd, F_SETFL, 0); 
    return (fd); 
} 

//set options for an open serial port 
void setOptions(int fd) 
{ 
    struct termios options; 
    tcgetattr(fd, &options); 
    cfsetispeed(&options, B9600); 
    cfsetospeed(&options, B9600); 

    //No parity 8N1 
    options.c_cflag &= ~PARENB; 
    options.c_cflag &= ~CSTOPB; 
    options.c_cflag &= ~CSIZE; 
    options.c_cflag |= CS8; 

    //No flow control 
    options.c_cflag &= ~CRTSCTS; 

    //Turn off s/w flow control 
    options.c_iflag &= ~(IXON | IXOFF | IXANY); 

    //Turn on read and ignore ctrl lines 
    options.c_cflag |= (CLOCAL | CREAD); 

    if(tcsetattr(fd, TCSANOW, &options) < 0) { 
    cerr << "Could not set attributes" << endl; 
    } 
} 

//write to serial port 
void writePort(int fd, string data) 
{ 
    int n = write(fd, data.c_str(), 9); 
    if (n < 0) 
    cerr << "Cannot write to port" << endl; 
} 

int main() { 
    string path = "/dev/tty.usbmodemfd131"; 
    //string path = "/dev/tty.usbmodemfa141"; 
    int fd = openPort(path); 
    setOptions(fd); 

    stringstream ss; 
    string output; 
    unsigned short vertical = 0; 
    unsigned short horizontal = 0; 
    unsigned short freq = 10; 

    for(int i = 0; i < 360; i++) { 
    vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90; 
    horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90; 
    ss << "V" << vertical << "H" << horizontal << endl; 
    output = ss.str(); 
    ss.str(""); 
    writePort(fd, output); 
// cout << output; //DEBUG 
    } 

    close(fd); 
    return 0; 
} 
+1

這聽起來像一個壞笑話...... *爲什麼串口跳過數據... * – thang

+1

你試過添加終止符(EOP)嗎? – KMC

+1

將「C++代碼」(我假設Linux?)連接到同一臺計算機上的終端程序時會發生什麼?首先檢查。然後您可以確定發件人或收件人是否是罪魁禍首。 – Lundin

回答

2

設備內部的「processIncomingByte」循環可能會遇到速度問題,因爲您在收到新模式後立即處理之前的狀態(handlePreviousState)。

這個問題可能是由串行引起的。打印相應的功能,而值數據字節仍然從PC連續輸入。串行打印在微控制器邏輯中是一個相對緩慢的過程。

我對Arduino硬件並不熟悉,但是一些低端的微控制器主板正在使用bitbanging方法來執行軟件串行接口,所以當你發送時,接收完全停止。要驗證這一點,您可以重新命名Serial.print以查看它是否有幫助。

無論如何,在輸入數據流的中間進行冗長的處理一直存在問題,除非在具有大量FIFO緩衝區的設備中有硬件串行接口。

解決此問題的正確方法是首先在緩衝區內接收整個消息,然後僅在接收到消息結束標記時對其進行處理。例如,將消息插入[]對中,如[V180H90]。收到「]」後,重置緩衝區「[」並處理緩衝區。當您將字節收集到緩衝區時,請確保您也檢查緩衝區溢出。

+0

謝謝你的幫助。我將實施更好的流量控制。我刪除了Serial.print,一切正常。沒有跳過數據。我可以明白爲什麼我的邏輯現在有缺陷。我將創建一個簡單的程序來正確實現這一點,並以雙向溝通方式進行遊戲。再次感謝。 – mightcouldb1

0

你出寫入數據太快串行設備和設備本身的速度比你可以讀回吐出數據在設備的另一側。

解決此問題的正確方法是限制寫入串行設備的速度以避免數據氾濫。

1

如果你只是把數據推到港口的喉嚨上,它會盡量不讓它着火,但多餘的數據不會被髮送。畢竟,港口運行速度有限,是一個相當有限的轉儲設備。

因此,在將字符發送到端口之前,您需要檢查端口的狀態以確定它是否確實準備好接受另一個數據字符進行傳輸。一些串行端口甚至可以產生中斷,因爲它們可以獲取更多數據以幫助您避免浪費狀態輪詢。此外,兩個設備上的兩個串行端口有時還可以連接一對額外的非數據信號(RTSCTS),以指示接收端是否準備好接收更多數據。如果您連接了這些設備並且您的設備正在使用它們來表示其準備就緒,那麼您的程序也應該考慮設備的CTS的狀態。

+0

我很好奇,如果我必須檢查CTS/RTS,或者可以通過設置termios.h中的標記來啓用它們。 – mightcouldb1

1

顯然,您的設備讀取/處理數據比通過串行端口發送數據要慢。我在這裏看到幾個可能的解決方案:

1)實現流量控制並通過串行端口以阻塞模式發送數據。發送後您仍然需要等待,但只能根據設備的需要讀取和處理數據。

2)實現雙向通信,以便您的設備發送確認消息(即任何單個ASCII符號)以表明它已準備好接受數據。 3)將代碼分成兩個並行部分,即:主循環(或ISR)只從串口讀取數據並將其存儲在ring buffer中,另一個循環輪詢環形緩衝區,並儘快從其中獲取/處理數據因爲有一些數據可用。這是三種最難的解決方案,因爲您需要兩個獨立的線程(或線程和ISR)並保護環形緩衝區免受併發訪問,但也是最強大和最靈活的。

+0

感謝您的回覆。我肯定會實施流量控制。雙向溝通是我想嘗試的地方。現在只是一個簡單的例子。環形緩衝區上的信息非常有用,我一定會在未來的項目中對此進行研究。 – mightcouldb1