目录

  • 前言
  • 一、代码部分
  • 二、使用和验证
    • 1.引入头文件
    • 2.初始化
    • 3.使用和验证
  • 三、可移植性
  • 总结

前言

接触HAL库差不多两年了,一直苦于HAL库没有自带微秒级的延时,网上的前辈们给出的解决方案要么是改写HAL_Delay的延时时间,要么就是额外占用一个定时器来实现,不太方便移植,以下是我给出的解决方案。

软件平台:STM32 Cube IDE 1.5.0

一、代码部分

Delay.c 代码如下

#include "main.h"#define USE_HAL_LEGACY
#include "stm32_hal_legacy.h"#define Timebase_Source_is_SysTick 1 //当Timebase Source为SysTick时改为1
//#define Timebase_Source_is_SysTick 0  //当使用FreeRTOS,Timebase Source为其他定时器时改为0#if   (!Timebase_Source_is_SysTick)extern TIM_HandleTypeDef htimx;        //当使用FreeRTOS,Timebase Source为其他定时器时,修改为对应的定时器#define Timebase_htim htimx#define Delay_GetCounter()       __HAL_TIM_GetCounter(&Timebase_htim)#define Delay_GetAutoreload()   __HAL_TIM_GetAutoreload(&Timebase_htim)
#else#define Delay_GetCounter()     (SysTick->VAL)#define Delay_GetAutoreload()  (SysTick->LOAD)
#endifstatic uint16_t fac_us = 0;
static uint32_t fac_ms = 0;/*初始化*/
void delay_init(void)
{#if (!Timebase_Source_is_SysTick)fac_ms = 1000000;             //作为时基的计数器时钟频率在HAL_InitTick()中被设为了1MHzfac_us = fac_ms / 1000;
#elsefac_ms = SystemCoreClock / 1000;fac_us = fac_ms / 1000;
#endif
}/*微秒级延时*/
void delay_us(uint32_t nus)
{uint32_t ticks = 0;uint32_t told = 0;uint32_t tnow = 0;uint32_t tcnt = 0;uint32_t reload = 0;reload = Delay_GetAutoreload();ticks = nus * fac_us;told = Delay_GetCounter();while (1){tnow = Delay_GetCounter();if (tnow != told){if (tnow < told){tcnt += told - tnow;}else{tcnt += reload - tnow + told;}told = tnow;if (tcnt >= ticks){break;}}}
}/*毫秒级延时*/
void delay_ms(uint32_t nms)
{uint32_t ticks = 0;uint32_t told = 0;uint32_t tnow = 0;uint32_t tcnt = 0;uint32_t reload = 0;reload = Delay_GetAutoreload();ticks = nms * fac_ms;told = Delay_GetCounter();while (1){tnow = Delay_GetCounter();if (tnow != told){if (tnow < told){tcnt += told - tnow;}else{tcnt += reload - tnow + told;}told = tnow;if (tcnt >= ticks){break;}}}
}/*重写HAL_Delay*/
void HAL_Delay(uint32_t Delay)
{uint32_t tickstart = HAL_GetTick();uint32_t wait = Delay;/*不太明白官方源码为啥这么写,会多延时1ms,注释掉后更准*/
//  /* Add a freq to guarantee minimum wait */
//  if (wait < HAL_MAX_DELAY)
//  {//    wait += (uint32_t)(uwTickFreq);
//  }while ((HAL_GetTick() - tickstart) < wait){}
}

Delay.h 代码如下

#ifndef DELAY_H
#define DELAY_H#include "main.h"extern void delay_init(void);
extern void delay_us(uint32_t nus);
extern void delay_ms(uint32_t nms);#endif

二、使用和验证

1.引入头文件

代码如下(示例):

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../Delay/Delay.h"       //引入头文件
/* USER CODE END Includes *///...

2.初始化

代码如下(示例):

  //.../* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 */delay_init();        //初始化/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE *///...

3.使用和验证

(示例)
所用开发板为 野火指南者 STM32F103VET6 开发板。
工程优化等级为默认的None,所测输出引脚为PC13和PC4

先测试什么也不做时输出的脉冲周期(此时系统时钟为72M)

  //.../* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */GPIOC->BSRR = GPIO_PIN_13;                   //PC13为高GPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u;   //PC13为低}/* USER CODE END 3 *///...

