stm32串口相关知识笔记

单片机的串口用的真的非常多,知识点也多,这里因为最近复习了一下做一个总结

这里的例子我都以103为例,407的话只是串口的配置不一样而已
在这里插入图片描述

USART初始化结构体

typedef struct
{
    
    
  uint32_t USART_BaudRate;      //波特率 
  uint16_t USART_WordLength;    //字长 
  uint16_t USART_StopBits;      //停止位
  uint16_t USART_Parity;        //校验控制 
  uint16_t USART_Mode;          //模式选择 接收或者发送
  uint16_t USART_HardwareFlowControl;//有无硬件流控制
} USART_InitTypeDef;

串口操作相关库函数

void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断

void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位

在这里插入图片描述

串口的一般配置步骤

串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
串口复位:USART_DeInit(); 这一步不是必须的
GPIO端口模式设置:GPIO_Init(); 
串口参数初始化:USART_Init();
开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
      NVIC_Init();
      USART_ITConfig();
使能串口:USART_Cmd();
编写中断处理函数:USARTx_IRQHandler();
串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

下面以串口一为例

GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//①串口时钟使能,GPIO 时钟使能,复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|
RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟

//②串口复位
USART_DeInit(USART1); //复位串口 1

//③GPIO 端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //ISART1_TX PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10

//④串口参数初始化
USART_InitStructure.USART_BaudRate = 115200; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl
= USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); 

//⑤初始化 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化

//⑤开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断

//⑥使能串口
USART_Cmd(USART1, ENABLE); //使能串口

中断服务函数

下面我们来逐步分析

标志位

首先是一些重要的状态标志位

在这里插入图片描述
单片机发送的时候是先将数据发送到发送数据寄存器,然后在通过发送移位寄存器一位一位发送到TX引脚,接收的时候是通过rx发送到接收移位寄存器,然后再给接收数据寄存器,在给单片机

标志位 作用
TXE 发送数据寄存器空,发送单个字节使用
TC 发送完成,发送多个字节时使用
TXIE 发送完成中断使能
RXNE 接收数据寄存器非空
RXNEIE 接收缓冲区非空中断使能

注意:TXE和TC的复位值为1,而RXNE的复位值为0

这里需要理解清楚

发送数据寄存器

TXE为1:TDR里的数据全部到了移位寄存器,并且没有新的数据进TDR
TXE为0;TDR里有数据,非空,则TXE=0

发送移位寄存器

TC为1:从TDR过来的数据全部被移送到TX引脚,并且TDR里也没有新的数据
TC为0:从TDR过来的数据还没有完全被移过去,或者之前TDR里的数据被移走了,但TDR里又来了新的数据

来几个例子试一下,这里省略了串口的配置,也没有开启中断

USART_SendData(USART1,'d');
USART_SendData(USART1,'y');
USART_SendData(USART1,'k');

结果: k
最后串口助手上只打印了一个k
原因:把‘d’发送到发送数据寄存器的时候,因为没有停顿,所以接着发送的‘y’就把‘d’覆盖了,同理可知‘y’也被’k’覆盖了,最后发送到发送移位寄存器,和TX引脚

USART_SendData(USART1,'d');
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
USART_SendData(USART1,'y');
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
USART_SendData(USART1,'k');
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);

结果:dyk
最后串口助手上只打印了dyk
第二个结果不一样是因为加了循环判断标志位,TDR里有数据,非空,则TXE=0,如果为0就会一直在循环里面,直到数据发送到发送移位寄存器,才结束循环

最后标志位全部改成TC

 USART_SendData(USART1,'d');
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,'y');
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,'k');
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);

结果:yk
最后串口助手上只打印了yk
在这里插入图片描述
可以看到TC是先读,再写入,然后就是前面强调的,TXE和TC的复位值为1
所以写完‘d’后直接就退出循环了,‘y’就把‘d’给覆盖了

发现状态标志不同,产生的结果也都不同
然后这里再提一下

USART_GetITStatus和USART_GetFlagStatus的区别

USART_GetITStatus
该函数不仅会判断标志位置1,同时还会判断是否使能了
相应的中断,所以在串口中断函数中,使用该函数

USART_GetFlagStatus
该函数只判断标志位

对发送函数的封装

发送一个字节

void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
    
    
	USART_SendData(pUSARTx, data);
	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
}

发送一个数组

/* 发送8位数据的数组 */
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num)
{
    
    
	uint8_t i;
	for( i=0; i<num; i++ )
  {
    
    
		Usart_SendByte(pUSARTx, array[i]);
	}
	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
}

发送字符串

/* 发送字符串 */
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
    
    
	uint8_t i=0;
	do
  {
    
    
		Usart_SendByte(pUSARTx, *(str+i));
		i++;
	}while(*(str+i) != '\0');
	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
}

中断接收

u8 res;
	 if(USART_GetITStatus(USART1,USART_IT_RXNE))
 {
    
    
     res= USART_ReceiveData(USART1); 
     USART_SendData(USART1,res);   
  } 

最后不要用while而用if

printf重定向

在这里插入图片描述
一定要把这个Use MicroLIB给勾上
否则就要 usart.h加入这段代码

//加入以下代码,支持 printf 函数,而不需要选择 use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
    
    
int handle; 
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
    
    
x = x;
} 
//重定义 fputc 函数
int fputc(int ch, FILE *f)
{
    
    
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
 USART_SendData(USART1,(uint8_t)ch);
return ch;
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
    
    
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    
    
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

猜你喜欢

转载自blog.csdn.net/qq_44866153/article/details/113624473