STM32使用HAL库实现串口通讯——理论讲解

简介: STM32使用HAL库实现串口通讯——理论讲解

一、查询模式

1.

二、中断模式

1.中断接收。

1.1先看中断接收的流程(以 USART2 为例)

在启动文件中找到中断向量

USART2_IRQHandler

找到USART2_IRQHandler的函数定义

 

可以看到这里又转到另一个函数里去了,再找下去:

该函数的源码:

/**

 * @brief  This function handles UART interrupt request.

 * @param  huart: pointer to a UART_HandleTypeDef structure that contains

 *                the configuration information for the specified UART module.

 * @retval None

 */

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)

{

  uint32_t isrflags   = READ_REG(huart->Instance->SR);

  uint32_t cr1its     = READ_REG(huart->Instance->CR1);

  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  uint32_t errorflags = 0x00U;

  uint32_t dmarequest = 0x00U;


 /* If no error occurs */

 errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));

 if(errorflags == RESET)

 {

   /* UART in mode Receiver -------------------------------------------------*/

   if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))

   {

     UART_Receive_IT(huart);

     return;

   }

 }  


 /* If some errors occur */

 if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))

 {

   /* UART parity error interrupt occurred ----------------------------------*/

   if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))

   {

     huart->ErrorCode |= HAL_UART_ERROR_PE;

   }

   

   /* UART noise error interrupt occurred -----------------------------------*/

   if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

   {

     huart->ErrorCode |= HAL_UART_ERROR_NE;

   }

   

   /* UART frame error interrupt occurred -----------------------------------*/

   if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

   {

     huart->ErrorCode |= HAL_UART_ERROR_FE;

   }

   

   /* UART Over-Run interrupt occurred --------------------------------------*/

   if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

   {

     huart->ErrorCode |= HAL_UART_ERROR_ORE;

   }


   /* Call UART Error Call back function if need be --------------------------*/    

   if(huart->ErrorCode != HAL_UART_ERROR_NONE)

   {

     /* UART in mode Receiver -----------------------------------------------*/

     if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))

     {

       UART_Receive_IT(huart);

     }


     /* If Overrun error occurs, or if any error occurs in DMA mode reception,

        consider error as blocking */

     dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);

     if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)

     {

       /* Blocking error : transfer is aborted

          Set the UART state ready to be able to start again the process,

          Disable Rx Interrupts, and disable Rx DMA request, if ongoing */

       UART_EndRxTransfer(huart);

       

       /* Disable the UART DMA Rx request if enabled */

       if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))

       {

         CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

         

         /* Abort the UART DMA Rx channel */

         if(huart->hdmarx != NULL)

         {

           /* Set the UART DMA Abort callback :

              will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */

           huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;

           if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)

           {

             /* Call Directly XferAbortCallback function in case of error */

             huart->hdmarx->XferAbortCallback(huart->hdmarx);

           }

         }

         else

         {

           /* Call user error callback */

           HAL_UART_ErrorCallback(huart);

         }

       }

       else

       {

         /* Call user error callback */

         HAL_UART_ErrorCallback(huart);

       }

     }

     else

     {

       /* Non Blocking error : transfer could go on.

          Error is notified to user through user error callback */

       HAL_UART_ErrorCallback(huart);

       huart->ErrorCode = HAL_UART_ERROR_NONE;

     }

   }

   return;

 } /* End if some error occurs */


 /* UART in mode Transmitter ------------------------------------------------*/

 if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))

 {

   UART_Transmit_IT(huart);

   return;

 }

 

 /* UART in mode Transmitter end --------------------------------------------*/

 if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))

 {

   UART_EndTransmit_IT(huart);

   return;

 }

}

 

也就是说,当串口中断触发以后,几经周转到了这里,该函数功能是读取寄存器的几个状态,判断无误后再转到另一个函数,就是上图小矩形框出来的UART_Receive_IT(huart);

然后我们再去看UART_Receive_IT(huart)这个函数原型:

回调函数就在这个UART_Receive_IT(huart)函数里:

在回调函数上边有两行很重要的代码:

这两行代码的作用是关闭串口接收中断,也就是说,在一次串口中断接收过程的最后,即串口接收完一组数据之后会关闭串口接收中断。(这个后面还会再讲,先记住)。

总结一下,串口中断接收的流程:

USART2_IRQHandler(void)    ->    HAL_UART_IRQHandler(UART_HandleTypeDef *huart)    ->    UART_Receive_IT(UART_HandleTypeDef *huart)    ->    HAL_UART_RxCpltCallback(huart);

Callback函数就是用户要重写在main.c里的回调函数。

再说明一下一个很重要的问题:STM32的每个串口中断有好几个(发送接收等),但是只要是与串口相关的中断发生系统都会先调用同一个函数,也就是中断向量表中的那个,比如usart2的话就是USART2_IRQHandler(void),然后这个函数再调用HAL_UART_IRQHandler,在HAL_UART_IRQHandler中去读取寄存器判断究竟是那几个位被置为1,确定好是哪个中断之后(接收还是发送)再调用不同的回调函数。

1.2如何使用接收中断。

在cube中配置完了之后并没有使能串口中断(有一个串口初始化函数,但是在这个函数中并未使能串口中断)需要用户手动使能。使能代码如下:

HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10);

什么意思呢?

