STM32F103(二十三)通用同步异步收发器(USART)

Posted 独独白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103(二十三)通用同步异步收发器(USART)相关的知识,希望对你有一定的参考价值。

学习板:STM32F103ZET6

通用同步异步收发器

一、USART简介

USART为通用同步异步收发器,支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。

USART双向通信至少需要两个引脚:收数据输入(RX)和发送数据输出(TX)。此外,CK引脚用于同步通信;CTS、RTS用于调制解调。

USART传输数据时,有一个起始位、数据位可通信双方软件约定8位或9位、停止位可设置为0.5或1位或1.5位或2位。

在传输线空闲状态下,TX线为高电平,起始位时,TX为低电平,故可以通过下降沿检测是否开始数据传输(后边还会详述);停止位时TX处于高电平。

(1)接收数据
外部设备——>串行输入移位寄存器——>输入数据缓冲寄存器——>MCU

(2)发送数据
MCU——>输出数据缓冲器——>串行输出移位寄存器——>外部设备

二、、USART相关寄存器

1、控制寄存器 1(USART_CR1)

位0: 发送断开帧

该位设置为1后,完成当前数据传输,会在TX线上发送一个断开符号,发送断开符号完成后该位被硬件置1 。空闲帧为高电平,断开帧为低电平,而起始位也为低电平。所以插入断开帧后,TX线会传送一个“1”,为了准确检测下一帧的起始位(低电平)。当然如果没有下一帧数据,会处于空闲状态,TX线保持高电平。

位1: 接收唤醒


该位设置USART是否为静默模式,该位不设置时,为默认正常工作状态。设置为唤醒模式时,需要先接收一个数据字节才能设置,否则无法唤醒。唤醒序列到来时,该位硬件清零。

静默模式:
①任何接收状态都不会被设置
②所有接收中断被禁止
③CR1寄存器位11 WAKE设置为0,可以被空闲总线唤醒;WAKE设置为1,可以被地址标记唤醒。

位2: 接收使能

该位使能接收,置1后开始搜索RX引脚上的起始位。

起始位:1110x0x0x0000(x为0或1,x为1时,认为有噪音)

在搜索起始位时,先检测低电平(因为空闲状态传输线上为高电平),检测到下降沿后,会通过两次采样。上图中的第3、5、7为第一次采样;第8、9、10为第二次采样。

如果俩次采样的每一次都全为0。(即图中红框中“每三位至少有2个0”,为全0),此时确定为起始位。

如果俩次采样中,有一次仅仅有2个0、或者两次采样每次都只有2个0,也会判断为起始位,此时表示有噪音,噪声标志位会被置1.

位3: 发送使能

位4: IDLE中断使能

该位使能空闲总线中断,该位置1后,使能中断。当检测到总线空闲时,SR寄存器的位4:IDLE硬件置1,发生中断。

位5~位8: :各中断使能

分别为接收缓冲区非空中断、发送完成中断、发送缓冲区空中断、帧错误中断。由SR寄存器可以得到状态。

位9~位10: :校验选择与校验控制


位10使能校验控制,根据帧长度的不同,校验码插入到不同的位置。校验码在数据位后面,停止位之前

位9选择奇偶校验。奇偶校验的目的是判断数据传输是否出错。偶校验通过校验位配置,使数据位与校验位的所有“1”加起来为偶数。奇校验通过校验位配置,使数据位与校验位的所有“1”加起来为奇数。

偶校验:如果数据位中1的个数为奇数,则校验位为1;如果数据位中1的个数为偶数,则校验位为0。

奇校验:如果数据位中1的个数为奇数,则校验位为0;如果数据位中1的个数为偶数,则校验位为1。

位11: :唤醒方法设置

该位设置唤醒静默状态下的唤醒方式。之前的位1设置什么工作状态,位1置1后进入静默状态。

WAKE=0时:

此时静默状态由空闲总线唤醒,即当检测到空闲帧后被唤醒;此时CR1寄存器位1(RWU)被硬件清零,USART进入正常工作状态。

WAKE=1时:

