您当前的位置: 首页 >  ide

正点原子

暂无认证

  • 0浏览

    0关注

    382博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【正点原子MP157连载】第七章 认识HAL库-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

正点原子 发布时间:2022-02-24 10:13:32 ,浏览量:0

1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614 在这里插入图片描述

第七章 认识HAL库

第四章生成的工程是基于HAL库的,这点我们在前面有提过,在第四章的操作中,我们通过第一个工程实验熟悉了STM32CubeIDE的基本使用方法。本章节,我们来认识HAL库。HAL库文件夹是STM32Cube固件包中重要的一部分,因为HAL库比较特殊,所以我们将其作为独立的章节来专门讲解。 在讲解之前我们需要说明一点,分析HAL库中的源码或者工程中的文件,不管它有多么复杂,无非就是一些.c源文件和.h头文件,还有一些类似.s的启动文件,而头文件中遇见比较多的就是一些宏定义和函数声明,.c文件比较常见的就是一些函数的定义。在分析过程中,我们去理解这些文件重要的宏定义和函数的作用,然后把这些函数和文件的关系弄清楚,在日后的项目开发中,我们才可以亲手操刀去打造属于自己的项目设计,在使用HAL库的时候才不至于逻辑混乱,犹如庖丁解牛,游刃有余。 本章将分为如下几个小节: 7.1、HAL库初识; 7.2、HAL库文件夹结构; 7.3、如何使用HAL库; 7.4、HAL库重要文件分析; 7.5、章节小结; 7.1 HAL库初识 7.1.1 获取HAL库 在上一章节的STM32Cube固件包中有一个STM32MP1xx_HAL_Driver文件夹,该文件夹下存放的就是HAL库,我们所说的HAL库就是指里边的库文件。 在这里插入图片描述

图7.1.1. 1 HAL库在固件包中 在介绍HAL库文件前,我们先来理解下面几个概念。 7.1.2 什么是HAL库 在介绍HAL库之前,我们先来理解两个概念,什么是API?什么是句柄(Handle)? 前面我们多次提到API这个词,API 全称Application Programming Interface,翻译过来就是应用程序编程接口。接口,可以理解为是一些已经封装好了的可以被调用的功能函数或者方法,我们把这些函数放到我们的工程中,当我们要实现某个功能时,就可以在工程中找到对应的函数,然后进行调用。 句柄(Handle),在STM32的手册上经常看到这个词,可以理解为它是一个指针,或者是一些表的索引,或者是用于描述和标记某些资源的的标识,这些资源可以是函数、可以是一段内存、可以是一组数字、可以是一个外设等等,总之很广泛,通过句柄我们可以访问到打开的资源。我们在调用API函数的时候,可以利用句柄来说明要操作哪些资源。 HAL的含义,ST官方手册里的解释是Hardware abstraction layer,即硬件抽象层,单词的首字母组合起来就是HAL。HAL库是一些封装好的驱动程序,向下可以操作硬件,向上可以给用户提供可操作的接口。 在这里插入图片描述

图7.1.2. 1 HAL库属于驱动程序 HAL库,笔者的理解是,ST把对不同系列MCU的操作经过一层一层的封装,将硬件进行抽象化表达出来,最后呈现给我们的就是HAL库。硬件抽象化,也就是将对寄存器的操作做了一系列封装,将外设抽象组织为句柄,使我们看不到寄存器的影子,最后分离出可以调用的API,使用者可以不去关注底层、不必关注复杂的硬件寄存器就可以进行编程,通过调用API和句柄操作可以实现对MCU的大部分外设进行操作,使用起来非常方便,而且很容易进行移植。 7.1.3 HAL库能做什么 STM32开发方式中,可以直接配置寄存器或者可以使用官方的库来实现。 我们在51单片机开发的时候就直接配置寄存器,但是到了32位单片机开发,如果开发大型项目,需要的功能外设很多,再使用这种方式就已经力不从心了。因为STM32的外设资源丰富,寄存器数量是51单片机寄存器的数十倍,那么多的寄存器根本无法记忆,而且开发中需要不停查找芯片手册,开发过程就显得机械和费力,完成的程序代码可读性差,可移植性不高,程序的维护成本变高了。当然了,采用直接配置寄存器方式开发会显得更直观,程序运行占用资源少。如果项目中只用少数几个外设,或者同一系列的芯片,项目组内部有一套成熟、可移植性高的祖传代码,开发起来就没那么困难了。 为了简化开发人员的工作,减少开发工作时间和成本,针对STM32 系列芯片,ST官方推出了标准外设库(STD库)、HAL 库和LL 库 。在这些库中,有很多用结构体封装好的寄存器参数,有常用的表示参数的宏,还有封装好的对寄存器操作的API,开发者可以调用这些资源实现配置相应的寄存器,效率大大提高了。使用库的框架来开发,程序控制语句结构化,程序单元模块化,贴近人的思维,易于阅读,易于移植和维护。 可以这么说,库是架设于寄存器与用户驱动层之间的代码,向下是直达硬件相关的寄存器,向上为用户提供配置寄存器的接口,用户代码通过这些接口来间接操作寄存器。 在这里插入图片描述

