C 读取串行端口速度更快
我有一个电脑软件,可以使用USB将RGB颜色代码发送到Arduino。当它们被缓慢地发送时,它工作得很好,但当每秒发送几十个时,它就会崩溃。我想发生的是Arduino串行缓冲区填充得太快,处理器无法按我读取的方式处理它C 读取串行端口速度更快,c,arduino,serial-port,embedded,usb,C,Arduino,Serial Port,Embedded,Usb,我有一个电脑软件,可以使用USB将RGB颜色代码发送到Arduino。当它们被缓慢地发送时,它工作得很好,但当每秒发送几十个时,它就会崩溃。我想发生的是Arduino串行缓冲区填充得太快,处理器无法按我读取的方式处理它 #define INPUT_SIZE 11 void loop() { if(Serial.available()) { char input[INPUT_SIZE + 1]; byte size = Serial.readBytes(input, INPU
#define INPUT_SIZE 11
void loop() {
if(Serial.available()) {
char input[INPUT_SIZE + 1];
byte size = Serial.readBytes(input, INPUT_SIZE);
input[size] = 0;
int channelNumber = 0;
char* channel = strtok(input, " ");
while(channel != 0) {
color[channelNumber] = atoi(channel);
channel = strtok(0, " ");
channelNumber++;
}
setColor(color);
}
}
例如,计算机可能发送
255 0 123
,其中数字以空格分隔。如果发送间隔足够慢,或者缓冲区总是只填充一个颜色代码,例如255
,它是11个字节(INPUT\u SIZE
)。但是,如果颜色代码不是11字节长,并且立即发送第二个代码,则该代码仍然从串行缓冲区读取11字节,并开始组合颜色并将其弄乱。我如何避免这种情况,同时尽可能保持其效率?首先,我同意@Thomas Padron McCarthy的观点。发送字符串而不是字节数组(11字节而不是3字节,解析过程也是如此)只会浪费资源。另一方面,您应该遵循的方法取决于您的发件人:
- 它是周期性的还是非周期性的
- 大小是否固定
typedef struct MyMessage
{
// unsigned char id; // id of a message maybe?
unsigned char colors[3]; // or unsigned char r,g,b; //maybe
unsigned char checksum; // more than one byte could be a more powerful checksum
};
unsigned char calcCheckSum(struct MyMessage msg)
{
//...
}
unsigned int validateCheckSum(struct MyMessage msg)
{
//...
if(valid)
return 1;
else
return 0;
}
现在,您应该以滑动窗口方式检查每4个字节(MyMessage的大小)是否有效:
void findMessages( )
{
struct MyMessage* msg;
byte size = Serial.readBytes(input, INPUT_SIZE);
byte msgSize = sizeof(struct MyMessage);
for(int i = 0; i+msgSize <= size; i++)
{
msg = (struct MyMessage*) input[i];
if(validateCheckSum(msg))
{// found a message
processMessage(msg);
}
else
{
//discard this byte, it's a part of a corrupted msg (you are too late to process this one maybe)
}
}
}
void findMessages()
{
结构MyMessage*msg;
字节大小=串行.readBytes(输入,输入大小);
字节msgSize=sizeof(struct MyMessage);
对于(int i=0;i+msgSize而言,这不是加快串行端口读取速度的问题,而是当输入数据具有可变长度时,不读取11个字符的固定块的问题
你告诉它一直读到收到11个字符或出现超时,但是如果第一组少于11个字符,第二组紧接着就没有超时,你将部分读第二组。你似乎理解这一点,所以我不确定你如何得出结论说“读得更快”会有所帮助
使用ASCII十进制空格分隔的三元组的现有数据编码,一种解决方案是一次读取一个字符,直到读取整个三元组,但是您可以更简单地使用ArduinoReadBytesUntil()
函数:
#define INPUT_SIZE 3
void loop()
{
if (Serial.available())
{
char rgb_str[3][INPUT_SIZE+1] = {{0},{0},{0}};
Serial.readBytesUntil( " ", rgb_str[0], INPUT_SIZE );
Serial.readBytesUntil( " ", rgb_str[1], INPUT_SIZE );
Serial.readBytesUntil( " ", rgb_str[2], INPUT_SIZE );
for( int channelNumber = 0; channelNumber < 3; channelNumber++)
{
color[channelNumber] = atoi(channel);
}
setColor(color);
}
}
请注意,我没有测试上面的任何代码,并且在某些情况下,Arduino文档在返回值的描述方面是缺乏的。您可能需要对代码进行一些调整
上述两种方法都不能解决同步问题——即,当颜色值流化时,您如何知道RGB三元组的开始?您必须依赖于获取第一个字段值并在之后保持计数和同步——这很好,直到Arduino在数据流启动后启动,或者重置,或者PC进程以异步方式终止和重新启动。然而,这也是您最初的实现中的一个问题,因此可能需要在其他地方处理。如果您真正想要的是更快的速度……这有点牵强
我能想到的满足您的需求并提供同步的最快方法是,为每种颜色发送一个字节,并以定义的方式更改奇偶校验位,假设您可以读取具有错误奇偶校验的字符的奇偶校验和字节值
您将不得不处理奇偶校验的变化,大多数字符将无法被人类读取,但这必须是发送三个字节数据的最快方式之一。您指的是字节,而不是位。但是如果您要读取的数据并不总是11字节长,那么您不能仅读取11个字节,并期望一切正常。如果第一条消息比如说,“1233”,第二个是“1234”,输入中的数据将是“1231434”。在我看来,不可能知道它是指“1233”后接“1234”,还是指“1231”后接“1234”?你能改变格式吗?例如,将三个字节作为二进制字节发送,而不是转换为文本吗?谢谢@ThomasPadron McCarthy!我更改了计算机上的程序以发送十六进制颜色代码。现在我总是收到六个字节,然后需要转换回RGB。我不明白它是如何转换为六个字节的?你应该发送一个字节用于每个通道有三个。另外,使用一个struct将原始输入映射到格式化版本。请参阅下面的文章。Arduino串行类已经进行了缓冲和中断驱动,并且由于没有多线程,因此辅助缓冲区将没有任何用途。+1用于解决同步问题。我从未使用过或甚至从未见过和ar杜尼奥:)感谢您的更正。字符串更易于调试,如果性能不是关键因素,它们可能是更好的选择。@fuzzxl,我不同意您的观点,如果您不将文本作为消息发送。我从未见过发送字符数组而不是结构化消息是更好的选择。@fuzzxl:正在发送的数据“眼睛清晰”可能使调试更容易,但在这种情况下,这是错误的实际原因(试图像处理固定长度的可变长度数据一样处理可变长度数据)。这突出了Arduino的最大问题-没有具有片上调试支持的调试器。在调试器中查看字节变量将是微不足道的。在这种情况下,在Arduino端使用调试打印输出将比不必要地消耗通道带宽更好。如何以三个字节发送颜色?@MikkoP:R、G、B组件。一个字节可以表示0到255的值。您的串行通信需要配置为8个数据帧(正常情况下
void loop()
{
if( Serial.available() )
{
for( int channelNumber = 0; channelNumber < 3; channelNumber++)
{
color[channelNumber] = Serial.Read() ;
}
setColor(color);
}
}