此时静默状态由地址标记唤醒,当传输的数据高位为1时,认为是地址,否则认为是数据。目标接收器的地址在第四位中存储低四位在CR2寄存器的位0:3设置。

当接收到的字节与接收器编程地址匹配时,被唤醒;否则还会处于静默状态;之前也总结到过,处于静默状态不会产生中断。

当接收到的字节与接收器编程地址匹配时,被唤醒,退出静默状态。CR1寄存器的位1被硬件清零,即USART处于正常工作状态。

位12: :设置字长

该位设置字长,停止位由CR2寄存器设置,之后与字长一起总结。

位13: :使能USART

2、控制寄存器 2(USART_CR2)


位3:0: :设备地址设置

这4位来设置目标设备的地址,用于静默状态下由地址标记唤醒USART设备。

位5: :断开符长度检测


该位用来设置断开符的长度。之前CR1寄存器位0置1后发送断开符。本寄存器位5设置断开符长度为10位或11位。

位6: :断开符中断使能

该位使能检测断开符中断。

如果检测到断开符,则状态寄存器SR的位8 LBD被硬件置1 ,当CR2寄存器位6 LBDIE设置为1,则会产生中断。

位8: :最后一位时钟脉冲

位9~10: :时钟与相位设置

注意是在同步模式下设置极性和相位。与SPI时钟的极性和相位一致。
附SPI数据时钟序图:

位11: 时钟使能

位12~13: 设置停止位

①不设置时,默认1位停止位。
②2个停止位:用于常规USART模式、单线模式、调制解调器模式
③0.5个停止位:智能卡模式下接收数据
④1.5个停止位:智能卡模式下发送和接收数据

前面CR1寄存器设置过字长、奇偶校验、起始位,本寄存器位13:12设置停止位。其数据传输如下:


位14: LIN模式使能

之前总结的CR1寄存器位0发送断开符。需要通过CR2寄存器位14使能断开符发送,使能后才能通过CR1寄存器位0发送断开符。

3、控制寄存器 3(USART_CR3)


位0:错误中断使能

该中断用于DMA传送时的错误中断,所以必须先使能DMA,即需将本寄存器的位6置1 。

位1~5:

分别为红外模式使能、红外功耗、半双工选择、智能卡不应答设置、只能卡使能
位6~7:使能DMA发送和接收

位8~10:

分别为RTS硬件流使能、CTS硬件流使能、CTS中断使能。

4、状态寄存器(USART_SR)

位0:校验错误状态

该位在接收模式出现奇偶校验错误时,硬件置1 。注意清除方式:先读SR寄存器、再读DR寄存器。读DR寄存器,要保证DR寄存器非空,所以读之前一定要保证SR寄存器位5 RXNE位为1 。

位1:帧错误状态

当检测到同步错位,过多的噪声或者检测到断开符,该位被硬件置位。注意清除方式:先读SR寄存器、再读DR寄存器。

位2:噪声错误标志


在接收到的帧检测到噪音时,由硬件对该位置位。注意清除方式:先读SR寄存器、再读DR寄存器。

位3:过载错误状态

当DR寄存器非空,但是又有数据传到RDR移位寄存器时,出现过载错误。即当SR寄存器位5 RXNE位为1时,有数据传进RDR移位寄存器,出现过载错误。注意清除方式:先读SR寄存器、再读DR寄存器。

位4:总线空闲状态

当总线处于空闲状态时,该位被硬件置1。注意清除方式:先读SR寄存器、再读DR寄存器。

位5:读数据寄存器非空

当数据从RDR移位寄存器转移到DR数据寄存器是,该位硬件置1 。注意清除方式:读DR寄存器或直接写入0。

位6:发送完成状态

当发送完一帧的数据,且TD寄存器数据被移入移位寄存器时,该位硬件置1 。注意清除方式:先读SR寄存器、再读DR寄存器。

位7:发送数据寄存器空

注意清除方式:写读DR寄存器。

位8: LIN断开检测标志

位9:CTS标志

5、数据寄存器(USART_DR)

该寄存器只有低9位有效

数据寄存器分为发送数据寄存器TDR和接收数据寄存器RDR。

