基于阿里云STM32L4R5ZI端到端物联网全栈开发学习笔记(3)2020/1/30

一、历程第一个函数

C语言是面向过程的,代码会从main()函数开始执行。
所以我打算从第一个函数开始,慢慢解析。争取每行都有备注
知识点
1.HAL 的 外文名 Hardware Abstraction Layer,即硬件抽象层。
在这里插入图片描述

二、详解HAL_Init() //硬件抽象层初始化

本函数涉及知识点较多,以下的知识点只做简介,先了解即可:
1.NVIC :嵌套向量中断控制器(Nested Vectored Interrupt Controller),以后学中断时详解
2.MSI:多速内置RC振荡器(Multispeedinternal RC oscillator),以后学时钟时详解
3.caches:高速缓冲存储器,以后学缓存时详解

/**
  * @brief  Configure the Flash prefetch, the Instruction and Data caches,             
  *         the time base source, NVIC and any required global low level hardware      
  *         by calling the HAL_MspInit() callback function to be optionally defined in user file
  *         stm32l4xx_hal_msp.c.
  *
  * @简介  通过调用在用户文件stm32l4xx_hal_sp.c中可自主配置的HAL_MspInit()回调函数来
           配置FLASH预读取,指令和数据高速缓冲存储器,时钟源,嵌套向量中断控制器和任何所需的全局底层硬件
  
  * @note   HAL_Init() function is called at the beginning of program after reset and before
  *         the clock configuration.
  *
  * @注意   HAL_Init()函数是在重置之后,程序开始时和时钟配置之前调用的。

  * @note   In the default implementation the System Timer (Systick) is used as source of time base.
  *         The Systick configuration is based on MSI clock, as MSI is the clock
  *         used after a system Reset and the NVIC configuration is set to Priority group 4.
  *         Once done, time base tick starts incrementing: the tick variable counter is incremented
  *         each 1ms in the SysTick_Handler() interrupt handler.
  *
  * @注意   在默认执行的配置中,系统计时器(Systick)用作时间基准的源。
            Systick配置为基于MSI时钟,因为MSI是系统复位后,且NVIC配置设置为优先组4时,使用的时钟。
            一旦完成,时间基计时开始递增:计时变量计数器在SysTick_Handler()中断处理程序中每1ms递增一次。  
  
  * @retval HAL status
  * @返回值  status
  */
HAL_StatusTypeDef HAL_Init(void)
{
  HAL_StatusTypeDef  status = HAL_OK;         //硬件抽象层状态变量定义

  /* Configure Flash prefetch, Instruction cache, Data cache */配置FLASH预读取,指令和数据cache 
  /* Default configuration at reset is:                      */复位时的默认配置
  /* - Prefetch disabled                                     */禁用预读取                
  /* - Instruction cache enabled                             */使能指令高速缓冲器
  /* - Data cache enabled                                    */使能数据高速缓冲器
#if (INSTRUCTION_CACHE_ENABLE == 0)    //INSTRUCTION_CACHE_ENABLE = 1,故未执行指令高速缓冲器禁止函数,即使能指令高速缓冲器
   __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();    
#endif /* INSTRUCTION_CACHE_ENABLE */

#if (DATA_CACHE_ENABLE == 0)           //DATA_CACHE_ENABLE = 1,故未执行数据高速缓冲器禁止函数,即使能数据高速缓冲器
   __HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */

#if (PREFETCH_ENABLE != 0)             //PREFETCH_ENABLE = 0,故未执行预读取使能函数,即使禁止预读取
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */设置优先级分组
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);    //下拉,见1.HAL_NVIC_SetPriorityGrouping()函数详解

  /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
                                                        //使用SysTick作为时间基准源,并配置1ms tick(复位后的默认时钟为MSI)
  if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)        //下拉,见3.HAL_InitTick()函数详解
  {  
    status = HAL_ERROR;
  }
  else
  {
    /* Init the low level hardware */
    HAL_MspInit();                                       //下拉,见7.HAL_MspInit()函数详解
  }

  /* Return function status */
  return status;
}

1. HAL_NVIC_SetPriorityGrouping()函数详解

优先级知识点参考:
牛人对STM32的NVIC_PriorityGroupConfig使用及优先级分组方式理解: https://blog.csdn.net/faihung/article/details/78742083
在这里插入图片描述
如上所示,HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); //NVIC_PRIORITYGROUP_4 = 3

