-
2020-03-05 08:59:30
文章目录
STM32 HAL库微秒延时函数的实现
天下苦
STM32 HAL
库微秒延时久已。不占用其他定时器资源又不使用循环的方式就不能实现微秒延时函数了吗?答案是否定的,我们还有方式实现,且还不止一种方法。详情且看下文分解:以下两种延时方式来源:
Arduino_Core_STM32
源码delayMicroseconds(uint32_t us)
函数的实现。利用SysTick再实现微秒延时函数
虽然
SysTick
已经被配置为1ms中断一次的模式,但每个1ms之间SysTick
的当前值寄存器是一直在计数的(每计一个数的时间是1/SytemCoreClock
)我们便可以利用该机制实现微秒延时函数。void delayMicroseconds(uint32_t us) { __IO uint32_t currentTicks = SysTick->VAL; /* Number of ticks per millisecond */ const uint32_t tickPerMs = SysTick->LOAD + 1; /* Number of ticks to count */ const uint32_t nbTicks = ((us - ((us > 0) ? 1 : 0)) * tickPerMs) / 1000; /* Number of elapsed ticks */ uint32_t elapsedTicks = 0; __IO uint32_t oldTicks = currentTicks; do { currentTicks = SysTick->VAL; elapsedTicks += (oldTicks < currentTicks) ? tickPerMs + oldTicks - currentTicks : oldTicks - currentTicks; oldTicks = currentTicks; } while (nbTicks > elapsedTicks); }
以上函数可以直接复制到工程中使用,不需要额外的任何配置。
Note
虽然函数参数
us
为uint32_t
类型,但是延时数不能过大,原因自己分析。建议超过1ms的延时时间使用HAL_Delay()
。
利用DWT(数据观测点)实现微秒延时函数
对于DWT大家可以搜索具体了解,这里我也不是很了解,就直说实现方法好了。
dwt.h
文件/** ****************************************************************************** * @file dwt.h * @author Frederic Pillon * @brief Header for dwt.c module ****************************************************************************** * @attention * * Copyright (c) 2019, STMicroelectronics * All rights reserved. * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef _DWT_H_ #define _DWT_H_ #include "stm32f4xx.h" #include <stdbool.h> #ifndef UNUSED #define UNUSED(x) (void)x #endif #ifdef DWT_BASE uint32_t dwt_init(void); #ifdef __cplusplus extern "C" { #endif //uint32_t dwt_init(void); void dwt_access(bool ena); static inline uint32_t dwt_max_sec(void) { return (UINT32_MAX / SystemCoreClock); }; static inline uint32_t dwt_max_msec(void) { return (UINT32_MAX / (SystemCoreClock / 1000)); }; static inline uint32_t dwt_max_usec(void) { return (UINT32_MAX / (SystemCoreClock / 1000000)); }; static inline uint32_t dwt_getCycles(void) { return (DWT->CYCCNT); }; #ifdef __cplusplus } #endif #endif /* DWT_BASE */ #endif /* _DWT_H_ */
dwt.c
文件/** ****************************************************************************** * @file dwt.c * @author Frederic Pillon * @brief Provide Data Watchpoint and Trace services ****************************************************************************** * @attention * * Copyright (c) 2019, STMicroelectronics * All rights reserved. * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ #include "dwt.h" #ifdef DWT_BASE #ifdef __cplusplus extern "C" { #endif uint32_t dwt_init(void) { /* Enable use of DWT */ if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; } /* Unlock */ dwt_access(true); /* Reset the clock cycle counter value */ DWT->CYCCNT = 0; /* Enable clock cycle counter */ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; /* 3 NO OPERATION instructions */ __asm volatile(" nop \n\t" " nop \n\t" " nop \n\t"); /* Check if clock cycle counter has started */ return (DWT->CYCCNT) ? 0 : 1; } void dwt_access(bool ena) { #if (__CORTEX_M == 0x07U) /* * Define DWT LSR mask which is (currentuly) not defined by the CMSIS. * Same as ITM LSR one. */ #if !defined DWT_LSR_Present_Msk #define DWT_LSR_Present_Msk ITM_LSR_Present_Msk #endif #if !defined DWT_LSR_Access_Msk #define DWT_LSR_Access_Msk ITM_LSR_Access_Msk #endif uint32_t lsr = DWT->LSR; if ((lsr & DWT_LSR_Present_Msk) != 0) { if (ena) { if ((lsr & DWT_LSR_Access_Msk) != 0) { //locked DWT->LAR = 0xC5ACCE55; } } else { if ((lsr & DWT_LSR_Access_Msk) == 0) { //unlocked DWT->LAR = 0; } } } #else /* __CORTEX_M */ UNUSED(ena); #endif /* __CORTEX_M */ } #ifdef __cplusplus } #endif #endif
delayMicroseconds()
函数void delayMicroseconds(uint32_t us) { #if defined(DWT_BASE) && !defined(DWT_DELAY_DISABLED) int32_t start = dwt_getCycles(); int32_t cycles = us * (SystemCoreClock / 1000000); while ((int32_t)dwt_getCycles() - start < cycles); #endif }
Note:
在使用DWT实现的延时函数时,程序下载到单片机中不能直接运行,需要按一下复位键才能正常运行(使用keil-MDK环境,ST-Link下载)。
结论
对比上面两种方式的延时效果:DWT实现的延时精度更高,SysTick的使用更加简单,大家可针对自己的需求选择。
更多相关内容 -
STM32CubeMX | STM32 HAL库方式的微秒延时函数
2019-11-06 10:22:15STM32CUBEMX系列教程之HAL库方式的微秒延时函数 标准库一般是使用系统嘀嗒定时器来进行微妙级别的延时,而HAL库官方使用SysTick的地方非常多,改代码容易引起错乱。网上的代码使用定时器进行微秒级别延时(不知道该...STM32CUBEMX系列教程之HAL库方式的微秒延时函数
标准库一般是使用系统嘀嗒定时器来进行微妙级别的延时,而HAL库将SysTick定时器用做了库函数的超时定时器,使用的地方非常多,自己修改代码使用嘀嗒定时器的话就会引起错乱。所以此时就需要自己实现一个微秒级别延时函数。
扫描以下二维码,关注公众号
雍正不秃头
获取更多STM32资源及干货!
方式一:系统滴答定时器
优点:全系列通用,只需要将宏定义
CPU_FREQUENCY_MHZ
根据时钟主频修改即可。
缺点:系统滴答定时器是HAL库初始化的,且必须有HAL库初始化。#define CPU_FREQUENCY_MHZ 72 // STM32时钟主频 void delay_us(__IO uint32_t delay) { int last, curr, val; int temp; while (delay != 0) { temp = delay > 900 ? 900 : delay; last = SysTick->VAL; curr = last - CPU_FREQUENCY_MHZ * temp; if (curr >= 0) { do { val = SysTick->VAL; } while ((val < last) && (val >= curr)); } else { curr += CPU_FREQUENCY_MHZ * 1000; do { val = SysTick->VAL; } while ((val <= last) || (val > curr)); } delay -= temp; } }
方式二:简单延时
优点: 实现简单,如果是F1系列,HAL_RCC_GetHCLKFreq()获取的值是72000000,此方式经过测试还是比较准的,如果不考虑通用性,F1系列建议使用此种方式。
缺点: 只适用F1系列72M主频。
void delay_us(uint32_t us) { uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us); while (delay--) { ; } }
方式三:普通定时器
优点: STM32全系列通用
缺点: 占用一个定时器该方法的思路是将定时器设置为1MHZ的计数频率,定时器计一个数就是1us,实现如下:
【F1系列】
#define DLY_TIM_Handle (&htim4) void delay_us(uint16_t nus) { __HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0); __HAL_TIM_ENABLE(DLY_TIM_Handle); while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus) { } __HAL_TIM_DISABLE(DLY_TIM_Handle); }
【F4系列】
#define DLY_TIM_Handle (&htim7) void delay_us(uint16_t nus) { __HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0); __HAL_TIM_ENABLE(DLY_TIM_Handle); while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus) { } __HAL_TIM_DISABLE(DLY_TIM_Handle); }
-
STM32 HAL库实现US微秒延时函数
2021-04-28 14:31:43现代的ST主推HAL库,但是这么强大的HAL库,居然没有一个微秒级别的延时函数???? 在HAL库中有毫秒级延时HAL_Delay(),原理是使用Systick作为延时计数器来实现的。如果需要增加精确的微秒级别延时,一般都是直接...STM32之CubeL4定时器控制实现微秒延时
现代的ST主推HAL库,但是这么强大的HAL库,居然没有一个微秒级别的延时函数????
在HAL库中有毫秒级延时HAL_Delay(),原理是使用Systick作为延时计数器来实现的。如果需要增加精确的微秒级别延时,一般都是直接更改Systick配置参数,但HAL固件很多的地方都使用了HAL_Delay()函数,因此不建议修改系统自动配置的Systick参数。
一、定时器微秒实现原理
CK_INT 80M时钟输入定时器,80分频后1Mhz = 1us,也就是计数器CNT每加一次就是1us。
STM32L4x1 参考手册P780我是用到是TIM2,首先需要知道TIM2的时钟,查看手册得知TIM2在APB1总线上
STM32L431xx 参考手册P16知道时钟输入后直接在CubeMX中配置定时器。
定时器计数配置
首先配置系统时钟
接下来配置定时器
编写思路设置CNT的值为0 --> (开始计数) --> 比较CNT值 --> (到达CNT值) --> 停止计数
具体代码实现
// Core\Src\delay.c void HAL_Delay_US(uint32_t Delay_us) { __HAL_TIM_SetCounter(&htim2, 0); __HAL_TIM_ENABLE(&htim2); while(__HAL_TIM_GetCounter(&htim2) < Delay_us); /* Disable the Peripheral */ __HAL_TIM_DISABLE(&htim2); }
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ GPIOA->BSRR = GPIO_PIN_2; // PA2 = 1 HAL_Delay_US(10); GPIOA->BSRR = (uint32_t)GPIO_PIN_2<< 16U; // PA2 = 0 HAL_Delay_US(10); } /* USER CODE END 3 */ }
2us
5us
10us
50us
500us
-
STM32 HAL 库延时函数 HAL_Delay 解析
2021-01-06 06:39:15HAL 库有提供延时函数,只不过它只能实现简单的毫秒级别延时,没有实现 us 级别延时。 下面我们列出HAL 库实现延时相关的函数。首先是功能配置函数: //调用 HAL_SYSTICK_Config 函数配置每隔 1ms 中断一次 __weak ... -
STM32HAL库微秒延时(μs)
2020-12-13 22:44:25STM32HAL库微秒(μs)延时STM32HAL库微秒延时(μs)
单片机:STM32F407ZET6
软件版本:STM32CubeMX 4.20.1
单片机固件包:STM32Cube FW_F4 V1.15.0本代码是我于2019年8月参加全国大学生电子设计竞赛前做赛前准备时参考网络上博客的程序代码编写,仅用于学习和交流。希望能给各位读者些许帮助。
本博客对于STM32单片机实现微秒(μs)延时采用的是定时器计数的方法。以STM32F407ZET6型号单片机的TIM4为例来进行代码展示。
先设置TIM4的时钟源:
STM32F407ZET6的TIM4的时钟为APB1的两倍即84MHz。
设置TIM4参数:
因为是微秒延时,所以计数频率应为f=1/1μs=1MHz。所以预分频Prescaler应为(84/f)-1=83,理论上来说计数周期Counter Period的设置将影响中断时长,而微秒延时只是计数并不需要开启定时器的中断。但是在实际开发过程中我将Counter Period设置为0结果延时时间变成随机时长,因此建议Counter Period不要设置为0。微秒延时函数:
void Delay_us(uint16_t myus)//基于TIM4定时器的μs延时函数 { uint16_t differ = 0xffff-myus-5; HAL_TIM_Base_Start(&htim4); __HAL_TIM_SetCounter(&htim4,differ); while(differ < 0xffff-5) { differ = __HAL_TIM_GetCounter(&htim4); } HAL_TIM_Base_Stop(&htim4); }
因为我是使用STMCubeMX搭建的工程,因此需要将这段延时函数加进tim.c中的 /* USER CODE BEGIN 1 / 和 / USER CODE END 1 / 中,并在tim.h中的 / USER CODE BEGIN Prototypes / 和 / USER CODE END Prototypes */ 中加上 void Delay_us(uint16_t ); 做函数声明。
接下来,我对这个微秒延时函数做一些解释说明:- 函数功能解释:
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim) 启动定时器;
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim) 关闭定时器;
__HAL_TIM_SetCounter和__HAL_TIM_GetCounter可在头文件stm32_hal_legacy.h中找到定义:
__HAL_TIM_SET_COUNTER 设定计数初值,默认是0;
__HAL_TIM_GET_COUNTER 获取当前计数值。 - STM32单片机的定时器基本都是16位计数器每次记到0xffff就会重载arr值。因此给定时器应至少赋予初值为0xffff-myus。
- 为什么我在定时器基本初值的基础上还要再“-5”呢?
这是为了防止因中断打断延时,造成计数错误。
以延时1μs为例:延时1us因而应从0xfffe开始计数,但假设在这过程产生中断。由于被中断打断(此时计数器仍在计数),本因计数至0xffff便停止计数,但由于错过计数值,并重载arr值,将会导致实际延时(0xffff+1)us。
- 函数功能解释:
-
STM32 HAL库方式的微秒延时函数
2021-10-12 16:23:47STM32CUBEMX系列教程之HAL库方式的微秒延时函数 标准库一般是使用系统嘀嗒定时器来进行微妙级别的延时,而HAL库将SysTick定时器用做了库函数的超时定时器,使用的地方非常多,自己修改代码使用嘀嗒定时器的话就会... -
HAL库中同时实现微秒级us以及毫秒级ms延时
2020-10-14 13:05:32延时函数就可以了。(这个函数每隔1ms就会产生一次中断) 但是,要实现微秒的话。。。貌似就没有现成的方法了。 所以我这边通过基础定时器Timer6,通过轮询的方式实现微秒级延时。(对于微秒级延时,如果通过中断... -
STM32 systick HAL库 得到微秒延时
2021-06-02 21:29:52SysTick是stm32系列单片机基本都有的外设,它是一个24位的倒计数定时器,标准库的滴答定时器可以有毫秒、微妙延时,但是HAL库只有 HAL_Delay();是一个毫秒延时。下面记录一下我的移植过程。 根据标准库的滴答... -
STM32CubeIDE HAL库微秒us的延时Delay实现
2020-01-28 22:06:45HAL库和LL库都没有自带的微秒级延时,但是修改SysTick的...一、利用定时器实现微秒延时 1.MX配置 2. <tim.c> /* USER CODE BEGIN 1 */ void delayXus(uint32_t us) { (&htim3)->Instance->... -
STM32hal库实现微秒延迟
2022-02-11 16:35:39许多人初次使用hal库,不知道HAL_Delay的实现原理。 大致来说,它的实现步骤如下: 1.用变量获得系统时钟源计数器的值 2.获得要延迟时间的参数值 3.比较两者大小,若时钟计数器的值大于要实现延迟的值,就会困在... -
STM32CubeMX HAL库自定义延时函数 ms和us
2020-02-18 11:05:401. stm32 HAL库 确定定时器时钟 确定定时器为哪个时钟? 找到初始化函数 MX_TIM1_Init(); 进入函数,找到 HAL_TIM_Base_Init(&htim1) 进入函数,找到 HAL_TIM_Base_MspInit(htim); 进入函数,依次找到定时器... -
STM32 HAL库实现微秒级别延时
2019-04-16 10:51:10参考了 http://www.stm32cube.com/article/176 http://www.stm32cube.com/question/434 https://blog.csdn.net/qq_22252423/article/details/76468161 -
STM32 HAL us delay(微秒延时)的指令延时实现方式及优化
2021-10-18 14:15:38STM32 HAL us delay(微秒延时)的指令延时实现方式及优化 STM32的HAL库,直接提供了1ms延时的实现函数HAL_Delay()。其原理是系统在上电后时钟配置阶段,配置了1ms产生一次中断,然后对一个32位寄存器uwTick逐次加1... -
【STM32】标准库与HAL库对照学习教程四--延时函数详解
2021-11-12 16:14:24【STM32】标准库与HAL库对照学习教程四--延时函数详解一、前言二、前期准备三、SysTick定时器介绍1、SysTick定时器简介2、SysTick定时器寄存器介绍(1) CTRL寄存器(2)LOAD寄存器(3)VAL寄存器(4)CALIB寄存器3... -
自己写一个STM32 HAL库的US微秒延时函数
2020-12-30 14:45:22现在ST已经把HAL库推向了主流,但是令我不解的是,HAL那么强大,居然没有一个微秒级别的延时函数?!黑人问号脸。好了,废话不多,直接上代码。#include "delay_us.h"/********************************************... -
用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本)
2020-12-01 22:16:37 在使用 DHT11 的时候,时序通信需要微秒来操作,STM32CubeMX 自带一个系统时钟,但是实现的是毫秒级别的。因此就自己用通用计时器实现一个。 文章目录概述1.配置定时器时钟2.计数器时钟频率及计数模式预分频系数... -
STM32 HAL/LL混合库查询方式实现精确微秒延时
2020-03-21 09:06:22通过HAL实现初始化5us初始化,后面在延时函数中可以再设置延时值。 1. 定时器实现1us延时 中断Enable不打钩 复制必要库文件,.c/.h单独一个文件,设置好。 生成代码如下: /* TIM3 init function */ void MX_TIM... -
通用定时器实现STM32单片机微秒级延时函数
2021-10-04 17:38:32在实际应用中,经常用到延时函数,而HAL库的延时函数是毫秒级的,虽然可以自行修改,但该函数使用的地方较多,修改不慎可能会引起其他问题,所以本文使用一个定时器,实现微秒级精确延时,不影响其他使用。... -
STM32微秒延时函数出现问题
2021-11-30 21:43:55问题遇到的现象和发生背景 我想要将您PS2手柄HAL库版本的代码移植到自己创建的STM32FI03C6开发板程序中,但是将您的文件移植过去后无法实现改功能,具体表现为t_s、t_ms、t_us三个值不发生改变 问题相关代码,请勿...