6、波特比率寄存器(USART_BRR)+波特率计算方法

该寄存器的的位15:4储存分频器除法因子的整数部分,位3:0储存分频除法因子的小数部分。

波特率计算:

STM32F1中USART1搭载在APB2线上,时钟最大为72MHZ;USART2、USART3、USART4、USART5搭载在APB1上,时钟最大为36MHZ。

以USART1、72MHZ时钟、115200波特率为例:

根据上述公式:

USARTDIV=72000000/(16*115200)=39.0625

十进制39=100111B=0x27,故将0x27存入BRR寄存器位15:4

小数部分转二进制:

故十进制0.0625=0.0001B=0x 0.1

故BRR寄存器位3:0存储 1

三、USART相关库函数

1、USART复位函数USART_DeInit()


该函数开启USARTx时钟,再打开,完成复位

2、USART初始化函数USART_Init()


参数1:
USART1~USART5

参数2:
结构体

成员:

  uint32_t USART_BaudRate;       		 //波特率    
  uint16_t USART_WordLength;      		 //字长 
  uint16_t USART_StopBits;        		 //停止位  
  uint16_t USART_Parity;            	 //校验码
  uint16_t USART_Mode;             		 //发送或接收模式
  uint16_t USART_HardwareFlowControl;	 //硬件控制流

3、结构体初始化函数USART_StructInit()

参数:
USART_Init()函数传递的那个结构体

操作:

波特率:9600
字长:8位
停止位:1位
校验位:无
模式:接收或发送
硬件控制流:无

4、同步模式下时钟配置函数USART_ClockInit()

参数1:
USART1~USART5

参数2:
结构体,成员:

typedef struct
{
  uint16_t USART_Clock;  //时钟使能
  uint16_t USART_CPOL;   //时钟极性
  uint16_t USART_CPHA;    //时钟相位
  uint16_t USART_LastBit; //时钟最后一个脉冲是否从ck输出
} USART_ClockInitTypeDef;

5、同步模式下初始化时钟配置结构体函数USART_ClockStructInit()

设置为:
①不使能同步时钟
②极性位0
③相位为0
④时钟脉冲最后一个周期不从Ck输出

6、USART使能函数USART_Cmd()


参数1:
USART1~USART5

参数2:
ENABLE 或DISABLE

7、USART中断配置函数USART_ITConfig()


参数1:
USART1~USART5

参数2:

USART_IT_PE          //奇偶校验错误中断               
USART_IT_TXE         //发送数据寄存器空中断              
USART_IT_TC          //发送完成中断       
USART_IT_RXNE        //接收数据就绪中断              
USART_IT_IDLE 	 	 //空闲总线中断
USART_IT_LBD          //断开标志中断              
USART_IT_CTS           //CTS中断      
USART_IT_ERR 		 //传输出错中断

参数3:
EnABLE 或DISABLE

8、DMA使能函数USART_DMACmd()

参数1:
USART1~USART5

参数2:

参数3:
ENABLE 或DISABLE

9、静默模式下设备地址设置函数USART_SetAddress()


该函数在多处理器通信下的静默模式中使用的,使用地址标记来唤醒某个USART设备。

参数1:
USART1~USART5

参数2:

小于0xf的一个数

10、静默模式下唤醒方式设置函数USART_WakeUpConfig()


参数1:
USART1~USART5

参数2:


分别为空闲总线唤醒、地址标记唤醒

11、设置静默模式函数USART_ReceiverWakeUpCmd()

参数1:
USART1~USART5

参数2:

ENABLE 或DISABLE

ENABLE 时将USARTx设置为静默模式,否则为正常工作模式。

12、LIN模式下断开符长度设置函数USART_LINBreakDetectLengthConfig()


参数1:
USART1~USART5

参数2:

13、使能LIN模式函数USART_LINCmd()


参数1:
USART1~USART5

参数2:

ENABLE 或DISABLE

14、发送数据函数USART_SendData()


参数1:
USART1~USART5

参数2:

一个小于等于0x1ff的数,DR寄存器为低9位有效寄存器