/**
  * @brief  Set the priority grouping field (pre-emption priority and subpriority)
  *         using the required unlock sequence.
  * @简介  设置优先级分组字段(优先级和次优先级),使用所需的解锁序列。
  
  * @param  PriorityGroup: The priority grouping bits length.     //参数  优先级分组:优先级分组位长
  *         This parameter can be one of the following values:    //此参数可以是以下值之一
  *         @arg NVIC_PRIORITYGROUP_0: 0 bit  for pre-emption priority,
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bit  for pre-emption priority,
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for pre-emption priority,
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for pre-emption priority,
  *                                    1 bit  for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for pre-emption priority,   /*历程参数*/   
  *                                    0 bit  for subpriority             //4位主优先级,0位次优先级
  
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible.
  *         The pending IRQ priority will be managed only by the subpriority.
  * @注意    当NVIC_PriorityGroup_0被选中时,IRQ抢占不再可能。挂起的IRQ优先级将仅由次优先级管理。
  * @retval None   没有返回值
  */
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 */ 
                                                                //根据PriorityGroup参数值设置寄存器的PRIGROUP[10:8] 位  
  NVIC_SetPriorityGrouping(PriorityGroup);                      //下拉,见2.NVIC_SetPriorityGrouping()函数详解 
}

2.NVIC_SetPriorityGrouping()函数详解

历程中调用:
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4 );

/**
  \brief   Set Priority Grouping
  \简介    设置优先级分组
  
  \details Sets the priority grouping field using the required unlock sequence.
           The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field.
           Only values from 0..7 are used.
           In case of a conflict between priority grouping and available
           priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set.
  \详细说明 使用所需的解锁序列设置优先级分组字段。
           参数PriorityGroup被分配到字段SCB->AIRCR [10:8] PRIGROUP字段。
           有效值为0到7
           在优先级分组和可用优先级位(__NVIC_PRIO_BITS)之间发生冲突时,将设置尽可能的最小优先级分组。
           
 \param [in]      PriorityGroup  Priority grouping field.  
 */
__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 << 8U)                      );              /* Insert write key and priorty group */
  SCB->AIRCR =  reg_value;  //0x5FA0 0300
}

PriorityGroup = NVIC_PRIORITYGROUP_4 = 3
PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL) = 0x03

哈哈,终于到了寄存器操作的最底层啦!!!
是时候祭出我的M4操作手册,下载地址:https://pan.baidu.com/s/1MZPrjvNoqt9Gzcu1YT-6Dw
查看手册228-229页,可知:
SCB->AIRCR = 0xFA05 0000
程序中查找,可知:
SCB_AIRCR_VECTKEY_Msk = 0xFFFF 0000
SCB_AIRCR_PRIGROUP_Msk = 0x700
SCB_AIRCR_VECTKEY_Pos = 16U

经过程序运算后,对SCB->AIRCR寄存器赋值0x5FA0 0300
如下图可知:

  1. Bits31:16 是寄存器的钥匙,想要写操作,必须赋值0x5FA0
  2. Bits 10:8 配置优先级分组,历程赋值为3,表示16个主优先级分组,每个主优先级组有0个次优先级
    在这里插入图片描述
    在这里插入图片描述

3.HAL_InitTick()函数详解

历程:HAL_InitTick(TICK_INT_PRIORITY)
TICK_INT_PRIORITY = 0
即HAL_InitTick(0)

