单片机中的 _nop_() 延时以及其相关的基础扩展

使用 _nop_() 函数做延时遇到的一些问题 以及对此延伸出的一些需要了解的基本概念 ...... by  矜辰所致
完善文章内容结构,补充指令周期、机器周期等一些基本概念             2023/10/25

前言

最近还是继续做着项目,因为在某 8051 内核芯片上使用到了 I2C 通讯,又需要 _nop_() 函数来实现 us 延时,那么正好来写一篇由_nop_() 函数引起的一系列基本概念 。

本文的内容包括:对 nop 的认识,单片机中的 nop 函数做延时的一些注意事项,以及单片机中基本的指令周期,机器周期等一些基本概念的说明。

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • 一、 NOP 指令
    • 1.1 NOP 指令的作用
  • 二、单片机中的 `_nop_()` 函数
    • 2.1 C语言中的 NOP
    • 2.2 _nop_ 函数消耗的时间
  • 三、用 `_nop_()` 延时的注意事项
    • 3.1 函数调用对延时的影响
    • 3.2 调用函数中的语句对延时的影响
      • 3.2.1 单片机执行一条指令所需要的时间
  • 四、指令周期、机器周期、时钟周期
  • 结语

一、 NOP 指令

_nop_() 函数产生的是 NOP 指令,先来简单介绍一下 NOP 指令,基本介绍走个流程把:

NOP 是编程语言中一个经常用到的指令,它的全称是 No Operation,即无操作指令。

NOP 是汇编语言中的一个伪指令,通过NOP一系列的编程语句,能够不改变任何程序可以访问的寄存器。

1.1 NOP 指令的作用

  1. 我们知道,指令、数据对齐可以有效地提高程序的性能, 使用 NOP 指令,可以使得指令按字对齐,从而提高效率 。
    比如一条指令占用 3 个字节,再加上一个 NOP 指令,就使得指令 4 字节对齐了。

  2. 通过 NOP 指令产生一定的延迟,这与 CPU 的频率有关系,适用于一些频率低的 单片机 场合。

  3. 计算机在输入或者输出的过程中,使用 NOP 指令可可以很好的等待计算机缓冲区清空,等待总线恢复正常,其实也算是延时的一种了

二、单片机中的 _nop_() 函数

2.1 C语言中的 NOP

如果使用汇编语言,我们可以直接使用 NOP 指令的 ,直接写一个 nop 就可以,比如下面示例:

        .text                   ; 代码段开始
        .syntax unified         ; 

start:
        mov r0, #0x55          ; 将0x55存储在寄存器R0中
        nop                    ; 插入NOP指令
        mov r1, #0xAA          ; 将0xAA存储在寄存器R1中

        add r2, r0, r1         ; 将R0和R1相加并将结果存储在R2中

但是我们在单片机中编程,现在都是使用 C 语言,对于 C 语言本身来说,是没有空语句的。

但是我们在做51单片机的开发中,在库文件中提供了一个void _nop_(void);函数,这个函数声明一般在 intrins.h 头文件当中,我们只需要 #include <intrins.h> 就可以使用 _nop_(); 函数了。

比如:
在这里插入图片描述

我们已经知道了 nop 是空语句,什么都不做,但是在这里我们还是得明确的知道 一个 _nop_()表示空循环一个机器指令的时间。

2.2 nop 函数消耗的时间

那么在我们的单片机中,一个 nop 的时间是多少呢?、

上面说到,一个 nop 表示一个机器周期,那么一个机器周期是多少?

机器周期当然与主频有关,在单片机中指的就是晶振的频率。

首先基本的东西还是要知道的 一个机器周期包含12个晶振周期。 所以我们可以通过下面的计算得知 nop 函数消耗的时间:

假设单片机 12M 晶振,晶振周期1/12微秒,一个机器周期包含12个晶振周期,所以12M晶振时机器周期 = 12x(1/12)us = 1us 。
.
所以12M 晶振中一个 nop 表示延时1us;
6M 晶振中延时2us,24M 晶振中延时 0.5 us

至于其他的晶振频率,我们可以按照上面的计算代入即可。

对于 _nop_() 函数 其实在我以前的文章 BH1750 传感器实战教学 —— 驱动移植篇 中有过说明:

在这里插入图片描述

三、用 _nop_() 延时的注意事项

到此,我们已经可以知道在我们的程序中,一个 nop 函数执行所需要时间,我们可以利用多个 _nop_() 函数来实现一些 us 级别的延时。

比如我以前一些帖子里面提到的在 51 上面的 I2C 通讯:

在这里插入图片描述

