任务安排

学习Arduino的串口使用;半个小时
使用print发送ASCII格式的格式化数据;发送数据,保存为csv格式,并在excel中绘制曲线;1小时
学习各个print函数的应用场景的差异;半小时
构造数据,同时输出方波、三角波、正弦波(即通过Arduino输出数据,假定3路ADC采样了上述三种波形),保存下来然后在excel中绘图验证;1小时
如果说眼睛是心灵的窗户,那么用好串口就打开了通往单片机心灵的通道。
在实际的工程开发中,工程师拿到开发板,通过烧录闪灯(blink)程序测试了开发环境的安装和开发板的好坏之后,就会开始测试串口的使用。
单片机开发的过程,就是工程师和单片机沟通、交流的过程。工程师编写代码,控制单片机完成指定的功能,并通过指示灯、信号输出配合示波器或逻辑分析仪等手段获得反馈,进而了解和确认预期功能是否实现。但是串口能提供的信息显然是最丰富的。通过串口输出,工程师不仅可以知道程序运行到哪一个环节了,还可以在不中断单片机运行流程的情况下,了解程序运行的各个细节。工程师可以通过串口随时输出关键变量在关键节点的值以及此时的单片机时间,快速了解单片机程序的运行状态,定位故障或者BUG所在的位置。

Arduino中的串口类

在官方的Arduino核心库中,通过 Serial 类来使用开发板上的UART串口。以Arduino Mega2560为例(例程来自Arduino管网),只需要指定串口的波特率,然后就可以直接使用了。
// Arduino Mega using all four of its Serial ports // (Serial, Serial1, Serial2, Serial3), // with different baud rates: voidsetup(){Serial.begin(9600);Serial1.begin(38400);Serial2.begin(19200);Serial3.begin(4800);Serial.println("Hello Computer");Serial1.println("Hello Serial 1");Serial2.println("Hello Serial 2");Serial3.println("Hello Serial 3");}voidloop(){}
上例中,Mega2560(Arduino开发板的一个具体型号)的4个串口(Serial , Serial1, Serial2 , Serial3)已经在板级支持库中提供了直接的定义,在使用时只需要指定波特率,然后就可以开心的使用了。
在已经适配了Arduino框架的STM32的单片机上使用串口,配置也非常简单。因为ARM系列的单片机上,UARTx(x为0,1,...)对应的引脚是可以用户设置的,因此在申明串口变量的时候可以指定具体的引脚。熟悉STM32编程的同学就会知道,PA9和PA10在几乎所有的STM32单片机上都可以用作UART。在下面的例子所用的开发板也就选择了这一配置。
//适用于的STM32F030的开发板的测试 // 1、可以成功下载; // 2、IO和串口 OK! //Version : 1.0 //Author : Yujin Huang //Email : yujinh@126.com //Location : Wuhan,Hubei,China //Date : 2021-3-12 #includeHardwareSerialpc(PA10,PA9);//注意,在STM32中,一般大序号在前,小序号在后 voidsetup(){// put your setup code here, to run once: pc.begin(115200);}voidloop(){pc.print("millis:");pc.print((int)millis());pc.print("PA0:");pc.println(analogRead(PA0));}
上述串口都是UART串口,连接到PC需要经过USB-串口桥接器(如CH340、FT3232、CP2101等)。在RPI Pico开发板中,内置有USB的IP和物理驱动,这个USB可以配置为串口(CDC,Communication Device Class,USB的一个应用的分类,表示用于通讯的设备,可能是串口,也可能是USB网卡等), HardwareSerial 就没有办法把这类新设备包含进去了。因此,提供了一个新的类 UART 对HardwareSerial 进行了继承封装,兼顾CDC或普通的UART串口,不改变普通用户(程序员)的体验。
无论是传统的Arduino还是新型的RPI Pico,基本上只需了解 begin()、print()、write()、read()、available()等几个函数的使用,就可以解决绝大多数串口开发的应用需求。

begin()

函数原型: void begin(unsigned long);
只有一个参数,通讯的波特率。常用的波特率为9600、115200、4800等。

print()

从Print类继承而来,提供了一系列同名不同参的函数,在编译阶段就可以确定该如何对数据进行处理,改变PC上标准输入输出库中 printf()的函数的变参带来的高昂的处理负担。
voidloop(){inta=34;serial.println(a);floatpi=3.14159;serial.println(pi);longvalue=12345678;serial.println(value);}

write()

函数原型size_twrite(constuint8_t*c,size_tlen)
除了可以向串口打印字符、字符串和各类基本数据类型外,也可以向串口写数据块。

available()

