STM32 hal库 NVIC初始化函数梳理

这篇具有很好参考价值的文章主要介绍了STM32 hal库 NVIC初始化函数梳理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用的是stm32f407vg,代码来源stm32cubemx。

已经配置了中断的是GPIOD_pin6和TIM2。

nvic_setpriority,stm32,单片机,嵌入式硬件

 这个是STM32CubeMX里面NVIC的控制面板;其中最上面priority grope是优先级组,我设置的是4位抢占优先级,0位响应优先级,就和以前刚学中断时中断嵌套那样。

下面是有哪些中断源,勾上之后,在最下面选择抢占优先级是几。

 接上一节,GPIO初始化函数中还有最后两句关于中断的配置。

/* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

同样,在TIM2初始化中也有这样的初始化:

    /* TIM2 interrupt Init */
    HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);

一、 函数的分析

1.1 中断优先级组设置

首先是主函数会调用 HAL_Init();

HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch, Instruction cache, Data cache */ 
#if (INSTRUCTION_CACHE_ENABLE != 0U)
  __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */

#if (DATA_CACHE_ENABLE != 0U)
  __HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE */

#if (PREFETCH_ENABLE != 0U)
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
  HAL_InitTick(TICK_INT_PRIORITY);

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}

这两个函数一个是初始化中断优先级组的配置,一个是设置了优先级组。

 void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
  NVIC_SetPriorityGrouping(PriorityGroup);
}

  #define NVIC_SetPriorityGrouping    __NVIC_SetPriorityGrouping

__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
  uint32_t reg_value;
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);             /* only values 0..7 are used          */

  reg_value  =  SCB->AIRCR;                                      /* read old register configuration    */
  reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change               */
  reg_value  =  (reg_value                                   |
                ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
                (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)  );            

                                                                  /* Insert write key and priority group */
  SCB->AIRCR =  reg_value;
}


#define SCB_AIRCR_VECTKEY_Pos              16U   /*!< SCB AIRCR: VECTKEY Position */
#define SCB_AIRCR_VECTKEY_Msk       (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos)                                                                            /*!< SCB AIRCR: VECTKEY Mask */

#define SCB_AIRCR_PRIGROUP_Pos             8U /*!< SCB AIRCR: PRIGROUP Position */
#define SCB_AIRCR_PRIGROUP_Msk             (7UL << SCB_AIRCR_PRIGROUP_Pos)                /*!< SCB AIRCR: PRIGROUP Mask */

这个函数是对SCB_AIRCR寄存器进行操作;

先是把SCB_AIRCR里面的数据读出来,再把对应位全置0,再和想要的参数相或就写好了。

nvic_setpriority,stm32,单片机,嵌入式硬件

 描述里写道,要给bit[31:16]位写入0x5fa,否则写入将会被忽略;

给bit[10:8]位写入就是优先级组的配置值;bit2是系统复位的,没有用到。

nvic_setpriority,stm32,单片机,嵌入式硬件

 #define NVIC_PRIORITYGROUP_0         0x00000007U

                                                /*!< 0 bits for pre-emption priority4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1         0x00000006U

                                                /*!< 1 bits for pre-emption priority3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2         0x00000005U

                                                /*!< 2 bits for pre-emption priority 2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3         0x00000004U

                                                /*!< 3 bits for pre-emption priority1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4         0x00000003U /*!< 4 bits for pre-emption priority
以上是可以写入的实参。可以看出HAL_Init()里面第一个调用此函数是把GROUP_4写入,算是一次初始化。

 void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_RCC_PWR_CLK_ENABLE();

  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_3);

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}


#define __HAL_RCC_PWR_CLK_ENABLE()     do { \
                                    __IO uint32_t tmpreg = 0x00U; \
                                    SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                     /* Delay after an RCC peripheral clock enabling */ \
                                    tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                    UNUSED(tmpreg); \
                                          } while(0U)

这里我把配置改了一下,生成了配置中断优先级组的函数,当默认时这儿没有这个函数,

原理和上面一样。

这儿也使能了系统配置控制器时钟,从休眠模式下使能这个时钟,前面也见到了,在上一节2.2.2外部中断的配置小节里面解释了一下,感觉和f4芯片的休眠模式有关。

此处还使能了RCC_APB1ENR中PWREN位,使能电源接口时钟。

