Arduino串口通讯
- 为什么要做串口通讯
- 串口通讯原理
- 串口通讯函数
- 字符串常用函数
- 串口通讯示例
- 入门示例
- 测试串口通讯
- 复杂指令处理
为什么要做串口通讯
串口通讯:串口通信是用来在不同电子设备之间交换数据用的技术,其实就是要实现不同电子设备之间的“通讯对话”。
串口给通讯应用例子:刚拿到Arduino进行Blink测试,程序通过引脚控制了小灯亮起,但是如果我们想要改变当前小灯的亮度,想要把这个“想法”传递给Arduino的时候,就需要串口通讯了。可以通过串口通讯,从PC端告诉Arduino程序我们想要改变当前小灯的亮度。
串口:Arduino和其它设备通信的接口。
串口通讯原理
Arduino通讯串口:Arduino的串口通常称为UART或USART,通过0(RX)和1(TX)引脚以及USB 端口,来和其它设备进行通信。
UART(Universal Asynchronous Receiver/Transmitter):UART是通用异步收发器,采用异步串行通信协议,通过信号线把需要发送的数据以二进制的形式发出,高电平为数据“1”,低电平为数据“0”。
通信传输线:串口通常会有三根线来完成通讯,分别是地线、发送线、接收线。串口通信是异步的,能够在一根线上发送数据同时在另一根线上接收数据,其他线用于握手。
数据帧:Arduino用Serial.print和Serial.println函数发送数据时候,其实是发出一连串数字信号,这些数字信号称为数据帧。数据帧通常由四个部分组成,分别是起始位、数据位、校验位和停止位。
- 起始位:数据帧开始传输的信号,通常起始位电平为低电平。
- 数据位:包含了实际发送的数据,Arduino默认使用8位数据,每次可以传输1B数据。
- 校验位:数据检错的方式,可以设置为奇校验和偶校验,Arduino默认无校验位。
- 停止位:表示该段数据帧传输结束,通常停止位都是高电平。
串口通讯缓冲区:Arduino的USB端口有串口寄存器,可以用来暂存电脑从USB发送的数据。Arduino从串口寄存器读取的速度比串口发送数据的速度快,所以串口通讯读取数据前需要加一定延时。和电脑通信的时候,Arduino UNO的串口寄存器空间默认是63帧的数据帧大小,超出后信息会挤掉前面的信息。
串口通讯函数
Serial 类:通过 Serial 类来使用开发板上的UART串口,只需要指定波特率,就能使用了。
波特率(Baud Rate):在信息传输通道中,携带数据信息的信号单元叫码元,单位时间内通过信道传输的码元数称为码元传输速率,简称波特率。常用的波特率为9600、115200、4800等,波特率越大表明通信的速率越快。。当传输的码元信息量刚好为1比特的时候,波特率等于比特率。通信双方需要使用一致的的波特率才能正常通信。
Serial.begin()
函数作用:开启串口。
使用方法:speed为波特率;config为设置数据位、校验位和停止位。
- Serial.begin(speed)
- Serial.begin(speed, config)
Serial.end()
函数作用:禁止串口传输,此时串口0(RX)和1(TX)引脚可以作为数字引脚使用。
Serial.print()
函数作用:串口输出数据,写入字符数据到串口。
使用方法:val是打印的值;config输出数据格式,BIN(二进制)、OCT(八进制)、DEC(十进制)、HEX(十六进制)等。
- Serial.print(val)
- Serial.print(val, format)
Serial.println()
函数作用:串口输出数据并换行。
使用方法:
- Serial.println(val)
- Serial.println(val, format)
Serial.available()
函数作用:判断串口缓冲区的状态,返回从串口缓冲区读取的字节数。
Serial.read()
函数作用:读取串口数据,一次读一个字符,读完后删除已读数据;当没有可读数据时返回-1,整数类型。
Serial.peek()
函数作用:读取串口数据,一次读一个字符,读完后不删除;当没有可读数据时返回-1,整数类型。。
Serial.readString()
函数作用:每次读取一个字符串,返回一个String类型。
Serial.readStringUntil()
函数作用:每次读取一个字符串,知道遇到截止字符,返回一个String类型。
使用方法:inByte为截止字符。
- Serial.readStringUntil(inByte)
字符串常用函数
了解字符串的常用函数,可以更好进行串口通讯中复杂指令的处理。
获取字符串中的部分内容:
- charAt(n):返回字符串中第n个字符。
- substring(from, to):从指定的起始索引 " from " 到结束索引 " to " (不包括)截取字符串的子串。
- substring(from):返回一个从给定索引" from " 到结尾的子串。
比较两个字符串:
- compareTo(S2):与S2字符串比较,如果两个字符串相同,返回值等于0;否则,返回值不等于0。
- equals(S2):比较两个字符串是否相等,区分大小写。
字符串类型转换:
- toInt():数字字符串转为整数值。
- toCharArray(buf,len) :把字符串转换成数组char[],其中buf是char[],len为复制长度。
获取字符串长度:
- length():返回字符串中的字符数。
- strlen():获取字符串的长度,不包括空终止符。
- sizeof():获取字符串的长度,包括空终止符。
两个字符串之间的合并:
- strcat(str1,str2):把str1(包括“\0”)复制到str2后面(删除str2原来末尾的“\0”)。
- concat(S2):返回字符串和字符串S2合并后的新字符串
串口通讯示例
入门示例
先处理一个简单的指令。
示例代码的解释:往串口每次输入一串指令“a1”后,Arduino返回“YES”。
String str;
void setup() {
Serial.begin(115200);// 设置波特率,上位机的波特率要保证相同,不然无法识别
str="";
}
void loop() {
delay(100);//等待100ms=0.1s,加延时是因为Arduino从串口寄存器读取的速度比串口发送数据的速度快
while (Serial.available() > 0)
{
str += char(Serial.read());// read是剪切,而不是复制
}
if(str=="a1\0"){
Serial.println("YES");
}
str="";
}
测试串口通讯
程序验证上传到Arduino后。
点击IDE右上角有个串口监视器。
点击后会有下方会出现串口监视器,调整波特率和代码对应,然后输入没有结束符的消息“a1”,就可以看到Arduino返回的“YES”。
复杂指令处理
在前面的入门示例中,要处理的上位机指令很简单,上位机每次只发送一个指令“a1”。
但是如果上位机发送的指令很长,而且还包含了数字,甚至可能发送指令的速度很快,“Serial.available() > 0”判断的时候可能有多条指令要区分。
下面是一个处理稍微复杂指令的示例代码,涵盖了处理复杂指令的基本操作。
示例代码的解释:指令格式为“指令类型+数字+\n”,前4位为指令类型分别有1001、1002和1003,分别对应的功能为对数字进行1次方、2次方和3次方,指令结尾为“\n”。该代码会对输入的指令进行解读,并且进行相应的功能,返回对应的数值。
String str,pre_str,last_str;
String type_str[] = {"1001", "1002", "1003"}; //指令前4位表示不同类型指令,其中1001表示1次方,1002表示2次方,1003表示3次方
int i,j,ans,cnt;
void setup() {
Serial.begin(115200);// 设置波特率
}
void loop() {
delay(100);//等待100ms=0.1s
while (Serial.available() > 0)
{
str= Serial.readStringUntil('\n');//读取指令,指令以“\n”结尾,通过“\n”来分割不同的指令
pre_str = str.substring(0, 4);//截取输入指令前4位
last_str= str.substring(5, str.length());//截取输入指令4位后的数据
cnt = last_str.toInt();//将字符串last_str转成数字cnt
ans=cnt;
// 根据不同指令计算cnt多少次方
for(i=0;i<3;i++){
if(pre_str.compareTo(type_str[i])==0){
for(j=0;j<i;j++){
ans*=cnt;
}
}
}
Serial.println(ans);
}
}
代码串口测试: