26.UART串口接收过程与配置

UART串口接收过程与配置

参考资料
《STM32Fx中文参考手册》第26章:通用同步异步收发器章节
开发板配套教程《STM32Fx开发指南》 串口实验章节

笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除

一、串口接收过程配置流程概述

1.串口接收流程(配置步骤)

① 编程USARTx_CR1的M位来定义字长。
② 编程USARTx_CR2的STOP位来定义停止位位数。
③ 编程USARTx_BRR寄存器确定波特率。
④ 使能USARTx_CR1的UE位使能USARTx。
⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。
⑥ 使能USARTx_CR1的RE位为1使能接收器。
⑦ 如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1的RXNEIE位为1。

2.当串口接收到数据时

① USARTx_SR(ISR)的RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。
② 如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)
③ 如果开启了其他中断(帧错误等),相应标志位会置1。
④ 读取USARTx_RDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。

3.串口接收流程(HAL库)
1)配置过程
  • 接收配置步骤①~⑥和发送流程一样,调用HAL_UART_Init函数
	HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
  • 步骤⑦开启接收中断:
	HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef*huart, uint8_t *pData, uint16_t Size);
2)接收数据过程
  • 步骤①获取状态标志位通过标识符实现:
	__HAL_UART_GET_FLAG //判断状态标志位
	__HAL_UART_GET_IT_SOURCE //判断中断标志位

**注意:**这里USART表示同步进行,UART表示异步进行

  • 步骤②~③中断服务函数:
	void USARTx_IRQHandler(void) ;//(x=1~3,6)
	void UARTx_IRQHandler(void) ;//(x=4,5,7,8)

在启动文件startup_stm32fxxx.s中查找。

  • 步骤④读取接收数据:
	HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
4.串口接收中断程序配置过程(HAL库)

① 初始化串口相关参数,使能串口:HAL_UART_Init();
② 串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。
③ 串口接收中断优先级配置和使能:
HAL_NVIC_EnableIRQ(); //使能中断通道
HAL_NVIC_SetPriority(); //设置抢占优先级和响应优先级
④ 使能串口接收中断:HAL_UART_Receive_IT();
⑤ 编写中断服务函数:USARTx_IRQHandler

  • 经过上面6个步骤,我们就可以写完整的串口接收实验。我们就可以在中断服务函数中编写中断处理过程。
    HAL库提供了详细的中断处理函数
    HAL_UART_IRQHandler
    我们在中断服务函数中会调用此函数处理中断。
    在这里插入图片描述

  • 串口接收中断流程
    在这里插入图片描述

5.串口中断服务函数执行流程

① 串口中断服务函数中调用HAL库串口中断通用处理函数:

	HAL_UART_IRQHandler();
该函数会对中断来源进行分析,调用相应函数。

② 对于不同的中断类型,我们只需要编写最终的中断处理函数:

	void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
	void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
	void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
	void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
	void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);

二、串口接收实验

实验目标:
通过电脑的串口小程序向单片机串口1发送数据,在单片机接收数据后同时在发送相同的数据给电脑。

步骤
① 初始化串口相关参数,使能串口:HAL_UART_Init();
② 串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。
③ 串口接收中断优先级配置和使能:
HAL_NVIC_EnableIRQ(); //使能中断通道
HAL_NVIC_SetPriority(); //设置抢占优先级和响应优先级
④ 使能串口接收中断:HAL_UART_Receive_IT();
⑤ 编写中断服务函数:USARTx_IRQHandler

  • 上一讲串口发送实验代码如下,我们在此基础上进行更改:
#include "sys.h"
#include "delay.h"
#include "usart.h"

UART_HandleTypeDef usart1_handler;		//定义结构体变量

void uart1_init()						//初始化uart1
{
	usart1_handler.Instance=USART1;
	usart1_handler.Init.BaudRate=115200; 				//定义波特率
	usart1_handler.Init.WordLength=UART_WORDLENGTH_8B;  //定义字长位8位
	usart1_handler.Init.StopBits=UART_STOPBITS_1;		//定义停止位
	usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;  //定义硬件流
	usart1_handler.Init.Mode=UART_MODE_TX_RX;			//定义模式
	usart1_handler.Init.Parity=UART_HWCONTROL_NONE;		//定义奇偶校验位
	
	HAL_UART_Init(&usart1_handler);		//初始换刚刚定义的结构体变量
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	GPIO_InitTypeDef  GPIO_Initure;			//初始化GPIO结构体
	
	if(huart->Instance ==USART1)					//如果是串口1,进行串口1 MSP初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
		
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FAST;		//高速
		GPIO_Initure.Alternate=GPIO_AF7_USART1;	//复用为USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
	}
}