15、接收数据函数USART_ReceiveData()


参数1:
USART1~USART5

DR寄存器可读、写

16、发送断开符函数USART_SendBreak()


参数1:
USART1~USART5

17、状态获取函数USART_GetFlagStatus()与USART_GetITStatus()


参数1:
USART1~USART5

参数2:

USART_IT_PE          //奇偶校验错误中断               
USART_IT_TXE         //发送数据寄存器空中断              
USART_IT_TC          //发送完成中断       
USART_IT_RXNE        //接收数据就绪中断              
USART_IT_IDLE 	 	 //空闲总线中断
USART_IT_LBD          //断开标志中断              
USART_IT_CTS           //CTS中断      
USART_FLAG_ORE			//数据溢出中断
USART_FLAG_NE				//噪声标记中断
USART_FLAG_FE				//帧错误中断

返回:

返回SET表示发生中断
返回RESET表示未发生中断

18、清除标志位函数USART_ClearFlag()与USART_ClearITPendingBit


参数1:
USART1~USART5

参数2:

USART_IT_PE          //奇偶校验错误中断               
USART_IT_TXE         //发送数据寄存器空中断              
USART_IT_TC          //发送完成中断       
USART_IT_RXNE        //接收数据就绪中断              
USART_IT_IDLE 	 	 //空闲总线中断
USART_IT_LBD          //断开标志中断              
USART_IT_CTS           //CTS中断      
USART_FLAG_ORE			//数据溢出中断
USART_FLAG_NE				//噪声标记中断
USART_FLAG_FE				//帧错误中断

四、USART编程顺序

1、串口时钟和GPIO时钟使能

其中:
USART1使用RCC_APB2PeriphClockCmd()使能
USART2~5使用RCC_APB1PeriphClockCmd使能

2、串口复位(可不必)

使用USART_DeInit()函数复位串口

3、GPIO初始化(+一个重点说明

使用GPIO_Init()函数初始化GPIO

在《中文参考手册》GPIO章节的“外设的GPIO配置”中得到以下信息:

所以一定要注意在什么模式下,GPIO应该配置为什么模式。

最长用的是TX、RX全双工模式,所以TX引脚配置为推挽复用输出、RX引脚配置为浮空输入或上拉输入(空闲状态下,总线为高电平,所以可以为上拉输入)

4、串口初始化

使用 USART_Init()函数初始化串口

5、串口使能

使用USART_Cmd()函数使能串口

6、中断配置(如果需要)

使用USART_ITConfig()配置中断
如果配置了中断,需要设置NVIC中断分组,使用NVIC_Init()函数

7、发送和接收数据

使用USART_SendData()函数发送数据
使用USART_ReceiveData()函数接收数据

8、获取串口状态

使用USART_GetFlagStatus()函数获取USART状态

9、获取中断状态(如果需要)

使用USART_GetITStatus()函数获取中断状态

五、例1(发送一个字符到串口)

1、题

利用按键,通过串口1发送一个字符:按一下KEY_UP按键发送一个字符a;按一下KEY1发送一个字符b;按一下KEY2,发送一个字符c;按一下KEY3,发送字符d;通过串口监视器察看串口发送情况。

2、分析

按键实验STM32F103五分钟入门系列(五)按键实验(库函数+寄存器)总结过,不再赘述。

在串口设置中,PA9为USART1的TX引脚,设置为复用推挽输出。PA10为USART1的RX引脚,设置为浮空输入(本实验没用到输入,所以可以不设置)。没有接收,所以接收中断、中断优先级分组都不用设置。

需要用到串口监视器,所以需要通过跳线帽,将PA9、PA10与RS232的引脚连接,通过RS232与电脑USB连接在一起

3、代码

(1)按键代码

key.h代码:

#ifndef KEY_H
#define KEY_H
void KEY_Init(void);
#endif

key.c代码:

#include "stm32f10x.h"
#include "sys.h"
#include "key.h"
void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct_A;
	GPIO_InitTypeDef GPIO_InitStruct_E;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE , ENABLE);//使能GPIOA和GPIOE(PA0 PE2、3、4)
	
	GPIO_InitStruct_A.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStruct_A.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct_A.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct_A);//PA0 key_up  下拉输入
	
	GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
	GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &GPIO_InitStruct_E);//PE2、3、4 key0、key1、key2 上拉输入
}