/**
  * @brief This function configures the source of the time base:
  *        The time source is configured to have 1ms time base with a dedicated
  *        Tick interrupt priority.
  * @简介  这个函数配置基本时钟源,时间源配置为1ms时基,具有专用的时钟中断优先级。
  
  * @note This function is called  automatically at the beginning of program after
  *       reset by HAL_Init() or at any time when clock is reconfigured  by HAL_RCC_ClockConfig().
  * @注意  这个函数在程序开始时被HAL_Init()重置后自动调用,或者在任何时候被HAL_RCC_ClockConfig()重新配置时钟时调用。
  
  * @note In the default implementation, SysTick timer is the source of time base.
  *       It is used to generate interrupts at regular time intervals.
  *       Care must be taken if HAL_Delay() is called from a peripheral ISR process,
  *       The SysTick interrupt must have higher priority (numerically lower)
  *       than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
  *       The function is declared as __weak  to be overwritten  in case of other
  *       implementation  in user file.
  * @注意  在默认实现中,SysTick timer是time base的来源。它用于在固定的时间间隔内生成中断。
  *       如果从一个外围ISR进程调用HAL_Delay(),必须小心,SysTick中断的优先级必须比外围中断高(数值上更低)。
  *       否则,调用方ISR进程将被阻塞。该函数被声明为_weak,以便在用户文件中遇到其他实现时重写。

  * @param TickPriority  Tick interrupt priority.
  * @retval HAL status
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)  //弱函数
{
  HAL_StatusTypeDef  status = HAL_OK;  

  /*Configure the SysTick to have interrupt in 1ms time basis*/配置系统定时器每1ms的产生一个中断
  if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U)      //下拉,见4.HAL_SYSTICK_Config()函数详解
  {
    status = HAL_ERROR;
  }
  else
  {
    /*Configure the SysTick IRQ priority */
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0);     //下拉,见6. NVIC_SetPriority()函数详解
  }

  /* Return function status */
  return status;
}

4.HAL_SYSTICK_Config()函数详解

历程:HAL_SYSTICK_Config(SystemCoreClock/1000UL)
SystemCoreClock = 4000000
即HAL_SYSTICK_Config(4000)

/**
  * @brief  Initialize the System Timer with interrupt enabled and start the System Tick Timer (SysTick): 
  *         Counter is in free running mode to generate periodic interrupts.
  * @简介 初始化系统计时器,使能中断,并启动系统计时:计数器处于自由运行模式,以生成周期性中断
  
  * @param  TicksNumb: Specifies the ticks Number of ticks between two interrupts.
  * @参数  TicksNumb:指定两个中断之间的节拍数。
  
  * @retval status:  - 0  Function succeeded.
  *                  - 1  Function failed.
  */
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);             //下拉,见5.SysTick_Config()函数详解
}

5.SysTick_Config()函数详解

历程:SysTick_Config(TicksNumb)
TicksNumb= 4000
即SysTick_Config(4000)

/**
  \brief   System Tick Configuration
  \简介    系统时钟配置
  
  \details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
           Counter is in free running mode to generate periodic interrupts.
  \详情    初始化系统定时器和它的中断,并启动系统节拍定时器。计数器是以自由震荡的模式产生的周期中断
  
  \param [in]  ticks  Number of ticks between two interrupts.
  \参数[输入]   ticks  两个中断之间的节拍数。

  \return          0  Function succeeded.
  \return          1  Function failed.
  
  \note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
           function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
           must contain a vendor-specific implementation of this function.
  \注意    如果本函数返回值为1,那么函数SysTick_Config是不被编译包含的,
           那么文件<b><i>device</i>.h</b>一定要包含一个特定于供应商的实现的这个系统时钟配置函数
 */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)        // if (3999 > 0xFFFFFF) ,不成立
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */手册248页,节拍数重载值
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
                                                                    //下拉,见6.NVIC_SetPriority()函数详解
                                                                    
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */当前系统定时器计数值
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
                                                             //使能系统定时器中断和系统定时器计数,选处理器时钟(AHB)为时钟源
  return (0UL);                                                     /* Function successful */
}

程序中可查:
SysTick_LOAD_RELOAD_Msk = 0xFFFFFFUL
SysTick_CTRL_CLKSOURCE_Msk = 0B100
SysTick_CTRL_TICKINT_Msk =0B010
SysTick_CTRL_ENABLE_Msk =0B001

扫描二维码关注公众号,回复: 8955312 查看本文章

手册中可查:
248页:SysTick->LOAD:节拍数重载值
每SysTick->LOAD个时钟周期,触发一次SysTick异常请求
在这里插入图片描述
249页:SysTick->VAL:当前系统时钟节拍数
在这里插入图片描述
247页:SysTick->CTRL:
Bit0置1, 开始计时;
Bit0置1 ,开系统计数器中断;
Bit2置1, 时钟源选择处理器时钟(AHB)
在这里插入图片描述

6. NVIC_SetPriority()函数详解

历程: NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
SysTick_IRQn = -1
__NVIC_PRIO_BITS = 4
即 SysTick_Config( -1, 0xF )

