STM32F0x HAL库学习笔记(3)使用HAL库延时函数(HAL_Delay())_Yonas-Luo的博客-程序员宅基地

技术标签: # 使用 STM32CubeMx 配置 STM32 外设  

本文开发环境:

  • 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()函数溢出问题

待写

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_17351161/article/details/89216473

智能推荐

关于安装redux失败的解决方法_redux-loggin下不了_南易武痴的博客-程序员宅基地

安装redux时,报错信息如下:npm WARN checkPermissions Missing write access to C:\Users\Administrator\Desktop\myapp\node_modules\reduxnpm WARN @typescript-eslint/[email protected] requires a peer of typescrip...

vue---el-upload使用FormData多文件同时上传_el-upload formdata_maidu_xbd的博客-程序员宅基地

需求:FormData 数据形式为键值对,数据可通过XMLHttpRequest.send()方式发送出去FormData.append(key,value):向FormData对象中添加一个键值对,如执行FormData.append(key1,value1)后FormData.append(key1,value2),key1对应值value1不会被覆盖,而是新增对应值value2FormData.get(key):返回FormData对象中给定key对应的第一个值FormDa..

新瓶旧酒ASP.NET AJAX(9) - 客户端脚本编程(Sys.Net命名空间下的WebServiceProxy)_weixin_34177064的博客-程序员宅基地

[索引页][×××]新瓶旧酒ASP.NET AJAX(9) - 客户端脚本编程(Sys.Net命名空间下的WebServiceProxy、WebServiceError、Generated Proxy Classes以及调用WebService、PageMethod)作者:webabcd介绍使用ASP.NET AJAX调用WebService(简单类型和复杂对象、JSON...

OracleSpatial函数实例_weixin_33923762的博客-程序员宅基地

Oracle Spatial操作geometry方法Oracle Spatial中SDO_GEOMETRY类型:CREATE TYPE SDO_GEOMETRY AS OBJECT(SDO_GTYPE NUMBER,--几何类型,如点线面SDO_SRID NUMBER,--几何的空间参考坐标系SDO_POINT SDO_POINT_TYPE,--如果几何为点类型,...

【并行计算10】GPU ARCHITECTURE_地波的博客-程序员宅基地

Thread executionExecution modelWarpWarp Divergence减少branch结构为什么是2是保证了if和else的情况减少判断的次数,unrollMemory hierarchylocal variable不一定非常快哦!Register&amp;Local memory一个block中thread越多,则lo...

wordpress主题 建站_liu709127859的博客-程序员宅基地

常用函数烂笔头&amp;lt;a href=&quot;&amp;lt;?php echo wp_login_url($_SERVER['REQUEST_URI']);?&amp;gt;&quot; 如果未登录,弹出登录页面后跳转回原页面wp_trim_words(get_the_title($qy[$i]-&amp;gt;ID),9) 截取字符串get_template_part(); 获取自定义模板&amp;lt;? echo get_...

随便推点

TeeChart用法(网络收集)默认分类 2010-03-05 21:46:05 阅读788 评论0 字号:大中小 ._lineseries1.add_一剑平江湖的博客-程序员宅基地

TeeChart用法(网络收集)<br />默认分类2010-03-05 21:46:05阅读788评论0字号:大中小1、TChart Hello world放一个控件到窗体上,然后写代码加入一个折线数据序列:varSeries: TLineSeries;beginSeries := TLineSeries.Create(Chart1);Series.Add(100, '头部', clRed);Series.Add(200, '颈部', clGreen);Chart1.AddSe

堆排序和插入排序(小博学习排序第四天)_一组无序的10个数用插入法构造一个大根堆_wlisonate的博客-程序员宅基地

1,堆排序堆排序的算法步骤;把无序数组构建成一个二叉堆,需要从小到大排序,则构建最大堆;需要从大到小排序,则构建小顶堆。 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。下来我们讲解他的实现过程构造堆将无序数组构造成一个大根堆(升序用大根堆,降序就用小根堆)假设存在以下数组主要思路:第一次保证0~0位置大根堆结构(废话),第二次保证0~1位置大根堆结构,第...

常见的集中式总线控制的方式及特点_Hydrion-Qlz的博客-程序员宅基地

常见的集中式总线控制的方式及特点问题常见的集中式总线控制有几种?各有何特点? 哪种方式响应时间最快?哪种方式对电路故障最敏感?回答常见的总线控制有三种Separate request每一个I/O设备都直接连接到总线控制器上,直接发送并接收总线的处理结果优先级可自定义响应速度快Chain polling新增加 Bus busy 线,用来通知每个线总线现在是否被占用离总线控制器越近优先级越高可以集成较大规模Counter timing polling新增加 Devic

Ubuntu配置samba实现与Windows文件夹共享_「已注销」的博客-程序员宅基地

1、samba的安装:sudo apt-get install sambasudo apt-get install smbclient2、创建共享目录:mkdir /home/li/share3、设置目录的权限: (777代表最高权限,可读可写可执行)chmod 777 /home/li/share4、修改配置文件:方法1:sudo vim /etc/samba/smb.conf 按键盘键“i”进入编辑模式,在文件最后写入按“Esc”退出编辑到命令行模式。按下“s

使用element UI过程中遇到的一些问题及解决办法_定格在这一刻的博客-程序员宅基地

一. dialog对话框响应式缩放1.首先要动态设置width的宽度,默认为0&lt;template&gt;&lt;el-dialog title="提示" :visible.sync="dialogVisible" :width="dialogWidth" :before-close="handleClose"&gt; &lt;span&gt;这是一段信息&lt;/span&gt; &lt;span slot="footer" class="dialog-footer"&g

cpu性能测试工具调用_性能调用_西北小农民的博客-程序员宅基地

工具1. valgrind安装 sudo apt-get install valgrind sudo apt-get install kcachegrind valgrind --tool=callgrind ./exe_file_name .........执行: Debug模式下执行结束之后生成: callgrind.out.**** 文件加载文件: ...

推荐文章

热门文章

相关标签