单片机:实现延时函数(附带源码)

延时函数的实现:单片机编程的核心需求

在嵌入式开发中,延时函数是一个非常常见的需求。在很多情况下,程序需要暂停执行一定时间,通常是为了控制外设的工作周期,或者是为了实现定时功能。比如,控制LED闪烁、按键消抖、定时读取传感器数据等,往往需要利用延时函数来控制时间间隔。

实现延时函数通常有两种方式:

  1. 基于软件的空循环延时(Software Delay)
  2. 基于硬件定时器的延时(Hardware Timer Delay)

在本篇解读中,我们将聚焦于 软件延时 函数的实现,尤其是基于 空循环 来实现延时的原理。对于要求较高的精度和稳定性的应用,硬件定时器方式将是另一种更精确的方案,但在一些不复杂的任务中,空循环延时方式仍然是广泛应用的解决方案。

1. 延时函数实现的基本思路

延时函数的核心思想是通过执行一定数量的空操作指令(例如 NOP,即 No Operation 指令)来消耗时钟周期,达到延迟的效果。具体来说,通过控制 CPU 执行一个空循环,反复执行一些指令,从而消耗出一定数量的时钟周期,进而实现时间延迟。

延时函数的实现思路:

  1. 时钟频率计算: 单片机的时钟频率(如 16 MHz,72 MHz 等)决定了每个时钟周期的时间。通过知道每个时钟周期的时长,我们可以计算出一个指定延时时间(如 1 毫秒,1 微秒)所需要的循环次数。

  2. 空循环延时: 空循环延时是最简单的一种延时方式,CPU 执行的每一条指令会消耗一定的时钟周期,因此通过执行多次空指令(NOP 或其他无效指令),就可以累积消耗一定的时钟周期,进而控制延时时间。

  3. 控制延时时间: 通过计算系统时钟频率和所需的延时,我们可以通过编写一个循环,控制 NOP 指令的次数,来实现指定时间的延时。

2. 延时函数的工作原理与实现细节

2.1 延时函数的设计:

延时函数通常需要两个参数:

  • Delay_us:以微秒(us)为单位的延时函数。
  • Delay_ms:以毫秒(ms)为单位的延时函数。

在 C 语言中,这些函数的实现原理类似,但精度不同。微秒级的延时通常更精确,而毫秒级延时是通过调用多个微秒级延时函数来实现的。

Delay_us 函数设计:

为了设计 Delay_us(微秒延时),首先需要根据单片机的系统时钟频率来计算每次空循环的时间。假设单片机的时钟频率为 72 MHz,每个时钟周期的时长是:

时钟周期时间=172,000,000≈13.89 纳秒\text{时钟周期时间} = \frac{1}{72,000,000} \approx 13.89 \, \text{纳秒}时钟周期时间=72,000,0001​≈13.89纳秒

如果我们想实现 1 微秒的延时,那么我们需要让 CPU 执行大约 72 次空循环指令(72 * 1)。这样可以消耗大约 1 微秒的时间。

2.2 Delay_ms 函数设计:

对于 Delay_ms(毫秒延时),我们通过多次调用 Delay_us 来实现。每调用一次 Delay_us(1000),就相当于延时了 1 毫秒。因此,通过将 ms 作为参数传递,并递减 ms 值,可以实现所需的毫秒级延时。

3. 延时函数的代码实现

接下来,我们提供一个基于上述原理的具体代码实现:

#include "stm32f10x.h"

// 延时函数(单位:微秒)
void Delay_us(uint32_t us) {
    // 假设系统时钟频率为 72 MHz(72,000,000 Hz)
    // 每个时钟周期的时间 = 1 / 72,000,000 = 13.89 纳秒
    // 为了实现微秒级延时,我们计算出需要多少个循环来实现目标延时。

    uint32_t count = 72 * us / 10;  // 系统时钟为 72 MHz,/10 来弥补编译器优化等因素
    while (count--) {
        __NOP();  // 执行空操作指令,消耗一个时钟周期
    }
}

// 延时函数(单位:毫秒)
void Delay_ms(uint32_t ms) {
    // 通过调用 Delay_us 实现毫秒级延时
    while (ms--) {
        Delay_us(1000);  // 每次延时 1 毫秒(即 1000 微秒)
    }
}

4. 代码解读:

  1. Delay_us 函数解读:

    • Delay_us 函数接收一个参数 us,即希望延迟的微秒数。
    • 根据系统时钟频率,计算每个微秒延时需要执行多少次循环。这里我们假设系统时钟为 72 MHz,所以每个时钟周期为 13.89 纳秒。为了实现精准的微秒延时,我们通过 count = 72 * us / 10 来计算所需的循环次数。
    • __NOP() 是一个空操作指令,它不会进行任何计算,但会消耗一个时钟周期,达到延时效果。
  2. Delay_ms 函数解读:

    • Delay_ms 函数通过调用 Delay_us(1000) 来实现每次延时 1 毫秒。
    • 循环递减 ms,每次调用 Delay_us(1000),这样可以实现总的毫秒级延时。