函数原型intavailable()
返回串口接收缓冲区目前已有的字节数。
这里特别要说一下,笔者在开发中选择Arduino家族的开发板,一个非常重要的原因就是Arduino软件框架提供了非常优秀的串口类。
这个类是异步的,也就是说当用户(使用类的程序员,相对开发类的程序员)往串口发送信息时,这个信息并不是立即就发送完了的(做一个简单的计算,9600的波特率,包含起止位的话每个字节10个bit,那么1s只能发送960个字节,大概1字节/ms),而是被Arduino的驱动软件进行了缓冲,自行管理,实现在一个字节发完之后再发另外一个字节。但是细节对与用户是屏蔽的,用户需要发的时候只管随心所欲的发[1]!
在串口的数据接收上,也采用了后台缓冲策略,不是每收到一个字节,用户都要自己马上处理。同样是后台通过中断对收到的数据进行缓冲,用户在空闲的时候或者需要的时候再对数据进行处理。
注意,前面讲到了Arduino框架为我们准备了串口发送和接收缓冲区,这个缓冲区的长度是有限制的。大家在收发数据的时候应该考虑一下串口通讯的速度,不建议一直不停的往串口print数据,适当的时候delay一下,或者确保每秒发送的数据的字节数不超过波特率的1/10,否则有可能造成烧录程序也不响应。

read()

返回串口缓冲区的一个字节。如果没有数据则返回-1。Arduino官方提供的一个参考例程很好的展示了如何从串口读数据。
intincomingByte=0;// for incoming serial data voidsetup(){Serial.begin(9600);// opens serial port, sets data rate to 9600 bps }voidloop(){// send data only when you receive data: if(Serial.available()>0){// read the incoming byte: incomingByte=Serial.read();// say what you got: Serial.print("I received: ");Serial.println(incomingByte,DEC);}}

从Arduino单片机往PC发格式化的数据

从串口往PC发送数据,通常有2类格式:1、二进制数据,2、ASC-II数据。ASC-II格式可读性好,便于初学者发现问题,本实验中就用ASC-II格式为例进行演示。大家可以用Arduino开发板或者单片机开发板生成需要传送到PC的数据。
假设在应用中需要传递温度、压力、湿度,或者磁场的X、Y、Z分量,每次传递20组数据。
下面提供了一个在stm32duino开发板上经过 验证的例程:
//main.cpp // //Version : 1.0 //Author : Yujin Huang //Email : yujinh@126.com //Location : Wuhan,Hubei,China //Date : 2020-5-12 #include//name pc means this serial port connects PC HardwareSerial&pc=Serial1;// 在RPI Pico上替换为 UART pc(16,17,-1,-1);即可 voidsetup(){// put your setup code here, to run once: pc.begin(115200);}intsn=0;//x y z,represent sensor data neede send to PC floatx=0.1;floaty=0.3;floatz=0.4;voidloop(){//Modify x y z to simulate varation of x y z x+=0.1;y+=0.2;z+=0.3;//计划发送的数据的格式 : "sn,x,y,z\n" //increase record sequence number sn++;if(sn==20)sn=0;pc.print(sn);pc.print(",");//send : x,y,z pc.print(x);pc.print(",");pc.print(y);pc.print(",");pc.print(z);pc.print("\n");delay(10);}
STM32Duino上调试通过的代码适配到RPI Pico,仅需行5进行小修改
在上面的例程中发送的数据的格式是 : "sn,x,y,z\n",用串口调试助手接收数据,并保存为.csv文件,就可以在excel表格中直接看到格式化的数据了。
Arduino程序发送数据,在串口助手接收到的信息
上述数据格式也称为逗号分隔的数据记录,在工业、科研上经常使用。
将串口助手接收的收据保存为.csv文件后缀后通过Excel打开,该格式也支持MATLab用csvRead()函数读入,进行进一步的处理

小结

1、在Arduino中,RPI Pico的最简串口使用参考
#include//name pc means this serial port connects PC UARTpc(16,17,-1,-1);voidsetup(){// put your setup code here, to run once: pc.begin(115200);}voidloop(){//print: time,x,y,z\n pc.print(millis());//time pc.print(",");pc.print(rand());//x pc.print(",");pc.print(rand());//y pc.print(",");pc.print(rand());//z pc.print("\n");delay(10);}
2、在PC中,等价的用于解析上述数据的语句是:
#includeintt;floatx,y,z;scanf("%d,%f,%f,%f\n",&t,&x,&y,&z);
3、构造3分量的随机游走的数据:
floatx=56,y=100,z=10;voidloop(){//print: x,y,z\n x+=rand()%10-5;y+=rand()%20-19.0/2;z+=rand()%30-29.0/2;pc.print(x);pc.print(",");pc.print(y);pc.print(",");pc.print(z);pc.print("\n");delay(10);}
4、用于测试2个串口的代码
#includeUARTpc1(4,5,NC,NC);//NC=Not Connected UARTpc0(16,17,-1,-1);//-1等价于NC voidsetup(){// put your setup code here, to run once: pc0.begin(115200);pc1.begin(115200);}voidloop(){// put your main code here, to run repeatedly: pc0.print("pc0 ");pc0.println(millis());pc1.print("pc1 ");pc1.println(millis());delay(100);}