STM32F4 HAL库学习笔记之串口通讯 2

STM32F4 HAL库学习笔记之串口通讯 2

  • 步骤
  • 函数使用
    • 串口初始化
    • 串口中断
    • 串口接收
    • 串口发送
  • 串口使用流程
  • 总结

步骤

  1. GPIO和串口时钟使能;
  2. 初始化GPIO,复用;
  3. 设置串口参数及中断;
  4. 使能串口;
  5. 编写中断服务函数。

函数使用

串口初始化

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;

UART_HandleTypeDef 中配置串口相关设置,在HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)调用时会使能响应串口,不需要另外单独使能。
执行HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)时会先调用MSP初始化回调函数进行MCU初始化,打开这个函数,在356行有

HAL_UART_MspInit(huart);

这句前面是看用户有没有自定义回调函数,判断是否执行这个。自定义下面会写。在这里面可以执行GPIO和中断相关配置。打开这个函数里面是

__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	  UNUSED(huart);
	  /* NOTE: This function should not be modified, when the callback is needed,
	           the HAL_UART_MspInit could be implemented in the user file
}

__weak是定义的一个弱函数,如果用户重新定义了这个函数,那么会优先执行用户定义的函数,可以重新定义这个函数配置与MCU级别相关的硬件初始化,这也是HAL库的优点。
所以HAL库中的初始化流程为:先初始化与MCU无关的串口协议,再初始化与MCU相关的硬件配置,这样在移植过程中只需要修改回调函数中的参数,即可完成适配。
回调函数:
函数A调用函数B的时候,通过参数将函数C的指针传递给了函数B(也就是函数B的入口参数写为函数C的指针)。在函数B执行过程中调用了函数C,这个动过叫做回调。在这个过程中先是被当作指针传入又被回调的函数C就是回调函数。
很多资料中说HAL_UART_MspInit(huart)是回调函数,其实这里只是写成了回调函数的样式,如果用户没有自定义回调函数,这个就可以看做是普通调用。
用户自定义回调函数:

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
	    UART_InitCallbacksToDefault(huart);
	    if (huart->MspInitCallback == NULL)
	    {
	      huart->MspInitCallback = HAL_UART_MspInit;
	    }
	    huart->MspInitCallback(huart);
	    #else
	    HAL_UART_MspInit(huart);
 #endif

如果定义了USE_HAL_UART_REGISTER_CALLBACKS=1,那么用户可以自定义串口的初始化函数

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void UART_InitCallbacksToDefault(UART_HandleTypeDef *huart)
{
    /* Init the UART Callback settings */
    huart->TxHalfCpltCallback        = HAL_UART_TxHalfCpltCallback;        /* Legacy weak TxHalfCpltCallback        */
    huart->TxCpltCallback            = HAL_UART_TxCpltCallback;            /* Legacy weak TxCpltCallback            */
    huart->RxHalfCpltCallback        = HAL_UART_RxHalfCpltCallback;        /* Legacy weak RxHalfCpltCallback        */
    huart->RxCpltCallback            = HAL_UART_RxCpltCallback;            /* Legacy weak RxCpltCallback            */
    huart->ErrorCallback             = HAL_UART_ErrorCallback;             /* Legacy weak ErrorCallback             */
    huart->AbortCpltCallback         = HAL_UART_AbortCpltCallback;         /* Legacy weak AbortCpltCallback         */
    huart->AbortTransmitCpltCallback = HAL_UART_AbortTransmitCpltCallback; /* Legacy weak AbortTransmitCpltCallback */
    huart->AbortReceiveCpltCallback  = HAL_UART_AbortReceiveCpltCallback;  /* Legacy weak AbortReceiveCpltCallback  */

}

这种状态就是回调函数了。
最后调用HAL_UART_Receive_IT函数开启接收中断,并设置接收缓冲以及最大接收数量。

串口中断

要使用中断,首先需要配置中断,由于NVIC属于MCU级别,所以放在HAL_UART_Msplnit中初始化,与库函数不同,HAL库中使用了两个函数配置中断:

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)//使能中断
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)//配置抢占优先级、子优先级。

串口接收

  1. 编写中断服务函数

USARTX_IRQHandler

HAL库中定义了一个串口中断处理通用函数:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

打开这个函数,找到如下部分:

if (errorflags == RESET)
{
	/* UART in mode Receiver -------------------------------------------------*/
 	   if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
 	   {
   		   UART_Receive_IT(huart);
    	    return;
  	   }
  }

isrflags 宏定义串口状态寄存器,cr1its控制状态寄存器,调用上面的通用函数后,会判断串口状态,是否进入UART_Receive_IT(huart),在这个函数中开始启动接收工作并调用串口数据处理函数:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

这个函数和上面一样是一个回调函数(用户可自定义)库中该函数的定义为弱函数,在这里进行串口数据处理,HAL库中所有的串口数据处理在这里面进行,所以这里面会判断是哪一个串口的数据再进行判断,剩下的流程就和用库函数处理是一样的了。

串口发送

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

这是HAL库中的串口发送函数,在标准库中串口发送只需要指定串口和数据,但是需要发送多个字节时是需要自己写函数的,如果要求比较高还要另外再做超时程序。在HAl库中使用这个函数可以定义发送长度,增加了超时处理,还是挺方便的,减少了开发的工作量。
调用后要等待发送完成

while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);

不然会有各种奇奇怪怪的错误。

串口使用流程

上面只是记录了各个函数的使用和原理,毕竟连函数的运行机制都没搞明白也写不出来好的程序,出了错误也找不到。
如果只是想用串口而已,知道下面写这些就可以了。

  1. 首先对串口与硬件级无关的参数进行初始化,对串口初始化
    时会调用串口硬件级初始化的回调函数,所以要做好回调函数,在回调函数中做好与硬件相关的串口参数配置,这样便于移植。
  2. 编写串口中断服务程序,中断触发后会调用串口数据处理回调函数。注意:串口处理函数是公用的,串口中断服务函数不是。
  3. 串口发送只需要调用库中定义好的函数,指定串口、数据、长度、超时即可。但要注意等待发送完成。

总结

C功底不够扎实,库中有很多东西看不明白,各种指针也是绕来绕去,我当初为什么要选择这个专业QAQ,想想当条咸鱼也没什么不好的。

你可能感兴趣的:(STM32,HAL,stm32,学习,单片机)