历史上的今天

今天是:2024年08月27日(星期二)

正在发生

2019年08月27日 | 【STM32H7教程】第16章 STM32H7必备的HAL库API(重要)

发布者:美人如玉剑如虹 来源: eefocus关键字:STM32H7  HAL库  API 手机看文章 扫描二维码
随时随地手机看文章

16.1 初学者重要提示


对于一些常用的函数,大家一定要熟练的掌握都实现了什么功能,比如HAL_Init,HAL_RCC_OscConfig,HAL_RCC_ClockConfig等。最好的办法是把这些函数的源码读一遍。


16.2 那些是必备的API


这里我们通过一个简单的初始化流程来了解STM32H7的工程模板所必备的库文件和API:


第1步:系统上电复位,进入启动文件startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。


在复位中断服务程序里面执行函数SystemInit,此函数在文件system_stm32h7xx.c里面。


之后是调用编译器封装好的函数,比如用于MDK的启动文件是调用__main,最终进入到main函数。


第2步:进入到main函数就可以开始用户应用程序编程了。在这个函数里面要做几个重要的初始化,依次是:


  MPU初始化,需要用到库文件stm32h7xx_hal_cortex.c和stm32h7xx_hal_cortex.h。

  Cache初始化,需要用到core_cm7.h文件。

  HAL库初始化函数HAL_Init,需要用到文件stm32h7xx_hal.c。

  系统时钟初始化,需要用到库文件stm32h7xx_hal_rcc.c。

 


前面的两步完成后,就可以开始做用户需要的按键、串口等方面的初始化和应用代码的实现了。这里把我们需要学习的几个库文件整理出来,依次有:


  startup_stm32h743xx.s

  system_stm32h7xx.c

  stm32h7xx_hal.c

  stm32h7xx_hal_cortex.c

  stm32h7xx_hal_rcc.c

  core_cm7.h

其中startup_stm32h743xx.s和system_stm32h7xx.c已经在第13章为大家讲解过,这里不再赘述。而MPU和Cache涉及到的文件core_cm7.h在第23和24章为大家讲解。本章教程重点为大家讲解文件stm32h7xx_hal.c、stm32h7xx_hal_cortex.c和sm32h7xx_hal_rcc.c。


16.3 源文件stm32h7xx_hal.c(重要)

这个文件比较杂,像基准电压大小配置,EXTI配置,IO补偿配置等都在这个文件里面设置。学习这个文件注意事项:


HAL库中各个外设驱动里面的延迟实现是基于此文件提供的时间基准,而这个时间基准既可以使用滴答定时器实现也可以使用通用的定时器实现,默认情况下是用的滴答定时器。


函数HAL_Init里面会调用时间基准初始化函数HAL_InitTick,而调用函数HAL_RCC_ClockConfig也会调用时间基准初始化函数HAL_InitTick。


如果在中断服务程序里面调用延迟函数HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。


16.3.1 函数HAL_Init

函数原型:


HAL_StatusTypeDef HAL_Init(void)

{

  /* 设置中断优先级分组 */

  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

 

  /* 使用滴答定时器做为默认时基,配置为1ms滴答,另外系统上电后默认使用的HIS时钟 */

  if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)

  {

    return HAL_ERROR;

  }

 

  /* 初始化底层硬件 */

  HAL_MspInit();

 

  /* 返回函数状态 */

  return HAL_OK;

}

函数描述:


此函数用于初始化HAL库,此函数主要实现如下功能:


设置NVIC优先级分组是4。

设置滴答定时器的每1ms中断一次。


HAL库不像之前的标准库,在系统启动函数SystemInit里面做了RCC初始化,HAL库是没有做的,所以进入到main函数后,系统还在用内部高速时钟HSI,对于H7来说,HSI主频是64MHz。


函数HAL_Init里面调用的HAL_MspInit一般在文件stm32h7xx_hal_msp.c里面做具体实现,主要用于底层初始化。当前此函数也在文件stm32h7xx_hal.c里面,只是做了弱定义。

函数参数:


返回值,返回HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

注意事项:


必须在main函数里面优先调用此函数。

用户务必保证每1ms一次滴答中断。

关于优先级分组的设置可以看第21章节。

使用举例:


