STM32HAL库的串口通讯流程详解

一 查询模式

待定,日后补充

二 中断模式

1 中断接收

1.1中断接收流程

先在启动文件中找到中断向量表(此处以USART1为例子)
在这里插入图片描述
然后找到USART1_IRQHandler的函数定义
在这里插入图片描述
在这里就会发现又跳转到了 HAL_UART_IRQHandler(&huart1);,追进去为下图所示:
在这里插入图片描述
在图中标记的前三步是读取寄存器SR、CR1和CR3中的值。
第四步就是提取出SR(状态寄存器)中的各个错误标志位,然后赋给errorflags,如果errorflags为0 ,就会执行下列程序

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

        其中判断句就是说:此时USART_SR_RXNE已经置1,表示接收缓冲区已有数据,USART_SR_RXNE置1是靠硬件置1,而清零是只要读取DR寄存器,该位就会清零;USART_CR1_RXNEIE已置1,表示当USART_SR_RXNE置1的时候,且该标志位也提前置1时,那么当接收缓冲区有数据的时候就会进入接收中断。我们就可以在中断函数中对接收缓冲区中的数据进行读操作。USART_SR_RXNE的置1和清零都需要依靠软件进行实施。因此如果想要使用中断法接收数据,就需要在初始化时同时将CR1->RXNEIE置为高电平。
      之后就会执行发送函数 UART_Receive_IT(huart),并且返回空值;反之,就是进行下面的错误分析,并将相应的错误信息进行返回。详细代码见如下所示:

/**
  * @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 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
            /*Call registered error callback*/
            huart->ErrorCallback(huart);
#else
            /*Call legacy weak error callback*/
            HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
          }
        }
        else
        {
          /* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
          /*Call registered error callback*/
          huart->ErrorCallback(huart);
#else
          /*Call legacy weak error callback*/
          HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
        }
      }
      else
      {
        /* Non Blocking error : transfer could go on.
           Error is notified to user through user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered error callback*/
        huart->ErrorCallback(huart);
#else
        /*Call legacy weak error callback*/
        HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

        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_Transmit_IT(huart)
UART_EndTransmit_IT(huart)
,对于此处的讲解详见中断发送章节。

在这里插入图片描述
在这里插入图片描述
        在接收数据的时候,校验位是不需要接收的,所以相比于无校验位而言就会少接收一位。因此对于九字节宽的数据,无校验位就依旧是接收九位数据,因此每次就选用占用两个字节,而对于有校验位就只需要占用一个字节;但我们通常使用的是八位宽无校验位的,所以每次接收一个字节,而对于八位宽有校验位而言,每次实际上接收的是七位宽。
        到此处我们就可以看出,HAL库实际上是接收一个字节 ,当RxXferCount递减为0时,才会调用最终我们寻找的接收中断回调函数HAL_UART_RxCpltCallback(huart),其中RxXferCount和pRxBuffPtr的赋值均是在
HAL_UART_Receive_IT中给定的。而最值的我们关注的一点是,在调用该中断回调函数之前先关闭了串口接收中断,也就是说,在一次串口中断接收过程的最后,即串口接收完一组数据之后会关闭串口接收中断。(这个后面还会再讲,先记住。完整代码如下所示:

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t *tmp;

  /* Check that a Rx process is ongoing */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
    if (huart->Init.WordLength == UART_WORDLENGTH_9B)
    {
      tmp = (uint16_t *) huart->pRxBuffPtr;
      if (huart->Init.Parity == UART_PARITY_NONE)
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
        huart->pRxBuffPtr += 2U;
      }
      else
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
        huart->pRxBuffPtr += 1U;
      }
    }
    else
    {
      if (huart->Init.Parity == UART_PARITY_NONE)
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }

    if (--huart->RxXferCount == 0U)
    {
      /* Disable the UART Data Register not empty Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

      /* Disable the UART Parity Error Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);

      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
      __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
      /*Call registered Rx complete callback*/
      huart->RxCpltCallback(huart);
#else
      /*Call legacy weak Rx complete callback*/
      HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

      return HAL_OK;
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

总结一下串口中断接收流程:
USART1_IRQHandler(void) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart) -> UART_Receive_IT(UART_HandleTypeDef *huart) -> HAL_UART_RxCpltCallback(huart);
其中HAL_UART_RxCpltCallback(huart)才是用户需要在主函数中进行重写的回调函数,也就是说,进入该函数之前,数据已经接收完,并且关闭了串口接收中断。