/**
  \brief   Set Interrupt Priority
  \简介    设置中断优先级
  \details Sets the priority of an interrupt.
  \细节    设置一个中断的优先级
  \note    The priority cannot be set for every core interrupt.
  \注意    不能为每个中断都设置优先级
  \param [in]      IRQn  Interrupt number.
  \参数    IRQn    中断号
  \param [in]  priority  Priority to set.
  \参数    priority  优先级设置
 */
__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if ((int32_t)(IRQn) < 0)  //IRQn = -1 ,成立
  {
    SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
      //SCB->SHP[11] = 0xF0   优先级为 240 ,即最低优先级
  }
  else
  {
    NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  }
}

程序中可查:
__NVIC_PRIO_BITS = 4

SCB->SHP[11] = 0xF0 :
在这里插入图片描述
如上图所示:故为 SHPR3 的 PRI_15[7:0] = 0xF0
即为 PRI_15[7:0] = 0xF0,所以,优先级为 240 ,即最低优先级
214页:SHPRX介绍
在这里插入图片描述
215页:SHPR3寄存器在这里插入图片描述

7.HAL_MspInit()函数详解

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

  /* USER CODE END MspInit 0 */
  
  __HAL_RCC_SYSCFG_CLK_ENABLE();   //下拉,见8. __HAL_RCC_SYSCFG_CLK_ENABLE()函数详解,本函数使能 SYSCFG + COMP + VREFBUF 时钟
  __HAL_RCC_PWR_CLK_ENABLE();      //下拉,见9. __HAL_RCC_PWR_CLK_ENABLE()函数详解,本函数使能电源接口时钟

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

8. __HAL_RCC_SYSCFG_CLK_ENABLE()函数详解

#define __HAL_RCC_SYSCFG_CLK_ENABLE()          do { \
                                                 __IO uint32_t tmpreg; \
                                                 SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN); \
                                                                  //由8.1可知本函数使能 SYSCFG + COMP + VREFBUF 时钟

                                                 /* Delay after an RCC peripheral clock enabling */ \
                                                 tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN); \
                                                                  //由8.2可知本函数对tmpreg变量赋值1
                                                                  
                                                 UNUSED(tmpreg); \   /* To avoid gcc/g++ warnings */
                                               } while(0)

外围总线由ST公司设计,故中文参考手册为ST的:zh.DM00310109.pdf:
下载地址:https://pan.baidu.com/s/1rsvY42u_nTXdIHTw-8xSmw

8.1 SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN)详解

程序中可查:
#define SET_BIT(REG, BIT) ((REG) |= (BIT))

RCC_APB2ENR_SYSCFGEN = 0x1

历程即为:
RCC->APB2ENR |= 0x1

手册中可查:
253页:
在这里插入图片描述
255页本函数使能 SYSCFG + COMP + VREFBUF 时钟
在这里插入图片描述

8.2 READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN)详解

程序中可查:
#define READ_BIT(REG, BIT) ((REG) & (BIT))

RCC_APB2ENR_SYSCFGEN = 0x1

历程即为:
RCC->APB2ENR &= 0x1
由上个函数可知,RCC->APB2ENR 的 bit0 为 0x1
故函数返回值为 0x1 &= 0x1 即为1

9. __HAL_RCC_PWR_CLK_ENABLE()函数详解

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

程序中可查:
#define SET_BIT(REG, BIT) ((REG) |= (BIT))

RCC_APB1ENR1_PWREN = 0x1 << 28

历程即为:
RCC->APB1ENR1 |= 0x1 << 28

手册可查:
205页:本函数使能电源接口时钟
在这里插入图片描述

三、总结

1.今天完成了第一个函数的详解,详细到寄存器的那种
2.涉及到时钟使能、中断优先级、中断优先级分组、系统计时器等很多底层的设置
3.两个手册的使用:
M4操作手册,下载地址:
https://pan.baidu.com/s/1MZPrjvNoqt9Gzcu1YT-6Dw
L4R5ZI由ST公司设计,故中文参考手册为ST的:zh.DM00310109.pdf:
下载地址:https://pan.baidu.com/s/1rsvY42u_nTXdIHTw-8xSmw
4.坚持第一,虽然今天很累,但我相信是值得的!!!

补充:阿里云大学的视频讲的很简洁,很多需要自己上网找资料去学习。这正是锻炼我们学习能力的好机会!也希望我能坚持下去!!!
共同进步交流群:697226678

发布了4 篇原创文章 · 获赞 5 · 访问量 451

猜你喜欢

转载自blog.csdn.net/qq_38688850/article/details/104109687