此函数的使用比较简单,上电后优先调用即可。


16.3.2 函数HAL_DeInit

函数原型:


HAL_StatusTypeDef HAL_DeInit(void)

{

  /* 复位所有外设 */__set_PRIMASK

  __HAL_RCC_AHB3_FORCE_RESET();

  __HAL_RCC_AHB3_RELEASE_RESET();

 

  /* 省略未写 */

 

  __HAL_RCC_APB4_FORCE_RESET();

  __HAL_RCC_APB4_RELEASE_RESET();

 

  /* 复位底层硬件初始化 */

  HAL_MspDeInit();

 

  /* 返回值 */

  return HAL_OK;

}

函数描述:


此函数用于复位HAL库和滴答时钟。


  复位了AHB1,2,3,4的时钟以及APB1L,APB1H,APB2,3,4的时钟。

  函数HAL_DeInit里面调用的HAL_MspDeInit一般在文件stm32h7xx_hal_msp.c里面做具体实现,主要用于底层初始化,跟函数HAL_Init里面调用的HAL_MspInit是一对。当前此函数也在文件stm32h7xx_hal.c里面,只是做了弱定义。

使用举例:


此函数的使用比较简单,需要调用的时候直接调用即可。


16.3.3 函数HAL_InitTick

函数原型:


__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)

{

  /* Configure the SysTick to have interrupt in 1ms time basis*/

  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)

  {

    return HAL_ERROR;

  }

 

  /* Configure the SysTick IRQ priority */

  if (TickPriority < (1UL << __NVIC_PRIO_BITS))

  {

    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);

    uwTickPrio = TickPriority;

  }

  else

  {

    return HAL_ERROR;

  }

 

  /* Return function status */

  return HAL_OK;

}

函数描述:


此函数用于初始化滴答时钟,此函数相关问题如下:


  此函数有个前缀__weak ,表示弱定义,用户可以重定义。

  此函数用于初始化滴答时钟1ms中断一次,并且为滴答中断配置一个用户指定的优先级。

  此函数由HAL_Init调用,或者任何其它地方调用函数HAL_RCC_ClockConfig配置RCC的时候也会调用HAL_InitTick。

  调用基于此函数实现的HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。

函数参数:


  形参TickPriority用于设置滴答定时器优先级。

  返回值,返回HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:


此函数由HAL_Init调用,无需用户操作,除非需要重定义。


16.3.4 Systick的相关函数

调用了函数HAL_Init后,Systick相关的函数就可以使用了。这些函数如下:


函数原型:


__weak void HAL_IncTick(void)

__weak uint32_t HAL_GetTick(void)

uint32_t HAL_GetTickPrio(void)

HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq)

HAL_TickFreqTypeDef HAL_GetTickFreq(void)

__weak void HAL_Delay(uint32_t Delay)

__weak void HAL_SuspendTick(void)

__weak void HAL_ResumeTick(void)

函数描述:


这些函数就比较简单了,下面把这些函数实现的功能做个简单的说明:


  函数HAL_IncTick在滴答定时器中断里面被调用,实现一个简单的计数功能,因为一般滴答定时器中断都是配置的1ms,所以计数全局变量uwTick每毫秒加1。

  函数HAL_GetTick用于获取全局变量uwTick当前的计数。

  函数HAL_GetTickPrio用于获取滴答时钟优先级。

  函数HAL_SetTickFreq和HAL_GetTickFreq是一对,前者用于设置滴答中断频率,后再用于获取滴答中断频率。

  函数HAL_Delay用于阻塞式延迟,默认单位是ms。

  函数HAL_SuspendTick和HAL_ResumeTick是一对,前者用于挂起滴答定时器,后者用于恢复。

注意事项:


  函数有个前缀__weak ,表示弱定义,用户可以重定义。

使用举例:


这些函数都比较简单,这里就不举例了。需要的时候,直接调用即可。


16.3.5 函数HAL_SYSCFG_VREFBUF_VoltageScalingConfig

函数原型:


void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)

函数描述:


此函数用于配置STM32H7内部电压基准大小。


  当形参VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE0时

    输出基准是2.048 V,条件是VDDA >= 2.4V。


  当形参VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE1时