1.2使用接收中断方式

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

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

我的使用方式,在初始化中调用此函数进行打开中断,如下:

uint8_t rev_data  ;

static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */
	
   HAL_UART_Receive_IT(&huart1, &rev_data, 1);
	
  /* USER CODE END USART1_Init 2 */

}

其中在 HAL_UART_Receive_IT()中有这三句代码就是打开接收中断相关的标志位,三句代码如下所示:

    /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

其中

#define UART_IT_RXNE    ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_RXNEIE))//使能RXNEIE

实现的功能就是PC机给串口发送数据,串口会先接收到,然后再通过串口中断服务函数返回刚刚发送的数据,在回调函数中的代码示意,如下所示:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   if(huart->Instance == USART1)
	 {
	    HAL_UART_Transmit(&huart1,&rev_data, 1, 1000);//
	    HAL_UART_Receive_IT(&huart1, &rev_data, 1); // 需要重新注册,因为在进入中断回调函数之前已将中断标志位清零
	 }
}

其中,如果想每发送两个数据或n个数据才进一次中断,那就需要首先将re_data变为数据uint8_t re_data[n-1] ;然后回调函数中的代码示意为:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   if(huart->Instance == USART1)
	 {
	    HAL_UART_Transmit(&huart1,rev_data, n, 1000);//次处n为常数,表示每接收n个数据才进一次中断,然后将这n个数据再发送出去
	    HAL_UART_Receive_IT(&huart1, rev_data, n); // 需要重新注册,因为在进入中断回调函数之前已将中断标志位清零
	 }
}

2中断发送

中断发送的意思,非常类似于中断接收,但其中有一些不同。如果在初始化函数中调用了如下所示的函数:

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

在该函数中会执行如下代码:

  /* Enable the UART Transmit data register empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
    #define UART_IT_TXE   ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_TXEIE))   //开启TXEIE

那么当调用HAL_UART_Transmit_IT发送数据的时候,就会进入发送中断函数,见下图:
在这里插入图片描述
        先满足①的条件,所以先执行①,在UART_Transmit_IT中,将数据写入DR寄存器,并且使能TC标志位,同时会关闭UART_IT_TXE。之后因为在UART_Transmit_IT中开启了UART_IT_TC,而在初始化结构体时就开启了USART_SR_TC,即使能接收,所以接下来就是进入②中,在②中就有最终需要我们用户调用的,发送中断回调函数 HAL_UART_TxCpltCallback(huart)
        由此可以看出,在进入发送中断回调函数前,数据已经发送出去,而且在进入发送中断回调函数前已经将TCIE标志位清零。详细代码如下所示:

static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart)
{
  /* Disable the UART Transmit Complete Interrupt */
  __HAL_UART_DISABLE_IT(huart, UART_IT_TC);

  /* Tx process is ended, restore huart->gState to Ready */
  huart->gState = HAL_UART_STATE_READY;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  /*Call registered Tx complete callback*/
  huart->TxCpltCallback(huart);
#else
  /*Call legacy weak Tx complete callback*/
  HAL_UART_TxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

  return HAL_OK;
}

发送中断回调函数非常类似于中断接收使能的函数。接收中断使能函数的作用是绑定接收缓存区并使能接收中断,但是对于发送;发送中断使能函数的作用是发送指定长度的指定数据并使能发送中断,可判断是否发送完成。

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

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

