STM32——USART

news/2024/4/30 6:24:32

 一、通信

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);

  1. 封装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才执行接收;(如果不满足就跳过)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/11304.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

傻瓜式启动关闭重启docker容器的脚本

运行脚本后,界面如下: 选择对应的编号后,会列举所有关闭的容器或者所有开启的容器列表,当我要启动一个容器 时输入1,就会出现下面的页面。 然后输入指定的编号后,就会启动对应的容器。 脚本代码如下&#…

Redis入门三(主从复制、Redis哨兵、Redis集群、缓存更新策略、缓存穿透、缓存击穿、缓存雪崩)

文章目录 一、主从复制1.单例redis存在的问题2.主从复制是什么?3.主从复制的原理4.主从搭建1)准备工作2)方式一3)方式二 5.python中操作1)原生操作2)Django的缓存操作 二、Redis哨兵(Redis-Sent…

Nuxt2 渲染时html比css加载快,导致闪屏/CSS样式迟滞/抖动问题记录

问题场景: 最近在用Nuxt2重写公司官网,但因为笔者不是专业前端,之前虽然也用vue2来写前端,但是用nuxt2来写项目还是第一次。在开发过程中虽然也磕磕碰碰,但因为开发的是官网,偏CMS型的网站,所以…

大数据主要组件HDFS Iceberg Hadoop spark介绍

HDFSIceberghadoopspark HDFS 面向PB级数据存储的分布式文件系统,可以存储任意类型与格式的数据文件,包括结构化的数据以及非结构化的数据。HDFS将导入的大数据文件切割成小数据块,均匀分布到服务器集群中的各个节点,并且每个数据…

windows系统安装RabbitMQ

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客…

MATLAB 公共区域的点云合并(46)

MATLAB 公共区域的点云合并(46) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 点云配准后,或者公共区域存在多片点云对场景进行冗余过量表达时,我们需要将点云进行合并,Matlab点云工具中提供了这样的合并函数,通过指定网格步长,对初始点云进行过滤。 函数主要实…