nvic_setpriority,stm32,单片机,嵌入式硬件

1.2  HAL_NVIC_SetPriority();

 void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
{ 
  uint32_t prioritygroup = 0x00U;
  
  /* Check the parameters */
  assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
  assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
  
  prioritygroup = NVIC_GetPriorityGrouping();
  
  NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
}

1.2.1 NVIC_GetPriorityGrouping();

#define NVIC_GetPriorityGrouping    __NVIC_GetPriorityGrouping

__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void)
{
  return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >>                                                                                 SCB_AIRCR_PRIGROUP_Pos));
}


#define SCB_AIRCR_PRIGROUP_Pos              8U                                          

                                                                         /*!< SCB AIRCR: PRIGROUP Position */
#define SCB_AIRCR_PRIGROUP_Msk             (7UL << SCB_AIRCR_PRIGROUP_Pos)                                                                              /*!< SCB AIRCR: PRIGROUP Mask */

函数作用是得到\读取优先级组,前面可知,优先级组写入到SCB_AIRCR寄存器bin[10:8]所以把7左移8位再和原寄存器值相与,就只剩下这三位,其他清零,再右移8位得到三位数据。

 1.2.2 NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority);

__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);   /* only values 0..7 are used          */
  uint32_t PreemptPriorityBits;
  uint32_t SubPriorityBits;

  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);
  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));

  return (
           ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) |
           ((SubPriority     & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL)))
         );
}


#define __NVIC_PRIO_BITS       4U /*!< STM32F4XX uses 4 Bits for the Priority Levels */
首先第一行是把优先级组的数[011 ~ 111]转换成3~7;

后面得到PreemptPriorityBits 和 SubPriorityBits 以GROUP_4为例

7 - 3 !> 4 则 PreemptPriorityBits = 7 - 3 = 4;PreemptPriorityBits = 4

4 + 4 !< 7 则  SubPriorityBits = (3 - 7) + 4 = 0 

这两个数就是写入到nvic寄存器里面,控制中断优先级为第几级。总共4位,GROUP_4把4位都用来做抢断优先级,那么共有16个优先级可以写入。如果是GROUP_3,则前三位写抢断优先级、后一位写响应优先级,有8个抢断优先级、2个响应优先级可以写。

最后一步操作就是把预设的抢断优先级和响应优先级的实参写到对应的位中,例如写入抢占优先级为2.

就是1 左移4位 得10000 ,减一得 ffff ,即15 和2相与得2,再左移0位。1左移0位再减一等于0 和响应优先级相与等于0最后写入的数据就是2.

1.2.3  NVIC_SetPriority(IRQn, NVIC_EncodePriority());

   #define NVIC_SetPriority            __NVIC_SetPriority

__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->IP[((uint32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  }
  else
  {
    SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  }
}


EXTI9_5_IRQn -> IRQn

EXTI9_5_IRQn = 23,  EXTI9_5_IRQn是一个枚举量。

就是给NVIC_IP[23]赋值,等于 2左移(8 - 4)位再和0xff相与。

也就是给IP[23]里面写入20000;

nvic_setpriority,stm32,单片机,嵌入式硬件

 IP[x]是用来控制中断优先级的,一共有240个中断源,即IP[0~239],每个元素有8位,那么一个寄存器有4个IP数组的元素,共用了60个寄存器,NVIC_IPR0~59。按照分配来说每个IP有8位控制,应该有2^8个,即256个优先级,但是设置了每个IP只有4位有效,所以只有16个优先级。

nvic_setpriority,stm32,单片机,嵌入式硬件

 每个优先级段只写入一个优先级数值,大小是0到255,数值越小,对应的中断优先级越高。处理器只实现bit[7:4],bit[3:0]读为0,并忽略写。

所以前面的操作要左移4位。

1.3 HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); 

 void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
{
  /* Check the parameters */
  assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
  
  /* Enable interrupt */
  NVIC_EnableIRQ(IRQn);
}

  #define NVIC_EnableIRQ              __NVIC_EnableIRQ

__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}

右移5位就是除以32,23 / 32 商0 ,即给NVIC_ISER[0]的对应位写入值。