(2)串口设置代码

myusart.h代码:

#ifndef _USART_
#define _USART_
#include "stm32f10x.h"
void Myusart_Init(u32);
#endif

myusart.c代码:

#include "myusart.h"
#include "stm32f10x.h"
void Myusart_Init(u32 baud)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef  USART_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //TX
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //RX
	
	USART_InitStructure.USART_BaudRate=baud;
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStructure.USART_Parity=USART_Parity_No;
	USART_InitStructure.USART_StopBits=USART_StopBits_1;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&USART_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

(3)主函数代码

#include "stm32f10x.h"
#include "myusart.h"
#include "delay.h"
#include "key.h"
char temp=0;
 int main(void)
 {	
	 Myusart_Init(115200);
	 KEY_Init();
	 delay_init(); 
	 while(1)
	 {
	 if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
		{
			delay_ms(10);                                 //消抖
			while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
			{
				temp='a';
			}//取消按下key_up
			if(temp=='a')
			{
				USART_SendData(USART1,temp);
				temp=0;
			}
					
		}
	if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//key0按下
		{
			delay_ms(10);  
			while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//key0按下
			{
				temp='b';
			}//取消按下key0
			if(temp=='b')
			{
				USART_SendData(USART1,temp);
				temp=0;
			}
		}
		
		
		else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)//key1按下
		{
			delay_ms(10);
			while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)//key1按下
			{
				temp='c';
			}
			//取消按下key1
			if(temp=='c')
			{
				USART_SendData(USART1,temp);
				temp=0;
			}
		}
		else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//key2按下
		{
			delay_ms(10);  
			while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//key2按下
			{
				temp='d';
			}//取消按下key2
			if(temp=='d')
			{
				USART_SendData(USART1,temp);
				temp=0;
			}
		}
		
	 } 
  }

4、测试效果:

分别摁一下KEY_UP、KEY0~3:

六、例2(STM32F1、F4之间的串口通信)(+一个注意事项

注意:两块板子必须共地!虽然不同的板子布线不一样,但是如果是四层以上板子,只需要将两块板子的任意GND引脚连起来就行,这样的话两片板子的负片GND就连在一起了。

1、题

F1板子发送字符给F4板子,F4板子做以下操作:

①如果F4接收到字符a,LED0亮、LED1灭;延迟200ms,LED0灭、LED1亮,延迟200ms。两个灯只呼吸一次。
②如果F4接收到字符b,LED0、LED1同时亮、延时200ms后,同时灭。两个灯只呼吸一次。
③如果F4接收到字符c,LED0亮一次,延迟200ms后灭,LED1保持熄灭。LED0呼吸一次。
④如果F4接收到字符d,LED1亮一次,延迟200ms后灭,LED0保持熄灭。LED1呼吸一次。
如果没有接收到字符或接收到其它字符,则LED0、LED1保持熄灭状态。

2、分析

F4板子中,需要接收数据,所以需要配置接收完成中断,告诉单片机什么时候去读数。此外。由于呼吸灯只呼吸一次,所以每次执行完LED闪烁代码后,数据需要清零。

另外F1与F4的波特率要保持相同!

3、代码

(1)F1代码

F1代码与例1代码完全相同。

(2)F4的LED代码

led.h代码

#ifndef LED_H
#define LED_H
void LED_Init(void);

#endif

led.c代码

#include "sys.h"
#include "stm32f4xx.h"
#include "led.h"
void LED_Init以上是关于STM32F103(二十三)通用同步异步收发器(USART)的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103(二十三)通用同步异步收发器(USART)

STM32F103(二十四)一篇博客精通《485通信》

STM32F103(二十五)完美解决USART发送接收floatu16u32数据

STM32F103X datasheet学习笔记---USART

STM32调试串口

stm32can通信和串口的区别