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

news2024/12/22 10:30:03

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

在嵌入式开发中,延时函数是一个非常常见的需求。在很多情况下,程序需要暂停执行一定时间,通常是为了控制外设的工作周期,或者是为了实现定时功能。比如,控制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. 总结与优化建议

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

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

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

相关文章

uniapp入门 01创建项目模版

0安装 hbuilder x 标准版 1.创建模版工程 2.创建官方 案例工程 index.uvuewen 文件解析 <!-- 模版 标签 --> <template><view></view></template><!-- 脚本 --> <script>export default {data() {return {}},onLoad() {},methods:…

kubeadm安装K8s高可用集群之集群初始化及master/node节点加入calico网络插件安装

系列文章目录 1.kubeadm安装K8s高可用集群之基础环境配置 2.kubeadm安装K8s集群之高可用组件keepalivednginx及kubeadm部署 3.kubeadm安装K8s高可用集群之集群初始化及master/node节点加入集群calico网络插件安装 kubeadm安装K8s高可用集群之集群初始化及master/node节点加入ca…

【NLP】序列到序列(seq2seq)建模工具fairseq使用详解

文章目录 一、fairseq简介二、安装方式2.1 pip安装2.2 源码安装 三、fairseq命令工具3.1 fairseq-preprocess3.2 fairseq-train3.3 fairseq-generate3.4 fairseq-interactivate3.5 fairseq-score3.6 fairseq-eval-lm 4. 常见报错报错1 参考资料 一、fairseq简介 fairseq 是 Fa…

28、论文阅读:基于像素分布重映射和多先验Retinex变分模型的水下图像增强

A Pixel Distribution Remapping and Multi-Prior Retinex Variational Model for Underwater Image Enhancement 摘要介绍相关工作基于模型的水下图像增强方法&#xff1a;无模型水下图像增强方法&#xff1a;基于深度学习的水下图像增强方法&#xff1a; 论文方法概述像素分布…

ArkTs组件的学习

一. AlphabetIndexer 可以与容器组件联动用于按逻辑结构快速定位容器显示区域的组件 参数名类型必填说明arrayValueArray<string>是字母索引字符串数组&#xff0c;不可设置为空selectednumber是初始选中项索引值若超出索引值范围则取默认值0 class Lxr{tImg:Resource…

Python读取Excel批量写入到PPT生成词卡

一、问题的提出 有网友想把Excel表中的三列数据&#xff0c;分别是&#xff1a;单词、音标和释义分别写入到PPT当中&#xff0c;每一张PPT写一个单词的内容。这种批量操作是python的强项&#xff0c;尤其是在办公领域&#xff0c;它能较好地解放双手&#xff0c;读取Excel表后…

百度面试手撕 go context channel部分学习

题目 手撕 对无序的切片查询指定数 使用context进行子协程的销毁 并且进行超时处理。 全局变量定义 var (startLoc int64(0) // --- 未处理切片数据起始位置endLoc int64(0) // --- 切片数据右边界 避免越界offset int64(0) // --- 根据切片和协程数量 在主线程 动态设…

Otsu 二值化算法:原理、实现与应用

摘要&#xff1a; 本文深入探讨了 Otsu 二值化算法&#xff0c;详细阐述其原理&#xff0c;包括类间方差的计算与阈值确定机制。分别给出了该算法在 C#、Python 和 C 中的实现代码示例&#xff0c;并对代码进行了详细注释与分析。此外&#xff0c;还探讨了 Otsu 二值化算法在图…

uniApp使用腾讯地图提示未添加maps模块

uniApp使用腾讯地图&#xff0c;打包提示未添加maps模块解决方案 这是报错信息&#xff0c;在标准基座运行的时候是没问题的&#xff0c;但是打包后会提示未添加&#xff0c;可以通过在mainfest里面把地图插件上腾讯地图的key更换高德地图的key&#xff0c;定位服务可以继续用腾…

Deepin/Linux clash TUN模式不起作用,因网关导致的问题的解决方案。

网关导致的问题的解决方案 查看路由 ip route寻找默认路由 默认路由应当为Mihomo default dev Mihomo scope link 如果不是&#xff0c;则 sudo ip route add default dev Mihomo在clash TUN开关状态发生变化时&#xff0c;Mihomo网卡会消失&#xff0c;所以提示找不到网卡…

scala中正则表达式的使用

正则表达式&#xff1a; 基本概念 在 Scala 中&#xff0c;正则表达式是用于处理文本模式匹配的强大工具。它通过java.util.regex.Pattern和java.util.regex.Matcher这两个 Java 类来实现&#xff08;因为 Scala 运行在 Java 虚拟机上&#xff0c;可以无缝使用 Java 类库&…

apache应用(客户机地址限制、用户授权限制、日志分割、AWStats日志分析)

目录 一、 客户机地址限制 二、 用户授权限制 三、 日志分割 使用rotatelogs分割工具 使用第三方工具cronolog 四、 AWStats日志分析 具体的apache软件安装可以阅读我之前的文章apache安装https://blog.csdn.net/m0_68472908/article/details/139348739?spm1001.2014.300…

护士资格实践题库(含解析)

1.患者女&#xff0c;30岁。诊断类风湿关节炎入院&#xff0c;经使用药物治疗后患者关节疼痛减轻&#xff0c;但出现体重增加、满月脸、向心性肥胖。提示存在何种药物的副作用&#xff08; &#xff09; A.泼尼松 B.环磷酰胺 C.硫唑嘌呤 D.吲哚美辛 E.阿司匹林 【答案】…

网络安全概论——防火墙原理与设计

一、防火墙概述 防火墙是一种装置&#xff0c;它是由软件/硬件设备组合而成&#xff0c;通常处于企业的内部局域网与 Internet 之间&#xff0c;限制 Internet 用户对内部网络的访问以及管理内部用户访问 Internet 的权限。换言之&#xff0c;一个防火墙在一个被认为是安全和可…

接口测试-Fidder及jmeter使用

一、接口测试的基础 1.接口的含义 也叫做API&#xff0c;是一组定义、程序及协议的集合&#xff0c;提供访问一组例程的能力&#xff0c;无需访问源码获理解内部工作细节 2.接口的分类 代码内部的接口&#xff0c;程序模块间的接口&#xff0c;对于程序接口测试&#xff0c;需…

postman设置cookie

postman发送请求的时候&#xff0c;如何顺带cookie? 示例&#xff1a; Cookie: locale_areazh; contryzh_cn;

Java-31 深入浅出 Spring - IoC 基础 启动IoC XML与注解结合的方式 配置改造 applicationContext.xml

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

jmeter连接mysql

查询mysql数据库版本 SELECT VERSION(); 下载jmeter mysql 驱动jar包&#xff0c;版本低于mysql版本&#xff0c;放在jmeter的lib 路径下 MySQL :: Download MySQL Connector/J (Archived Versions) 添加JDBC Connection Configuration 填写 variable name 及数据库信息 注意…

计算机网络基础(2):网络安全/ 网络通信介质

1. 网络安全威胁 网络安全&#xff1a;目的就是要让网络入侵者进不了网络系统&#xff0c;及时强行攻入网络&#xff0c;也拿不走信息&#xff0c;改不了数据&#xff0c;看不懂信息。 事发后能审查追踪到破坏者&#xff0c;让破坏者跑不掉。 网络威胁来自多方面&#xff1a…

数据分析实战—IMDB电影数据分析

1.实战内容 1.加载数据到movies_df&#xff0c;输出前5行&#xff0c;输出movies_df.info(),movies_df.describe() # &#xff08;1&#xff09;加载数据集&#xff0c;输出前5行 #导入库 import pandas as pd import numpy as np import matplotlib import matplotlib.pyplo…