5. 延时函数的优化与问题

  1. 编译器优化问题:

    • 在编写空循环延时函数时,编译器可能会进行优化,导致延时不准确。为了避免编译器优化掉空循环,我们使用了 volatile 关键字来声明 count 变量。这可以告诉编译器不要优化该变量,从而确保延时函数的正确性。
  2. 时钟频率问题:

    • 系统时钟频率对延时精度有直接影响。如果系统时钟频率较低,延时函数的精度就会降低。反之,时钟频率越高,延时越精确。如果时钟频率过高,空循环的延时会变得非常短,可能会超出计算的精度范围。
  3. 延时精度问题:

    • 空循环延时的精度较低,尤其在高频时钟下,延时误差可能会增加。因此,在一些对延时精度要求较高的应用中,建议使用硬件定时器来替代软件延时。
  4. 优化空循环:

    • 在计算循环次数时,需要考虑编译器对代码的优化,因此通常会稍微增加循环次数(如 /10 的修正)来弥补编译器优化的误差。

6. 延时函数的应用场景

延时函数在单片机开发中广泛应用,以下是一些典型应用场景:

  1. LED 闪烁:

    • 在嵌入式系统中,控制 LED 灯的闪烁是一项常见需求。通过调用延时函数,我们可以定时控制 LED 的开关,实现在固定时间间隔内闪烁的效果。
  2. 按键消抖:

    • 按键输入的扫描通常会受到 抖动 的影响,延时函数常用于 按键消抖。通过延时函数,可以延时一段时间后再重新扫描按键,从而避免重复触发。
  3. 串口通信:

    • 在串口通信中,延时函数用于控制数据的发送和接收。通过控制时间间隔,避免数据丢失或串口冲突。
  4. 传感器轮询:

    • 延时函数常用于 传感器数据轮询。例如每隔一定时间读取一次温湿度传感器的值,或者在给定时间内等待传感器的响应。
  5. 蜂鸣器控制:

    • 蜂鸣器的控制也常依赖延时函数,通过控制蜂鸣器的响铃时长和频率,可以实现报警、提示等功能。

7. 总结与优化建议

延时函数在嵌入式系统中的作用非常重要,虽然通过空循环延时的方式简单易实现,但它也有一些局限性,特别是在高精度要求的场合。为了提高延时函数的精度,建议使用硬件定时器。此外,了解系统时钟频率、编译器优化等因素对于编写准确的延时函数非常重要。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Restaurants WebAPI(一)—— clean architecture

文章目录 项目地址一、Restaurants.Domain 核心业务层1.1 Entities实体层1.2 Repositories 数据操作EF的接口二、Restaurants.Infrastructure 基础设施层2.1 Persistence 数据EF CORE配置2.2 Repositories 数据查询实现2.3 Extensions 服务注册三、Restaurants.Application用例…

全栈开发----Mysql基本配置与使用

本篇是在已下载Mysql的情况下进行的,若还未下载或未创建Mysql服务,请转到这篇: 2024 年 MySQL 8.0.40 安装配置、Workbench汉化教程最简易(保姆级)_mysql8.0.40下载安装教程-CSDN博客 本文对于mysql的操作均使用控制台sql原生代码…

AI可信论坛亮点:合合信息分享视觉内容安全技术前沿

前言 在当今科技迅猛发展的时代,人工智能(AI)技术正以前所未有的速度改变着我们的生活与工作方式。作为AI领域的重要盛会,CSIG青年科学家会议AI可信论坛汇聚了众多青年科学家与业界精英,共同探讨AI技术的最新进展、挑…

逆变器中自举电路设计要点及其取值分析

自举电源具有电路简单,成本低等优点。可以减小变压器尺寸,可以使用较小的磁芯骨架即可满足整机对电源的需求。不过其也有不足之处,比如只能用于小功率设计(驱动器已验证到11KW50A模块的驱动设计),对输出响应…

如何测量分辨率

一、什么是分辨率? 分辨率指的是分清物体细节的能力。分辨率是一个成像系统还原空间频率的能力。一些人只是简单的用分辨率去描述极限分辨率,但是相机在在不同的对比度的情况下还原低,中和高频率的能力,也可以显示全面综合的信息。…

springboot中——Logback介绍

程序中的日志&#xff0c;是用来记录应用程序的运行信息、状态信息、错误信息等。 Logback基本使用 springboot的依赖自动传递了logback的依赖&#xff0c;所以不用再引入依赖 之后在resources文件下创建logback.xml文件&#xff0c;写入 <?xml version"1.0" …

git 删除鉴权缓存及账号信息