图7.1.3. 1库和直接操作寄存器对比 7.1.4 HAL库和其它库有什么不同 前面我们提到,针对STM32 系列芯片,ST官方推出了标准外设库(STD库)、HAL 库和LL 库 ,那么HAL库和其它两种库有什么差别?下图是三种库对STM32系列产品的支持,目前STM32MP1仅支持HAL库。 在这里插入图片描述

图7.1.4. 1 HAL库对ST系列产品的支持情况

  1. 标准外设库 STD(Standard Peripheral Libraries)标准外设库,它把实现功能中需要配置的寄存器以结构体的形式封装起来,使用者只需要配置结构体变量成员就可以修改外设的配置寄存器,比直接操作寄存器方便了不少。但标准外设库仍然接近于寄存器操作,它的方便也是针对某一系列芯片而言的,在不同系列芯片上使用标准外设库开发的程序可移植性比较差,例如,在F4上开发的程序移植到F3上,使用标准库是不通用的。目前STM32系列产品中仅F0-F4以及L1系列支持标准外设库。
  2. HAL库 为了解决标准库存在的问题,ST 在标准库的基础上又推出了 HAL 库。个人认为,HAL库是用来取代之前的标准库的,因为这几年ST官方大力推广HAL 库,而且在ST新出的 STM32 芯片中, ST 直接只提供 HAL 库。 HAL库在设计的时候更注重软硬件分离,HAL库的目的应该就是尽量抽离物理层,HAL库的API集中关注各个外设的公共函数功能,以便定义通用性更好的API函数接口,从而具有更好的可移植性。HAL库写的代码在不同的STM32产品上移植,非常方便,效率得到提升。目前HAL库支持STM32各个系列产品。
  3. LL库 LL库(Low Layer)是 ST 继HAL库之后新增的库,与 HAL 库捆绑发布,在前面的STM32CubeIDE第一个工程实验中,我们看到在STM32CubeMX插件里有选择使用HAL库还是LL库的选项。 LL库的英文名字翻译过来就是底层的意思,实际上LL 库更接近硬件层,它和STD库类似,都是直接操作的寄存器,只不过LL库可以在STM32Cube中实现。LL库提供一组轻量级、优化、面向专家的API,具有最佳的性能和运行时效率。LL库可以完全独立使用,也可以结合HAL库一起使用。当HAL库需要优化运行时,可以调用LL库来处理,而对于复杂的外设(例如:USB驱动),两者混合使用,才能正常驱动这个复杂的外设。 在这里插入图片描述

图7.1.4. 2 HAL库和LL库 7.1.5 怎样学习HAL库 (1)不管HAL库封装的有多好,本质上还是通过配置MCU/MPU的寄存器来实现我们想要的功能。所以我们学习HAL库的同时,还需要学习外设的工作原理和寄存器的配置方法,通过原理来理解HAL库是怎样实现我们想要的功能,要知其然更要知其所以然。 (2)HAL库不仅仅是底层驱动,它更是一套行业内可以公开和认可的架构。学习HAL库,我们要了解它的架构,了解库中每个文件夹下大概有哪些文件,文件之间的关系是什么,函数之间的调用关系是什么,调用条件是什么,常见的数据结构怎么用。 (3)学习HAL库,遇到疑问的地方可以查HAL的帮助文档,可以上网上查询相关说明,可以在Wiki上对共同的主题进行扩展或者探讨,多看相关的例程,跟着例程操作,多总结。 (4)要学会使用ST提供的优秀的开发工具,掌握STM32Cube工具套件的使用方法,熟话说得好,磨刀不误砍柴工。 不管怎样,我们的目的就是为了使用HAL库来开发,学会调用HAL库的API函数,配置对应外设按照我们的要求来工作,实现想要的功能。 下面,我们进入主题,分析HAL库。 7.2 HAL库文件夹结构 STM32MP1xx_HAL_Driver文件下的Src(Source的简写)文件夹存放是所有外设的驱动程序源码,Inc(Include的简写)文件夹存放的是对应源码的头文件。Release_Notes.html是HAL库的版本更新说明信息。STM32MP157Cxx_CM4_User_Manual.chm是HAL库的用户手册。 在这里插入图片描述

