为什么串行端口在发送数据时跳过数据
Why is serial port skipping data when sending data?
我已经编写了一些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;
}
设备内部的"processIncomingByte"循环可能遇到速度问题,因为您在接收到新模式后立即处理以前的状态(handlePreviousState)。
该问题可能是由于在相应的函数中执行Serial.print而值数据字节仍从PC连续输入造成的。串行打印在微控制器逻辑中是一个相对缓慢的过程。
我不熟悉Arduino的硬件,但一些低端的微控制器板正在使用比特敲击的方法执行软件串行接口,所以当你发送时,接收完全停止。为了验证这一点,您可以对Serial.print进行注释,看看它是否有帮助。
无论如何,在输入数据流中间进行长时间处理总是有问题的,除非你在设备中有一个带有大量FIFO缓冲区的硬件串行接口。
解决这个问题的正确方法是首先在缓冲区内接收整个消息,然后只有在接收到消息结束标记时才对其进行处理。例如,将您的消息插入[]对中,如[V180H90]。在"["时重置缓冲区,并在收到"]"后处理缓冲区。在将字节收集到缓冲区时,请确保同时检查缓冲区是否溢出。
如果你只是把数据塞进端口的喉咙,它会尽最大努力不起火,但多余的数据不会被发送。毕竟,端口以有限的速度运行,是一个非常有限的转储设备。
因此,在向端口发送字符之前,您需要检查端口的状态,看看它是否真的准备好接受另一个字符的数据进行传输。一些串行端口甚至可以在占用更多数据时生成中断,以帮助您避免浪费的状态轮询。
此外,有时两个设备上的两个串行端口可以与额外的一对非数据信号(RTS
和CTS
)连接,以指示接收侧是否准备好接收更多数据。如果你已经连接了这些,并且你的设备正在使用它们来指示它的准备状态,那么你的程序也应该考虑设备的CTS
的状态。
很明显,您的设备读取/处理数据的速度比通过串行端口发送数据的速度慢。我在这里看到了一些可能的解决方案:
1) 实现流量控制,并在阻塞模式下通过串行端口发送数据。发送后您仍然需要等待,但只需等待设备读取和处理数据所需的时间即可。
2) 实现双向通信,使您的设备发送确认消息(即任何单个ASCII符号),以表明它已准备好接受数据。
3) 将代码分成两个并行部分,即:主循环(或ISR)只从串行端口读取数据并将其存储在环形缓冲区中,另一个循环轮询环形缓冲区,并在有数据可用时立即从中获取/处理数据。这是三种解决方案中最困难的一种,因为您需要两个独立的线程(或一个线程和一个ISR)来保护环形缓冲区免受并发访问,但也是最强大和最灵活的。
您将数据写入串行设备的速度过快,而设备本身输出数据的速度快于您在设备另一端读取数据的速度。
应对这种情况的正确方法是限制对串行设备的写入速度,以避免数据泛滥。
- 在 linux c++ 中没有通过串行端口发送的数据
- 检查串行端口Linux中是否有传入数据(cbInQue for linux)
- 串行端口写入究竟如何从缓冲区实际写入数据?
- 使用来自串行端口的字符串数据来操作振镜扫描仪在更高的速度下会出错
- Qt串行端口数据接收
- 发送特定字节模式时串行端口数据损坏
- 我无法从串行端口发送连续数据
- 从串行端口(带整数的字符串)解码数据
- 使用 C++ 从 Linux 串行端口读取会提供混乱的数据
- C :如果串行端口没有新数据,则如何忽略ReadFile()
- 读取使用COM0COM构建的虚拟串行端口的数据的问题
- 清除boost::asio中串行端口的输入数据
- 仅当数据从另一侧(c++)发送时,才从串行端口读取
- 使用C++/Visual Studio从串行端口读取数据
- 如何通过串行端口发送请求从目录中检索数据
- 如何更改此代码以从一个部分(而不是两个部分)中读取串行端口的所有数据
- 使用升压asio从串行端口读取最新数据
- 使用com端口通过cpp中的串行端口发送数据
- Qt串行端口:写入和读取数据
- 从c++中的串行端口读取不同包大小的数据流的最佳方式