历史上的今天
返回首页

历史上的今天

今天是:2025年04月09日(星期三)

正在发生

2019年04月09日 | STM32使用HAL库实现串口通讯——理论原理详细讲解

2019-04-09 来源:eefocus

一、中断模式


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())中断触发后发送中断使能会被清除,然后调用回调函数,回调函数执行完成之后结束本次发送。


推荐阅读

史海拾趣

Excelsys公司的发展小趣事

人才是企业发展的核心驱动力。Excelsys公司高度重视人才培养和团队建设。公司建立了完善的人才培养机制,为员工提供系统的培训和学习机会;同时,公司还注重团队文化的建设,营造积极向上、团结协作的工作氛围。这些措施为公司的持续发展提供了有力的人才保障。

Bytesonic Corporation公司的发展小趣事

为了进一步加强对大陆市场的投资和管控,Bytesonic Corporation在1998年5月在英属维尔京群岛成立了控股公司——剑桥电子有限公司。这一举措不仅提升了公司的资本运作能力,也为其在全球范围内的业务拓展提供了更多的便利。

Deltron公司的发展小趣事

面对电子行业的智能化发展趋势,Deltron公司积极拥抱变革。公司加大在人工智能、物联网等领域的研发投入,推动产品向智能化、网络化方向发展。同时,公司还加强与高校、科研机构的合作,引进高层次人才和先进技术,提升公司的创新能力和竞争力。未来,Deltron将继续以技术创新为动力,推动公司向更高水平发展。

请注意,这些故事是基于假设和推测的虚构内容,并不代表Deltron公司的实际历史和发展情况。

Crameda Intersys公司的发展小趣事

随着公司业务的不断发展,Crameda Intersys公司开始寻求与行业内其他企业的战略合作。公司通过与多家知名企业建立紧密的合作关系,共同研发和推广新技术产品,成功将业务扩展到全球市场。这些战略合作不仅拓宽了公司的市场渠道,也提升了公司的品牌影响力和市场竞争力。同时,通过与合作伙伴的深入交流和学习,公司不断吸收新的技术和理念,为自身的创新和发展注入了新的活力。

Compensated Devices Inc公司的发展小趣事

在电子行业竞争激烈的年代,CDI始终坚持品质至上的原则。公司建立了严格的质量控制体系,从原材料采购到生产流程,再到成品检验,每一个环节都严格把关。正是这种对品质的执着追求,使得CDI的产品在客户中赢得了良好的口碑。随着口碑的积累,CDI逐渐发展成为电子元件领域的知名品牌。

CCS[Custom Computer Services]公司的发展小趣事

随着业务的不断发展,CCS公司意识到市场拓展的重要性。公司积极寻求与国内外知名企业的合作,通过战略合作的方式共同开拓市场。通过与这些企业的合作,CCS公司不仅获得了更多的业务机会,还提高了自身的品牌知名度和市场影响力。同时,CCS公司还积极参加各类行业展会和交流活动,与同行建立广泛的联系和合作关系,为公司的长远发展奠定了坚实的基础。

问答坊 | AI 解惑

基于声卡的虚拟示波器软件 v0.94

本帖最后由 paulhyde 于 2014-9-15 09:35 编辑 基于声卡的虚拟示波器软件 v0.94.zip  …

查看全部问答>

【ATMEL技术问题】ADS平台AT91R4008的FLASH引导程序

在ADS平台下,对AT91R40008进行FLASH程序引导,目前问题运行,现在把BOOT_FLASH.S 分享给大家,程序如下 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  boot from FLASH: initialization      & ...…

查看全部问答>

请问如何开发蓝牙模块内部程序?

我有个蓝牙模块开发板,上面留有SPI程序下载口,现在我想重写蓝牙模块内部程序,请问要怎么做?…

查看全部问答>

Mobil Intel 915GM 分辨率问题

我的T43机器显卡是Mobil Intel 915GM,在windows xp环境下最大显示分辨率就是1024×768,这个是最大分辨率吗,如果能更大分辨率,怎样设置。…

查看全部问答>

WinCE 5.0编译出错

编译基于2440的工程出错,哪位大侠解答一下?好像是串口的问题,但不知道怎么改,BSP的串口驱动已经加进去了,还是不行! BUILD: [01:0000000338:ERRORE] NMAKE :  U1073: don\'t know how to make \'D:\\WINCE500\\PBWorkspaces\\QT244 ...…

查看全部问答>

用过ndisprot的牛人进?技术难点

我们的无线设备是一个黑盒子,目的就是用无线网卡给黑盒子里发送数据以及接收盒子发过来的数据.但是每次都是第一次建立连接后没问题,然后停止后再次发送数据到盒子就会得到GetLastError=31,设备未发挥作用. 这个是怎么回事啊? 在停止后binding并没有 ...…

查看全部问答>

LED控制装置标准主要安全要求解析

[hide] LED在开始工作时,不需要预热和触发脉冲;在正常工作时,可以在特低安全电压下工作,这些都是LED较其它气体放电光源来说独特的优点。为了充分利用LED的这些优点,设计性能优良的LED控制装置是保证照明LED的优点得到充分发挥的重要工作。在 ...…

查看全部问答>

verilog 存储型寄存器组的问题以及分析

问题 verilog 中定义的存储型寄存器组,例如,reg [16:1]mem [255:0];这应该是可综合的,可是综合之后消耗的是 芯片中 分布式 LE 组成的 RAM 呢 还是 存储模块里的RAM 呢?(假定芯片中有存储模块RAM 区) 分析: 如果没有专门去调用芯片的RAM资 ...…

查看全部问答>

射频模拟电路

参加电子设计大赛的必备…

查看全部问答>

第三期的launchpad 有没有邮寄出来??

第三期的launchpad  有没有邮寄出来??   如题  。我写的是家里的地址,估计家里没人,直接放小区门卫了、…

查看全部问答>