图7.2. 1 HAL库文件夹 打开Inc文件夹,里边是一些以stm32mp1xx_hal和stm32mp1xx_ll_开头的.h文件,对应地,在Src下也有一堆以stm32mp1xx_hal和stm32mp1xx_ll_开头的.c文件,其中stm32f1xx_hal_开头的是HAL库文件, stm32f1xx_ll_开头的文件是LL库文件。 在这里插入图片描述

图7.2. 2 HAL库文件 HAL库下的文件很多,有一部分文件的功能可以归为一类,例如stm32mp1xx_hal_i2c.h/c、stm32mp1xx_hal_adc.h/c、stm32mp1xx_hal_dma.h/c等等这些文件,他们属于一些外设的配置文件,那么我们后面会以stm32mp1xx_hal_ppp.h/c来统称这些文件。有的是特殊文件,我们会重点介绍。HAL库关键文件介绍如下表: 在这里插入图片描述

表7.2. 1 HAL库关键文件介绍 除了HAL库的文件命名有规则以外,库中的函数以及句柄等的命名均有规律,如下表所示,其中PPP表示某个外设名。 在这里插入图片描述

表7.2. 2 HAL库的句柄和函数命名规律 例如I2C相关的,如stm32mp1xx_hal_i2c.h、stm32mp1xx_hal_i2c.c、I2C_HandleTypeDef、HAL_I2C_Init()等。对于HAL的API函数,常见的有以下几种: 初始化/反初始化函数:HAL_PPP_Init(),HAL_PPP_DeInit() 外设读写数:HAL_PPP_Read(),HAL_PPP_Write(),HAL_PPP_Transmit()和HAL_PPP_Receive() 控制函数:HAL_PPP_Set (),HAL_PPP_Get () 状态和错误:HAL_PPP_GetState (), HAL_PPP_GetError () HAL库封装的很多函数都是通过定义好的结构体将参数一次性传给所需的函数,参数也有一定的规律,主要有以下三种: 配置和初始化用的结构体 一般为PPP_InitTypeDef或PPP_ ConfTypeDef的结构体类型,根据外设的寄存器封装成易于理解和记忆的结构体成员。 特殊处理的结构体 专为不同外设而设置的,带有“Process”的字样,实现一些特异化的中间处理操作等。 外设控制句柄(PPP_Handler) HAL驱动的重要参数,可以同时定义多个句柄结构以支持多外设多模式,HAL驱动的操作结果也可以通过这个句柄获得。有些HAL驱动的头文件中还定义了一些跟这个句柄相关的一些外设操作。如用外设结构体句柄与HAL定义的一些宏操作配合,即可实现一些常用的寄存器位操作,即可实现对外设的操作。 在这里插入图片描述

表7.2. 3 HAL库驱动部分与外设句柄相关的宏 但对于SYSTICK/NVIC/RCC/ GPIO这些外设,不使用PPP_HandleTypedef这类外设句柄进行控制,如HAL_GPIO_Init() 只需要初始化的GPIO编号和具体的初始化参数。 HAL_StatusTypeDef HAL_GPIO_Init (GPIO_TypeDef* GPIOx, GPIO_InitTypeDef *Init) { /GPIO 初始化程序……/ } 此外,HAL库中很多地方使用了回调函数,前面我们解释过回调函数可以被用户重定义,HAL库中的回调函数很多命名如下: 在这里插入图片描述