在上图中,就是一个简单的 I2C 其实信号的实现方式, 在上图中,有说明 多几个 nop 少几个 nop 无所谓,实际上现在看来是有问题的,这让我付出了代价,这一点我后面会在写某个传感器测试博文的时候会提到。

3.1 函数调用对延时的影响

那么本文这里要说明的是一些使用时候的问题,依然是我以前文中提到的,在 STM32 HAL 库中没有 us 延时,所以我一直用的是:

void delay_us(uint32_t Delay)
{
	uint32_t cnt = Delay * 8;   // 32Mhz ,其他频率其他倍数
	uint32_t i = 0;
	for(i = 0; i < cnt; i++)__NOP();
}

于是乎,对于本次使用的 16MHZ 晶振的 51 芯片,我改成了如下:

void delay_us(uint32 Delay)
{
  uint32 cnt = Delay * 4;   // 32Mhz 8 ,其他频率其他倍数  	  16Mhz慢一点  4
  uint32 i = 0;
  for(i = 0; i < cnt; i++)_nop_();
}

然后自然的把上面的 I2C_Start 改成如下:

void I2C_Start1(void)
{
   sda_high();
   delay_us(5);
   scl_high();
   delay_us(10);
   sda_low();
   delay_us(10);
   scl_low(); //使SCL置低,准备发送或者接受数据
   delay_us(10);
}

反正改完以后传感器通讯是不正确的,于是乎最后上了示波器,惊讶的发现,在我使用的 51 上面采用上面的方式的波形图如下(注意看波形的时间):

在这里插入图片描述

是不是很意外,时间周期居然可以达到 ms 级别,就是使用一个一个循环调用 nop 的函数……,我一个 I2C 传感器的初始化工作,居然持续了好几秒时间……

而在 STM32 平台下面,我观察到的波形图如下(us级别算是正常的):

在这里插入图片描述

虽然知道调用函数会占用时间,但是上面的情况也太离谱了点,即便我最后把循环里面的 *4 都直接删除,波形周期还是 ms 级别。

这…… 真的是有点太离谱了,一个简单的 nop 延时函数在实际上会有这么久的延时……

反正最后我还是去掉了函数,采用直接使用很多个 nop 函数直接写的方式,如下图的上面部分:

在这里插入图片描述

实际上,除了调用函数,在函数中的使用什么语法也决定了这个函数执行的时间长短,这个问题对于我们现在大家常用的 ARM 内核来说,可能都不太容易发现,或者影响没那么大,但是对于老一点的 51 内核,影响就大了,但是大到上面这种程度,也是我没想到的。

3.2 调用函数中的语句对延时的影响

那说到除了调用函数,函数中的语句是如何影响时间的呢,这因为在C51编译器中,对不同的循环方法,采用不同的指令来完成的,对于不同的指令,单片机执行所需要的的时间也是不一样的。

3.2.1 单片机执行一条指令所需要的时间

完成一条指令需要的时间,也就是指令周期。

指令周期就是 单片机 取出一条指令并执行这条指令所需要的时间。指令周期,是从取指令、分析指令到执行完所需的全部时间。

指令周期一般由若干个机器周期组成(我们上面讲过,一个 _nop_() 就是一个机器周期),他是以机器周期为单位的!!!

其实通过我们前文的介绍,我们已经知道如何算一个 单片机的 机器周期(一个 nop 的时间,他是由 12 个时钟周期组成的),我们只需要知道这条指令是由几个机器周期组成的就可以,这一点会在单片机的使用手册中有说明,比如下图:

在这里插入图片描述

上图中上面一些指令需要 12 个时钟周期,就是一个机器周期,最后一个需要 2个 机器周期。

大家都能看到其实后面有一个 6T 模式的说明,很容易理解,就是1机器周期等于 6 个时钟周期的模式,这样会使得单片机执行效率提升 2 倍 ,现代单片机有许多都有这种高效率模式。


了解完了指令周期,那我们是不是很容易的就明白了,在函数中为什么不同的语句会对延时产生不同影响了。

这里呢,我就不对不同的语句进行单独的分析了,大家有时间可以自己生成汇编文件自己研究,这里我就从网上截取了部分说明:

在选择C51中循环语句时,要注意以下几个问题
.
第一、定义的 C51 中循环变量,尽量采用无符号字符型变量。
.
第二、在 for 循环语句中,尽量采用变量减减来做循环。
.
第三、在do…while,while语句中,循环体内变量也采用减减方法。

我们要知道的是,上面的做法都是为了减少额外的时间开销,使得我们想要的延时时间更加准确。