输出基准是2.5 V,条件是VDDA >= 2.8V。


  当形参VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE2时

    输出基准是1.5 V,条件是VDDA >= 1.8V。


  当形参VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE3时

输出基准是1.8 V,条件是VDDA >= 2.1V。


16.3.6 H7自带电压基准相关函数

函数原型:


void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)

void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue)

HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void)

void HAL_SYSCFG_DisableVREFBUF(void)

函数描述:


  函数HAL_SYSCFG_VREFBUF_VoltageScalingConfig

此函数用于配置STM32H7内部电压基准是否在芯片内部与VREF+引脚接通。


  形参为SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE时,表示导通。

  形参为SYSCFG_VREFBUF_HIGH_IMPEDANCE_ENABLE时,表示高阻,即不导通。

  函数HAL_SYSCFG_VREFBUF_TrimmingConfig

此函数用于内部电压基准的校准调节。


  函数HAL_SYSCFG_EnableVREFBUF和HAL_SYSCFG_DisableVREFBUF是一对,分别用于内部电压参考基准的禁止和使能。

16.3.7 函数HAL_SYSCFG_AnalogSwitchConfig

函数原型:


void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch , uint32_t SYSCFG_SwitchState )

函数描述:


  引脚PA0,PA1,PC2,PC3用于ADC时,还有一组对应的可选引脚PA0_C,PA1_C,PC2_C和PC3_C。此函数的作用就是切换可选引脚。关于这个问题的详情可看此贴:http://forum.armfly.com/forum.php?mod=viewthread&tid=87707 。

16.3.8 BOOST的使能和禁止(用于ADC)

函数原型:


void HAL_SYSCFG_EnableBOOST(void)

void HAL_SYSCFG_DisableBOOST(void)

函数描述:


  这两个函数用于使能或者禁止Booster。如果使能了booster的话,在供电电压低于2.7V时,可以减少模拟开关总的谐波失真,这样的话,模拟开关的性能跟正常供电电压时的全范围测量一样。

16.3.9 函数HAL_SYSCFG_CM7BootAddConfig

函数原型:


void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)

函数描述:


用于配置BOOT = 0或者BOOT = 1时的启动地址,详情可以看第13章的13.4小节。


16.3.10   IO补偿相关函数

函数原型:


void HAL_EnableCompensationCell(void)

void HAL_DisableCompensationCell(void)

void HAL_SYSCFG_EnableIOSpeedOptimize(void)

void HAL_SYSCFG_DisableIOSpeedOptimize(void)

void HAL_SYSCFG_CompensationCodeSelect(uint32_t SYSCFG_CompCode)

void HAL_SYSCFG_CompensationCodeConfig(uint32_t SYSCFG_PMOSCode, uint32_t SYSCFG_NMOSCode )

函数描述:


  函数HAL_EnableCompensationCell和HAL_DisableCompensationCell

分别用于使能或者禁止IO补偿,只有在供电电压范围是2.4V到3.6V之间时,使用此功能才有意义。


  函数HAL_SYSCFG_EnableIOSpeedOptimize和HAL_SYSCFG_DisableIOSpeedOptimize

分别用于优化IO速度或者禁止优化,不过仅在供电电压低于2.5V时可用,高于2.5V是不可以使用的,另外使用这个功能的前提是用户使能了PRODUCT_BELOW_25V(是可选字节配置选项里面的一个bit)。


  函数HAL_SYSCFG_CompensationCodeSelect

IO补偿单元的选择,函数形参可以是SYSCFG_CELL_CODE,即寄存器SYSCFG_CCVR,也可以是SYSCFG_REGISTER_CODE,即寄存器SYSCFG_CCCR。


  函数HAL_SYSCFG_CompensationCodeConfig

用于设置GPIO内部构造中NMOS和PMOS的补偿值,两个形参的范围都是0-15。根据用户调用函数HAL_SYSCFG_CompensationCodeSelect选择的寄存器,这里仅有一个形参的设置是有效的。


16.3.11   低功耗和EXTI相关函数

低功耗和EXTI相关的函数暂时不做讲解,后面章节用到时候再说明。


16.4 源文件stm32h7xx_hal_rcc.c