int main(void)
{
    u8 data[]="-Apollo by wire-   ";			//定义一个数组,用于存放要发送的数据
	
    HAL_Init();                     //初始化HAL库    
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
	delay_init(180);				//初始化延时函数
	uart1_init();					//调用前面写的初始化函数
	
	while(1)
	{
		HAL_UART_Transmit(&usart1_handler,data,sizeof(data),1000);
		delay_ms(300);
	}
}
1.串口接收中断优先级配置和使能

首先要进行串口接收中断优先级配置,这里要针对串口一来进行操作
需要使用HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)函数
位置:工程文件-HALLIB-stm32f4xx_hal_cortex.c里面
在这里插入图片描述

函数HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)可以看到有三个入口参数:

1)IRQn_Type IRQn (使能哪个项目)

查看定义有
在这里插入图片描述
在这里插入图片描述

里面列举了所有IRQn可以使用的变量名,我们这里使用USART1_IRQn

2)uint32_t PreemptPriority (抢占优先级)和uint32_t SubPriority(响应优先级)

这里可以看到其定义的都是32为数字,按照前面所学,数字越大其级别越小,故这里我们把级别设小一点,其中抢占优先级为3;响应优先级为3

所以HAL_NVIC_SetPriority函数设置代码和位置如下

HAL_NVIC_SetPriority(USART1_IRQn,3,3)

加粗样式

2.使能中断通道

使能中断通道问哦们要调用HAL_NVIC_EnableIRQ函数
函数体HAL_NVIC_EnableIRQ(IRQn_Type IRQn),只有一个变量
位置:工程文件-HALLIB-stm32f4xx_hal_cortex.c里面
在这里插入图片描述

函数体中只有一个变量IRQn_Type IRQn,意思是使能哪个中断通道,这里我们使用USART1_IRQn
代码和位置如下:

HAL_NVIC_EnableIRQ(USART1_IRQn);

在这里插入图片描述

3.使能串口接收中断:HAL_UART_Receive_IT();

这里我们要使用HAL_UART_Receive_IT();函数
位置:工程文件-HALLIB-stm32f4xx_hal_uart.c里面
在这里插入图片描述

函数体:HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
有三个入口参数:和上一小节串口发送函数很像
UART_HandleTypeDef *huart(串口句柄)
uint8_t *pData(接收的数据)
uint16_t Size(接收的数据大小)

  • 串口句柄设置为&usart1_handler(同上一小节串口发送的句柄一样)
  • 接收的数据,我们在前面新定义一个数组用来存放接收来的数据:u8 rdata 所以第二个入口参数为刚刚定义的数组名rdata
  • 接收的数据大小,我们选择自适应rdata的数据大小,设置为sizeof(rdata)

函数体最终设置代码和位置为

u8 rdata[1];
int main(void)
{
HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata ));
}

在这里插入图片描述
在这里插入图片描述

4. 编写中断服务函数:USARTx_IRQHandler

在main.c里面添加中断服务函数:USARTx_IRQHandler,并在里面调用官方HAL库的中断服务函数HAL_UART_IRQHandler(&usart1_handler);
位置:工程文件-HALLIB-stm32f4xx_hal_uart.c里面
在这里插入图片描述

在HAL_UART_IRQHandler函数内部,会对中断的类型进行判断如果执行的是接收中断,它会调用UART_Receive_IT函数
在这里插入图片描述

查看UART_Receive_IT函数定义如下
在这里插入图片描述

在这个函数内部,MCU每接收到一个数据都会执行这个函数,然后把接收到的数据保存到变量pRxBuffPtr中

在接收完数据后,需要继续使能接收中断,所以加入使能接收中断函数HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata ));

最终中断服务函数代码和位置如下

void USART1_IRQHandler(void)			//中断服务函数
{
	HAL_UART_IRQHandler(&usart1_handler);		//调用官方HAL库的中断服务函数
	HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata ));			//使能接收中断
}

在这里插入图片描述

5.编写接收中断完成函数:HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

在main.c里面添加接收中断完成函数:HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
在这里插入图片描述

