一.串口
1.1 串口简介
通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。比如STM32芯片,它里面集成了很多功能模块,什么定时计数、PWM输出、AD采集。这些都是芯片内部的电路,这些电路的配置寄存器,数据寄存器都在芯片里面,操作这些寄存器十分简单,直接读写就可以了。但是也有一些功能是STM32内部没有的。比如蓝牙无线遥控的功能,想要陀螺仪加速度计测量姿态的功能。STM32没有,所以就只能外挂芯片来完成。那外挂的芯片,数据都在STM32外面,STM32如何能获得这些数据呢?这就需要我们两个设备之间,连接上一根或多根通信线。通过通信线路发送或者接收数据,完成数据交换。从而实现控制外挂模块和读取外挂模块数据的目的。
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
全双工:就是指通信双方能够同时进行双向通信,一般来说,全双工的通信都有两根通信线,比如串口,一个TX发送,一个RX接收等等,发送线路和接收线路互不影响,剩下的这些,I2C、CAN和USB都只有一根数据线,CAN和USB两根差分线也是组合成为一根数据线的,所以是半双工,当然还有一种方式,就是单工,单工是指数据只能从一个设备到另一个设备,而不能反着来,
然后是时钟特性,比如你发送一个波形,高电平然后是低电平,接收方怎么知道你是1、0,还是1、1、0、0呢?这就需要有一个时钟信号来高速接收方,你什么时候需要采集数据,时钟特性分为同步和异步,这里I2C和SPI都有单独的时钟线,所以它们是同步的,接收方可以在时钟信号的指引下进行采样,剩下的串口、CAN和USB没有时钟线,所以需要双方约定一个采样频率,这就是异步通信。 并且还需要加一些帧头帧尾等,进行采样位置的对齐。
之后是电平特性,上面三个都是单端信号,也就是它们引脚的高低电平都是对GND的电压差,所以单端信号通信的双方必须要共地,就是把GND接在一起。所以说,这里通信的引脚,前三个还应该加一个GND引脚,不接GND是没法通信的。之后CAN和USB是差分信号,它是靠两个差分引脚的电压差传输信号的。在通信的收,可以不需要GND。不过USB协议里面也有一些地方需要单端信号。所以USB还是需要共地的。使用差分信号可以极大的提高抗干扰特性。所以差分信号一般传输速度和距离都非常高。
最后看一下设备特性,串口和USB属于点对点的通信,中间三个是可以在总线上挂载多个设备的
7.2 HEX和文本模式的区别
实际在串口中,只能发送二进制数,也就是十六进制最直接的数据,如果想发送字符,那我们就需要一个数据到字符的映射表,最简单最常用的表就是ASCII码表。在ASCII码表里,0x41这个数,就映射为字符A,所以发送0x41,如果以HEX模式显示,就是数据本身,如果以文本模式显示,它就会先去找一下映射表。最终发现,0x41对应的字符A,所以就是显示A了。这就是HEX模式和文本模式的区别。
1.2 USB转串口(TTL)模块
先了解一下什么是TTL。
TTL一般是从单片机中发出的电平,高电平为5V(51单片机)或者3.3V(STM32)。
TTL电平:+3.3V或+5V表示1,0V表示0。低压小型设备,如单片机。 (2)RS232电平:-3到-15V表示1,+3到+15V表示0。一般在大型机器使用,由于环境比较恶劣静电干扰比较大,所以电压较大且允许波动的范围较广。 (3)RS485电平:两线压差+2到+6V表示1,-2到-6V表示0(差分信号)。通信距离可达上千米,上面两种最远几十米。
使用这个模块之前,要安装驱动。51普中下载程序就是靠串口下载的。
上面有个芯片,型号是CH340,这个芯片可以把串口协议转换为USB协议,它一边是USB口,可以插在电脑上,另一边是串口的引脚,可以和支持串口的芯片接在一起。这样就能实现串口和电脑通信了。
接线方法
USB转串口模块共有6个引脚,分别是VCC,GND 3.3V 5V RXD TXD。将VCC和3.3V短接,5V接到STLINK的5V,GND接单片机上的GND,RXD接单片机的TX(PA9),TXD接单片机上的RX(PA10)。
拔掉GND的线,单片机任然可以发送数据到单片机上。
将3.3V和GND短接的线拔掉,单片机任然可以发送数据。
拔掉5V供电,用单片机的3.3V给串口供电,也可以发送数据。
供电端不接任何电源,也可以发送数据。
了解那几个串口打印函数的使用方法。
为什么按复位按钮的时候单片机回发送数据。
因为是在main函数里,因为每次给单片机重新上电或者点击复位的时候,都会重新执行一遍main函数里面的内容。
使用文本方式接收的话,接收到的是字符A,因为十六进制0x41对应的是字符A。
思考,为什么41之间有一个空格,A之间没有空格。
发送数字
这里可以看到,发送数字的底层也是将我们传入的十进制数字转换为字符型的。一个字符一个字符发送到上位机上通过串口打印出来的。
直接打印printf("\r\nNum2=%d", 222);
因为底层调用了
int fputc(int ch, FILE *f)
{
//在里面,要把fputc重定向到串口
Serial_SendByte(ch);
return ch;
}
所以我们可以输出重定向到串口。
同理
Serial_Printf("\r\nNum4=%d", 444);
这个也要了解一下。
void Serial_Printf(char *format, ...) //这个参数用来接收格式化字符串。 可变参数的用法...
{
char String[100];
va_list arg; //定义一个参数列表变量
va_start(arg, format); //从format位置开始接收参数表,放在arg里面
vsprintf(String, format, arg); //因为sprintf只能接收直接写的参数,对于这种封装格式,要用vsprintf
va_end(arg); //释放参数表
Serial_SendString(String);
}
对于串口接收来说,可以有查询和中断两种方法。
这里是怎么知道RXNEIE位为1的,因为在代码中并没有配置,只是写了和中断函数相关的代码,并没有直接操作这移一位。
对于一个全局变量或者数组,只要在其.h文件下加上extern 就可以在任意文件下调用了。
Static与全局变量的区别
Strcmp 判断两个函数是否相等,相等的话,返回值为0.
Memset
Mecpy
发送完\r\n之后,要加一句这个
Serial_RxPacket[pRxPacket] = '\0'; //最后要加一个\0,方便我们对字符串进行处理。这样就是一个完整的字符串。
如果不加这个的话,会造成发送错误。
比如我要发送@LED_ON\r\n 可以正常发送,然后发送@LED_OFF\r\n,也可以正常发送,但是此时如果我要发送@LED_ON\r\n的话,就会显示发送错误,系统显示我发送的是
@LED_ONF。
7.4 CH340介绍
CH340是一个USB总线的转换芯片,实现USB转串口或者打印口,在串口方式下,CH340提供常用的MODEM联络信号,用于为计算机扩展异步串口。或者将普通的串口设备升级到USB总线。
Ch340具有以下特点:
全速USB设备接口,兼容USBV2.0
仿真标准串口,用于升级原串口外围设备或者通过USB增加额外串口
硬件全双工串口,内置收发缓冲区
支持常用的MODEM联络信号(RTS、DTR、DCD、RI、DSR、CTS)(了解一下)
支持5V电源电压和3.3V电源电压。
CH340的引脚
VCC:为电源输入端,需要外接0.1uF退耦电容,支持5V和3.3V电源电压。
GND:为公共地。
V3:V3引脚在3.3V电源电压时连接VCC输入外部电源。在5V电源电压时外接容量为0.1uF退耦电容。
XI:XI为晶体震荡的输入端。
XO:XO为晶体震荡的输出端。XI与XO需外接12MHz晶体震荡器及震荡电容
UD+和UD-:USB信号端,分别接USB的D+和D-数据线。
TXD:TXD为串行数据输出,RXD为串行数据输入。
R232:用于辅助RS232使能,高电平有效。内置下拉电阻。R232为高电平时RXD输入自动反向
DTR#,DSR#,RTS#,CTS#,DCD#,RI#为MODEM联络信号引脚。
DTR#:MODEM联络输出信号,数据终端就绪。
DSR#:MODEM联络输入信号,数据装置就绪。
RTS#:MODEM联络输出信号,请求发送。
CTS#:MODEM联络输入信号,清除发送。
DCD#:MODEM联络输入信号,载波检测。
RI#:MODEM联络输入信号,振铃提示。
这些均为低电平有效。所有这些MODEM联络信号都是由计算机应用程序控制并定义用途。
其中DTR引脚和RTS引脚一般情况下为高电平。在下载程序时会变为低电平知道程序下载完。可以用于通过复位引脚复位单片机后通过电路实现单片机启动。
7.5 串口通信连接
简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线
当电平标准不一致时,需要加电平转换芯片
像我们这种,直接从控制器里出来的信号,一般都是TTL电平。相同的电平才能互相通信。不同的电平信号,需要加一个电平转换芯片,转接一下。
串口常用的几种电平标准:
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
使用RS485电平标准,通信距离可以达到上千米,而上面这两种电平,最远只能达到几十米,再远就传不了了。像单片机这种低压小型设备,使用的都是TTL电平,如果你做设备需要其它的电平,那就再加电平转换芯片就可以了。在软件层面,它们都属于串口,所以程序并没有什么变化。因为STM32是3.3V器件,所以如果线路对地是3.3V,就代表发送了逻辑1,如果线路对地是0V,就代表发送了逻辑0
7.6 串口参数及时序
波特率:串口通信的速率,串口一般是异步通信,所以需要双方约定一个通信速率,比如我每隔1s发送一位,那你就得也每隔1s接收1位,如果你接收快了,那就重复接收某些位,如果接收慢了,那就会漏掉某些位,所以发送和接收,必须约定好速率。这个速率参数,就是波特率。波特率本来的意思是每秒传输码元的个数,单位是码元/s,或者直接叫波特(Baud),另外还有个速率表示,叫比特率,比特率的意思是每秒传输的比特数,单位是bit/s,或者叫bps.在二进制的情况下,一个码元就是一个bit,此时波特率就等于比特率,像我们单片机的串口通信,基本都是二进制调制,也就是高电平表示1,低电平表示0,一位就是1bit。所以说串口的波特率,经常会和比特率混用,不过这也是没关系的,因为这两个说法的数值相等。如果是多进制调制,那波特率和比特率就不一样了。(了解波特率和比特率的关系)
比如我们双方规定波特率为1000bps,那就表示,1s要发1000位,每一位的时间就是1ms。发送方每隔1ms发送一位,接收方每隔1ms接收一位,这就是波特率,它决定每隔多久发送一位。
起始位:标志一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位:用于数据验证,根据数据位计算得来
停止位:用于数据帧间隔,固定为高电平
首先,看一下下面的这个波形,首先,串口的空闲状态是高电平,也就是没有数据传输的时候引脚必须要置高电平,作为空闲状态。然后需要传输的时候,必需先发一个起始位,这个起始位必须是一个低电平,来打破空闲状态的高电平,产生一个下降沿。这个下降沿就告诉接收设备,这一帧数据就要开始了。如果没有起始位,当我们发送8个1的时候,是不是数据线就一直都是高电平,没有任何波动,这样,接收方怎么知道我发送数据了呢?所以这里必须要有一个固定为低电平的起始位。同理,在一个字节数据发送完成后,必须要有一个停止位,这个停止位的作用是,用于数据帧间隔,固定为高电平,同时这个停止位,也是为下一个起始位做准备的。如果没有停止位,那当我数据最后一位是的时候,下次再发送新的一帧,是不是就没法产生下降沿了。这就是起始位和停止位的作用。
数据位:是低位先行的,这个要记住。
校验位:有三种方式,无校验,奇校验和偶校验。如果使用了奇校验,那么包括校验位在内的9位数据会出现奇数个1,同样的,偶校验也是相同的方法。如果两位同时出错,那么奇偶校验就检验不出来了。所以奇偶校验只能保证一定程度上的数据校验,如果想要更高的检出率,可以了解一下CRC校验。(了解CRC校验)
7.7 串口波形
用示波器检测的,操作方法是,把探头的GND连接在负极,探头接在发送设备的TX引脚,然后发送数据,就能够发送波形了。
第一个波形,发送一个字节数据0x55,用二进制表示位0101 0101,但是图中显示的是1010 1010,这是因为串口数据是从低位发送的。波特率是9600所以每一位的时间就是1/9600,大概是104us。
7.8 USART简介
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器S表示同步的意思,另外我们经常遇到串口,叫UART,这少了个S,就是异步收发器。一般我们串口很少使用这个同步功能,所以USART和UART使用起来,也没有什么区别。其实这个STM32的USART同步模式,只是多了一个时钟输出而已。它只支持时钟输出,不支持时钟输入。所以这个同步模式是为了兼容别的协议或者用途而设计的。并不支持两个USART之间进行同步通信。所以我们学习串口,主要还是异步通信。
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
自带波特率发生器,最高达4.5Mbits/s
可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
这个同步模式,就是多了一个时钟CLK的输出。
硬件流控制,这个是,比如A设备有个TX向B设备的RX发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制。那B就只能抛弃新数据或者覆盖原数据了。如果有硬件流控制,在硬件电路上,会多出一根线,如果B没准备好接收,就置高电平,如果准备好了,就置低电平。A接收到了B反馈的准备信号,就只会在B准备好的时候,才发数据。如果B没有准备好,那数据就不会发送出去。这就是硬件流控制,可以防止因B处理慢而导致数据丢失的问题。硬件流控制,一般STM32中也是有的,不过我们一般不用。
之后DMA,是这个串口支持DMA进行数据转运。
STM32F103C8T6 USART资源: USART1、 USART2、 USART3其中USART1是APB2总线上的设备,剩下都是APB1总线的设备。
7.9 USART框图
如果发送设备发的太快,接收设备来不及处理。就会出现丢弃或覆盖数据的现象,有了流控,就可以避免这个问题了,这里流控有两个引脚,一个是nRTS,一个是nCTS。nRTS(Request To Send)是请求发送,是输出脚,也就是告诉别人,我当前能不能接收。nCTS(Clear To Send)是清除发送,是输入脚,也就是接收别人nRTS信号的,这里前面加个n是低电平有效。那这两个引脚是怎么工作的呢?首先,得找另一个支持流控的串口,它的TX接到我的RX,然后我的RTS要输出一个能不能接收到反馈信号,接到对方的CTS,当我能接收的时候,RTS就置低电平,请求对方发送,对方的CTS接收到之后,就可以一直发。当我处理不过来时,比如接收数据寄存器我一直没有读,又有数据过来了,现在就代表我没有及时处理,那RTS就会置高电平,对方CTS接收到之后,就会暂停发送。直到这里接收数据寄存器被读走。RTS置低电平,新的数据才会继续发送。那反过来,当我的TX给对方发送数据时,我们CTS就要接到对方的RTS,用于判断对方,能不能接收。TX和CTS时一对的。RX和RTS是一对的。CTS和RTS也要交叉连接。这就是流控的工作模式。
7.10 串口通信流程图
最左边是波特率发生器,用于产生约定的通信速率,时钟来源是PCLK2或1,经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送控制器和接收控制器,用来控制发送移位和接收移位,之后,由发送数据寄存器和发送移位寄存器这两个寄存器配合。将数据一位一位的移出去。通过GPIO的复用输出,输出到TX引脚。产生串口协议规定的波形,这里画了几个右移的符号,就代表这个移位寄存器是往右移的,是低位先行,当数据由数据寄存器转移到移位寄存器时,会置一个TXE的标志位(TX Empty 发送寄存器为空),我们判断这个标志位,如果置1了,我们就可以在TDR写入下一个数据了。注意一下,当TXE置1了,数据其实还没有发送出去,只要数据从TDR转移到发送移位寄存器了,TXE就会置1,我们就可以写入新的数据。然后发送移位寄存器就会在下面的这里的发送器控制的驱动下,向右移位,然后一位一位地,把数据输出到TX引脚,这里是向右移位的,所以正好和串口协议规定的低位先行,是一致的。
然后接收部分也是类似的,RX脚的波形,通过GPIO输入,在接收控制器的控制下,一位一位地移入接收移位寄存器,这里画了右移地符号,也是右移的。因为是低位先行,所以要从左边开始移进来,移完一帧后,数据就会统一转运到接收数据寄存器,在转移的同时,同时置一个RXNE标志位(RX Not Empty 接收数据寄存器非空)当我们检测RXNE置1之后,就可以把数据读走了,我们检查这个标志位,就可以知道是不是接收到数据了。同时这个标志位可以申请中断,这样就可以在收到数据时,直接进入中断函数。然后快速的读取和保存数据。右边看起来是4个寄存器,其实只有一个DR寄存器可以供我们读写,写入DR时,数据走上面这条路,进行发送。读取DR时,数据走下面这条路,进行接收。这就是USART进行串口数据收发的过程。最后右下角是一个开关控制,就是配置完成之后,用Cmd开启一下外设。
串口的输出TX应该是比输入RX简单很多,输出时定时翻转高低电平就可以了。但是输入就复杂一些,你不仅保证,输入的采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位的正中间,只有在每一位的正中间采样,这样高低电平都进来,才是最可靠的。如果你采样点过于靠前或者靠后,那有可能高低电平还在翻转,电平还不稳定或者稍有误差,数据就采样错了。另外,输入最好还要对噪声有一定的判断能力,如果是噪声,最好能置个标志位提醒我一下。
7.11 波特率发生器
假设我们想要一个波特率为9600的,如何配置这个BRR寄存器呢,我们带入公式,就是9600=72000 000 / (16 * DIV) DIV=468.75 是一个带小数的分频系数,最终写到寄存器还需要转换成二进制 得到的二进制是1110 0100.11,所以最终写到这个寄存器就是,整数部分为11101 0100,前面多出的补0,小数部分为11,后面多出来的补0。这就是根据波特率写BRR寄存器的方法。不过我们用库函数的话,非常方便,需要多少波特率直接写就可以了。库函数会帮我们计算。
7.12 串口的引脚是指定的
使用串口,都是有指定的IO口的,比如使用USART1,那TX必须是PA9,RX必须是PA10,或者看一下重映射这里,有没有重映射。
7.13 USB转串口模块注意事项
USB转串口模块要用跳线帽将VCC和3.3V接在一起,这样选择通信的TTL电平为3.3V。
7.14 串口的一些库函数
USART_DMACmd(),这个可以开启USART到DMA的触发通道。
USART_SendData(),发送数据,就是写DR寄存器
uint16_t USART_ReceiveData(),接收数据 就是读DR寄存器。
7.15 串口引脚
串口TX引脚是USART外设控制的输出脚,所以要选复用推挽输出,RX引脚是USART外设数据输入脚,所以要选择输入模式,输入模式并不分什么普通输入,复用输入,一根线只能有一个输出,但可以有多个输入,所以输入脚,外设和GPIO都可以同时用。一般RX配置成浮空输入或者是上拉输入,因为串口波形空闲状态下是高电平,所以不使用下拉输入。
7.16 HEX和文本模式
HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
文本模式/字符模式:以原始数据编码后的形式显示
在Hex模式下,只能写16进制数。不用写0x
常用的汉字字符集有GB2312,GBK,GB18030等。为了防止不同国家编码的不兼容现象,我们可以把所有国家的字符收录到一个统一的字符集,这就是Unicode字符集,Unicode最常用的传输形式是UTF-8。
7.17 转义字符
0 的转义字符表示也是 ‘\0’,表示空字符,一般用于判断是否到末尾了。如果等于0,就是结束了,停止循环。
Serial_SendString(“HelloWorld”); 在写完这个字符之后,编译器会自动补上结束标志位。所以字符串的存储空间,会比字符的个数大1。如果想实现换行,可以使用转义字符\r\n,来执行换行命令。‘\r’(0x0D)是回车,前者使光标移动到首行,’\n’(0x0A)是换行,后者使光标移动到下一格。
7.18 printf函数的移植方法
介绍一下printf函数的移植方法,使用printf之前,我们需要打开工程选项,把这个Use MicroLIB勾上
MicroLIB是Keil为嵌入式平台优化的一个精简库,我们等会儿要用到的printf函数就可以用这个MicroLIB,所以先勾上这个。然后我们还需要对printf进行重定向。将printf函数打印的东西输出到串口。因为printf函数默认是输出到屏幕,我们单片机没有屏幕,所以要进行重定向。步骤就是,在串口模块里,最开始加上#include<stdio.h>,之后,在这后面,重写fputc函数int fputc(int ch, FILE *f)这个照着写就行了。然后在里面,我们要把fputc重定向到串口。
int fputc(int ch, FILE *f)
{
//在里面,要把fputc重定向到串口
Serial_SendByte(ch);
return ch;
}
那重定向fputc和printf有什么关系呢?
这是因为,这个fputc是printf函数的底层。Printf函数在打印的时候,就是不断调用fputc函数一个个打印的。我们把fputc重定向到串口,那printf自然输出到串口了。
之后在主函数里直接写。
Printf(”Num=%d\r\n”,666);
给单片机复位,就可以看到单片机的数据发送到我们的电脑上了。
接下来再介绍两种printf函数的移植方法。
上面的那种方法printf只能有一个,你重定向到串口1了。那么串口2就没有了。如果多个串口都想用printf怎么办呢?这时候就可以用sprintf,sprintf可以把格式化字符输出到一个字符串里。所以这里可以先定义一个字符串。
char String[100];
然后sprintf,第一个参数,是打印输出的位置。
sprintf(String, "\r\nNum3=%d", 333);
最后还需要一个
Serial_SendString(String);
因为sprintf可以指定打印的位置,不涉及重定向的问题,所以每个串口都可以用sprintf进行格式化打印。
最后,再介绍一种方法,就是你看sprintf,每次都得先定义字符串,再打印到字符串,再发送字符串。太麻烦了,我们要是能封装一下这个过程,就再好不过了。所以第三种方法就是封装sprintf,有printf这种函数比较特殊,它支持可变的参数,像我们之前写的函数,参数的个数都是固定的。可变参数这个执行起来比较复杂。这里时间关系,我们只讲封装的步骤。首先在串口模块里,添加头文件#include<stdarg.h>,然后在最后这里,对sprintf函数进行封装。
void Serial_Printf(char *format, ...) //这个参数用来接收格式化字符串。
{
char String[100];
va_list arg; //定义一个参数列表变量 va_list是一个类型名,arg是变量名。
va_start(arg, format); //从format位置开始接收参数表,放在arg里面
vsprintf(String, format, arg); //因为sprintf只能接收直接写的参数,对于这种封装格式,要用vsprintf
va_end(arg); //释放参数表
Serial_SendString(String);
}
之后就可以在主函数里直接写
Serial_Printf("\r\nNum4=%d", 444);
Serial_Printf("\r\n");
7.19 解决串口汉字乱码的问题
- Keil和串口助手都选择UTF8,且Keil加上—no-multibyte-chars参数
- 要么都使用GB开头的中文编码格式,参数不用加。
7.20 串口接收的两种方法
对于串口接收来说,可以使用查询和中断两种方法,如果使用查询,那初始化就结束了,如果使用中断,那还需要再这里开启中断,配置NVIC。查询的流程是,在主函数里不断判断RXNE标志位。如果置1了,就说明收到数据了。那再调用ReceiveData,读取DR寄存器。
7.21 串口数据包的方式
数据包的意思是把一个个单独的数据打包起来,方便我们进行多字节的数据通信,我们之前学习了串口的代码,发送一个字节,接收一个字节都没有问题,但在实际应用中。我们可能需要把多个字节打包一个整体进行发送。比如说,我们有个陀螺仪传感器,需要用串口发送数据到STM32,陀螺仪的数据,比如X轴一个字节,Y轴一个字节,Z轴一个字节。总共3个数据,需要连续不断的发送,当你像这样,XYZXYZXYZXYZ连续发送的时候,就会出现一个问题,就是接收方它不知道哪个数据对应X,哪个对应YI,哪个对应Z,因为接收方可以从任意位置开始接收,所以会出现数据错误的现象。这时候,我们就需要研究一种方式,把这个数据进行分割,把XYZ这一批数据分隔开,分成一个个数据包。这样再接收的时候,就知道了,数据包第一个数据就是X,第二个就是Y,第三个就是Z,这就是数据包的任务,就是把属于同一批的数据进行打包和分割,方便接收方进行识别。那有关分割打包的方法,可以自己发挥想象力设计。我们串口数据包,通常使用的是额外添加包头包尾这种方式。
7.22 HEX数据包
我们串口数据包,通常使用的是额外添加包头包尾这种方式,如上列举了两种数据包格式,第一种是固定包长,含包头包尾,也就是每个数据包的长度都固定不变。数据包前面是包头,后面是包尾,第二种是可变包长,含包头包尾,也就是每个数据包的长度可以是不一样的。
那我这里规定的是,比如固定包长这里,我一批数据,规定有4个字节,再这4个字节之前,加一个包头,比如我定义了0xFF为包头,在4个字节之后,加一个包尾,比如我定义0xFE为包尾,那当我接收到0xFF之后,我就知道了,一个数据包来了,接着我再接收4个字节。就当做数据包的第1、2、3、4个数据,存在一个数组里最后跟一个包尾,当我收到0xFE之后,就可以置一个标志位,告诉程序,我收到了一个数据包,然后新的数据包过来,再重复之前的过程。这样就可以在一个连续不断的数据流中,分割出我们想要的数据包了。
7.23 文本数据包
在HEX数据包里面,数据都是以原始的字节数据本身呈现的,而在文本数据包里面,每个字节就经过了一层编码和译码,最终表现出来的,就是文本格式。这里和上述HEX是差不多的。
7.24 HEX与文本的优点与缺点
HEX数据包,优点是,传输最直接,数据解析非常简单,比较适合一些模块发送一些原始的数据,比如一些使用串口通信的陀螺仪,温湿度传感器,缺点是灵活性不足,载荷容易和包头包尾重复。
文本数据包,优点是数据直观易理解,非常灵活,比较适合一些输入指令进行人机交互的场合,比如蓝牙模块常用的AT指令,CNC和3D打印机常用的G代码。缺点就是效率低,如果发送一个数100,HEX数据包就是一个字节100,完事。文本数据包就是3个字节的字符,’1’,’0’,’0’,收到之后还要把字符转换成数据,才能得到100。
7.25 状态机的思维解析HEX和文本数据包
发送数据包容易理解,接收比较难理解一点 接收使用“状态机”的思维方法。
7.26 FlyMcu和STLINKUtility的使用
第一个软件是FlyMcu可以通过串口给STM32下载程序。如果你没有STLINK,就可以用这个软件通过串口下载程序。这里必须是USART1,因为我们这个芯片的串口下载只适配了USART1,接在其它USART上是不行的。
第二个软件是STLINK Utility,这个软件是配合STLINK使用的一个工具,可以通过STLINK给STM32下载程序,也可以进行一些其它的操作,总的来说,这两个软件的功能差不多,都可以下载程序。FlyMcu是串口下载,STLINK Utility是STLINK下载。
FlyMcu是绿色软件,直接打开,不需要安装。为了串口下载,这里需要配置工程,生成一个HEX文件 点击工程选项àOutputàCreat Hex file.然后编译一下。 有一个 FromELF:createing hex file...表示生成了hex文件。之后打开Object文件,找一个Project.hex的文件。
这就是我们串口下载所需要的程序文件。打开FlyMcu,先点击搜索串口,然后Port这里选择我们串口通信的COM号。然后选择波特率bps,这里选择115200,接着点击三个点的按钮,选择hex文件。然后再开始之前,我们还需要配置BOOT引脚,让STM32执行BootLoader程序。否则点击开始编程,会卡住。如何进入BootLoader呢?找到那两个黄色的跳线帽,是用来配置BOOT引脚的。上面的是配置Boot0引脚的。下面是配置Boot1引脚的。都是默认配置的0,现在把BOOT0的跳线帽拔下来,接到右边,配置Boot0为1.切换Boot引脚后要按一下复位键,让程序重新开始运行。因为STM32在刚复位时才会读取BOOT引脚。程序运行之后,切换BOOT引脚是无效的。那这样,芯片就进入BootLoader程序了。进入BootLoader程序之后,STM32执行的程序是不断接收USART1的数据,刷新到主闪存。之前点击开始编程,就可以正常下载了,这样,我们这个LED闪烁的程序,就通过BootLoader成功刷新到主闪存里了。回到STM32,目前LED还没有亮起,这是因为BootLoader还在执行BootLoader的刷机程序,我们还需要把BOOT引脚换回来。拔掉BOOT0的跳线帽,换到左边两个引脚。然后按一下复位。可以看到LED闪烁,程序正常运行。
研究两个问题
- BOOT引脚是干啥的,为啥这样配置,BootLoader又是干啥的,串口下载的原理是什么?
在ROM区的0800位置,存储的是编译后的程序代码,你把什么程序写道这个位置,STM32就执行什么样的程序。如果想使用串口下载程序的话,我们只需要把程序数据通过串口发送给STM32,STM32接收数据,然后刷新到0800这一块位置就行了。但是接收并转存数据,这个过程本身也是程序,如何实现程序自我更新,这是一个问题。就像是一个机器人,给自己换电池一样,换电池,需要先拆掉旧电池,再装上新电池,但是一旦把旧电池拆掉,机器人本身就无法工作了,这样之后装上新电池的工作就没法完成了。所以为了让机器人自己换电池,我们还需要额外做一个小机器人,需要换电池的时候,就启动这个小机器人,小机器人完成换电池工作之后,再返回大机器人运行,那同理,STM32通过串口进行程序的自我更新就需要一个小机器人,这个小机器人就是BootLoader,BootLoader是ST公司写好的一段程序代码,这段程序的存储位置,就是ROM区的最后,1FFF F000,这段区域叫做系统存储器,存储的是BootLoader程序,或者叫自举程序。用途是自我更新,串口下载。在更新过程中,BootLoader接收USART1数据,刷新到程序存储器。这时主程序就处于瘫痪状态。更新好之后,再启动主程序。执行新程序。这就是串口下载的流程。
- 每次下载程序,都要拔插两遍跳线帽,太麻烦了,有没有什么解决方法。
想要程序自我更新,就必须有一个切换小机器人的过程,BOOT0引脚和RST复位引脚必须得有高低电平的变化,那我能不能配置两根线,当电脑点击下载时,自动帮我设置BOOT0和RST的电平呢?答案是可行的。这里可以看一下串口模块的电路,这里除了TXD和RXD用于通信的引脚,还有RTS、CTS、DTR和DSR,这些引脚都是流控的引脚,我们不使用流控,可以把它们当作普通的GPIO来控制,其中RTS和DTR是输出引脚,我们可以用这两个引脚来控制BOOT0和RST,当然在外围还需要设计一个控制电路,一般可以用两个三极管开关来进行控制,可以去网上搜一搜,当我们的串口具备一键下载电路之后,就不需要再频繁切换跳线帽和复位键了,非常方便。FlyMcu下面有一个下拉框,就是用来配置DTR和RTS的,是高电平复位还是低电平复位,等等。这些配置需要根据一键下载电路来选。一般的最常用的配置是DTR的低电平复位,RTS高电平进BootLoader,我们这个硬件没有一键下载电路,所以还得手动切换跳线帽,所以哪个下拉框选择哪个也就无所谓了。如果没有一键下载电路,我们还可以再FlyMcu软件上有一个变成后执行,勾选。(这里需要去掉编程到FLASH时写选项字节的勾,要不然下载之后会提示执行失败。这里需要提前将BOOT0换到1的位置,只不过下载好之后不用换到BOOT0的位置而已。),但是这样只是一次性的。复位一下,LED不亮,程序任然是BootLoader。
这个软件还有很多有意思的东西,首先,是这个读Flash,点一下,放到桌面,起个名字led,保存。这样就可以把芯片里的程序读出来。比如你看到别人用STM32做了一个产品,你觉得不错,就可以超一下它的PCB板,然后程序文件,又可以通过这个软件读出,这样就可以大批量山寨别人的产品了。不过STM32可以配置读保护,之后再说。但是反过来,如果你开发产品,不注意这个问题,就容易泄露程序,那我们看一下读出来的这个文件,这个文件是.bin格式的。里面记录的就是STM32从0800开始存储的程序数据。.bin格式是没有地址信息的原始数据文件。我们之前生成的.hex文件,是有地址信息的。不过作为记录程序代码的文件,这两种格式的作用其实都是一样的。这里还有一个问题,就是这个FlyMcu,下载的时候只能选择.hex文件。如果选.bin文件,下载就会报一个这样的错误。但是它读出来只能选.bin文件,读出来的.bin文件不能再下载回去了。
点击清除芯片按钮,可以把主程序区域全部擦除,擦除之后,所有的数据都是FF,之后读器件信息,试一下,这时会把芯片的序列号什么的信息读出来。然后这里显示我这个芯片的Falsh容量是256K。但是我们这个C8T6芯片的标称Flash容量是64K,属于中容量产品。原因可能是生产的时候,为了省工序,很多型号是一个模子做出来的。但是C8T6只能保证前64K没有问题,当然还有其它说法。
选项字节,选项字节是ROM区最后的一小块存储单元,用途是存储一些独立于代码的配置参数,那究竟是哪些参数呢?有什么作用呢?,点击设定选项字节的按钮,选择STM32F1这一项,这个界面就是选项字节里的参数了,第一块,就是读保护,如果你做产品不开读保护,别人很容易就把你程序偷走了。所以,为了保护程序的安全,选项字节里就有一个参数可以配置读保护,这里可以看到,设置A5运行读出,设成FF阻止读出,注意一下,如果你阻止读出了。再回到Keil下载程序就会失败,如果因为读保护下载失败,那就再到这个地方来,取消读保护,另外,在取消读保护时,会同时清空芯片的程序,这样程序就不会被偷走了。这就是读保护功能。
接下来还有一些硬件的选项字节,其中包括看门狗,停机和待机模式不产生复位。
然后是用户数据字节,这些东西如果你有需求的话可以使用。
那选项字节里的参数有什么好处呢。第一,就是选项字节的数据相当于世外桃源了。无论程序怎么更新,选项字节的数据都可以不变,你可以用这些字节来存储不随程序变化而变化的参数。另外选项字节还有一个好处,就是可以利用上位机很方便的修改。比如我们这个FlayMcu或者STLINK Utility在上位机里,可以直接修改选项字节的内容,是不是可以用作一些产品中,可供用户配置的参数啊。
最后一项,就是写保护了。这里可以对Flash的每几个页单独进行写保护,比如你在主程序的最后几页写了一些自定的数据,不想再下载的时候被擦除了。就可以把最后几页设置写保护锁起来,设置写保护之后,就无法写了。如果想再次写入的话,解除写保护就行了。另外注意下,设置写保护之后,再下载,如果需要写入保护区的话,就会出错,比如你把前面几页写保护了。下载一次之后再下载就会出错,而它这个软件设计,不能单独写入选项字节,只能下载Flash,顺便写入选项字节,但写保护,下载不了,下载不了,就不能解除写保护,形成死循环了。选择字节总共就是四大块,读保护、写保护、硬件参数和用户参数。配置号之后,可以点击采用这个设置。然后把编程到Flash时写选项字节的勾勾上。再执行争产的下载流程,就能更新选项字节的配置。
STLINK Utility 这个软件时需要安装的。下载流程:硬件这部分,只需要把STLINK连接好就行了。串口可以不接。然后跳线帽恢复成两个都在最左边,复位,之后点击这个按钮进行连接。
连接好之后,就会出现一些器件信息了,然后下面这个大框框展示的是STM32里面0800开始的程序数据,如果被擦出过,那么数据将都是FF。如果点击第二个按钮保存,就可以把这个程序给存起来了。这里可以选择保存为.hex或者.bin文件,之后点击第四个按钮,就是断开连接。点击第五个橡皮擦,就是擦除芯片。如果想下载程序的话,要先点击第一个按钮,打开文件,这里文件支持.hex和.bin。我们可以打开刚才读出来的.bin文件,然后点击第六个编程按钮,File path:显示的是程序文件的路径。当然也可以直接打开要下载的程序文件。之后点击Start,开始下载。这就是STLINK的下载流程。
选项字节的配置,这里点击Target,Option Bytes,这就可以打开选项字节的配置了。和前面讲的一样,都是四块内容。。这里配置好之后,直接点击Apply,就能直接单独更改选项字节的参数了。不像FlyMcu,必须要下载程序顺便更新选项字节。所以如果你有芯片被读保护后者写保护锁住了,就可以到这个地方来,解除保护。
最后介绍一个STLINK固件更新的功能,我们可以点击ST-LINK,Firmware update,然后打开这个ST-Link Upgrade,这个界面就是用于给STLINK更新固件的。然后点击这里的Device Connect,连接(这里要显示重启一下,就是把STLINK拔掉,重新插)。可以看到目前这个STLINK的固件是V2.J29.S7,最新的固件是V2.J37.S7,如果要升级的话,点击yes,就能给STLINK升级固件了。