总结一下发送中断:

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

三 补充

中断事件 事件标志 使能控制位
发送数据寄存器为空 TXE      TXEIE
CTS标志 CTS     CTSIE
发送完成 TC       TCIE
准备好读取接收到的数据 RXNE     RXNEIE
检测到上溢错误 ORE     RXNEIE
检测到空闲线路 IDLE     IDLEIE
奇偶校验错误 PE      PEIE
断路标志 LBD     LBDIE
多缓冲通信中的噪声标志、上溢错误和帧错误 NF/ORE/FE  EIE

热门文章

暂无图片
编程学习 ·

JVM——Java的内存回收

Java引用的种类对于JVM的垃圾回收机制来说,如果一个对象,没有一个引用指向它,那么它就被认为是一个垃圾。那该对象就会回收。可以把JVM内存中对象引用理解成一种有向图,把引用变量、对象都当成有向图的顶点,将引用关系当成图的有向边(注意:有向边总是从引用变量指向被引…
暂无图片
编程学习 ·

springboot+Netty搭建web服务器实现物联网温湿度采集

前言:这段时间做了一个课程设计,内容是将温湿度传感器采集到的温湿度数据上传到web服务器并以表格或者折线图的方式可视化展示出来。 话不多说:上代码 ①Netty服务器搭建 NettyServer.java /*** @author cx* @Time 2020/6/29 22:00* @Description netty 服务器配置*/ public…
暂无图片
编程学习 ·

NLP 任务中有哪些巧妙的 idea?

文章目录1. 分布式假设(Distributional Hypothesis)2. 词袋模型(Bag-of-Words)3. 潜在语义分析(Latent Semantic Analysis)4. 概率主题模型(Probabilistic Topic Models )5. 基于BMES的中文分词或基于BIO的NER/Chunking6. 基于PageRank的TextRank转载来源:https://www…
暂无图片
编程学习 ·

c++ string操作

c++ string操作 #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std;void test01() {///*string& operator=(const char* s)* string& operator=(const string &s)* string& operato…
暂无图片
编程学习 ·

Java设计模式-单例模式(全例记录)

1. 基础介绍核心作用:保证一个类只有一个实例,并提供一个访问该实例的全局访问点; 优点:由于单例模式只生成一个实例,所以减少了系统的开销,当一个系统启动需要较多的资源时,可以直接在系统启动时产生一个单例对象,然后使其永久驻留内存;单例模式可以在系统设置全局访…
暂无图片
编程学习 ·

Java程序员技能图谱—必须具备的6个技能!

作为历史最为悠久的编程语言,Java的发展势头一直非常好,而Java从业人员的选择范围也非常多,大致上可以将Java开发人员分为两类,一类是技术人员,一类是管理人员。无论是哪一类,想要成为一名优秀的Java开发工程师,有些技能是必须要具备的。下面,整理的优秀的Java开发人员…
暂无图片
编程学习 ·

Mybatis多数据源配置

pom.xml <!-- Spring Boot Mybatis 依赖 --> <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.2.0</version> </dependency><!-- MySQL 连…
暂无图片
编程学习 ·

qtdesigner-请假(仅仅是尝试使用软件)

下面是我给他们起的名字。现在修改完名字之后,导出成MainWindow.ui文件打开anaconda的shell现在就产生了MainWindow.py,打开它 打开pycharm,创建一个新的名为askForLeave的project,把MainWindow.py移进来。 创建Leave.py作为主程序(起名废) 现在给MainWindow.py配置环境写…
暂无图片
编程学习 ·

2020.7.1崔庆才教材《Python3网络爬虫开发实战》3.4爬取猫眼电影排行代码更正(绕过美团验证码)

前情提要 首先附上崔大神的github源码:3.4爬取猫眼电影排行 毕竟此段代码完成时间较早,截至2020.7.1日,发现了此段代码中两个需要修改的地方。 希望能给学习崔大神的小白一些帮助,希望大家有个好前途。 一、猫眼电影反爬更新 下图是崔大神的代码:估计是太多人学习爬虫拿猫…
暂无图片
编程学习 ·

2020李宏毅学习笔记——33.Network Compression(2_6)

3.为什么要pruning? 首先有一个问题:既然最后要得到一个小的network,那为什么不直接在数据集上训练小(有local minima的问题)的模型,而是先训练大模型?解释一:模型越大,越容易在数据集上找到一个局部最优解,而小模型比较难训练,有时甚至无法收敛。 解释二:2018年的…
暂无图片
编程学习 ·

本地项目提交到Github上

1.在个人github主页创建一个空仓库2.填写完相关资料后再项目文件中打开本地git客户端3.进入到刚刚的新建仓库中,如图操作3.依次在git客户端内输入以下命令,这部会用到上面复制到的地址 git initgit add .origin后面的地址是你刚刚自己复制的地址 git remote add origin https:/…
暂无图片
编程学习 ·

在Eclipse EE上搭建基于Scala文件的Maven项目

在Eclipse EE上搭建基于Scala文件的Maven项目这篇博客是笔者在进行创新实训课程项目时所做工作的回顾。对于该课程项目所有的工作记录,读者可以参阅下面的链接。 注意!这篇博文不会涉及在机器上安装Hadoop,Spark等环境,仅仅是能够在Eclipse EE上搭建基于Scala文件的Maven项…
暂无图片
编程学习 ·

数据结构之查找(期末复习)

【知识框架】一、查找的基本概念 1、查找表 查找表是由同一类的数据元素(或记录)构成的集合。集合中的数据元素之间存在着完全松散的关系,因此查找表是一种非常灵便的数据结构。 2、关键字 关键字是数据结构(或记录)中某个数据项的值,用它可以标识一个数据元素(或记录)…
暂无图片
编程学习 ·

HADOOP YARN原理及资源调度

文章目录1、ResourceManager2、NodeManager3、Applicacation Master4、yarn调度方式5、Yarn架构及各角色职责6、Yarn作业执行流程7、特点8、MapReduce在Yarn上的运转 1、ResourceManager ReaouceManager(资源管理器RM)常驻守护进程: 管理集群资源,负责全局资源的监控、分配和…
暂无图片
编程学习 ·

Java语言基础(二)变量和数据类型

Java语言基础(二)二、变量和数据类型2.1 变量的基本概念2.2 变量的声明和使用2.3 变量使用是注意事项2.4 标识符的命名规则(笔试)关键字2.5 变量输入输出的案例实现2.6 变量输入输出案例的优化和手册介绍2.7 数据类型的分类2.8 常用的进制2.9 十进制与二进制之间的转换(1)正…
暂无图片
编程学习 ·

一个简单的聊天系统

声明:谢绝一切形式的转载 socket套接字 socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进…
暂无图片
编程学习 ·

vue 插件大全

vue 插件大全 Vue是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件特别整理了常用的vue插件,来了个大汇总,方便查找使用,便于工作和学习。很全的vue插件汇总,赶紧收藏下吧! 一、UI组件及框架element …
暂无图片
编程学习 ·

String类、Arrays类

String类 概念:之前都已经接触到了String的对象,是一个字符串。String类就代表的是字符串 特点 1.双引号引起来的"abc",这种也是String对象,这种对象不是保存到堆中而是方法区的常量池中 2.字符串对象是不可变的对象,只要你观察到字符串的内容变化了,那么肯定是…
暂无图片
编程学习 ·

RocketMQ消费者之消息消费过程分析

心跳机制在Consumer启动后,它就会通过定时任务不断地向RocketMQ集群中的所有Broker实例发送心跳包心跳包内容包含了消息消费分组名称订阅关系集合消息通信模式客户端id的值Broker端在收到Consumer的心跳消息后,会将它维护在ConsumerManager的本地缓存变量—consumerTable,同…