四、指令周期、机器周期、时钟周期

在文章上面部分由反复的提到过几个概念:指令周期,机器周期,时钟周期。

为了防止有的小伙伴还是迷迷糊糊的,这里来简单的总结一下(以 8051 单片机为例):

时钟周期 = 1/ 晶振频率
单片机的心跳,基本时间单位
.
机器周期 = 时钟周期 * 12
单片机的基本操作周期,一个机器周期,单片机完成一项基本操作,如取指令,读/写存储器
.
指令周期 :
CPU 执行一条指令所需要的时间, 以机器周期为单位。
指令周期所需要的的机器周期,可以通过单片机使用手册中的指令表查询得到。

当然,其实与上面这些概念相关的还有一个状态周期,他等于 2个 时钟周期,这里也提一下。

结语

本文通过一个简单的 _nop_() 函数,我们探讨了在单片机中实现 us 延时的一些时间问题以及注意事项,进而引出了一些时间周期的基本概念,相信能让大家在日后使用到的时候能够更好的理解与计算自己所需要的延时时间。

好了本文就到这里,谢谢大家,赶在 10.24.发,仓促结尾! 不好意思!

文章已经完善更新,谢谢大家! … by 矜辰所致

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

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

相关文章

【MySQL】用户与权限管理

文章目录 一、用户管理1、用户信息表2、创建用户3、删除用户4、修改用户密码 二、权限管理1、MySQL 权限2、给用户授权3、回收用户权限 一、用户管理 之前为了方便&#xff0c;我们学习 MySQL 时统一使用的都是 root 账号进行登录&#xff0c;但在实际的开发场景中必然是需要进…

opencalib中lidar2camera安装记录

目录 一、opencalib安装 二、lidar2camera的安装 三、测试运行 四、出现过的问题 一、opencalib安装 代码地址&#xff1a;https://github.com/PJLab-ADG/SensorsCalibration/blob/master/README.md # pull docker image sudo docker pull scllovewkf/opencalib:v1 # Aft…

RISC-V架构——中断委托和中断注入

1、中断委托 1.1、中断委托的作用 &#xff08;1&#xff09;默认情况下&#xff0c;所有的陷入&#xff08;中断和异常&#xff09;都是在M模式下处理&#xff0c;然后再返回到发生陷入前的模式&#xff1b; &#xff08;2&#xff09;所有陷入都在M模式处理会涉及到模式切换…

1个月5次发版:测试人的模块测试策略分类归纳

笔者所在项目经历了一个月开发周期&#xff0c;该项目有5名开发人员&#xff0c;1名项目经理&#xff0c;1名测试人员&#xff0c;涵盖OA系统8个模块&#xff0c;在短短1个月中进行了5次发布。 现进行模块测试策略分类归纳。 已有模块 配置项优化 对于已有模块的配置项优化&…

【JavaEE】线程安全的集合类 -- 多线程篇(9)

线程安全的集合类 多线程环境使用 ArrayList多线程环境使用队列多线程环境使用哈希表 多线程环境使用 ArrayList 自己使用同步机制 (synchronized 或者 ReentrantLock)Collections.synchronizedList(new ArrayList); synchronizedList 是标准库提供的一个基于 synchronized 进…

【深度学习 | Transformer】释放注意力的力量:探索深度学习中的 变形金刚,一文带你读通各个模块 —— 总结篇(三)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

基本指令(2):通配符,重定向,命令行管道

