STM32中微秒延时的实现方式

news2025/7/19 12:58:08

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=1us
    SysTick->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) {        // 当前值小于开始数值,说明未计到0
                tcnt += 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中使用时最好避免内核对该线程进行调度,否则调度到其他线程可能要等到下一个时间片才能恢复运行。笔者对这部分还没有仔细了解,如有条件最好还是对相关线程添加关闭和恢复线程调度的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1160987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表指定节点的插入

向现有链表中插入结点&#xff0c;根据插入位置的不同&#xff0c;可分为以下 3 种情况&#xff1a; 插入到链表的头部&#xff0c;作为新的链表中第一个存有数据的结点&#xff08;又称为”首元结点”&#xff09;&#xff1b;插入到链表中某两个结点之间的位置&#xff1b;插…

通达信对子数的指标公式大全

对子数&#xff1a;形如9.22,12.21, 4.44,11.00等 顺子数&#xff1a;形如&#xff13;.89,4.98,7.56等 进一步的了解,自行百度。 在通达里查找价格为对子数的个股&#xff0c;有两个自带的函数&#xff0c; INTPART(X),返回数值的整数部分&#xff0c; FRACPART&#xff…

安达发|APS生产排程解决五金制造企业的需求

在五金制造行业中&#xff0c;生产排程一直是一个非常重要的环节。然而&#xff0c;由于五金行业的特点和痛点&#xff0c;传统的生产排程方法往往难以满足企业的需求。本文将针对五金行业的痛点&#xff0c;探讨如何利用APS生产排程解决这些问题。 首先&#xff0c;我们需要了…

代码随想录二刷Day 56

1143.最长公共子序列 本题和动态规划&#xff1a;718. 最长重复子数组 (opens new window)区别在于这里不要求是连续的了&#xff0c;但要有相对顺序&#xff0c;即&#xff1a;"ace" 是 "abcde" 的子序列&#xff0c;但 "aec" 不是 "abcd…

大模型时代目标检测任务会走向何方?

参考&#xff1a; 大模型时代目标检测任务会走向何方&#xff1f; 细数从常见的目标检测到现在 MLLM 盛行的时代&#xff0c;和 Object Detection 的任务以及近期涌现的新任务。>>加入极市CV技术交流群&#xff0c;走在计算机视觉的最前沿 你或许很好奇&#xff0c;现在…

人工智能时代八大类算法你了解吗?(文末包邮送书6本)

文章目录 本文导读1. 关联规则分析2. 回归分析3. 分类分析4. 聚类分析5. 集成学习6. 自然语言处理7. 图像处理8. 深度学习9. 书籍推荐&#xff08;包邮送书6本&#xff09; 本文导读 从零带你了解人工智能时代需要掌握的8大类算法&#xff0c;包括基础理论、关联规则分析、回归…

Vue 创建自定义 ref 函数

Vue 创建自定义 ref 函数 customRef customRef 用于&#xff1a;创建一个自定义的 ref 函数&#xff0c;并对其依赖项跟踪和更新触发进行显式控制。 使用 customRef 创建自定义 ref 函数 // 创建自定义 ref 函数 function myRef(value) {return customRef((track, trigger) &g…

自动曝光算法(第一讲)

序言 失业在家无事&#xff0c;想到以后换方向不做自动曝光了&#xff0c;但是自动曝光的工作经验也不能浪费了&#xff0c;准备写一个自动曝光的教学&#xff0c;留给想做自动曝光的小伙伴参考。笔者当时开发自动曝光没有按摄影的avtvevbvsv公式弄&#xff0c;而是按正确的增…

[架构之路-251/创业之路-82]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 商业智能、决策支持系统、知识管理

目录 前言&#xff1a; 一、企业信息化的结果&#xff1a;常见企业信息化软件 1.1 商业智能 - 管理层 1.1.1 什么是商业智能What 1.1.1.1 商业智能常见工具 1.1.2 为什么需要商业智能Why&#xff1f; 1.1.3 谁需要商业智能who&#xff1f; 1.1.4 商业智能在企业管理中的…

微信小程序开发(搭建)

首先去微信开发者网站下载微信开发者工具 然后打开电脑命令框wincmd 全局安装 vue-clinpm install --global vue-cli创建一个基于 mpvue-quickstart 模板的新项目vue init mpvue/mpvue-quickstart my-project安装依赖cd my-projectnpm install启动构建npm run dev 记得为vue配…

Docker dnmp 多版本php安装 php8.2

Laravel9 开发需要用到php8.1以上的版本&#xff0c;而dnmp只支持到php8.0。安装php8.2的步骤如下&#xff1a; 1. 从/services/php80目录复制一份出来&#xff0c;重命名为php82&#xff0c;extensions目录只保留 install.sh 和 install-php-extensions 这两个文件 2. 修改.en…

C++使用栈实现简易计算器(支持括号)

使用C实现&#xff0c;使用系统自带stac 支持括号处理支持小数计算支持表达式有效性检查支持多轮输入。 运行结果示例&#xff1a; 代码&#xff1a; #include <iostream> #include <stack> #include <string> using namespace std;//判断是否是数字字符 …

手动仿射变换

开发环境&#xff1a; Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example参考代码目的&#xff1a;学习与总结 demo解决问题&#xff1a;通过仿射控件vtkAffineWidget对目标actor进行手动的拖拽的仿射变换 关键类&#xff1a;vtkAffineWi…

PostGIS轨迹分析——简化轨迹

需求 对轨迹线进行简化,并将原始轨迹上的两个特征点拉取到简化后的轨迹上 简化线 红色线是简化后的轨迹线,蓝色线是原始轨迹,有两个特征点 知识点: st_makeline函数将点连成线st_simplify简化线函数,其中第二个参数为坐标系的单位,0.002度大概代表0.002x1.11x10^5≈22…

使用 ElementUI 组件构建 Window 桌面应用探索与实践(WinForm)

零、实现原理与应用案例设计 1、原理 基础实例 Demo 可以参照以下这篇博文&#xff0c; 基于.Net CEF 实现 Vue 等前端技术栈构建 Windows 窗体应用-CSDN博客文章浏览阅读291次。基于 .Net CEF 库&#xff0c;能够使用 Vue 等前端技术栈构建 Windows 窗体应用https://blog.c…

通过xshell传输文件到服务器

一、user is not in the sudoers file. This incident will be reported. 参考链接&#xff1a; [已解决]user is not in the sudoers file. This incident will be reported.(简单不容易出错的方式)-CSDN博客 简单解释下就是&#xff1a; 0、你的root需要设置好密码 sudo …

java 申请堆外内存吗? java如何使用堆外内存?

java 申请堆外内存吗&#xff1f; java如何使用堆外内存&#xff1f; Java堆外内存管理 JVM可以使用的内存分外2种&#xff1a;堆内存和堆外内存&#xff1a; 堆内存完全由JVM负责分配和释放&#xff0c;如果程序没有缺陷代码导致内存泄露&#xff0c;那么就不会遇到java.lan…

【DriveGPT学习笔记】自动驾驶汽车Autonomous Vehicle Planning

原文地址&#xff1a;DriveGPT - Lei Maos Log Book 自动驾驶汽车的核心软件组件是感知、规划和控制。规划是指在给定场景或一系列场景的情况下为自动驾驶汽车制定行动计划的过程&#xff0c;以实现安全和理想的自动驾驶。 用于规划的场景是从感知软件组件获得的。计划的行动将…

Node学习笔记之跨域

1.跨域是什么&#xff1f; 跨域&#xff0c;是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的&#xff0c;是浏览器对JavaScript实施的安全限制。 同源策略限制了一下行为&#xff1a; Cookie无法读取DOM 和 JS 对象无法获取Ajax请求发送不出去 同源是指&#…

如何通过 NAT 模式连接VMware虚拟机以及存在和不存在ens33文件的解决方案

文章目录 前言1 VMware配置1.1 打开vmvare虚拟网络编辑器1.2 取消使用本地DHCP1.3 NAT设置 2 虚拟机的配置2.1 存在ens332.2.1 修改ifcfg-ens33文件2.2.1.1 为什么设置BOOTPROTOstatic&#xff1f;2.2.1.2 如何选择使用static还是dhcp&#xff1f; 2.2.2 关闭防火墙 2.2 不存在…