这个文件主要是实现内部和外部时钟(HSE、HSI、LSE、CSI、LSI、HSI48、PLL、CSS、MCO)以及总线时钟(SYSCLK、AHB3、 AHB1、AHB2、AHB4、APB3、APB1L、APB1H、APB2、 APB4)的配置。


学习这个文件注意事项:


  系统上电复位后,通过内部高速时钟HSI运行(主频64MHz),Flash工作在0等待周期,所有外设除了SRAM、Flash、JTAG 和 PWR,时钟都是关闭的。这里特别注意SRAM,官方手册里面说的太笼统,这个SRAM到底是指的哪个SRAM。关于这个问题,详情看此贴:http://forum.armfly.com/forum.php?mod=viewthread&tid=87784 。

  AHB和APB总线无分频,所有挂载这两类总线上的外设都是以HSI频率运行。

  所有的GPIO都是模拟模式,除了JTAG相关的几个引脚。

  系统上电复位后,用户需要完成以下工作:

  选择用于驱动系统时钟的时钟源。

  配置系统时钟频率和Flash设置。

  配置分频器。

  使能外设时钟。

  配置外设时钟源,部分外设的时钟可以不来自系统时钟,此时通过配置寄存器RCC_D1CCIPR、RCC_D2CCIP1R、RCC_D2CCIP2R 和 RCC_D3CCIPR 实现。

RCC局限性:


使能了外设时钟后,不能立即操作对应的寄存器,要加延迟。不同外设延迟不同:


  如果是AHB的外设,使能了时钟后,需要等待2个AHB时钟周期才可以操作这个外设的寄存器。

  如果是APB的外设,使能了时钟后,需要等待2个APB时钟周期才可以操作这个外设的寄存器。

 


当前HAL库的解决方案是在使能了外设时钟后,再搞一个读操作,算是当做延迟用。


 


比如下面使能GPIOA的时钟:


#define __HAL_RCC_GPIOA_CLK_ENABLE()   do {

                                        __IO uint32_t tmpreg;

                                        SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);

                                        /* Delay after an RCC peripheral clock enabling */

                                        tmpreg = READ_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);

                                        UNUSED(tmpreg);

                                       } while(0) 

关于时钟方面的知识点补充


1、正确理解HCLK1,2,3,4以及PCLK1,2,3,4对应哪些总线的时钟,下面一张图可以说明问题:


由上图可以得出:


HCLK1,2,3,4对应的是AHB总线AHB1,AHB2,AHB3和AHB4时钟。


PCLK1、2、3、4对应的是APB总线APB1,APB2,APB3和APB4时钟。


2、内部和外部时钟配置:


  HSI (high-speed internal)

高速内部RC振荡器,可以直接或者通过PLL倍频后做系统时钟源。缺点是精度差些,即使经过校准。


  CSI (Low-power internal oscillator)

低功耗内部RC振荡器,作用同HSI,不过主要用于低功耗。


  LSI (low-speed internal)

低速内部时钟,主要用于独立看门狗和RTC的时钟源。


  HSE (high-speed external)

高速外部晶振,可接4 - 48MHz的晶振,可以直接或者通过PLL倍频后做系统时钟源,也可以做RTC的是时钟源。


  LSE (low-speed external)

低速外部晶振,主要用于RTC。


  CSS (Clock security system)

[1] [2]
关键字:STM32H7  HAL库  API 引用地址:【STM32H7教程】第16章 STM32H7必备的HAL库API(重要)

上一篇:【STM32H7教程】第17章 STM32H7之GPIO的HAL库API
下一篇:【STM32H7教程】第15章 STM32H7的GPIO基础知识(重要)

推荐阅读