HAL库的串口接收思路是这样的:用户你可以随便定义一个缓存区,大小随意,然后通过上边这个函数把这个缓存区对应到串口的接收,上面函数的意思就是把kRxBuffer(这是一个数组)作为缓存区,指定大小为10。然后usart2接收数据的时候就防到kRxBuffer这个数组中,只有当接收到10个数据之后才调用一次callback函数(回调函数)。当然不要忘了该函数的使能串口接收中断功能, 在:二、中断模式 的1.1节中说到了串口接收完数据后会关闭使能,所以,在回调函数中一定要再写一次HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10),使能接收中断。

小小的总结下串口中断接收怎么用:

(1)指定一个缓存区(串口接收到的数据会全部堆到这个缓存区)

(2)使能串口接收中断,并把缓存区对应到串口

(3)在回调函数中实现接收到数据之后的操作(比如处理数据)并再次使能串口接收中断。

所以更具体一下串口接收的流程就是这样的:

(1)串口一个接一个的接收到数据填充到缓存区

(2)缓存区满(大小是用户定义的)程序几经辗转最后会调用到回调函数。

(3)执行用户在回调函数中实现的功能。

2.中断发送。

2.1发送中断的触发流程。

由于在STM32中usart2的入口中断只有一个:

就是上图的中断向量表中红框标出来的。其他的所有中断其实都是从这里出发的,我们再和捋接收一样捋一遍发送。

首先是USART2_IRQHandler,找到这个函数原型(这一步和接收完全一样):

再找HAL_UART_IRQHandler(&huart2);原型:

到这里还是和接收完全一样,注意是完全一样,源码也就是上面接收贴出来的一样。这次我们主要注意该函数最后几行(可以翻上去看源码):

把中间代码收起来以后看最后红框,这就很明显了,这里触发了发送中断(软件触发)

接着去找这个UART_EndTransmit_IT(huart)的函数原型:

第一个红框里清除了发送中断使能(同接收一样,在用完之后就关掉,但是不同于接收,发送完成就不用再在回调函数中使能了,因为在中断发送的时候就会使能),第二个红框调用回调函数。

2.2如何使用发送中断。

中断发送的意思,非常类似于中断接收,但其中有一些不同,看下面这个函数:

HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

一个非常类似于中断接收使能的函数。接收中断使能函数的作用是绑定接收缓存区并使能接收中断,但是对于发送,该函数的作用是发送指定长度的指定数据并使能发送中断。

比如有一个unsigned char 数组a[10],HAL_UART_Transmit_IT(&huart2, a, 10),这一句的意思是用usart2(串口2)发送a数组中的10个数据,然后使能发送中断。

当发送完成之后(或者发送一半,发送一半也有个中断)就会执行回调函数。

总结一下发送中断:

使用HAL_UART_Transmit_IT函数发送指定长度的数据,并使能发送中断,发送到一半和发送结束会触发中断(相关的回调函数是HAL_UART_TxHalfCpltCallback()和HAL_UART_TxCpltCallback())中断触发后发送中断使能会被清除,然后调用回调函数,回调函数执行完成之后结束本次发送。

相关文章
|
1月前
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
168 0
|
1月前
STM32CubeMX 串口收发一帧数据
STM32CubeMX 串口收发一帧数据
33 9
|
1月前
|
芯片
STM32CubeMX 串口数据收发
STM32CubeMX 串口数据收发
23 2
|
1月前
|
监控
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
|
2月前
|
IDE 开发工具
使用STM32F103标准库实现自定义键盘
通过本文,我们学习了如何使用STM32F103标准库实现一个简单的自定义键盘。我们首先初始化了GPIO引脚,然后实现了一个扫描函数来检测按键状态。这个项目不仅能够帮助我们理解STM32的GPIO配置和按键扫描原理,还可以作为进一步学习中断处理和低功耗设计的基础。希望本文对你有所帮助,祝你在嵌入式开发的道路上不断进步!
294 4
|
2月前
|
存储 数据采集 数据安全/隐私保护
使用STM32F103读取TF卡并模拟U盘:使用标准库实现
通过以上步骤,你可以实现用STM32F103将TF卡内容变成U盘进行读取。这种功能在数据采集、便携式存储设备等应用中非常有用。如果你有更多的需求,可以进一步扩展此项目,例如添加文件管理功能、加密存储等。希望这篇博客能帮到你,如果有任何问题,欢迎在评论区留言讨论!
66 1
|
1月前
|
传感器 编解码 API
【STM32开发入门】温湿度监测系统实战:SPI LCD显示、HAL库应用、GPIO配置、UART中断接收、ADC采集与串口通信全解析
SPI(Serial Peripheral Interface)是一种同步串行通信接口,常用于微控制器与外围设备间的数据传输。SPI LCD是指使用SPI接口与微控制器通信的液晶显示屏。这类LCD通常具有较少的引脚(通常4个:MISO、MOSI、SCK和SS),因此在引脚资源有限的系统中非常有用。通过SPI协议,微控制器可以向LCD发送命令和数据,控制显示内容和模式。
|
2月前
使用STM32F103标准库实现定时器控制LED点亮和关闭
通过这篇博客,我们学习了如何使用STM32F103标准库,通过定时器来控制LED的点亮和关闭。我们配置了定时器中断,并在中断处理函数中实现了LED状态的切换。这是一个基础且实用的例子,适合初学者了解STM32定时器和中断的使用。 希望这篇博客对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。
158 2
|
3月前
|
传感器
|
2月前
|
传感器
【经典案例】STM32F407使用HAL库配置I2C详解
STM32F407是一个强大的微控制器,广泛应用于嵌入式系统中。在许多应用中,我们需要使用I2C总线来与传感器、EEPROM、显示屏等外设进行通信。本文将详细介绍如何使用STM32 HAL库来配置和使用I2C接口。
224 2