2013-02-05 40 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