为什么串口在发送数据时会跳过数据?

时间:2013-02-05 06:16:49

标签: c++ serial-port arduino termios

我已经写了一些C ++代码来通过串口与我的arduino交谈。它只是尝试使用正弦和余弦在两个伺服电机上产生振荡,但它正在跳过数据。我不确定为什么会这样。我使用termios.h作为连续的东西。 C ++的输出类似于“V180H90”,即Vertical 180,Horizo​​ntal 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;
}

4 个答案:

答案 0 :(得分:2)

设备内部的“processIncomingByte”循环可能遇到速度问题,因为您在收到新模式后立即处理上一个状态(handlePreviousState)。

问题可能是由相应函数中的Serial.print执行,而value-data字节仍然是从PC连续传入的。串行打印是微控制器逻辑中相对较慢的过程。

我不熟悉Arduino硬件,但是一些低端微控制器板正在使用bitbanging方法执行软件串行接口,所以当你发送时,接收完全停止。要验证这一点,您可以注释Serial.print以查看它是否有帮助。

无论如何,在输入数据流中间进行冗长的处理总是有问题的,除非你的设备中有一个带有大量FIFO缓冲区的硬件串行接口。

解决此问题的正确方法是首先在缓冲区内接收整个消息,然后仅在收到消息结束标记时处理它。例如,将您的信息插入[]对中[V180H90]。在“[”上重置缓冲区,并在收到“]”后处理缓冲区。当您将字节收集到缓冲区时,请确保还检查缓冲区溢出。

答案 1 :(得分:1)

如果您只是将数据推到端口的喉咙上,它会尽力不要点火,但是不会发送多余的数据。毕竟,端口以有限的速度运行,并且是一个相当有限的转储设备。

因此,在将字符发送到端口之前,您需要检查端口的状态,以查看它是否已准备好接受另一个数据字符进行传输。有些串口甚至可以在获取更多数据时帮助您避免浪费状态轮询,从而产生中断。

此外,有时两个设备上的两个串行端口可以连接一对额外的非数据信号(RTSCTS),以指示接收方是否已准备好接收更多数据。如果您已将这些设备连接起来并且您的设备正在使用它们来表明其准备就绪,那么您的程序也应该考虑设备CTS的状态。

答案 2 :(得分:1)

显然,您的设备读取/处理数据的速度比通过串行端口发送的速度慢。我在这里看到几个可能的解决方案:

1)实现流量控制并在阻塞模式下通过串口发送数据。您仍需要在发送后等待,但只需要设备读取和处理数据所需的数量。

2)实现双向通信,以便您的设备发送确认消息(即任何单个ASCII符号),以表明它已准备好接受数据。

3)将代码划分为两个并行部分,即:主循环(或ISR)仅从串行端口读取数据并将其存储在ring buffer中,另一个循环轮询环形缓冲区并从中获取/处理数据只要有一些数据可用。这是三者中最困难的解决方案,因为您需要两个独立的线程(或线程和ISR)并保护环形缓冲区不受并发访问的影响,同时也是最强大和最灵活的。

答案 3 :(得分:0)

您正在快速地将数据写入串行设备,并且设备本身正在以比您在设备另一端读取数据更快的速度传输数据。

正确的解决方法是限制对串行设备的写入速度,以避免数据泛滥。