简体   繁体   English

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

[英]Why is serial port skipping data when sending data?

I have written some C++ code to talk to my arduino via serial. 我已经编写了一些C ++代码以通过串行与arduino进行通信。 It just tries to make oscillations on two servo motors using sine and cosine, but it is skipping data. 它只是试图使用正弦和余弦在两个伺服电机上产生振荡,但它正在跳过数据。 I'm not sure why this is happening. 我不确定为什么会这样。 I am using the termios.h for the serial stuff. 我正在使用termios.h作为串行设备。 The output from C++ is something like "V180H90" ie Vertical 180, Horizontal 90. I was using fstream and usleep() to send data before and it was working, but I'd like to use a better method than delaying by some arbitrary number. C ++的输出类似于“ V180H90”,即“垂直180”,“水平90”。我之前使用fstream和usleep()发送数据并且可以正常工作,但是我想使用一种比延迟任意数更好的方法。

Thanks for any help or guidance. 感谢您的帮助或指导。

My arduino code 我的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

My C++ code 我的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;
}

The "processIncomingByte" loop inside the device may have suffered a speed problem as you are processing the previous state (handlePreviousState) immediately after you receive a new mode. 设备内部的“ processIncomingByte”循环可能会遇到速度问题,因为您在收到新模式后立即正在​​处理先前状态(handlePreviousState)。

The problem may be caused by doing a Serial.print in the corresponding function while the value-data bytes are still incoming continuously from the PC. 该问题可能是由于在仍从PC连续输入数值数据字节时,在相应功能中执行Serial.print引起的。 Serial print is a relatively slow process in micro-controller logic. 在微控制器逻辑中,串行打印是一个相对较慢的过程。

I am not familiar with Arduino hardware, but some lower end micro-controller board is performing software serial interface using bitbanging method, so when you transmit, the receiving is completely stopped. 我不熟悉Arduino硬件,但是某些低端微控制器板正在使用bitbanging方法执行软件串行接口,因此在发送时,接收完全停止了。 To verify this you can remark the Serial.print to see whether it helps. 要验证这一点,您可以标记Serial.print以查看它是否有帮助。

Anyway, doing lengthy processing in the middle of incoming data stream is alway problematic, unless you have a hardware serial interface in the device with lots of FIFO buffers. 无论如何,除非在设备中具有带有许多FIFO缓冲区的硬件串行接口,否则在传入数据流的中间进行冗长的处理总是有问题的。

A proper way to this problem is to receive the whole message inside a buffer first and then process it only when a end-of-message marker is received. 解决此问题的正确方法是先在缓冲区中接收整个消息,然后仅在收到消息结束标记时才对其进行处理。 For example, insert your message inside the [] pair like [V180H90]. 例如,将消息插入[]对内,例如[V180H90]。 Reset the buffer upon the "[" and process the buffer after you receive the "]". 在“ [”上重置缓冲区,并在收到“]”后处理缓冲区。 When you are collecting bytes into the buffer, make sure you also check for buffer overflow. 当您将字节收集到缓冲区中时,请确保还检查缓冲区溢出。

If you just shove data down the port's throat, it'll do its best not to set on fire, but the excess data isn't going to be sent. 如果您只是将数据推送到端口的喉咙,则将尽其所能,不要着火,但是多余的数据不会被发送。 After all, the port operates at a finite speed and is a pretty limited and dump device. 毕竟,端口以有限的速度运行,并且是相当有限的转储设备。

So, before sending a character to the port you need to check the status of the port to see if it's actually ready to accept another character of data for transmission. 因此,在将字符发送到端口之前,您需要检查端口的状态,以查看它是否实际上已准备好接受另一个字符数据进行传输。 Some serial ports can even generate interrupts when they can take more data to help you avoid wasteful status polling. 当某些串行端口可以获取更多数据以帮助您避免浪费的状态轮询时,甚至可以生成中断。

Also, sometimes two serial ports on the two devices can be connected with an extra pair of non-data signals ( RTS and CTS ) to indicate whether the receiving side is ready to receive more data. 同样,有时可以将两个设备上的两个串行端口与一对额外的非数据信号( RTSCTS )连接,以指示接收方是否准备好接收更多数据。 If you have those connected and your device is using them to indicate its readiness, your program should take the state of the device's CTS into account as well. 如果已连接这些设备,并且设备正在使用它们指示其就绪状态,则程序还应考虑设备CTS的状态。

Clearly your device reads/process data slower than you send it via serial port. 显然,您的设备读取/处理数据的速度比通过串行端口发送数据的速度慢。 I see few possible solutions here: 我在这里看到一些可能的解决方案:

1) Implement flow control and send data via serial port in blocking mode. 1)实现流量控制,并在阻塞模式下通过串行端口发送数据。 You still have to wait after sending, but only as much as it is needed for your device to read and process data. 发送后,您仍然需要等待,但是仅需要设备读取和处理数据所需的时间即可。

2) Implement two way communication so your device sends confirmation message (ie any single ASCII symbol) to indicate that it is ready to accept data. 2)实现双向通信,以便您的设备发送确认消息(即任何单个ASCII符号)以表明已准备好接受数据。

3) Divide your code into two parallel parts ie : main loop (or an ISR) only reads data from serial port and stores it in a ring buffer , another loop polls the ring buffer and takes/process data from it as soon as there is some data available. 3)将您的代码分为两个并行部分,即:主循环(或ISR)仅从串行端口读取数据并将其存储在环形缓冲区中 ,另一个循环轮询环形缓冲区并在有环形缓冲区时立即从中获取/处理数据一些可用数据。 This is the most difficult solution of the three as you need two separate threads (or a thread and an ISR) and protect ring buffer from concurrent access, but also the most powerful and flexible. 这是这三个中最困难的解决方案,因为您需要两个单独的线程(或一个线程和一个ISR),并保护环形缓冲区免受并发访问,但同时也是最强大和灵活的。

You are writing data out too quickly to the serial device and the device itself is spitting out data faster than you can read it back in on the other side of the device. 您将数据写出到串行设备的速度太快,并且设备本身发出数据的速度比在设备另一侧读回数据的速度快。

The correct way to cope with this is to throttle the speed of writes to the serial device to avoid flooding it with data. 解决此问题的正确方法是限制对串行设备的写入速度,以避免数据泛滥。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM