STM32第四课(UART,HAL)

HAL 库函数。这些函数和定义主要分布在 stm32f4xx_hal_usart.h 和 stm32f4xx_hal_usart.c 文件中。

+++++++++++++++++++++++++++++++++
串口作为 STM32 的一个外设, HAL 库为其配置了串口初始化函数。

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

入口参数 huart,为 UART_HandleTypeDef 结构体指针类型,称其为串口句柄。
一般情况下,我们会定义一个 UART_HandleTypeDef结构体类型全局变量,然后填充各个成员变量。

typedef struct
{
	USART_TypeDef *Instance;
	UART_InitTypeDef Init;
	uint8_t *pTxBuffPtr;
	uint16_t TxXferSize;
	uint16_t TxXferCount;
	uint8_t *pRxBuffPtr;
	uint16_t RxXferSize;
	uint16_t RxXferCount;
	DMA_HandleTypeDef *hdmatx;
	DMA_HandleTypeDef *hdmarx;
	HAL_LockTypeDef Lock;
	__IO HAL_UART_StateTypeDef State;
	__IO uint32_t ErrorCode;
}UART_HandleTypeDef;

该结构体成员变量非常多,一般情况下载调用函数 HAL_UART_Init 对串口进行初始化的
时候,我们只需要先设置 Instance 和 Init 两个成员变量的值。
Instance 是 USART_TypeDef 结构体指针类型变量,它是执行寄存器基地址,实际上这个基
地址 HAL 库已经定义好了,如果是串口 1,取值为 宏USART1 即可。
Init 是 UART_InitTypeDef 结构体类型变量,它是用来设置串口的各个参数,包括波特率,
停止位等。

typedef struct
{
	uint32_t BaudRate; //波特率
	uint32_t WordLength; //字长
	uint32_t StopBits; //停止位
	uint32_t Parity; //奇偶校验
	uint32_t Mode; //收/发模式设置
	uint32_t HwFlowCtl; //硬件流设置
	uint32_t OverSampling; //过采样设置
}UART_InitTypeDef

BaudRate 为串口波特率,我们设置为 8 位字长数据格式 UART_WORDLENGTH_8B。
StopBits 为停止位设置, 可以设置为 1 个停止位或者 2 个停止位,
这里我们设置为 1 位停止位 UART_STOPBITS_1。
Parity 设定是否需要奇偶校验,我们设定为无奇偶校验位。
Mode 为串口模式, 可以设置为只收模式,只发模式,或者收发模式。这里我们设置为全双工收发模式。
HwFlowCtl 为是否支持硬件流控制,我们设置为无硬件流控制。
OverSampling 用来设置过采样为 16 倍还是 8 倍。

函数 HAL_UART_Init 使用的一般格式为:

UART_HandleTypeDef UART1_Handler; //UART 句柄
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=115200; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式

HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1

HAL_UART_Init 内部会调用串口使能函数使能相应串口。
HAL_UART_Init 内部,会先调用 MSP 初始化回调函数进行 MCU 相关的初始化,函数为:

 void HAL_UART_MspInit(UART_HandleTypeDef *huart);

一般情况下,该函数内部用来编写 IO 口初始化,时钟使能以及 NVIC 配置。

+++++++++++++++++++++++++++++++++++++
上面只是开启了UART外设,外设功能,还要连接到对应的pin脚。
在HAL_UART_MspInit中,就是配置GPIO的复用,使pin脚被复用为UART的功能。

GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_9|GPIO_PIN_10; //PA9/PA10
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/PA10

在HAL_UART_MspInit中,对于中断优先级配置,方法就非常简单,

HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3

+++++++++++++++++++++++++++++++++++++
HAL 库中定义了一个使能串口中断的宏拟函数__HAL_UART_ENABLE_IT。
例如我们要使能接收完成中断,方法如下:

__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断

有开启中断就有关闭中断,操作方法为:

__HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断

串口 1 中断服务函数为:

void USART1_IRQHandler(void) ;

HAL 库实际上对中断处理过程进行了完整的封装,
HAL 库中,对中断服务函数的编写有非常严格的讲究。
首先 HAL 库定义了一个串口中断处理通用函数 HAL_UART_IRQHandler,该函数声明如下:

 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

串口句柄 huart,使用我们在调用 HAL_UART_Init 函数时设置的同一个变量即可。
该函数一般在中断服务函数中调用,作为串口中断处理的通用入口。一般调用方法为:

void USART1_IRQHandler(void)
{
	HAL_UART_IRQHandler(&UART1_Handler); //调用 HAL 库中断处理公用函数
	…//中断处理完成后的结束工作
}

真正的串口中断处理逻辑我们会最终在函数 HAL_UART_IRQHandler 内部执行。
在函数 HAL_UART_IRQHandler 内部通过判断中断类型是否为接收完成中断,确定是否调用 HAL 另外一个函数 UART_Receive_IT()。
函数 UART_Receive_IT()的作用是把每次中断接收到的字符保存在串口句柄的缓存指针 pRxBuffPtr 中,同时每次接收一个字符,其计数器 RxXferCount 减 1,直到接收完成 RxXferSize 个字符之后 RxXferCount 设置为0,并调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理。

串口接收中断的一般流程进行概括:
当接收到一个字符之后, 在函数UART_Receive_IT中会把数据保存在串口句柄的成员变量pRxBuffPtr缓存中,同时RxXferCount计数器减 1。
如果我们设置RxXferSize=10,那么当接收到 10 个字符之后, RxXferCount 会由 10减到 0( RxXferCount 初始值等于 RxXferSize)。
在RxXferCount 为0时,调用callback,进行post-process。

+++++++++++++++++++++++++++++++++++++++++++
HAL_UART_RxCpltCallback是一个弱函数,我们需要在自己的文件中重定义同名函数来覆盖它。

__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)

而__weak宏,其定义如下:

#define __WEAK                                 __attribute__((weak))
#define __weak                                 __attribute__((weak))

我们可以将callback定义在stm32f4xx_it.c文件中,覆盖掉同名弱函数。

++++++++++++++++++++++++++++++++++++
寄存器 USART_DR是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。

HAL 库操作 USART_DR 寄存器发送数据的函数是:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, 
							uint8_t *pData, uint16_t Size, uint32_t Timeout);

HAL 库操作 USART_DR 寄存器读取串口接收到的数据的函数是:

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart,
							uint8_t *pData, uint16_t Size, uint32_t Timeout);

HAL库使用中断完成发送和接收的函数是:

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

以接收为例,
将pData配置给串口句柄后,会启动串口的标准接收模式,并开启中断。
每当有串口中断事件发生时,会触发ISR的执行。ISR每接收一个byte,会放到串口句柄中指定的buffer中。当ISR检查到buffer中存放了指定长度的数据时,调用一次callback。

++++++++++++++++++++++++++++++++++++++++++++++++++
实例:
首先在cubemx中,配置usart1,nvic,并生成工程框架源码。
然后,在main中,使用HAL_UART_Receive_IT函数,发起每一次接收事务。需要的buffer,定义为全局变量。需要和callback通信的标志变量,也定义成全局变量。
然后,在stm32f4xx_it.c中,覆盖定义HAL_UART_RxCpltCallback回调函数。在回调函数中,打标志。在main中,判断标志,并清标。

中断事件是不停的发生的,所以,ISR是不停的被中断事件触发执行的。
但是callback却不是不停的触发的,callback事件的触发,有两个条件,
第一个条件是,中断事件发生,并触发了ISR执行,
第二个条件是,设备句柄中的某些变量的值,处于合适的状态。

对于rx complete callback,这个设备状态,指的的就是句柄中所保存的,当前的接收事务,处于完成状态,只有ISR检查到接受事务处于完成状态,才会触发一次rx complete callback的执行。

callback的触发执行,是在ISR的运行上下文中的。
在一个前后台的程序架构中,callback通常只做少量的工作,最少占用CPU,通常是做一些打标的工作。用来向main传递相应的完成信息。
在main中,检查相应的标志,从而获取需要的完成信息。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值