STM32中微秒延时的实现方式

2023-11-02 06:52

本文主要是介绍STM32中微秒延时的实现方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

STM32中微秒延时的实现方式

  • 0.前言
  • 一、裸机实现方式
  • 二、FreeRTOS实现方式
  • 三、定时器实现(通用)
  • 4、总结


0.前言

  最近在STM32驱动移植过程中需要用到微秒延时来实现一些外设的时序,由于网上找到的驱动方法良莠不齐,笔者在实现时序过程中也浪费了不少时间。这里就将笔者觉得比较好的几种方式记录一下,方便后续使用,也可以作为参考。

一、裸机实现方式

  在STM32的裸机程序中,实现微秒延时比较简单,通过SysTick计时即可。关于SysTick的相关知识可以站内搜索,这里就不再过多赘述了,相关的delay_us函数参考了正点原子例程中的实现方式:

// 注意:nus的值,不要大于798915us(最大值即2^24/fac_us {fac_us=21})
void delay_us(uint32_t nus)
{uint32_t temp;SysTick->LOAD = nus * fac_us; // 时间加载,fac_us与时钟频率有关,例如:168Mhz时// fac_us=168,168/168M=1usSysTick->VAL = 0x00;                      // 清空计数器SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开始倒数do{temp = SysTick->CTRL; // 读取控制及状态寄存器的值} while ((temp & 0x01) && !(temp & (1 << 16))); // 等待时间到达,使能且位16为0(未计到0)SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;      // 关闭计数器SysTick->VAL = 0X00;                            // 清空计数器
}

步骤:
1、根据延时时间和定时器所选时钟频率,计算出定时器要计数的时间数值;
2、将该数值加载到重装载寄存器中;
3、将当前值寄存器清零,打开定时器开始计数;
4、等待控制及状态寄存器的位16变为1;
5、关闭定时器,退出。

二、FreeRTOS实现方式

在FreeRTOS中,SysTick定时器的重装载值是固定的,且定时器是一直在工作的。因此像裸机编程中将时间数值加载到重装载寄存器的方法不再适用。经过多方的查找和修改,最终使用的以下的实现方式:

#include "main.h"
#include "FreeRTOSConfig.h"extern void vPortSetupTimerInterrupt( void );void delay_us(uint32_t nus)
{uint32_t ticks;uint32_t told, tnow, reload, tcnt = 0;if ((0x0001 & (SysTick->CTRL)) == 0) { // 定时器未工作vPortSetupTimerInterrupt();      // 初始化定时器}reload = SysTick->LOAD;                    // 获取重装载寄存器值ticks = nus * (SystemCoreClock / 1000000); // 计数时间值told = SysTick->VAL;                       // 获取当前数值寄存器值(开始时数值)while (1){tnow = SysTick->VAL; // 获取当前数值寄存器值if (tnow != told)    // 当前值不等于开始值说明已在计数{if (tnow < told) {        // 当前值小于开始数值,说明未计到0tcnt += told - tnow; // 计数值=开始值-当前值} else {                              // 当前值大于开始数值,说明已计到0并重新计数tcnt += reload - tnow + told; // 计数值=重装载值-当前值+开始值  (已从开始值计到0)}told = tnow; // 更新开始值if (tcnt >= ticks) {break; // 时间超过/等于要延迟的时间,则退出.}}}
}// SystemCoreClock为系统时钟(system_stmf1xx.c中),通常选择该时钟作为systick定时器时钟,根据具体情况更改

步骤:
1、根据延时时间和定时器所选时钟频率,计算出定时器要计数的时间数值;
2、获取当前数值寄存器的数值;
3、以当前数值为基准开始计数;
4、当所计数值等于(大于)需要延时的时间数值时退出。
注:由于笔者使用STM32CubeIDE中通过CMSIS封装后的FreeRTOS API,所以需要额外extern引入与初始化定时器相关的API。如果使用原生FreeRTOS API,则需要根据情况引入。

三、定时器实现(通用)

  这种方式的优点就是简单且易用,直接使用一个闲置定时器分频后即可。在CubeMX中配制定时器,笔者选择基本定时器 TIM7,预分频PSC设置为(TIM总线时钟 - 1),分频后频率为1MHz,即每次计数累加1/1000000秒 = 1us,计数区间65535,单次定时最长65535微秒,计数模式为up 向上计数,是否自动重新加载选disable,一次计数满后不再重新计数。
在这里插入图片描述

void  delay_us(uint16_t nus)
{__HAL_TIM_SetCounter(&htim6,0);__HAL_TIM_ENABLE(&htim7);while(__HAL_TIM_GetCounter(&htim7)<nus);__HAL_TIM_DISABLE(&htim6);
}

步骤:
1、首先对定时器中的计数器进行置0;
2、使能定时器;
3、循环等待;
4、关闭定时器退出;

4、总结

  由于微秒延时间隔较短,所以在FreeRTOS中使用时最好避免内核对该线程进行调度,否则调度到其他线程可能要等到下一个时间片才能恢复运行。笔者对这部分还没有仔细了解,如有条件最好还是对相关线程添加关闭和恢复线程调度的操作。

这篇关于STM32中微秒延时的实现方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/328884

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S