表7.2. 4 HAL库驱动中常用的回调函数API 至此,我们大概对HAL库驱动文件的一些通用格式和命名规则有了初步印象,记住这些规则可以帮助我们快速对HAL库的驱动进行归类和判定这些驱动函数的用法。 7.3 如何使用HAL库 HAL库下的文件实在是太多了,而且每个文件里的API函数也很多,我们学习的时候该从哪里看起呢?虽然文件多,API函数也多,不过我们可以将其进行分类,先将重要的配置文件了解一遍,然后再去学习某几个外设文件,只要学会了其中某部分外设,其它外设的就很类似了。 针对庞大的HAL库,ST官方提供了HAL库的用户手册,例如STM32MP157系列的芯片,ST在HAL库里提供了STM32MP157Cxx_CM4_User_Manual.chm,我们双击打开此文件: 目录结构下有Modules、Data Structures、Files和Directories共4个主题,我们先来查看Modules。 Modules下有STM32MP1xx HAL_ Driver和STM32MP1xx_LL_Driver,分别对应HAL库和LL库的驱动,我们看HAL库下的驱动部分。 打开GPIO列表下的IO operation functions / functions,查看里面的API函数接口描述,我们选择查看HAL_GPIO_WritePin函数,双击即可以打开此函数的说明,如下图所示。可以看到函数的定义、函数的作用、函数注意事项、参数说明以及函数在哪个文件,位置是在哪里等等: 在这里插入图片描述

图7.3. 1 HAL库用户手册 通过说明,函数是viod类型,没有返回值。第一个形参GPIO_TypeDef *GPIOx,x可以是A~K,通过此参数来选择对应的GPIO外围设备。第二个形参uint16_t GPIO_Pin是指定要写入的端口位(某个pin),这个参数可以是GPIO_PIN_x中的一个,其中x可以是0~15。第三个形参GPIO_PinState PinState是指定要写入到所选位的值,GPIO_PinState参数可以是枚举类型中的GPIO_PIN_RESET(复位)或者GPIO_PIN_SET(置位)之一。 提示中说明,这个函数使用GPIOx_BSRR寄存器来允许原子的读/修改访问,即操作的是GPIOx_BSRR寄存器,关于此寄存器,我们可以查看参考手册中的说明。 所以,如果我们要将某个GPIO的某个pin置1的话,例如GPIOA的第2位,可以这样调用此函数: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); 如果要查找某个函数或者参数,可以直接在搜索框中进行搜索,如下图查找GPIO_PIN_RESET: 在这里插入图片描述

图7.3. 2查找功能 其它的主题,例如Data Structures、Files和Directories也可以给我们提供更多的帮助信息: 在这里插入图片描述

图7.3. 3其它主题 关于此用户手册的使用就讲解到这里,在后面的实验中,遇见不明白的地方也可以查看此手册。 7.4 HAL库重要文件分析 下面我们先分析2个非常重要的文件:stm32mp1xx_hal_conf.h和stm32mp1xx_hal.c文件。其它文件,我们会在后面对应的实验章节做专门的讲解。 7.4.1 stm32mp1xx_hal_conf.h文件 stm32mp1xx_hal_conf_template.h是HAL库的配置文件模板,用于用户自定义驱动。如果使用MDK来开发,用户可以复制此文件到自己的工程目录,然后将其改名字为stm32mp1xx_hal_conf.h,STM32CubeIDE在生成工程的时候已经自动为我们做好了这一步,我们不用管。下面我们将stm32mp1xx_hal_conf_template.h文件的代码分为3部分进行讲解。

