[HAL学习笔记]
HAL库源文件stm32h7xx_hal.c学习笔记,此文件极其重要(2018-07-21 V1.0)
原文地址:forum.armfly.com/forum.php?mod=viewthread&tid=87760
说明:
1、在中断里面使用HAL_Delay要特别注意。
2、函数HAL_InitTick会被HAL_Init和HAL_RCC_ClockConfig调用。
3、这个文件比较杂,像基准电压大小配置,EXTI配置,IO补偿配置等都在这个文件里面。
==============================================================================
初始化和复位初始化
==============================================================================
1、初始化NVIC配置,初始化时钟(主要是滴答定时器)以及备份域的初始化。
2、复位HAL的部分配置。
3、配置一个1ms的时间基准。
(1)、默认的时间基准是来源于滴答定时器,也可以来自其他同通用定时器。
(2)、函数HAL_Init里面会调用时间基准初a始化函数HAL_InitTick,而调用函数HAL_RCC_ClockConfig也会调用时间基准初始化函数HAL_InitTick
(3)、如果在中断服务程序里面调用延迟函数HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此以来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比他们高。
1、函数HAL_StatusTypeDef HAL_Init(void)
(1)此函数用于初始化HAL库,必须在main函数里面优先调用。此函数主要实现如下功能
a、设置NVIC优先级分组是4。
b、设置滴答定时器的每1ms中断一次。
c、HAL库不像之前的标准库,在系统启动函数SystemInit里面做了RCC初始化,HAL库是没有做的,所以进入到main函数后,系统还在用内部高速时钟HSI,对于H7来说,HSI主频是64MHz。
d、函数HAL_Init里面调用的HAL_MspInit一般在文件stm32h7xx_hal_msp.c里面做具体实现,主要用于底层初始化。当前此函数也在文件stm32h7xx_hal.c里面,只是做了弱定义。
(2)用户务必保证每1ms一次滴答中断。
(3)返回值,HAL_ERROR和HAL_OK。
HAL_StatusTypeDef HAL_Init(void)
{
/* 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) */
if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
{
return HAL_ERROR;
}
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
2、函数HAL_St
atusTypeDef HAL_DeInit(void)
(1)用于复位HAL库和滴答时钟
a、复位了AHB1,2,3,4的时钟,APB1L,APB1H,APB2,3,4的时钟。
b、函数HAL_DeInit里面调用的HAL_MspDeInit一般在文件stm32h7xx_hal_msp.c里面做具体实现,主要用于底层初始化,跟函数HAL_Init里面调用的HAL_MspInit是一对。当前此函数也在文件stm32h7xx_hal.c里面,只是做了弱定义。
HAL_StatusTypeDef HAL_DeInit(void)
{
/* Reset of all peripherals */
__HAL_RCC_AHB3_FORCE_RESET();
__HAL_RCC_AHB3_RELEASE_RESET();
__HAL_RCC_AHB1_FORCE_RESET();
__HAL_RCC_AHB1_RELEASE_RESET();
__HAL_RCC_AHB2_FORCE_RESET();
__HAL_RCC_AHB2_RELEASE_RESET();
__HAL_RCC_AHB4_FORCE_RESET();
__HAL_RCC_AHB4_RELEASE_RESET();
__HAL_RCC_APB3_FORCE_RESET();
__HAL_RCC_APB3_RELEASE_RESET();
__HAL_RCC_APB1L_FORCE_RESET();
__HAL_RCC_APB1L_RELEASE_RESET();
__HAL_RCC_APB1H_FORCE_RESET();
__HAL_RCC_APB1H_RELEASE_RESET();
__HAL_RCC_APB2_FORCE_RESET();
__HAL_RCC_APB2_RELEASE_RESET();
__HAL_RCC_APB4_FORCE_RESET();
__HAL_RCC_APB4_RELEASE_RESET();
/* De-Init the low level hardware */
HAL_MspDeInit();
/* Return function status */
return HAL_OK;
}
3、函数__weak HAL_StatusTypeDef
HAL_InitTick(uint32_t TickPriority)
(1)此函数有个前缀__weak ,表示弱定义,用户可以重定义。
(2)此函数用于初始化滴答时钟1ms中断一次,并且为滴答中断配置一个用户指定的优先级。
(3)此函数由HAL_Init调用,或者任何其它地方调用函数HAL_RCC_ClockConfig配置RCC的时候也会调用HAL_InitTick。
(4)调用基于此函数实现HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此以来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比他们高。
(5)形参TickPriority用于设置滴答定时器优先级
(6)返回值HAL_ERROR和HAL_OK。
__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;
}
4、函数__weak voi
d HAL_IncTick(void)
(1)此函数有个前缀__weak ,表示弱定义,用户可以重定义。
(2)此函数在滴答定时器中断里面被调用,实现一个简单的计数功能,因为一般滴答定时器中断都是配置的1ms,所以计数全局变量uwTick每秒加1.
__weak void HAL_IncTick(void)
{
uwTick += (uint32_t)uwTickFreq;
}复制代码
5、函数__weak void HAL_GetTick(void)
(1)此函数有个前缀__weak ,表示弱定义,用户可以重定义。
(2)此函数用于获取当前系统计数。
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}复制代码
6、函数HAL_GetTickPrio
(1)此函数用于获取滴答时钟优先级。
uint32_t HAL_GetTickPrio(void)
{
return uwTickPrio;
}复制代码
7、函数HAL_SetTickFreq
(1)此函数用于重新配置滴答中断周期
HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq)
{
HAL_StatusTypeDef status = HAL_OK;
assert_param(IS_TICKFREQ(Freq));
if (uwTickFreq != Freq)
{
uwTickFreq = Freq;
/* Apply the new tick Freq */
status = HAL_InitTick(uwTickPrio);
}
return status;
}复制代码
8、函数HAL_TickFreqTypeDef HAL_GetTickFreq(void)
(1)此函数用于获取滴答中断周期
HAL_TickFreqTypeDef HAL_GetTickFreq(void)
{
return uwTickFreq;
}复制代码
9、函数 __weak void HAL_Delay(uint32_t Delay)
(1)此函数有个前缀__weak ,表示弱定义,用户可以重定义。
(2)此函数用于阻塞式延迟,默认单位是ms。
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}复制代码
10、函数__weak void HAL_SuspendTick(void)和__weak void HAL_ResumeTick(void)
(1)此函数有个前缀__weak ,表示弱定义,用户可以重定义。
(2)分别用于滴答定时器的挂起和恢复。
__weak void HAL_SuspendTick(void)
{
/* Disable SysTick Interrupt /
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
}
__weak void HAL_ResumeTick(void)
{
/ Enable SysTick Interrupt */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
}
复制代码
STM32H7自带的电压基准设置函数,主要是供自带ADC和DAC使用。
1、函数void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)
(1)此函数用于配置内部电压基准大小配置。
(2)当形参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。
void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)
{
/* Check the parameters */
assert_param(IS_SYSCFG_VREFBUF_VOLTAGE_SCALE(VoltageScaling));
MODIFY_REG(VREFBUF->CSR, VREFBUF_CSR_VRS, VoltageScaling);
}复制代码
2、函数void HAL_SYSCFG_VREFBUF_HighImpedanceConfig(uint32_t Mode)
(1)此函数用于配置内部电压基准是否在芯片内部分VREF+引脚接通。
(2)形参为SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE时,表示导通。
形参为SYSCFG_VREFBUF_HIGH_IMPEDANCE_ENABLE时,表示高阻,即不导通。
void HAL_SYSCFG_VREFBUF_HighImpedanceConfig(uint32_t Mode)
{
/* Check the parameters */
assert_param(IS_SYSCFG_VREFBUF_HIGH_IMPEDANCE(Mode));
MODIFY_REG(VREFBUF->CSR, VREFBUF_CSR_HIZ, Mode);
}复制代码
3、函数void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue)
(1)此函数用于内部电压基准的的校准调节。
void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue)
{
/* Check the parameters */
assert_param(IS_SYSCFG_VREFBUF_TRIMMING(TrimmingValue));
MODIFY_REG(VREFBUF->CCR, VREFBUF_CCR_TRIM, TrimmingValue);
}复制代码
4、函数HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void)和void HAL_SYSCFG_DisableVREFBUF(void)
(1)分别用于内部电压参考基准的禁止和使能。
HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void)
{
uint32_t tickstart = 0;
SET_BIT(VREFBUF->CSR, VREFBUF_CSR_ENVR);
/* Get Start Tick*/
tickstart = HAL_GetTick();
/* Wait for VRR bit */
while(READ_BIT(VREFBUF->CSR, VREFBUF_CSR_VRR) == RESET)
{
if((HAL_GetTick() - tickstart) > VREFBUF_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
void HAL_SYSCFG_DisableVREFBUF(void)
{
CLEAR_BIT(VREFBUF->CSR, VREFBUF_CSR_ENVR);
}
复制代码
1、函数void HAL_SYSCFG_ETHInterfaceSelect(uint32_t SYSCFG_ETHInterface)
(1)此函数用于以太网的MII和RMII接口选择
void HAL_SYSCFG_ETHInterfaceSelect(uint32_t SYSCFG_ETHInterface)
{
/* Check the parameter */
assert_param(IS_SYSCFG_ETHERNET_CONFIG(SYSCFG_ETHInterface));
MODIFY_REG(SYSCFG->PMCR, SYCFG_PMCR_EPIS_SEL, (uint32_t)(SYSCFG_ETHInterface));
}复制代码
1、函数void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch , uint32_t SYSCFG_SwitchState )
(1)对于引脚PA0,PA1,PC2和PC3用于ADC时,还有有一组对应的可选引脚PA0_C,PA1_C,PC2_C和PC3_C。此函数的作用就是切换可选引脚用的。关于这个问题的详情可看此贴:http://forum.armfly.com/forum.php?mod=viewthread&tid=87707
void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch , uint32_t SYSCFG_SwitchState )
{
/* Check the parameter */
assert_param(IS_SYSCFG_ANALOG_SWITCH(SYSCFG_AnalogSwitch));
assert_param(IS_SYSCFG_SWITCH_STATE(SYSCFG_SwitchState));
MODIFY_REG(SYSCFG->PMCR, (uint32_t) SYSCFG_AnalogSwitch, (uint32_t)(SYSCFG_SwitchState));
}复制代码
2、函数void HAL_SYSCFG_EnableBOOST(void)和void HAL_SYSCFG_DisableBOOST(void)
(1)这两个函数用于使能或者禁止Booster。如果使能了booster的话,在供电电压低于2.7V时,可以减少模拟开关总的谐波失真,这样的话,模拟开关的性能跟正常供电电压时的全范围测量一样。
void HAL_SYSCFG_EnableBOOST(void)
{
SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_BOOSTEN) ;
}
void HAL_SYSCFG_DisableBOOST(void)
{
CLEAR_BIT(SYSCFG->PMCR, SYSCFG_PMCR_BOOSTEN) ;
}复制代码
1、函数void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)
(1)用于配置boot = 0和boot = 1时的启动地址,详情看此贴:http://forum.armfly.com/forum.php?mod=viewthread&tid=87459
void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)
{
/* Check the parameters /
assert_param(IS_SYSCFG_BOOT_REGISTER(BootRegister));
assert_param(IS_SYSCFG_BOOT_ADDRESS(BootAddress));
if ( BootRegister == SYSCFG_BOOT_ADDR0 )
{
/ Configure CM7 BOOT ADD0 /
MODIFY_REG(SYSCFG->UR2, SYSCFG_UR2_BOOT_ADD0, ((BootAddress >> 16) << POSITION_VAL(SYSCFG_UR2_BOOT_ADD0)));
}
else
{
/ Configure CM7 BOOT ADD1 */
MODIFY_REG(SYSCFG->UR3, SYSCFG_UR3_BOOT_ADD1, (BootAddress >> 16));
}
}复制代码
IO补偿相关的配置
1、函数void HAL_EnableCompensationCell(void)和void HAL_SYSCFG_EnableIOSpeedOptimize(void)
(1)这两个函数用于使能或者禁止IO补偿,只有在供电电压范围是2.4V到3.6V之间时,使用此功能才有意义。
void HAL_EnableCompensationCell(void)
{
SET_BIT(SYSCFG->CCCSR, SYSCFG_CCCSR_EN) ;
}
void HAL_DisableCompensationCell(void)
{
CLEAR_BIT(SYSCFG->CCCSR, SYSCFG_CCCSR_EN) ;
}复制代码
2、函数void HAL_SYSCFG_EnableIOSpeedOptimize(void)和void HAL_SYSCFG_DisableIOSpeedOptimize(void)
(1)这两个函数用于优化IO速度或者禁止优化,不过仅在供电电压低于2.5V时可用,高于2.5V是不可以使用的,另外使用这个功能的前提是用户使能了PRODUCT_BELOW_25V(是可选字节配置选项里面的一个bit)
void HAL_SYSCFG_EnableIOSpeedOptimize(void)
{
SET_BIT(SYSCFG->CCCSR, SYSCFG_CCCSR_HSLV) ;
}
void HAL_SYSCFG_DisableIOSpeedOptimize(void)
{
CLEAR_BIT(SYSCFG->CCCSR, SYSCFG_CCCSR_HSLV) ;
}复制代码
3、函数void HAL_SYSCFG_CompensationCodeSelect(uint32_t SYSCFG_CompCode)
(1)IO补偿单元的选择,可以是SYSCFG_CELL_CODE,即寄存器SYSCFG_CCVR,也可以是SYSCFG_REGISTER_CODE,即寄存器SYSCFG_CCCR
void HAL_SYSCFG_CompensationCodeSelect(uint32_t SYSCFG_CompCode)
{
/* Check the parameter */
assert_param(IS_SYSCFG_CODE_SELECT(SYSCFG_CompCode));
MODIFY_REG(SYSCFG->CCCSR, SYSCFG_CCCSR_CS, (uint32_t)(SYSCFG_CompCode));
}复制代码
4、函数void HAL_SYSCFG_CompensationCodeConfig(uint32_t SYSCFG_PMOSCode, uint32_t SYSCFG_NMOSCode )
(1)用于设置补偿值,两个形参的范围都是0-15。根据用户调用函数HAL_SYSCFG_CompensationCodeSelect选择的寄存器,这里会仅有一个形参的设置是有效的。
void HAL_SYSCFG_CompensationCodeConfig(uint32_t SYSCFG_PMOSCode, uint32_t SYSCFG_NMOSCode )
{
/* Check the parameter */
assert_param(IS_SYSCFG_CODE_CONFIG(SYSCFG_PMOSCode));
assert_param(IS_SYSCFG_CODE_CONFIG(SYSCFG_NMOSCode));
MODIFY_REG(SYSCFG->CCCR, SYSCFG_CCCR_NCC|SYSCFG_CCCR_PCC, (((uint32_t)(SYSCFG_PMOSCode)<< 4)|(uint32_t)(SYSCFG_NMOSCode)) );
}复制代码
下面的函数主要用于低功耗模式下的调试。
/**
- @brief Enable the Debug Module during Domain1 SLEEP mode
- @retval None
*/
void HAL_EnableDBGSleepMode(void)
{
SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_SLEEPD1);
}
/**
- @brief Disable the Debug Module during Domain1 SLEEP mode
- @retval None
*/
void HAL_DisableDBGSleepMode(void)
{
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_SLEEPD1);
}
/**
- @brief Enable the Debug Module during Domain1 STOP mode
- @retval None
*/
void HAL_EnableDBGStopMode(void)
{
SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STOPD1);
}
/**
- @brief Disable the Debug Module during Domain1 STOP mode
- @retval None
*/
void HAL_DisableDBGStopMode(void)
{
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STOPD1);
}
/**
- @brief Enable the Debug Module during Domain1 STANDBY mode
- @retval None
*/
void HAL_EnableDBGStandbyMode(void)
{
SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBYD1);
}
/**
- @brief Disable the Debug Module during Domain1 STANDBY mode
- @retval None
*/
void HAL_DisableDBGStandbyMode(void)
{
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBYD1);
}
/**
- @brief Enable the Debug Module during Domain3 STOP mode
- @retval None
*/
void HAL_EnableDomain3DBGStopMode(void)
{
SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STOPD3);
}
/**
- @brief Disable the Debug Module during Domain3 STOP mode
- @retval None
*/
void HAL_DisableDomain3DBGStopMode(void)
{
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STOPD3);
}
/**
- @brief Enable the Debug Module during Domain3 STANDBY mode
- @retval None
*/
void HAL_EnableDomain3DBGStandbyMode(void)
{
SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBYD3);
}
/**
- @brief Disable the Debug Module during Domain3 STANDBY mode
- @retval None
*/
void HAL_DisableDomain3DBGStandbyMode(void)
{
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBYD3);
}复制代码
FMC内存映射切换
1、函数void HAL_SetFMCMemorySwappingConfig(uint32_t BankMapConfig)
(1)此函数用于FMC内存映射切换配置、形参是FMC_SWAPBMAP_DISABLE, FMC_SWAPBMAP_SDRAM_SRAM, FMC_SWAPBMAP_SDRAMB2
void HAL_SetFMCMemorySwappingConfig(uint32_t BankMapConfig)
{
/* Check the parameter */
assert_param(IS_FMC_SWAPBMAP_MODE(BankMapConfig));
MODIFY_REG(FMC_Bank1->BTCR[0], FMC_BCR1_BMAP, BankMapConfig);
}复制代码
2、函数uint32_t HAL_GetFMCMemorySwappingConfig(void)
(1)此函数用于获取当前的内容映射配置
uint32_t HAL_GetFMCMemorySwappingConfig(void)
{
return READ_BIT(FMC_Bank1->BTCR[0], FMC_BCR1_BMAP);
}复制代码
剩下的几个函数是EXTI的,暂时不去学习了。
void HAL_EXTI_EdgeConfig(uint32_t EXTI_Line , uint32_t EXTI_Edge )
void HAL_EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
void HAL_EXTI_D1_ClearFlag(uint32_t EXTI_Line)
void HAL_EXTI_D1_EventInputConfig(uint32_t EXTI_Line , uint32_t EXTI_Mode, uint32_t EXTI_LineCmd )
void HAL_EXTI_D3_EventInputConfig(uint32_t EXTI_Line, uint32_t EXTI_LineCmd , uint32_t EXTI_ClearSrc )