我们在函数体中编写上图框起来的部分的代码

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)		//接收中断完成函数
{
	u8 receive;
	if(huart->Instance ==USART1)				//如果是串口1,
	{
		receive=*((huart->pRxBuffPtr)-1);			//把接收到的数据赋值给receive
		//或使用receive=rdata[0];
	}
}

在这里插入图片描述

6.改写发送程序

这里我们要发送接收到的数据,即rdata[1]数组中的数据也是receive的地址中的数据
在中断完成函数里面编写串口发送函数HAL_UART_Transmit(&usart1_handler,&receive,1,1000);
代码和位置如下:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)		//接收中断完成函数
{
	u8 receive;
	if(huart->Instance ==USART1)				//如果是串口1,
	{
		receive=*((huart->pRxBuffPtr)-1);			//把接收到的数据赋值给receive
		//或使用receive=rdata[0];
		HAL_UART_Transmit(&usart1_handler,&receive,1,1000);		//使能发送中断
	}
}

在这里插入图片描述

7.把上一小节的发送函数注释掉

如图
在这里插入图片描述

因为库函数里面对串口接收和发送已经定义好了,在位置:工程文件-SYSTEM-usart.c文件里面,这里我们想要使用自己编写的代码,需要把usart.c文件删除
在这里插入图片描述

不删掉的话会报错说重复定义
在这里插入图片描述

8.main.c全部代码
#include "sys.h"
#include "delay.h"
#include "usart.h"

u8 rdata[1];			//定义一个数组,用于存放要接收的数据

UART_HandleTypeDef usart1_handler;		//定义结构体变量

void uart1_init()						//初始化uart1
{
	usart1_handler.Instance=USART1;
	usart1_handler.Init.BaudRate=115200; 				//定义波特率
	usart1_handler.Init.WordLength=UART_WORDLENGTH_8B;  //定义字长位8位
	usart1_handler.Init.StopBits=UART_STOPBITS_1;		//定义停止位
	usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;  //定义硬件流
	usart1_handler.Init.Mode=UART_MODE_TX_RX;			//定义模式
	usart1_handler.Init.Parity=UART_HWCONTROL_NONE;		//定义奇偶校验位
	
	HAL_UART_Init(&usart1_handler);		//初始化刚刚定义的结构体变量,调用官方HAL库的初始化函数
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	GPIO_InitTypeDef  GPIO_Initure;			//初始化GPIO结构体
	
	if(huart->Instance ==USART1)				//如果是串口1,进行串口1 MSP初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
		
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FAST;		//高速
		GPIO_Initure.Alternate=GPIO_AF7_USART1;	//复用为USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
		HAL_NVIC_SetPriority(USART1_IRQn,3,3);	//串口1.抢占优先级3,响应优先级3
		HAL_NVIC_EnableIRQ(USART1_IRQn);		//使能中断通道1
	}
}

void USART1_IRQHandler(void)			//中断服务函数
{	
	HAL_UART_IRQHandler(&usart1_handler);		//调用官方HAL库的中断服务函数
	HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata ));			//使能接收中断
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)		//接收中断完成函数
{
	u8 receive;
	if(huart->Instance ==USART1)				//如果是串口1,
	{
		receive=*((huart->pRxBuffPtr)-1);			//把接收到的数据赋值给receive
		//或使用receive=rdata[0];
		HAL_UART_Transmit(&usart1_handler,&receive,1,1000);		//使能发送中断
	}
}

int main(void)
{
    //u8 data[]="-Apollo by wire-   ";			//定义一个数组,用于存放要发送的数据
	
    HAL_Init();                     //初始化HAL库    
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
	delay_init(180);				//初始化延时函数
	uart1_init();					//调用前面写的初始化函数
	
	HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata ));			//使能接收中断
	
	while(1)
	{
		//HAL_UART_Transmit(&usart1_handler,data,sizeof(data),1000);		//使能发送中断
		//delay_ms(300);
	}
}
9.下载程序到开发板

下载程序到开发板,打开串口调试助手,点击发送数据a,观察现象
在这里插入图片描述

在前面编写的数组rdata[1]中,只定义了一个数据位,那么接下来我们发送更多数据试试,写发送数据apollo,得到结果如下
在这里插入图片描述

MCU也返回了apollo,其实他是一个一个返回的,apollo六个字母,系统返回了六次

  • 1
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值