近期使用arduino和PC进行串口通信,但是传输过程中速度比较缓慢,每秒20次左右,而且丢包率严重,容易乱码。
为了提高串口传输的速度和准确率,重新看了串口通信的原理,根据原理把整个单片机接收代码进行了优化。
文章主要包含以下五部分内容:
1. 信息的编码方式介绍2. 串口的通信过程3. 利用ASCII码实现信息压缩4. arduino接收端代码优化5. python发送端代码实例
信息的编码方式
常见的一些字符编码方式有:ASCII、Unicode、GBK、UTF-8,编码方式指的是字符串中字符的编码方式。
(1)ASCII
ASCII对应的是字符和二进制的关系,它能表示128个字符,其中包括英文字符、阿拉伯数字、西文字符以及32个控制字符。每个ASCII是由8个二进制表示,8个二进制内存1字节,所以用ASCII表示的每个字符内存大小是1字节,单位比特(B)
(2)Unicode
Unicode包含了世界上所有的符号,并且每一个符号都是独一无二的。因为每个国家的字复杂程度不同,所以用Unicode编码的字符占用内存大小是不一样的,它是一种变长的编码方式。这种编码使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8就是在互联网上使用最广的一种unicode的实现方式,UTF-8是Unicode的实现方式之一。
GBK是针对简体字的编码。
根据编码方式可以看出来ASCII码的编码方式最节省内存,如果传输的信息为数字类型,可以利用ASCII码巧妙进行转换,节省内存,具体方法在第三节进行介绍
串口的通信过程
电脑和arduino通过USB数据线进行连接,大概的通信过程可以用下面的内容概括:
1. 发送一个字符串比如“ABCD”,电脑会把字符串的每一个字符进行编码,编码方式可以自己选择,这里采用ASCII的编码方式。比如字符‘A’对应的二进制ASCII值为“01000001”,串口把‘A’以二进制的方式发送出去,发送完成之后继续发送‘B’,以此类推
2. 单片机接收到一串二进制,会先把内容存入单片机的缓存区,这里应该加上适当的延时,直到这个字符串缓存完成之后在进行二进制的处理,否则会出现一些问题。单片机在缓存区每次读取8位二进制,通过解码把二进制变为发送的字符’A’,利用循环不断读缓存区,直到读完
利用ASCII码实现信息压缩
我在实例中需要一次性给单片机发送4个带符号的两位数,符号位用0和1表示,比如“010120030140”,表示 10、-20、 30、-40四个数,占用内存12B,测试过程中发现传输速度较为缓慢。
现在采用利用ASCII码值的方式进行中转,首先把每一个数加上68,然后转换为一个字符,这样就变成了“K-_%“,共占用4B,在单片机中把字符转换为十进制减去68就可以得到要传递的数据,这样就可以节省66%的内存。
arduino接收端代码优化
char shuju[6] ={};//串口不能定义为整数类型,定义成字符型才没问题int i=0;void setup(){ Serial.begin(115200);}void loop(){ if(Serial.available()) { delay(10); //延时的目的是为了把串口的数据全部存入缓存区,否则不准 //Serial.print(“Serial.available()=”); //Serial.println(Serial.available());//调试使用 int numdata = Serial.available(); //把字节数赋值给变量,不能多次读取否则不准 for(i=0;i<numdata;i ) //如果使用串口调试发送字符串会默认在最后面加上回车,所以字符数量会多1个 { shuju[i]=char(Serial.read()); //读一个删一个,串口传递的是ASCII,要转换为字符型 } shuju[5]=’1′; //校验位,验证是否有数据 } if(shuju[5]==’1′) { Serial.println(shuju); //shuju[5]把回车覆盖掉了,所以打印回车 shuju[5]=’0′; //校验位归零 }}
python发送端代码实例
import serial # 导入串口包import time # 导入时间包ser=serial.Serial(“COM11”,115200,timeout=5)#开启com3口,波特率115200,超时5ser.flushInput()# 清空缓冲区time.sleep(2)while True: data=input(‘输入:’) ser.write(data.encode(“gbk”)) for i in range(100): count = ser.inWaiting() # 获取串口缓冲区数据 if count !=0 : recv = ser.read(count).decode(“gbk”) # 读出串口数据,数据采用gbk编码 print(“recv –> “, recv) # 打印一下子 break time.sleep(0.1) # 延时0.1秒,免得CPU出问题