历史上的今天
返回首页

历史上的今天

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

正在发生

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

2019-08-27 来源:eefocus

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)

推荐阅读

史海拾趣

Arctic Silicon Devices公司的发展小趣事

在电子行业中,产品质量是企业生存和发展的关键。Arctic Silicon Devices深知这一点,因此非常重视品质管理。公司建立了严格的质量管理体系,从原材料采购到产品生产、测试、包装等各个环节都进行严格把控。同时,公司还加强了对供应商的管理和评估,确保原材料的质量稳定可靠。这些措施使得Arctic Silicon Devices的产品在市场上赢得了良好的口碑和信誉。

Allied Controls Incorporated公司的发展小趣事

在电子行业的发展过程中,创新合作是推动产业进步的重要动力。Arctic Silicon Devices积极与高校、科研机构等合作,共同开展技术研发和人才培养。通过共享资源、互通有无,公司不仅获得了更多的创新灵感和技术支持,还推动了整个电子行业的技术进步和产业升级。

Asia Electronics Ind Co Ltd公司的发展小趣事

Asia Electronics Ind Co Ltd深知人才是企业发展的核心。因此,公司注重人才培养和团队建设。公司定期举办各类培训活动,提升员工的技能和素质。同时,公司还建立了一套完善的激励机制,鼓励员工积极创新、勇于担当。这些举措有效激发了员工的工作热情和创造力,为公司的发展注入了源源不断的动力。

Galaxy ( Bel )公司的发展小趣事

面对日益激烈的市场竞争,Asia Electronics Ind Co Ltd深知优化供应链管理的重要性。公司加强与供应商的合作与沟通,确保原材料的质量和供应的稳定性。同时,公司还引入先进的供应链管理系统,实现供应链的透明化和高效化。通过优化供应链,公司降低了生产成本,提高了生产效率,为公司的持续发展提供了有力保障。

力芯微(ETEK)公司的发展小趣事

随着下游电子产品的更新换代和手机的快速普及,力芯微公司敏锐地捕捉到了这一市场机遇。公司及时调整研发方向,推出一系列低压低功耗的电源类产品,成功进入手机市场。这一转型不仅使公司实现了业务的多元化,也为公司带来了新的增长点。

亿宝科技(CNIBAO)公司的发展小趣事

亿宝科技在成立初期,就明确了以技术创新为核心的发展战略。公司创始人带领研发团队,深入研究市场需求,不断推出具有竞争力的电子产品。在一次偶然的机会中,他们成功研发出一款具有高性能、低功耗特点的芯片,这一创新技术迅速获得了市场的认可。此后,亿宝科技不断加大研发投入,形成了一套完整的自主研发体系,为公司后续的发展奠定了坚实的基础。

问答坊 | AI 解惑

色艺俱佳,华丽丽的notebook们

本帖最后由 jameswangsynnex 于 2015-3-3 20:00 编辑 现在在笔记本销售专柜看到笔记本在长相上几乎都差不多,见棱见角的设计、黑白银的配色甚至在体积 和重量上也相差无几。面对如此大众化的风格,现今的时尚男女们怎么能够接受呢?以下我们来看 ...…

查看全部问答>

以奇瑞汽车为例,怎么取出来车速的脉冲信号。

在搞一个试验,和车速有关的。怎么能把车速的脉冲信号取出来呢?在哪里取。别笑话我,我本来就是一外行。…

查看全部问答>

上班了,该收心了

今天我们正式上班了,来到办公室后,大家静悄悄的,每个人都在工作。整个上午,大家都是这样,基本上也没有聊天。看来每个人都进入状态了啊。不过也是,好多事情一下子堆过来,不忙不行啊。 这个年过得太快了,实在是有些意犹未尽。…

查看全部问答>

关于共享内存的理解

windows驱动的共享内存如何理解?谢谢各位…

查看全部问答>

大家用tms320f28335都开发那些产品啊?

你要开发产品? 选用tms320f28335是中意它那些特点啊?…

查看全部问答>

QEMU扯淡。。。。。

本人新手,最近看到QEMU的一些介绍,想扯淡耍一下。    网上关于这方面的资料看了不少,本人安的VMWARE,但一直感觉用起来不耍、 哪位哥子能上一点资料或心得。。。。。。。。。非常感谢了。。。。…

查看全部问答>

Ivor Horton的C++入门经典

当你看到 Ivor Horton时,我想,我说啥都是多余的。话说资源还真难找,费了不小的劲,然后再一次引发对CSDN的愤怒......最后这玩意不是在CSDN上下载的。它是扫描版,清晰度还行,不影响阅读,当然了,比不上那种文件版。挺大的,30来M,,我拆成几个 ...…

查看全部问答>

半导体知识之一(PN结,半导体基本概念,MOSFET)

内容 1.PN结原理: P-N 结的形成和能带 2.电子基本概念 30问:正解 and 误解 3.MOS器件的重要特性之15 个为什么? 上图 …

查看全部问答>

招聘嵌入式软件工程师 - 电池管理行业 - 深圳

职责: 编写软件及客户服务支持 要求: 1.至少5年嵌入式软件工程师经验 2.熟悉电池行业(应用于车载,通信,电力等行业) 3.英文可做为工作语言 4.熟练使用embedded C 5.有客户服务工作经验为佳 感兴趣的朋友,欢迎发送简历至judy.wang@tale ...…

查看全部问答>