日前,一则宁德时代计划明年量产NCM811动力电池的消息,将动力电池行业新一轮的技术竞赛摆在了人们眼前。稍早时候,比亚迪也以非官方公开发言的形式,表示其NCM811动力电池会在2019年下半年投入使用。NCM811,即正极材料中镍钴锰的含量比例为80%:10%:10%的三元锂电池,代表目前动力电池领域最高能量密度与最高技术含量的路线。然而,在现下的语境,两大...
14.1 初学者重要提示1、电源管理部分涉及到的各种低功耗方式会在后面章节中为大家讲解,当前阶段仅需了解低功耗属于电源管理部分即可。2、电源管理部分最繁琐的就是CPU,D1,D2,D3域的各种运行,待机,停机状态切换,这部分知识点也放在后面低功耗章节学习。14.2 电源电源是系统稳定运行的根本,主要分为以下几个知识点,电源供电、供电监控、电源管理...
喜欢的朋友拿去吧,大神请绕道,喜欢动手的朋友进实物图: 仿真图:单片机源程序如下:#include<reg52.h> //QQ1137035271#include <string.h>#include <intrins.h>#define uint unsigned int#define uchar unsigned char#define wd 1 //定义是否有温度功能 =0时无温度,=1时有温度#define ...
8月25日,市场几度传出台积电即将全线涨价,从最初的明年成熟制程上涨15%至20%、先进制程涨幅达10%,到全面涨价20%,并从即日起生效。对此,据中央社报道,多家IC设计厂商证实收到台积电涨价通知,并表示不会因而影响与台积电的合作关系。IC设计业者指出,台积电包含7纳米及5纳米的先进制程涨价约7%至9%,其余成熟制程代工价格涨幅约20%。此外,台积电罕见...

史海拾趣

问答坊 | AI 解惑

RICHTEK  PDF資料

[i=s] 本帖最后由 jameswangsynnex 于 2015-3-3 19:58 编辑 [/i]哪位仁兄手上有RICHTEK品牌,規格爲:RT9193-33PB SOT-23-5 3.3V LDO的規格書呀?兄弟我現在急需,如能提供感激不盡。 …

查看全部问答∨

怎样做一块好的PCB板

大家都知道做PCB板就是把设计好的原理图变成一块实实在在的PCB电路板,请别小看这一过程,有很多原理上行得通的东西在工程中却难以实现,或是别人能实现的东西另一些人却实现不了,因此说做一块PCB板不难,但要做好一块PCB板却不是一件容易的事情。 微 ...…

查看全部问答∨

呵呵,第一次做单片机学习板,拿出来秀秀

呵呵,这是我第一次做单片机学习板,用的AVR芯片。ATmega16单片机学习板。 学习板总要起个名字啊,想来想去想不到好名字,干脆用我儿子的名字来命名吧。于是就叫“傲飞电子 AVR-ATmega16”学习板。 说明:板子的功能说一下, 1、所有的IO口除在 ...…

查看全部问答∨

经典过流保护电路

经典过流保护电路…

查看全部问答∨

PCB

谁有PCB的详细资料,最好是做板子的视频…

查看全部问答∨

单片机基础问题 关于AT89C52的RAM和SFR寄存器

   学了一段时间单片机,主要是AT89C52   其中遇到了关于AT89C52内部ram和sfr是不是物理相同的部分?或者说地址为80H 到0FFH之间的特殊功能寄存器 和 RAM 中  80H到0FFH中的堆栈或数据存贮器是否相同? 一直没有搞 ...…

查看全部问答∨

2410/2440裸奔时MMU以及寄存器访问和植入Wince时候有何不同?

最近想把内存改为128M,现在正在看u241mon启动代码,里面也涉及到MMU等,但是我看到2410addr.Inc文件里面的寄存器地址是物理地址的 GBLL   BIG_ENDIAN__ BIG_ENDIAN__   SETL   {FALSE} ;================= ; Me ...…

查看全部问答∨

大三了才发现自己对硬件最感兴趣,专家建议一下要学哪方面的知识?

rt,大三了自己也感觉有点晚了,不过还是想根据自己的爱好来学习一下;我的本来专业是网络工程,数电、模电、电路分析都学过,还有一些硬件的基础知识;高手帮忙介绍一下硬件方面目前的就业情况,以及哪方面或哪几方面的知识比较重要?谢谢了@…

查看全部问答∨

请教:怎么获得USB设备的句柄

可以找到该设备,但得不到它的句柄和驱动程序通信…

查看全部问答∨

evc中mfc类 CListBox

evc的联机帮助Microsoft Foundation Class Library for Windows CE.NET中 对CListBox的DrawItem的说明,说DrawItem 可以重写,如果重写了,请问,如何让系统调用自己重写的DrawItem函数。 msdn中,有段代码 // CMyListBox is my owner-drawn lis ...…

查看全部问答∨
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved