一、通信
1.1通信是什么;
通信是将一个设备的数据发送到另一个设备中,从而实现硬件的扩展;
1.2通信的目的是什么;
实现硬件的扩展-在STM32中集成了很多功能,例如PWM输出,AD采集,定时器等,在STM32中是通过内部硬件电路实现的,可以通过指针操作相应的寄存器,来控制硬件电路,通过读来获取电路状态,通过写来操控电路;而有一些功能是STM32内没有集成的例如蓝牙无线遥控,陀螺仪测姿态等,此时需要外挂模块,来实现这些功能,而这些功能的数据是保存在外挂模块的寄存器中的,STM32想要获取这些数据来控制外挂模块就需要与该设备进行通信,通过读写外挂模块相应的寄存器,实现对外挂模块的控制,从而达到硬件扩展的功能;
1.3设备间如何进行通信
通过在设备间连接一根或者多根通信线,实现数据的接收和数据的发送,从而达到主控模块控制外挂模块的功能;
1.4通信协议是什么;
通信协议是指通信双方规定的通信规则,双方按照协议进行数据的收发;
1.5有哪些通信协议;
主要的通信方式:串口通信(USART),I2C,SPI,CAN,USB通信;
1.6通信协议有哪些模式;
通信方式的特点主要由以下几种模式决定:双工模式,时钟模式,电平模式,设备模式;
1.7通信特性具体是什么;
1.7.1双工模式:
双工模式分为全双工,半双工,单工;
全双工:通信双方可以同时接收或者发送数据,一般有两根通信线,接收线路和发送线路互不干扰,全双工;
半双工:通信双方在指定时间,只能接收或者只能发送,一根通信线,半双工;
单工:数据只能由一个设备发送另一个设备接收,一根通信线(全双工撤去一根通信线可转换为单工);
1.7.2时钟模式
同步时钟:通信双方在时钟线的时钟脉冲驱动下,进行数据的收发;
异步时钟:通信双方没有时钟线,需要双方约定传输频率(波特率),根据传输频率来接收数据;
*波特率和比特率
波特率:单位时间内接收的码元个数,单位是码元/s,也称波特;在通信系统中,二进制的一位称为码元或者符号;波特率是指单位时间内传送二进制数据的位数,单位用bps(位/秒)表示,记作波特。
比特率:单位时间内接收的比特的个数,单位是bit/s,比特率来衡量异步串行通信的数据传输速率,即单位时间内传送二进制有效数据的位数,单位用bps表示。
在二进制下波特和比特是相同的,多进制下是不同的;
1.7.3电平模式
单端信号:通信线上的电平是对GND的电平,所以通信设备需要共地;
差分信号:俩根传输线上的电位差,差分信号具有很强的抗干扰性,所以差分信号一般可以传输很远的距离;
1.7.4设备模式
点对点设备:
多设备;
多设备分为一主多从模式和多主多从模式;
一主多从模式:指的是有一个主机,多个从机,主机对总线的时钟线有绝对的控制权,从机在任何时候都只能接收,不能发送;主机在数据线空闲时候,可以调用,从机只能在接收或者发送数据的时候才可以短暂的控制;
多主多从模式
一根总线上挂载了多个设备,这些设备既可以作为从机又可以作为主机;
又分为:固定多主机模式和可变多主机模式;
固定多主机模式:主机的数量是固定的,每个主机都可以掌握总线的控制权,当多个主机同时申请总线控制权时,总线进行仲裁,失败的让出总线控制权;
可变多主机模式:每一个挂载在总线的设备都可以作为主机,当需要作为主机与其他设备进行通信时,申请总线控制权,对从机设备进行寻址即可,通信完成后,让出总线控制权,变回从机;当多个主机同时申请总线控制权时,总线进行仲裁,失败的让出总线控制权;
1.8总结:
二、USART串口通信协议
2.1串口通信介绍:
串口是一种应用十分广泛的通讯接口,串口按位bit发送和接收字节,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力;
2.2串行通信和并行通信
1.通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是 8 根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用 8、16、32 及 64 根或更多的数据线进行传输的通讯方式,它们的通讯传输对比说明见下图:
很明显,因为一次可传输多个数据位的数据,在数据传输速率相同的情况下,并行通讯传输的数据量要大得多,而串行通讯则可以节省数据线的硬件成本(特别是远距离时)以及 PCB 的布线面积,串行通讯与并行通讯的特性对比见下表:
不过由于并行传输对同步要求较高,且随着通讯速率的提高,信号干扰的问题会显著影响通讯性能,现在随着技术的发展,越来越多的应用场合采用高速率的串行差分传输。
2.3UART协议
UART全称是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),它通常称作UART,是一种异步收发传输器,是设备间进行异步通信的关键模块。UART负责处理数据总线和串行口之间的串并转换,并规定了帧格式;通信双方只要采用相同的帧格式和波特率,就能在未共享时钟信号的情况下,仅用两根信号线(RX 和TX)就可以完成通信过程,因此也称为异步串行通信。
对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层(硬件规定)和协议层(软件规定)。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
2.4USART串口硬件规定:
简单的双向串口通信需要两个通信线(TX数据发送端,RX数据接收端)
TX 数据发送引脚
RX 数据接受引脚
TX和RX需要交叉连接(一个设备的TX连接在另外一个设备的RX);
GND是单端信号,即所有的电平信号是相对于GND的,所以需要共地;
VCC当从设备没有单独供电时,需要接VCC;
当只需要单向数据传输时,可以只接一个通信线;
当电平标准不一样的时候需要接电平转换芯片;
全双工模式,发送端设置为复用推挽输出,接收端设置为浮空输入或上拉输入;
2.5USART的软件规定
2.3.1时序组成:
串口的参数:起始位,停止位,校验位,数据位,波特率;
波特率,单位时间接收二进制的位数,单位是bsp/s(位/s);
起始位:标志一个数据帧的开始,固定为低电平;
停止位: 用于数据帧的间隔,固定为高电平;
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行;
校验位:用于数据验证,根据数据位计算得来;
串口的时序:
1位起始位+8位有效数据位+1位停止位=1帧(10位);
1位起始位+8位有效数据位+1位停止位+1位校验位=1帧(11位);
过程:串口处于空闲状态时,为默认为高电平为1,串口需要传输的时候,必须要发送一个起始位,这个起始位必须是低电平,打破空闲状态的高电平,这个下降沿就是告诉接收设备,这一帧的数据要开始了,发送数据后,停止位固定为高电平,为下一帧发送做准备;
奇偶校验位:例如奇校验:就是如果数据中1的数为奇数则校验位为0,默认1为奇数,如果1为偶数,则校验位补1,使1为奇数个;
引脚高低电平反转是STM32USART外设自动完成,也可以通过软件模拟,就是设置一个对应波特率时间的定时器,然后定时调用GPIO完成引脚反转;
接收时候需要一个外部中断,在接收位下降沿触发,进入接收状态,对齐采样时钟,依次采样八次;
*停止位可以设置长度;
2.3.2时序图
总结:TX引脚发送高低电平,RX引脚读取高低电平,每个字节的数据加上起始位,停止位 ,可选的校验位打包成数据帧,依次输出在TX引脚,另一端RX引脚依次接收,完成数据传输,这就是串口通信;
三、电平标准
*电平标准:
电平标准是数据0和数据1的表达方式,在传输线缆中人为的规定电压和数据的对应关系,串口通信常见的三种电平标准:
TTL电平:0V表示0,3.3V~5V表示1;
RS232:-3.3V~-15V表示1,3.3V~15V表示0;
RS485:两线压差-2V~-6V表示0,2V~6V表示1;RS485是差分信号;
四、STM32的USART串口外设
4.1USART串口外设的介绍
通用同步/异步收发器;
*同步模式多加一个时钟输出,没有时钟输入,主要是为了兼容其他通信协议,不支持两个USART直接的同步;
USART是STM32内部集成的硬件外设,可以根据数据寄存器的一个字节数据,自动生成数据帧的时序,从TX引脚发送,也可以自动接收RX引脚的数据帧时序,拼接成一个字节数据,存放在数据寄存器中;
自带波特率发生器,最高可达4.5Mbits/S;(APB2总线给一个时钟频率72MKHZ,波特率发生器进行分频得到想要的时钟频率,作为采样频率)
可以配置数据位长度(8/9),停止位长度(0.5/1/2/2.5)
可选择校验位(无校验/奇校验/偶校验)
支持同步模式(多一个CLK时钟输出),硬件流控制(设备发送数据太块,会导致另外一个设备没有做好接收准备,通过实现一个设备准备好接收,另一个设备才继续发送,此时设置硬件流控制,就可以防止其中一个发送太块,导致另外一个没有接收导致数据丢失的问题),DMA(转运数据),智能卡,IRDA(红外光通信),LIN(局域网通信);
4.2USART串口外设的结构图
过程分析:TX,RX分别是接收和发送引脚,通过DR寄存器,硬件上分为两部分TDR发送寄存器和RDR数据接收寄存器组成;
剩下的三个引脚是智能卡和IrDA通信的引脚;
我们需要发送数据时候,操作TDR发送数据寄存器(只写),当我们读取数据时候,通过RDR接收数据寄存器(只读),我们进行写操作,此时数据会被写入TDR中,然后检测发送移位寄存器内是否有数据,如果没有,那么TRD中的数据会立刻转移到发送移位寄存器中,然后置标志位TXE(发送寄存器为空)为1,表示转运完成,然后新的数据会装载在TRD中,此时数据还没有发送出去,之后移位寄存器中的数据,在发送控制器的作用下,向右移位(低位先行),一位一位的对应数据帧的时序,把数据输出到TX引脚,通过TX发送出去,当数据移位完成后,新的数据就会再次自动从TDR发送到移位寄存器里来,如果当前移位还没有完成,TDR数据就会进行等待,直到移位完成;有了TRD和移位寄存器的双重缓存,可以保证连续发送数据,数据帧不会有空闲;
我们需要接收数据时候,数据从RX段输入,到接收移位寄存器中,在接收器控制下,一位一位的读取RX电平,先放在最高位,然后移位8次,接收一个字节(从高位到低位),当一个字节的移位完成后,这个字节的数据就会整体转移到RDR接收数据寄存器中,转移过程中也会置一个标志位RXNE(接收寄存器非空),之后可以通过读取寄存器获取值;
发送控制器,控制发送移位寄存器,硬件数据流控制,又称流空,防止数据丢失或覆盖,nRTS是请求发送,是输出脚,nCTS用于接收其他设备的nRTS(n低电平有效)
原理:发送设备的TX引脚接入接收设备的RX引脚,同时接收设备的nRTS接到发送设备的nCTS上,RTS输出一个能不能接收的反馈信号,如果可以接收时,RTX置低电平,表示可以接收,如果不能接收,则置高电平表示不能接收;直到置低电平重新发送;
同步模式:产生同步的时钟信号,配合发送移位寄存器使用,发送寄存器每移位依次,同步时钟电平跳变一个周期,时钟告诉对方,移出去一位,只支持输出,不指出输入;
用途:1.串口加时钟类似于SPI,可以于SPI兼容;
2.可以做自适应波特率;原理:当接收设备不知道发送设备的波特率,可以通过测量是时钟周期来计算波特率;
唤醒单元:一般串口只支持点对点通信,而对于多设备通信,即一根总线上挂在多个设备,想和某个设备通信,只需要进行寻址,确定通信对象后,在进行数据收发;
唤醒单元可以实现多设备通信,当发送指定地址时,唤醒单元开始工作;从而实现多设备通信
各种中断标志位;
其中中断控制就是控制中断是否能到NVIC;
波特率发生器:
APBx时钟
过程:TE为1发送器波特率控制,RE为1接收器波特率控制,然后再波特率控制器中分为整数部分和小数部分(因为有些波特率整数除不尽可能会有误差,所以有小数部分),然后将分频系数输出对输入进来的时钟频率进行分频,然后/16得到发送器时钟和接收器时钟 ,通向控制部分;
USART引脚复用GPIO参考引脚复用复用;
4.3USART串口外设实现过程
1.RCC开启GPIO和USART外设时钟;
2.初始化GPIO,配置GPIO的输出引脚为复用推挽输出,输入引脚为上拉输入或者浮空输入(最好是上拉输入,给引脚一个默认的电平,防止外部干扰,造成引脚跳变);
3.初始化USART,配置波特率发生器(预分频器,对输入的时钟进行分频),配置发送和接受控制器;
4.使能CMD;
4.4USART串口外设时序图分析
输入数据问题:第一就是要在输入数据中间进行采样,才能确保采样的准确性,否则可能电平还在翻转就采样,导致数据不准;
第二数据输入要对噪声有一定的判断能力,如果是噪声,置标志位判断;
STM32中,通过对输入的波特率进行16分进行采样
过程:空闲状态每一位进行16次采样,对应结果一直为1,如果某一个时刻采样为0,那么表示出现下降沿,那么继续在一位中采样16次,并且之后每三位进行一次判断,如果三位中至少有两个为0,则认为为起始位标志,但是出现了噪声,同时将噪声标志位NE置1,如果只有一个0那么默认为前面的标志位0为噪声影响,全部忽略不计重新开始计算;如果通过了起始位侦测,则接收状态由空闲位,变成接收起始位,同时第8、9、10次采样的位置为起始位的正中间,之后每次都在第8、9、10次采样,这样就能保证后续每次采样都在正中间进行采样;
数据采样流程:1-16,一个数据位有16个采样时钟对应16位,由于起始帧测已经对齐采样时钟,直接在第8、9、10次采样,为了保证数据的可靠,即三次采样,如果都为1则位1,如果都为0则为0,如果不全为1或者0,则根据2:1的原则判断1或者0;
Printf 打印到串口方法
1.重定向方法:
int fputc(int ch,FILE *f)
{
Serial_SendByte(ch);
return ch;
}
更改底层,但是只能应用一个串口;
2.sprintf指定打印位置;把格式化字符输出到一个字符串里;
Char string[100];
Sprint(string,“num=%d”,666);
Serial_SendString(String);
- 封装Sprintf(可变参数)
Include“stdarg.H”
Void Serial_print(char *format ,......)
{
Char String[100];
Va_list arg;
Va_start(arg,format);
Vsprintf(String,format,arg);
Va_end(arg);
Serial_SendString(String);
}
串口接收模式:使用查询或者中断两种方式;
查询:在主函数循环不断判断RXNE标志位,如果置1表示接收到数据;,在读取寄存器就可以了;
五、数据包
数据包作用:将一个个数据打包起来,方便进行多字节数据通信;
例如陀螺仪数据,发送X,Y,Z,共三个字节需要连续不断的发送,出现一个问题,接收方不知道那个数据对应X那个对应Y,会出现数据错位的情况,把数据分割,xyz当作一组数据,把同一批的数据分割成一个个数据包来接收;
额外添加包头包尾的方式,不改变原有的数据结构;
HEX数据包格式
文本数据包格式
数据包发送
HEX:传输直接,解析数据简单,适合模块发送数据,例如陀螺仪,湿度传感器等;
文本数据包:数据直观易理解,适合人机交换的场合;;蓝牙AT指令,CNC,三D打印机的G代码;
HEX数据包发送
定义一个缓冲数组
在发送输出的数据添加包头包尾,实现数据打包;
void Serial_SendPacket(void)
{
Serial_SendByte(0xFF);//发送包头
Serial_SendArray(Serial_TxPacket,4);//发送一个数组
Serial_SendByte(0xFE);//发送包尾;
}
如何构建状态机:
状态机方法,根据项目要求,定几个状态,然后考虑各个状态在什么情况下进行转移,如何转移;
方法:定义一个状态量,然后判断状态量的值确定处于那种状态,然后考虑转移;
状态机1:HEX数据包发送
void USART1_IRQHandler (void)
{
static uint8_t RxState=0;//状态变量S
static uint8_t PRxPacket=0;//指示接收到哪一个了
if(USART_GetFlagStatus( USART1, USART_IT_TXE)==SET)//发送寄存器TDR为空,置TXE标志位为1;
{
uint8_t RxData =USART_ReceiveData(USART1);//输入的数据给到RXData中;
if(RxState==0)//判断状态S选择不同的过程
{如果接收数据为0xff则是包头,切换状态变量为接收状态;同时清零PRxPacket
if(RxData==0xFF)//收到包头
{
RxState=1;//转移状态
PRxPacket=0;//从第0个开始接收
}
}
else if(RxState==1)//使用else if而不使用if防止状态转移过程中,两个同时成立
{
Serial_RxPacket[PRxPacket]=RxData;//第N个接收数据
PRxPacket++;//数据转存一次
if( PRxPacket>=4)//四个载荷数据接收完成
{
RxState=2;//进入下一个状态
}
}
else if(RxState==2)//等待包尾
{
if(RxData==0xFE)//接收到包尾
{
RxState=0;//回到最初的状态
Serial_RxFlag=1;//置一个接收标志位;
}
}
USART_ClearITPendingBit(USART1, USART_IT_TXE);
}
}
void Serial_SendPacket(void)
{
Serial_SendByte(0xFF);
Serial_SendArray(Serial_RxPacket,4);
Serial_SendByte(0xFE);
}
状态机2:文本数据包
void USART1_IRQHandler (void)
{
static uint8_t RxState=0;//状态变量S
static uint8_t PRxPacket=0;
if(USART_GetFlagStatus( USART1, USART_IT_TXE)==SET)
{
uint8_t RxData =USART_ReceiveData(USART1);
if(RxState==0)
{
if(RxData== '@')//判断包头
{
RxState=1;//进入下一个状态
PRxPacket=0;//此时为1
}
}
else if(RxState==1)//防止状态转移时候,两个同时成立
{
if( PRxPacket=='\r')//判断第一个包尾
{
RxState=2;//进入下一个状态
}
else
{
Serial_RxPacket[PRxPacket]=RxData;
PRxPacket++;
}
}
else if(RxState==2)
{
if(RxData=='\n')
{
RxState=0;
Serial_RxFlag=1;
Serial_RxPacket[PRxPacket]='\0';
}
}
USART_ClearITPendingBit(USART1, USART_IT_TXE);
}
}
五、API实现
5.1 API1:实现软串口接收或者发送一个数据;
5.1.1程序规划:
首先明确想实现的功能-实现发送一个字节,发送一个数组,发送一个字符串,发送数字(依次发送每一位);
5.1.1.1建立通信层模块(底层):
初始化串口后,对各部分进行封装;
5.1.1.2应用层
mian函数里调用驱动层函数,实现功能;
5.1.2库函数分析
库函数:
void USART_DeInit(USART_TypeDef* USARTx);//复位
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);//初始化
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);//初始化结构体
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);//配置同步时钟输出
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//使能
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);//中断使能
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);//开启到DMA的通道
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);//设置 USART 节点的地址。
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);//配置唤醒单元
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);//使能唤醒单元
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);//设置USART LIN模式下的断点检测长度
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);//LIN使能
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//功能:通过USARTx外设传输单个字节数据
注释:DR数据寄存器只有DR[8:0]可用,一次发送1字节的数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//功能:返回由USARTx外设接收的最新数据
注释:DR数据寄存器只有DR[8:0]可用,一次接收16位的数据
void USART_SendBreak(USART_TypeDef* USARTx);//功能:发送断开帧
注释:如果设置SBK=1,在完成当前数据发送后,将在TX线上发送一个断开符号
单独发送断开符号时:不能发送,波形无变化(持续高电平)
TC中断时发送断开符号:接收端接收,认为是数据0x00
结论是:断开符号对防止接收端把两包看做一包没什么用,无法起到真正的断开作用void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);//功能:设置指定的USART保护时间
注释:以波特时钟为单位的保护时间。在智能卡模式下,需要这个功能,当保护时间过去后,才会设置发送完成标志
UART4和UART5上不存在这一位
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);//功能:设置对系统时钟预分频器的数值
注释:红外低功耗模式[7:0]位,红外正常模式数值确定,智能卡模式[4:0]位
位[7:5]在智能卡模式下没有意义;UART4和UART5上不存在这一位
例如:USART_SetPrescaler(USART1 , 00000001);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者失能USARTx的智能卡模式
注释:UART4和UART5上不存在这一位
例如:USART_SmartCardCmd(USART1 , ENABLE);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者失能NACK传输
注释:校验错误时,是否发送NACK位;UART4和UART5上不存在这一位
例如:USART_SmartCardNACKCmd(USART1 , ENABLE);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者失能USART半双工通信
注释:是否选择选择单线半双工模式
例如:USART_HalfDuplexCmd(USART1 , ENABLE);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者失能USART的8X过采样模式
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者失能USART的one bit采样模式
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);//功能:配置USART的IrDA(红外)接口
注释:低功耗与正常模式
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);//功能:使能或者红外模式失能
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);//标志位函数
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
5.1.3思路:
5.1.3.1初始化串口
1.RCC开启GPIO和USART的时钟;
2.初始化GPIO,配置GPIO的输出为复用推挽输出(片上外设输出控制引脚电平),输入为上拉输入或者浮空输入;
3.初始化USART,配置USART的模式(接收还是发送),是否需要流控,是否需要校验位和位长;
4.使能USARTcmd;
5.1.3.2输出一个字节:
1.输出一个字节;
2.判断是否发送至移位寄存器,不需要手动清0标志位;
5.1.3.3输出一个数组
1.通过for循环依次输出数组的每一位;
5.1.3.4输出一个字符串for循环依次发送字符串的每一位,直到标志位“\0”;
5.1.3.5发送数字
1.依次发送数字的每一位;
2.通过定义函数表达函数的每一位;
5.1.3.6将输出重定义至串口
5.1.4实现
1.RCC开启GPIO和USART的时钟;
2.初始化GPIO,配置GPIO的输出为复用推挽输出(片上外设输出控制引脚电平),输入为上拉输入或者浮空输入;
3.初始化USART,配置USART的模式(接收还是发送),是否需要流控,是否需要校验位和位长;
4.使能USARTcmd;
5.1.3.2输出一个字节:
1.输出一个字节;
2.判断是否发送至移位寄存器,不需要手动清0标志位;
5.1.3.3输出一个数组
1.通过for循环依次输出数组的每一位;
5.1.3.4输出一个字符串for循环依次发送字符串的每一位,直到标志位“\0”;
5.1.3.5发送数字
1.依次发送数字的每一位;
2.通过定义函数表达函数的每一位;
5.1.3.6将输出重定义至串口
应用层:
5.2API2实现串口接收一个数据:
基本于发送数据初始化基础上,开启中断控制输出,到NVIC,配置NVIC后,但是接收数据时候需要产生中断,在中断函数中判断标志位后,读取数据和标志位,清除标志位,最后用俩个函数返回读取的数据和标志位
应用层:
5.3API3串口收发HEX数据包(固定包长)
在串口发送和接收数据API1和API2基础上建立;
如何建立状态机:
状态机方法,根据项目要求,定几个状态,然后考虑各个状态在什么情况下进行转移,如何转移;
方法:定义一个状态量,然后判断状态量的值确定处于那种状态,然后考虑转移;
1.定义发送缓存区和接收缓存区;
2.封装数据包,在数据前后分别加上包头和包尾;
定义一个状态量用于判断状态,定义一个数据指示接收到哪一个了
状态机逻辑:根据分析得到几种状态:等待包头,接收数据,等到包尾
各个状态在什么样的情况下转变:等待包头在接收到包头后转移至接收数据,接收数据接收够数据后转移至等待包尾,等待包尾,收到包尾后,切换至等待包头模式;
首先静态变量初始值为0,是一个状态—等待包头,判断接收的数据是不是包头,如果是包头0xFF,切换至下一个状态为1,是第二个状态接收数据,判断接收的数据是否够4个,如果接收够4个,切换为下一个状态2,是第三个状态等待包尾,判断是否接收到包尾,如果接收到状态切换为0,第一个状态;
应用层:
5.4API4串口发送字节数据包 (随机包长)
在串口发送和接收数据API1和API2基础上建立;
逻辑结构:
根据分析得到几种状态:等待包头,接收数据,等到包尾
各个状态在什么样的情况下转变:等待包头在接收到包头后转移至接收数据,接收数据接收够数据后转移至等待包尾,等待包尾,收到包尾后,切换至等待包头模式;
首先静态变量初始值为0,是一个状态—等待包头,判断接收的数据是不是包头或者是不是第一次接收数据,如果是包头0xFF,切换至下一个状态为1,是第二个状态接收数据,判断接收的数据是否够4个,如果接收够4个,切换为下一个状态2,是第三个状态等待包尾,判断是否接收到包尾,如果接收到状态切换为0,第一个状态;
通过串口发送相应的数据,来操作LED;
通过Strcmp(par1,par2)(判断字符串1和2是否相等,相等为1,不相等为0),套用IF循环,如果相等,执行点亮LED并向串口回传一个数据LED点亮,并用OLED显示,否则反之;
如果连续发送数据包,程序处理不及时,可能会导致数据包错位 ,文本数据包,每个数据包是独立的,如果错位了问题就比较大了,所以在每次程序处理完成后,在接收下一个程序包;
在主循环里执行完程序后清0标志位;
判断等待包头的时候在加一个条件如果数据等于包头并且RXflag==0才执行接收;(如果不满足就跳过)