1   #ifndef STM32MP1xx_HAL_CONF_H
2   #define STM32MP1xx_HAL_CONF_H
3  
4   #ifdef __cplusplus
5    extern "C" {
6   #endif
7   /* 
8   * 模块选择
9   */
10  #define HAL_MODULE_ENABLED
11  #define HAL_ADC_MODULE_ENABLED
12  #define HAL_CEC_MODULE_ENABLED
13  ......
14  #define HAL_USART_MODULE_ENABLED
15  #define HAL_WWDG_MODULE_ENABLED
16  /* 
17  * 注册回调函数选择
18  */
19  #define USE_HAL_ADC_REGISTER_CALLBACKS    0u
20  #define USE_HAL_CEC_REGISTER_CALLBACKS    0u
21  ......
22  #define USE_HAL_USART_REGISTER_CALLBACKS  0u
23  #define USE_HAL_WWDG_REGISTER_CALLBACKS   0u
24  /* 
25  * 在HAL SPI驱动中激活CRC功能
26  */
27  #define USE_SPI_CRC                     1U
第4行到第6行,对这个语法我们前面有讲解,这里不讲。
第10行到第15行,这是将在HAL驱动程序中使用的模块列表,一旦使用了某个模块,那么就要使能相应的模块。例如,如果要使用TIMER功能,就在文件里添加:

#define HAL_TIM_MODULE_ENABLED 相应的,在stm32mp1xx_hal_conf.h文件最后也要加载对应模块的头文件。如果的是在在STM32CubeIDE上通过图形界面来配置的话,生成的工程已经自动为我们处理好了,如需要修改,可以再返回配置界面重新配置。

#ifdef HAL_TIM_MODULE_ENABLED
 #include "stm32mp1xx_hal_tim.h"
#endif /* HAL_TIM_MODULE_ENABLED */
第19行到第23行,这段代码也是一些宏定义,表示将某个宏定义为0。这些宏定义有什么作用呢?例如USE_HAL_USART_REGISTER_CALLBACKS带有USART,应该和USART有关。打开Inc下的stm32mp1xx_hal_usart.h和stm32mp1xx_hal_usart.c文件,可以找到很多条件编译选项#if (USE_HAL_USART_REGISTER_CALLBACKS == 1)。例如stm32mp1xx_hal_usart.h文件中声明的函数指针,void (* TxHalfCpltCallback)(struct __USART_HandleTypeDef *husart)中,TxHalfCpltCallback是一个指向函数的指针,(* TxHalfCpltCallback)是一个带有参数*husart、返回类型为void的函数,参数husart也是一个指针:
1   #if (USE_HAL_USART_REGISTER_CALLBACKS == 1)
2     void (* TxHalfCpltCallback)(struct __USART_HandleTypeDef *husart); 
3     void (* TxCpltCallback)(struct __USART_HandleTypeDef *husart);      
4     void (* RxHalfCpltCallback)(struct __USART_HandleTypeDef *husart);  
5     void (* RxCpltCallback)(struct __USART_HandleTypeDef *husart);      
6     void (* TxRxCpltCallback)(struct __USART_HandleTypeDef *husart);   
7     void (* ErrorCallback)(struct __USART_HandleTypeDef *husart);       
8     void (* AbortCpltCallback)(struct __USART_HandleTypeDef *husart);  
9     void (* RxFifoFullCallback)(struct __USART_HandleTypeDef *husart);  
10    void (* TxFifoEmptyCallback)(struct __USART_HandleTypeDef *husart); 
11
12    void (* MspInitCallback)(struct __USART_HandleTypeDef *husart);      
13    void (* MspDeInitCallback)(struct __USART_HandleTypeDef *husart);    
14  #endif  /* USE_HAL_USART_REGISTER_CALLBACKS */
stm32mp1xx_hal_usart.c文件中USART_InitCallbacksToDefault函数的参数就是stm32mp1xx_hal_usart.h文件中的函数的指针,例如TxHalfCpltCallback就是它的参数(别忘了,TxHalfCpltCallback有自己的参数)
1   #if (USE_HAL_USART_REGISTER_CALLBACKS == 1)
2   void USART_InitCallbacksToDefault(USART_HandleTypeDef *husart)
3   {
4     /* Init the USART Callback settings */
5     husart->TxHalfCpltCallback        = HAL_USART_TxHalfCpltCallback;     
6     husart->TxCpltCallback             = HAL_USART_TxCpltCallback;          
7     husart->RxHalfCpltCallback        = HAL_USART_RxHalfCpltCallback;       
8     husart->RxCpltCallback             = HAL_USART_RxCpltCallback;           
9     husart->TxRxCpltCallback          = HAL_USART_TxRxCpltCallback;         
10    husart->ErrorCallback              = HAL_USART_ErrorCallback;            
11    husart->AbortCpltCallback         = HAL_USART_AbortCpltCallback;        
12    husart->RxFifoFullCallback        = HAL_USARTEx_RxFifoFullCallback;     
13    husart->TxFifoEmptyCallback       = HAL_USARTEx_TxFifoEmptyCallback;     
14  }
15  #endif /* USE_HAL_USART_REGISTER_CALLBACKS */
上面的就是回调(Callback)过程。(* TxHalfCpltCallback)叫做回调函数。回调函数就是一个通过函数指针调用的函数。以上的过程可以这么形容:
A是回调函数,B是调用函数。A函数有参数a,B函数有参数b。参数b是一个指向A的函数指针,这样一来,就是B调用A,A有参数a,既然有参数就要给参数赋值,所以B函数内部给A的参数a赋值。那么,就是B调用A,A又利用B给A的参数a赋值,这个过程就叫做回调。
总之,第19行到第23行,我们可以通过将0U改为1U来改变条件编译选项,从而实现我们前面说的回调过程。关于回调函数的使用,我们后面会进行讲解。
我们继续往下看剩下的代码,这部分主要是时钟配置:
28  /* 
29  * 时钟配置
30  */
31  #if !defined  (HSE_VALUE) 
32    #define HSE_VALUE    24000000U  		/*高速外部振荡器HSE的值,24MHz */
33  #endif 
34  
35  #if !defined  (HSE_STARTUP_TIMEOUT)
36    #define HSE_STARTUP_TIMEOUT  100U  	/* HSE启动超时,100ms*/
37  #endif /* HSE_STARTUP_TIMEOUT */
38 
39  #if !defined  (HSI_VALUE)
40    #define HSI_VALUE            64000000U  /*高速内部振荡器HSI的值,64MHz */
41  #endif
42 
43  #if !defined  (HSI_STARTUP_TIMEOUT) 
44    #define HSI_STARTUP_TIMEOUT  5000U  	/*HSI启动超时,5000ms */
45  #endif /* HSI_STARTUP_TIMEOUT */  
46 
47  #if !defined  (LSI_VALUE) 
48    #define LSI_VALUE    32000U   			/*低速内部振荡器LSI的值,32KHz */
49  #endif       
50  
51  #if !defined  (LSE_VALUE)
52    #define LSE_VALUE     32768U    /*低速外部振荡器LSE的值,32.768KHz */
53  #endif
54 
55  #if !defined  (LSE_STARTUP_TIMEOUT)
56    #define LSE_STARTUP_TIMEOUT  5000U  	/*LSE启动超时,5000ms */
57  #endif /* LSE_STARTUP_TIMEOUT */
58 
59  #if !defined  (CSI_VALUE)
60    #define CSI_VALUE    4000000U 			/*低功耗内部振荡器CSI的值,4MHz */
61  #endif 
62 
63  #if !defined  (EXTERNAL_CLOCK_VALUE)
64    #define EXTERNAL_CLOCK_VALUE    12288000U /*外部时钟的值,单位是Hz */
65  #endif 
STM32MP157的M4内核有5个时钟可以用:2个外部振荡器HSE、LSE和3个内部振荡器HSI、CSI、LSI。
HSE (High-speed external oscillator)是高速外部振荡器,可通过外接有源晶振驱动。官方HSE_VALUE默认是配置24Hz,这个参数表示外部高速晶振的频率,如果要使用外部晶振,请根据板子上外部焊接的晶振频率来配置,正点原子STM32MP157开发板的核心板上外接了24MHz有源晶振。
LSE (Low-speed external oscillator)是低速外部振荡器,通过外接有源或者无源晶振驱动,官方默认配置32.768 kHz。正点原子STM32MP157开发板的核心板上外接了32.768KHz无源晶振,主要用于驱动RTC 实时时钟。
HSI (High-speed internal oscillator)是高速内部振荡器,频率可以是8、16、32、64 MHz。这里默认配置为64MHz。
CSI (Low-power internal oscillator)是低功耗内部振荡器,这里频率默认配置为 4MHz。
LSI是内部低速振荡器,这里频率默认配置为32 kHz,实际值可能会因为电压和温度而变化。使用内部时钟优势是成本低,缺点是精度差。
这里是默认的值,如果我们不配置时钟,M4内核会默认采用HSI作为时钟源,频率为64MHz。如果我们配置时钟,可以直接在STM32CubeIDE的Clock Configuration图形界面来配置,配置好以后保存,然后生成工程,在生成的stm32mp1xx_hal_conf.h文件中,这里的值就会跟着变。关于时钟的配置,我们后面会有专门的章节来讲解。
下面我们看最后部分代码:
66  /* 
67  * HAL系统配置
68  */
69  #define  VDD_VALUE            			3300U  		/* VDD的值,单位是mv */  
70  /*  滴答定时器初始中断优先级 */
71  #define  TICK_INT_PRIORITY   ((uint32_t)(1U            
关注
打赏
1665308814
查看更多评论
0.0458s