在Windows系统下 清除凭证管理器中的Git凭据 按下Win R键&#xff0c;打开“运行”对话框&#xff0c;输入control&#xff0c;然后回车&#xff0c;打开控制面板。在控制面板中找到“用户账户”&#xff0c;然后点击“凭据管理器”。在凭据管理器中&#xff0c;找到“Windows…

Apache Solr RCE(CVE-2017-12629)--vulhub

Apache Solr 远程命令执行漏洞&#xff08;CVE-2017-12629&#xff09; Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发&#xff0c;主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个…

vue预览和下载 pdf、ppt、word、excel文档,文件类型为链接或者base64格式或者文件流,

** 方法1&#xff1a;word、xls、ppt、pdf 这些文件&#xff0c; 如果预览的文件是链接可以直接打开&#xff0c;可用微软官方的预览地址 ** <iframe width"100%" :src"textVisibleURl " id"myFramePPT" style"border: none;backgroun…

Postbot使用教程

1.什么是Postbot&#xff1f; Postbot 是 Postman 中 API 工作流的 AI 助手。您可以让 Postbot 帮助您排查 API 请求问题、编写测试脚本和文档以及理解大型数据集。如果您需要有关使用 Postman 的帮助或不确定下一步该怎么做&#xff0c;也可以向 Postbot 询问。 2.开始使用 …

考试报名管理系统

集中实践报告 ---《项目综合实习1(数据结构)》 题目:考试报名管理系统 系统设计与开发 摘 要 随着教育信息化的不断推进,考试报名管理系统在各类教育机构中的重要性日益凸显。本文旨在设计与实现一个高效、稳定且功能完备的考试报名管理系统,以满足现代教育考试的…

冬日养仓鼠小指南:温暖与陪伴同行

随着冬日的脚步悄然来临&#xff0c;家中可爱的小仓鼠也需要我们给予更多的关怀与呵护。仓鼠虽小&#xff0c;但它们的冬日养护却大有学问&#xff0c;关乎着这些小生命能否健康快乐地度过寒冷季节。 保暖是冬季养仓鼠的首要任务。我们可以为仓鼠的小窝增添一些保暖材料&#…

用C#(.NET8)开发一个NTP(SNTP)服务

完整源码&#xff0c;附工程下载&#xff0c;工程其实也就下面两个代码。 想在不能上网的服务器局域网中部署一个时间服务NTP&#xff0c;当然系统自带该服务&#xff0c;可以开启&#xff0c;本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见&#xff0c;…

[机器学习]XGBoost(3)——确定树的结构

XGBoost的目标函数详见[机器学习]XGBoost&#xff08;2&#xff09;——目标函数&#xff08;公式详解&#xff09; 确定树的结构 之前在关于目标函数的计算中&#xff0c;均假设树的结构是确定的&#xff0c;但实际上&#xff0c;当划分条件不同时&#xff0c;叶子节点包含的…

51c视觉~合集33

我自己的原文哦~ https://blog.51cto.com/whaosoft/12163849 #Robin3D 3D场景的大语言模型&#xff1a;在鲁棒数据训练下的3DLLM新SOTA! 论文地址&#xff1a;https://arxiv.org/abs/2410.00255代码将开源&#xff1a;https://github.com/WeitaiKang/Robin3D 介绍 多模态…

【游戏中orika完成一个Entity的复制及其Entity异步落地的实现】 1.ctrl+shift+a是飞书下的截图 2.落地实现

一、orika工具使用 1)工具类 package com.xinyue.game.utils;import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory;/*** author 王广帅* since 2022/2/8 22:37*/ public class XyBeanCopyUtil {private static MapperFactory mappe…

黑马Redis数据结构学习笔记

Redis数据结构 动态字符串 Intset Dict ZipList QuickList SkipList 类似倍增 RedisObject 五种数据类型 String List Set ZSet Hash

GTID详解

概念和组成 1&#xff0c;全局事务表示&#xff1a;global transaction identifiers 2, GTID和事务一一对应&#xff0c;并且全局唯一 3&#xff0c;一个GTID在一个服务器上只执行一次 4&#xff0c;mysql 5.6.5开始支持 组成 GTID server_uuid:transaction_id 如&#xf…

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法

怎么将pdf中的某一个提取出来&#xff1f;传统上&#xff0c;我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息&#xff0c;但这种方法往往不够精确&#xff0c;且无法保留原文档的排版和格式。此外&#xff0c;很多时候我们需要提取的内容可能涉及多个页面、多个…

RTU 通信模块赋能智慧路灯远程开关管理,点亮智慧城市节能增效

RTU&#xff08;Remote Terminal Unit&#xff09;远端测控单元在智慧路灯远程开关管理系统中主要负责数据通信和开关控制。能够实现对路灯设备的远程监测和控制&#xff0c;将路灯的状态信息&#xff08;如开关状态、故障信息、亮度参数等&#xff09;上传到管理平台&#xff…