nvic_setpriority,stm32,单片机,嵌入式硬件

 如果一个挂起的中断被启用,NVIC会根据它的优先级激活这个中断。如果中断未启用,断言其中断信号将中断状态更改为挂起,但NVIC不会激活中断,无论其优先级如何。

写操作时,0,无动作;1,使能中断。读操作时,0,中断未使能;1,中断使能。

 二、剩余函数和寄存器分析

 2.1 hal_cortex.c下和nvic相关的函数

void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);  设置优先级组,形参是优先级组名。

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);                 设置优先级,形参是中断序号(中断源),抢断优先级,响应优先级;

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);         使能中断,形参是中断源;

void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);         关闭中断,形参是中断源;

void HAL_NVIC_SystemReset(void);        nvic系统复位,实现方法是把默认值写入到SCB_AIRCR寄存器,前面提到bit[2]作用是复位。

2.2 core_cm4.h下和nvic相关的函数

 __STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup);

__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void);读优先级组,读AIRCR;

__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn);

__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn);

__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn);

__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn);读中断挂起信息。读取NVIC_ISPR寄存器对应位得到挂起信息。

__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn);设置中断挂起,写NVIC_ISPR寄存器。

__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn);清除中断挂起,操作NVIC_ICPR寄存器。

__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn);获得中断活动信息,操作NVIC_IABR寄存器。

__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority); 写入中断优先级,形参是中断源和中断优先级。

__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn);

读取中断优先级,形参是中断源。

__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority);编码中断优先级,按分组把抢占优先级和响应优先级放到对应的位上。形参是中断优先级分组、抢占优先级、响应优先级数值。

__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority);把编码好的优先级数值反编码。形参是中断优先级、中断优先级分组、抢占优先级的指针、响应优先级的指针。
__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector);设置中断向量表,读取SCB_VTOR,向量表基偏移字段。设置中断向量在基于中断向量表的SRAM中设置一个中断向量。中断号可以是正数来指定设备特定的中断,也可以是负数来指定处理器异常。VTOR必须重新定位到SRAM之前。按照函数内容来看,是给startup汇编函数中断向量表写入中断处理函数,正常使用不到。

__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn);读取中断向量表,得到中断处理函数的地址。

2.3 剩余相关寄存器

nvic_setpriority,stm32,单片机,嵌入式硬件

 NVIC_ICER和ISER写操作作用相反,写一时关闭中断。

nvic_setpriority,stm32,单片机,嵌入式硬件

 NVIC_ISPR控制中断是否挂起,写1时挂起。读寄存器时,0表示没有挂起;1表示挂起。

nvic_setpriority,stm32,单片机,嵌入式硬件

NVIC_ICPR寄存器写操作作用和ISPR相反,写1时取消中断挂起。读操作一样。

nvic_setpriority,stm32,单片机,嵌入式硬件NVIC_IAPR寄存器数据是和活动性相关这一位读为1表示这个中断是活动的或者挂起中。

三、总结 

中断初始化这一部分内容不算多,但还是有点抽象。我知道有抢占优先级和响应优先级,通过一点点地分析这个函数,我知道大概控制哪些寄存器可以配置这些优先级。但是梳理完了以后还是有一些疑惑,比如nvic是怎么区分抢占优先级和响应优先级的?我的猜测是抢占优先级发射中断时,优先级高的直接打断别的中断,而响应优先级有个挂起的操作,等待中断结束以后再进行中断。这个表面的行为还是很好理解的,通过分组的那个寄存器配合,感觉nvic还是很厉害的,把中断管理得有条不紊。

在进行梳理的时候,最开始最困扰我的是,在nvic的初始化里面,没有对中断优先级级组的初始化。后来找了很久才发现在主函数里面调用了hal库的初始化,在hal库里面初始化了。还是对hal库不太熟悉。文章来源地址https://www.toymoban.com/news/detail-801242.html

