STM32F0x HAL库学习笔记(3)使用HAL库延时函数(HAL_Delay())[通俗易懂]

STM32F0x HAL库学习笔记(3)使用HAL库延时函数(HAL_Delay())[通俗易懂]本文开发环境:MCU型号:STM32F051R8T6IDE环境:MDK5.25代码生成工具:STM32CubeMx5.0.1本文内容:systick定时器简介使用HAL_Delay()延时函数实现LED灯闪烁Systick定时器HAL_Delay()延时函数while(1){/*USERCODEENDWHI…

大家好,又见面了,我是你们的朋友全栈君。

本文开发环境:

  • MCU型号:STM32F051R8T6
  • IDE环境: MDK 5.25
  • 代码生成工具:STM32CubeMx 5.0.1
  • HAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)

本文内容:

  1. Systick 定时器延时原理
  2. 使用HAL_Delay()实现LED灯闪烁
  3. 修改HAL_Delay()的延时单位
  4. 使用HA_Delay()需要注意的情况
  5. 关于HAL_Delay()函数溢出问题的讨论

Systick 定时器延时原理

Systick(滴答时钟)是一个24位,向下计数的定时器,当倒计时完成后,定时器可以产生一个中断,所以,当频率一定,计数个数一定时,这个中断就会以一定的时间间隔发生,如果每个中断发送后调用的中断函数中给一个变量累加,这样我们就可以获得一个与时间相关的变量。有关于滴答时钟相关知识,官方手册和网上已经有非常多的篇幅介绍讲解,这里不再赘述。

HAL_Delay()延时函数的使用

如果你使用STM32CubeMx来生成一个工程,那么使用Systick来延时是非常方便的,你只需要调用HAL库的一个虚函数,它的原型如下:

__weak void HAL_Delay(__IO uint32_t Delay)

可以看到,HAL_Delay()是一个虚函数,这表明用户可以在其它的位置重定义,如果这样,新的函数将会取代它,编译过程中也不会出现重定义的错误。该函数只有一个32位的参数,明显的,这个形参指定了延时的时间,它的单位是毫秒(ms)。关于这个函数的使用,是简单的,下面的例子中,我们在拉高和拉低LED1引脚的程序间插入了延时500ms的语句,编译下载后,你就可以发现LED1灯在以1s的频率闪烁。

    while (1)
    { 
   
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
		HAL_Delay(500);
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
		HAL_Delay(500);
    }
    /* USER CODE END 3 */

至此我们已经很好的在实际项目中运用到这个函数。如果你对这个函数还有其它的兴趣,我们可以进一步讨论和这个函数的其它方面。

HAL_Delay()函数延时单位的调整

虽然毫秒级的延时可能是应用最广泛的,但这个函数没有被命名为HAL_DelayMs()也可能正是考虑到用户会修改延时数的单位。改变延时的单位有几种方法,最简单,在HAL_Delay()函数内把形参乘一个系数,那么这个单位就会相应的这个系数的倍数。但是由于这种方式并没有改变系统的滴答时钟的中断频率,所以并不能影响到系统的开销。

这里介绍一种通过降低Systick中断频率来修改延时单位的方法,这种方式也节约了系统的开销。具体操作以下行数的形参改为比1000跟小的值,比如1,这样SysTick 1s 中断一次,而延时函数的单位,和最小单位,也成了1s。

  /*Configure the SysTick to have interrupt in 1ms time basis*/
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);

这里再简单介绍它的原理,首先来看Systick的中断函数:

void SysTick_Handler(void)
{ 
   
	/* USER CODE BEGIN SysTick_IRQn 0 */
	
	/* USER CODE END SysTick_IRQn 0 */
	HAL_IncTick();
	 /* USER CODE BEGIN SysTick_IRQn 1 */
	
	* USER CODE END SysTick_IRQn 1 */
}

__weak void HAL_IncTick(void)
{ 
   
	uwTick++;
}

Systick定时器每中断一次,就调用一次HAL_incTick()函数,来对变量uwTick累加。接着我们来看HAL_Delay()函数的定义:

__weak void HAL_Delay(__IO uint32_t Delay)
{ 
   
		uint32_t tickstart = HAL_GetTick();
		uint32_t wait = Delay;
		
		/* Add a period to guarantee minimum wait */
		if (wait < HAL_MAX_DELAY)
		{ 
   
			 wait++;
		}
		
		while((HAL_GetTick() - tickstart) < wait)
		{ 
   
		}
}

当程序进入延时函数时,就调用HAL_GetTick()获取当前uwTick的值。接着判断wait的值,若不大于可以延时的最大值,则wait自加1,最后不断的获取HAl_GetTick的值,直到这个值和初始值的差不小于等待的时间。所以,当我们的Delay形参越大,wait也越大,也就需要更长的时间来调出while循环。
综上所述,我们只要改变中断的频率,就可以修改延时单位的效果。
在main函数中,程序会调用SystemClock_Config()函数来配置系统时钟,当然也包括Systick,最后是通过虚函数HAL_InitTick来配置的,它的形参用来配置中断的优先级。

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{ 
   
  /*Configure the SysTick to have interrupt in 1ms time basis*/
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);

  /*Configure the SysTick IRQ priority */
  HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);

   /* Return function status */
  return HAL_OK;
}