一、通配符 rm -rf ./* # * —— 通配符&#xff0c;指定路径下的所有文件&#xff08;不包括隐藏文件&#xff09;二、重定向 在理解重定向前&#xff0c;先要有一个概念&#xff1a;Linux下一切皆文件&#xff0c;大部分硬件设备都可以看做有读写方法&#xff0c;只不过有些方…

国腾GM8775C完全替代CS5518 MIPIDSI转2 PORT LVDS

集睿致远CS5518描述&#xff1a; CS5518是一款MIPI DSI输入、LVDS输出转换芯片。MIPI DSI 支持多达4个局域网&#xff0c;每条通道以最 大 1Gbps 的速度运行。LVDS支持18位或24位像素&#xff0c;25Mhz至154Mhz&#xff0c;采用VESA或JEIDA格 式。它只能使用单个1.8v电源&am…

化工园区数字孪生可视化管控平台,赋予园区安全环保智慧发展

化工行业作为国民经济的支柱和工业发展的引擎&#xff0c;对安全生产、环保节能、应急管控有着很高的要求。目前国内外化工园区面临安全和环保两大压力。为有效解决这两大难题&#xff0c;巨蟹数科综合运用物联网、数字孪生等新一代信息技术&#xff0c;建设了数字孪生园区智慧…

Echarts多曲线数值与Y周刻度不符合

发现问题&#xff1a; 在展示多曲线图表的时候&#xff0c;发现图表曲线数值相差不大&#xff0c;但是图表展示的曲线相差很大&#xff0c;仔细观察之后发现是展示有问题(其实这并不能算是错误&#xff0c;只是由于忽略&#xff0c;导致的配置与预期不符合)。 问题复现&#x…

【分布式】大模型分布式训练入门与实践 - 04

大模型分布式训练 数据并行-Distributed Data Parallel1.1 背景1.2 PyTorch DDP1&#xff09; DDP训练流程2&#xff09;DistributedSampler3&#xff09;DataLoader: Parallelizing data loading4&#xff09;Data-parallel&#xff08;DP&#xff09;5&#xff09;DDP原理解析…

CrossOver 23.6 让Mac可以运行Windows程序的工具

在当今数字化时代&#xff0c;虚拟机技术被广泛应用于软件开发、系统测试、网络安全等领域。虚拟机提供了一个隔离的虚拟环境&#xff0c;使得我们能够在一台物理计算机上同时运行多个操作系统和应用程序。下面我们就来看虚拟机软件怎么安装&#xff0c;虚拟机怎么使用吧&#…

【Linux】TCP协议

文章目录 &#x1f4d6; 前言1. TCP协议格式2. 确认应答机制3. 16位窗口大小4. 6个标记位4.1 URG紧急指针标记位&#xff1a; 5. 超时重传机制&#xff1a;6. 连接管理机制6.1 TCP三次握手&#xff08;重点&#xff09;&#xff1a;6.1 - 1 三次握手的原因6.1 - 2 RST复位标志位…

javaEE -6(10000详解文件操作)

一&#xff1a;认识文件 我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备&#xff0c;当我们想要进行数据保存时&#xff0c;往往不是保存成一个整体&#xff0c;而是独立成一个个的单位进行保存&#xff0c;这个独立的单位就被抽象成文件的概念&#xff0c…

机关事务管理局数字化平台,让数据纵向直报,业务横向打通

机关事务管理局的核心职能是实现对机关事务的管理、保障、服务&#xff0c;是面向政府机关部门的“后勤服务”部门。 主要职能有&#xff1a;推进国有资产管理、公务用车管理、办公用房管理、公共机构节能管理、后勤管理等。党和政府“过紧日子”的要求为机关事务工作提出了更…

光影之梦2:动画渲染前后对比,揭示视觉艺术的惊人转变!

动画渲染是影视艺术中不可或缺的一环&#xff0c;它赋予了角色和场景鲜活的生命。渲染过程中的光影、色彩、材质等元素&#xff0c;像是画家的调色板&#xff0c;将平淡无奇的线条和形状转化为充满韵味与情感的画面。动画角色仿佛拥有了自己的灵魂&#xff0c;无论是一颦一笑&a…

集成学习方法之随机森林-入门

1、 什么是集成学习方法 集成学习通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器/模型&#xff0c;各自独立地学习和作出预测。这些预测最后结合成组合预测&#xff0c;因此优于任何一个单分类的做出预测。 2、 什么是随机森林 在机器学习中&…

Parallels Client for Mac:改变您远程控制体验的革命性软件

在当今数字化的世界中&#xff0c;远程控制软件已经成为我们日常生活和工作中不可或缺的一部分。在众多远程控制软件中&#xff0c;Parallels Client for Mac以其独特的功能和出色的性能脱颖而出&#xff0c;让远程控制变得更加简单、高效和灵活。 Parallels Client for Mac是…

Python 面向对象编程:类、对象、初始化和方法详解

Python 是一种面向对象的编程语言。在 Python 中&#xff0c;几乎所有东西都是对象&#xff0c;都具有其属性和方法。 类似于对象构造函数或用于创建对象的“蓝图”的类。 创建一个类 要创建一个类&#xff0c;请使用关键字 class&#xff1a; 示例&#xff0c;创建一个名为…

深度学习——图像分类(CIFAR-10)

深度学习——图像分类&#xff08;CIFAR-10&#xff09; 文章目录 前言一、实现图像分类1.1. 获取并组织数据集1.2. 划分训练集、验证集1.3. 图像增广1.4. 引入数据集1.5. 定义模型1.6. 定义训练函数1.7. 训练模型并保存模型参数 二、生成一个桌面小程序2.1. 使用QT设计师设计界…