测得此时的脉冲周期

延时500us时

  //.../* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */GPIOC->BSRR = GPIO_PIN_13;                   //PC13为高delay_us(500);GPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u; //PC13为低}/* USER CODE END 3 *///...


延时50us时

  //.../* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */GPIOC->BSRR = GPIO_PIN_13;                   //PC13为高delay_us(50);GPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u;  //PC13为低}/* USER CODE END 3 *///...


延时10us时

  //.../* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */GPIOC->BSRR = GPIO_PIN_13;                   //PC13为高delay_us(10);GPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u;  //PC13为低}/* USER CODE END 3 *///...


加入FreeRTOS

Cube建议我们更改时基源

更改Timebase Source为其他定时器

更新工程后修改Delay.c文件

#include "main.h"#define USE_HAL_LEGACY
#include "stm32_hal_legacy.h"//#define Timebase_Source_is_SysTick 1   //当Timebase Source为SysTick时改为1
#define Timebase_Source_is_SysTick 0    //当使用FreeRTOS,Timebase Source为其他定时器时改为0#if   (!Timebase_Source_is_SysTick)extern TIM_HandleTypeDef htim7;        //当使用FreeRTOS,Timebase Source为其他定时器时,修改为对应的定时器#define Timebase_htim htim7#define Delay_GetCounter()       __HAL_TIM_GetCounter(&Timebase_htim)#define Delay_GetAutoreload()   __HAL_TIM_GetAutoreload(&Timebase_htim)
#else#define Delay_GetCounter()     (SysTick->VAL)#define Delay_GetAutoreload()  (SysTick->LOAD)
#endif//...

修改测试任务

//...
/* USER CODE BEGIN Header_StartDefaultTask */
/*** @brief  Function implementing the defaultTask thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN 5 *//* Infinite loop */for(;;){GPIOC->BSRR = GPIO_PIN_13;delay_us(50);                              //PC13高电平时间为50usGPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u;}/* USER CODE END 5 */
}/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){GPIOC->BSRR = GPIO_PIN_4;osDelay(1);                                   //PC4高电平时间为1msGPIOC->BSRR = (uint32_t)GPIO_PIN_4 << 16u;}/* USER CODE END StartTask02 */
}
//...

如图所示微秒级延时仍然工作正常

三、可移植性

//...
/*初始化*/
void delay_init(void)
{#if (!Timebase_Source_is_SysTick)fac_ms = 1000000;             //作为时基的计数器时钟频率在HAL_InitTick()中被设为了1MHzfac_us = fac_ms / 1000;
#elsefac_ms = SystemCoreClock / 1000;fac_us = fac_ms / 1000;
#endif
}
//...

使用滴答定时器作为时基时自然不用多说,当使用其他定时器作为时基时(如本文的例子),Src目录下会自动生成一个stm32f1xx_hal_timebase_tim.c文件,其中的HAL_InitTick函数重构了在stm32f1xx_hal.c中的、__weak修饰的同名函数,它设置了所选定时器的时钟频率为1MHz:

//.../* Compute TIM7 clock */uwTimclock = 2*HAL_RCC_GetPCLK1Freq();/* Compute the prescaler value to have TIM7 counter clock equal to 1MHz */uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);
//...

因此本方案在绝大多数由cube生成工程的情况下应该是通用的。


总结

经过实验,我们发现本方案实现了精度还算可以接受的微秒级延时,不过本方案的延时方式和HAL_Delay差不多,不建议在任务中过多地调用。

不同时基下的初始化过程建议参阅HongAndYi大佬写的
《HAL和FreeRTOS的基础时钟》