到了这里,关于STM32 hal库 NVIC初始化函数梳理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 cubemx CAN STM32 CAN初始化详解

    接收用到的结构体如下: CAN概念:         全称Controller Area Network,是一种半双工,异步通讯。 物理层:         闭环:允许总线最长40m,最高速1Mbps,规定总线两端各有一个120Ω电阻,闭环        开环:最大传输距离1Km,最高速125Kbps,规定每根线串联一个2.2kΩ的电阻,

    2024年02月13日
    浏览(37)
  • STM32 串口的初始化(内附详细代码)

    首先我们先要根据原理图来确认我们用的串口接到了那个引脚  我这边的串口1为例,接收端是PA10,发送端是PA9首先我们需要配置PA9和PA10. 把接受端配置成浮空输入,完全靠引脚来判断。把发送端配置成复用推挽模式,并打开GPIOA的时钟和复用时钟多的看代码吧,我把注释都写

    2024年02月13日
    浏览(33)
  • STM32的GPIO初始化配置-学习笔记

            由于刚开始没有学懂GPIO的配置原理,导致后面学习其它外设的时候总是产生阻碍,因为其它外设要使用前,大部分都要配置GPIO的初始化,因此这几天重新学习了一遍GPIO的配置,记录如下。         首先我们要知道芯片上的引脚,并不是只有GPIO的功能,还能复用成

    2024年04月17日
    浏览(37)
  • STM32 GPIO设置(GPIO初始化)学习笔记

    GPIO 都知道是 通用输入输出接口 的意思就不详细解释 那么我们就直接进入怎么设置GPIO接口: 这里我的编译软件是keil5,相信大家都应该知道stm32有各种的工作模式上拉、下拉、推挽、开漏等等。如果想要了解具体的工作模式原理这里我推荐大家看:推挽 开漏 高阻 这都是谁

    2024年03月28日
    浏览(39)
  • STM32—TIM定时器初始化结构体详解

      注:高级控制定时器可以用到所有初始化结构体,通用定时器不能使用 TIM_BDTRInitTypeDef 结构体,基本定时器只能使用时基结构体。    时基结构体TIM_TimeBaseInitTypeDef用于定时器基础参数设置,与TIM_TimeBaseInit函数配合使用完成配置。 (1) TIM_Prescaler:定时器预分频器设置,

    2024年02月02日
    浏览(34)
  • STM32单片机同时初始化GPIOA和GPIOB

    要同时初始化STM32F1xx的GPIOA和GPIOB,您可以按照以下步骤进行: 首先,在代码中包含stm32f1xx.h头文件 , 例如: 然后,使能GPIOA和GPIOB的时钟 ,例如: 这将使能GPIOA和GPIOB的时钟,以便进行配置和使用。需要注意的是,STM32F103C8T6使用APB2总线驱动GPIOA和GPIOB。 接下来,设置GPIOA和

    2024年02月14日
    浏览(30)
  • STM32/GD32学习指南-踩坑之(一)外部晶振配置,初始化失败,不起振

    GD32使用外部有源晶振和无源晶振的问题,型号为GD32 F450 一、GD32配置使用外部晶振 1.使用外部无源晶振 找到startup_gd32f450_470.s汇编文件,找到SystemInit()函数跳转进去 在底部找到system_clock_config()函数,再次跳转进去 选中宏定义:__SYSTEM_CLOCK_200M_PLL_IRC16M,跳转,如图 将内部时钟

    2024年02月13日
    浏览(32)
  • ORB_SLAM3:单目+IMU初始化流程梳理

    单目+IMU模式下,前面的一些配置完成后,处理第一帧图像时: 1、每帧图像都会调用该函数: 目的:使灰度直方图分布较为均匀,从而使整体对比度更强,便于后面特征点的提取等工作; 2、第一帧图像(ni=0)时无IMU数据(vImuMeas容器为空),进入下面的这个函数: 该函数先

    2024年02月10日
    浏览(28)
  • STM32CubeMX v6.9.0 BUG:FLASH_LATENCY设置错误导致初始化失败

    今天在调试外设功能时,发现设置了使用外部时钟之后程序运行异常,进行追踪调试并与先前可以正常运行的项目进行对比之后发现这个问题可能是由于新版本的STM32CubeMX配置生成代码时的BUG引起的。 MCU: STM32H750VBT6 STM32CubeIDE: Version: 1.13.0 Build: 17399_20230707_0829 (UTC) STM32CubeMX: v

    2024年02月15日
    浏览(42)
  • 【STM32&RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)

    硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 本章主要讲线程的工作机制和管理方法,通过实例讲解如何使用多线程完成多任务开发。 RT-Thread用线程控制块来描述和管理一个线程,一个线程对应一个线程控制块。线程控

    2024年02月12日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包