我们重点关注以下这个函数:

  /*Configure the SysTick to have interrupt in 1ms time basis*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);

通过CubeMx的注释,表明这个函数决定了SysTick为1ms中断一次,如果形参我们不除以1000,这个函数则会1s中断一次,那么我们HAL_Delay()的延时单位将会是1s。这是因为这个值形参就是SysTick的倒计时个数,如果把SysTick的频率值,作为SysTick倒计时个数,那么单位肯定是1。可以具体数字来协助理解,比如SysTick的频率是10000Hz,意味着它1s减去10000个数,若把10000作为到倒计时数,他们需要则是1s钟的时间。

注意:由于这段代码存在用户代码区,即/*CODE BEGIN *//*CODE END */之间,当你在使用CubeMx对这个工程生成代码的时候,它会恢复成默认代码。

HAL_Delay()函数的注意事项

特别注意,在中断中使用 HAL_Delay() 很容易造成程序异常,原因是 HAL_Delay() 使用 滴答定时器的中断,如果在高于滴答定时器中断的中断函数中使用这个函数,程序将会锁死在 HAL_delay() 中,原因是,滴答定时器无法别调用, HAL_delay() 就无法跳出函数内部的 while 循环。

HAL_Delay()函数溢出问题

待写

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/146044.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 讲解最到位的粒子群算法,附matlab代码求解函数最优值

    讲解最到位的粒子群算法,附matlab代码求解函数最优值从鸟群觅食行为到粒子群算法粒子群算法的核心例:求解函数最小值粒子群算法的驱动因素从鸟群觅食行为到粒子群算法鸟群寻找食物的过程中,鸟与鸟之间存在着信息的交换,每只鸟搜索目前离食物最近的鸟的周围区域是找到食物的最简单有效的办法。粒子群算法(以下简称PSO)就是模拟鸟群觅食行为的一种彷生算法。解=粒子=鸟(鸟的位置象征着离食物的距离,粒子的位置也象征着…

  • 小米手机解BL锁、线刷详细教程,适用于小米全系列手机[通俗易懂]

    小米手机解BL锁、线刷详细教程,适用于小米全系列手机[通俗易懂][教程]小米手机解BL锁、线刷详细教程,适用于小米全系列手机这几天看到论坛里很多人在问怎么线刷,下面我就做个详细的线教程大家看一下高手别喷我哈此教程只适合刷官方MIUI包进入正题。第一步:解BL锁1.浏览器打开申请解锁小米手机点击立即解锁,然后再点击下载解锁工具,下载后保存到电脑也可以点击这里立即下载2.解压后双击miflash_unlock.exe打开手机解锁工具V3.33.点同意,然后登陆帐号,手机会检测您的帐号是否可以解锁,如果不支持点击申请解锁,好像是大约一周左右可以通过申

  • lmdb转换「建议收藏」

    lmdb转换「建议收藏」一、LMDB介绍:lmdb数据库LMDB全称为LightningMemory-MappedDatabase,就是非常快的内存映射型数据库,LMDB使用内存映射文件,可以提供更好的输入/输出性能,对于用于神经网络的大型数据集(比如ImageNet),可以将其存储在LMDB中。LMDB属于key-value数据库,而不是关系型数据库(比如MySQL),LMDB提供key-value存储,其中每个键值对都是我们数据集中的一个样本。LMDB的主要作用是提供数据管理,可以将各种

  • 课设-基于51单片机的智能小车(循迹+避障+APP控制)[通俗易懂]

    课设-基于51单片机的智能小车(循迹+避障+APP控制)[通俗易懂]基于51单片机的智能小车,可以实现循迹、避障、APP控制等功能;顺便提了一下自己大学的“造车”史!

    2022年10月11日
  • pycharm安装后运行不了_pycharm暂停程序

    pycharm安装后运行不了_pycharm暂停程序原博客链接:http://blog.csdn.net/qingyuanluofeng/article/details/46501427问题:pycharm安装后不能执行python脚本。我的是执行后老是报错,但是之前在cpython中都是可以的。于是上网查询解决方法原因:pycharm没有设置解析器/解释器设置错误(我的就是因为这个之前设置错了,位置也是错的,结果导致程序不能正常运行出

  • linux下ant安装和使用教程,ant安装与简单应用

    linux下ant安装和使用教程,ant安装与简单应用ant安装与简单应用1、下载ant软件包,本次下载的是apache-ant-1.9.6-bin.tar.gz2、传到Linux服务器上,我传到/usr/local/下3、解压缩,并创建软连接[root@localhostlocal]#tarxfapache-ant-1.9.6-bin.tar.gz[root@localhostlocal]#ln-svapache-ant-1.9.6…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号