无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)相关推荐

  1. 一、STM32用HAL库实现uS级延时

    一.应用场景 STM32CubeMX可视化初始化配置,结合 HAL 库,给STM32软件开发提高了效率,但 HAL 库封装的延时函数目前仅支持 mS 级别的延时,日常很多情况下会用到 uS延时,比如I ...

  2. STM32 使用HAL库实现微秒级长延时

    STM32 使用HAL库实现微秒级长延时 背景 定时器初始化 主程序中的设计 背景 STM32 HAL库中有一个延时函数HAL_Delay(),可以实现毫秒级的延时,能够满足一般延时需求.在有些场合下 ...

  3. Linux下的微秒级定时器: usleep, nanosleep, select, pselect

    Linux下的微秒级定时器: usleep, nanosleep, select, pselect 标签: linuxnulldelaystructdate 2012-02-07 23:29 4979 ...

  4. stm32实现Systick的毫秒级延时和微妙级延时

    学习目标: stm32实现Systick的毫秒级延时和微妙级延时 学习内容: 1.Systick 工作原理 Systick(系统定时器)是ARM Cortex M3/M4 内核的一个外设,因为所有的C ...

  5. linux下获取微秒级精度的时间【转】

    转自:https://blog.csdn.net/u011857683/article/details/81320052 使用C语言在linux环境下获得微秒级时间 1. 数据结构 int getti ...

  6. linux下获取微秒级精度的时间

    使用C语言在linux环境下获得微秒级时间 1. 数据结构 int gettimeofday(struct timeval*tv, struct timezone *tz); 其参数tv是保存获取时间 ...

  7. windows下实现微秒级的延时

    1.微秒级的延时肯定不能基于消息(SetTimer函数),因为一出现消息堵塞等就会影响精 度,而且setTimer单位才是毫秒.实际响应时间可能要到55毫秒左右. 2.微秒级的延时也不能不能基于中断, ...

  8. STM32 HAL 硬件IIC+DMA+简单图形库控制OLED

    目录 前言 一.建立工程 二.编写和移植 前期准备 驱动部分修改 三.使用和验证 结论 (2022年1月22日重制)本文主要是移植带简单图形库的程序,如果只是实现DMA控制,建议看[0.96寸 OLE ...

  9. linux 微秒级定时,Linux下的微秒级定时器: usleep, nanosleep, select, pselect

    /* * @FileName: test_sleep.c * @Author: wzj * @Brief: * * * @History: * * @Date: 2012年02月07日星期二22:20 ...

最新文章

  1. UNIX/Linux 系统管理技术手册阅读(二)
  2. sas 检测到开型代码语句的递归_对于标准答案的递归很多人都看不懂,其实就是一个深度优先的遍历。我写了段伪代码,将递归步骤还原并注释了一下,供大家参考,希望大家有所收获。...
  3. Spring注解和XML配置文件孰优孰劣
  4. 基于Python + Redis实现分布式锁
  5. JDBC中给Mysql加时区问题!
  6. 加密、解密、openssl的基本应用以及CA的实现过程
  7. web版微信自动发消息(实现微信个人号机器人)
  8. omnet++tictoc2案例解析
  9. 通过股票数据接口如何看懂Level-2行情?
  10. android高德地图清除marker,删除多个点标记-点标记-示例中心-JS API 示例 | 高德地图API...
  11. ArcGIS API For JavaScript官方文档之Dojo
  12. mysql 交换分区吗_MySQL分区表——交换分区
  13. 即刻App产品分析报告
  14. 计算机毕业设计Java“小蜜蜂”校园代取快递系统(源码+系统+mysql数据库+lw文档)
  15. bind dlz mysql ptr_bind-dlz结合mysql实现智能DNS
  16. Java实现图片水印文字换行、平铺、旋转效果
  17. java版超级马里奥
  18. 张超 计算机 清华 论文,张超-清华大学航天航空学院
  19. round robin arbiter 轮询仲裁器设计
  20. 服务器抽屉式显示屏HL2158,大屏幕显示系统等采购结果公告 - 千里马招标网

热门文章

  1. 【iCore3 双核心板_FPGA】例程十二:Modelsim仿真实验
  2. 防火墙配置十大任务之十,构建虚拟防火墙
  3. JS splice()方法
  4. 每个人都应该使用的Python 3中被忽略的3个功能
  5. 在Python中使用Twitter Rest API批量搜索和下载推文
  6. 因为你的电脑安装了即点即用_即你所爱
  7. leetcode421. 数组中两个数的最大异或值(贪心算法)
  8. leetcode696. 计数二进制子串
  9. 开源 数据仓库_使用这些开源工具进行数据仓库
  10. arn linux编译系统时错误的解决