STM32-基于汇编来分析延时

2019-06-14来源: eefocus关键字:STM32  汇编  延时

上一篇文章写了一个延时函数,是这样的:

void Delay(uint32_t nCount) 

{

     for(; nCount != 0; nCount--);

}

为了延时1秒,设置了一个值:1600000。

为什么取这样一个值,这是我实测出来的一个值,是通过多次累计闪灯次数,对应电脑时间,计算出来的。

看见这个值之后,我有一个推测:

1.6M=8M/5

我没有使用外部晶振HSE,使用了默认的内部晶振HSI,主频为8M。

所以,可能这个延时函数循环一次所需要的机器周期数就是5!

怎么验证?

可以看下汇编代码来进行分析。


具体操作步骤:

在成功编译程序后,点击工具栏上一个红色的"D",进入调试状态,再把鼠标点到c代码处,右键查看汇编代码,就可以看到所有c代码编译后的汇编代码了。

延时函数的汇编是这样的:


    53:      for(; nCount != 0; nCount--); 

0x08000206 E000      B        0x0800020A

0x08000208 1E40      SUBS     r0,r0,#1

0x0800020A 2800      CMP      r0,#0x00

0x0800020C D1FC      BNE      0x08000208    


可以看到有4条汇编指令:

B,跳转

SUBS,减

CMP,比较

BNE,根据标志跳转


大体理解,就是这样:进入循环后,先跳转去进行比较。比较后,查看比较结果,若不相等,则跳转去执行减操作。

对于循环次数很多的情况,可以忽略第一次的跳转,所以一般情况下的循环一次,就是执行3条指令:

0x08000208 1E40      SUBS     r0,r0,#1

0x0800020A 2800      CMP      r0,#0x00

0x0800020C D1FC      BNE      0x08000208    


3条指令,5个机器周期,能对应上么?

我上网查了一下,大概的结论是:


stm32 属于ARM ,ARM都是精简指令集,大部分的指令(除STM、LDM、BNE等外)都是单周期指令。

对于跳转指令,需要增加两个指令周期。


按这种方式来估算:

SUBS,1

CMP,1

BNE,3

则 1+1+3=5 ,应该是符合的。


但是我心里不太踏实,毕竟没有谁明确说哪个指令是几个周期,这中间还是有部分猜测的。是否有更靠谱的方式呢?


然后又找到一个好方法:

具体步骤参见:

https://blog.csdn.net/qq_41092963/article/details/82759097


大概说下步骤:

进keil的调试模式,单步调试,记录时间。

查看时间:Logic Analyzer窗口

汇编代码:Disassembly窗口

另外,还可以开一个Watch窗口,查看我们关心的变量值。


其样式见下图:

通过几次单步操作,记录下时间,然后我们可以列出一个表来:


  累计(ns) 指令耗时(ns)

初始 247750  

CMP 247875 125

BNE 248250 375

SUBS 248375 125

125ns,一个机器周期,即频率的倒数:1/8M,完全吻合。

这样,就确实验证了之前的猜测:

SUBS,1个机器周期(125ns)

CMP,1个机器周期(125ns)

BNE,3个机器周期(375ns)

这样,我们就可以从微观的循环周期向宏观的时间进行计算了:

循环一次所需机器周期数: 1+1+3=5 。

5个机器周期,耗时就是125ns*5=625ns

循环1.6M次的耗时:625ns*1600000=625*1.6ms=1000ms=1s


如此,心里就踏实了。



如果试试不同的延时函数的写法呢?


写成while循环,结果会怎样呢?

看看汇编,基本上是一样的:


0x080012D8 E000      B        0x080012DC

0x080012DA 1E40      SUBS     r0,r0,#1

   368:         while(TimeDelay > 0){ 

   369:                 TimeDelay--; 

   370:         } 

0x080012DC 2800      CMP      r0,#0x00

0x080012DE D1FC      BNE      0x080012DA


也是这样3个指令:SUBS,CMP,BNE。还是5个机器周期,没有问题。



如果选择不一样的编译方式呢?

之前使用的是o0优化,现在使用o3优化(Options for Target --> C/C++ -->Optimization),看看:

    42: void Delay(uint32_t nCount)  

    43: { 

0x0800048C 4C03      LDR      r4,[pc,#12]  ; @0x0800049C

0x0800048E 4620      MOV      r0,r4

0x08000490 1E40      SUBS     r0,r0,#1

    44:      for(; nCount != 0; nCount--); 

0x08000492 D1FD      BNE      0x08000490        

        

主循环中只有2个指令了:SUBS,BNE。居然只有4个机器周期了!


现在来推测一下,需要循环多少次:

4*125*n=1000,000,000ns

n=2000,000

来放心的验证吧!


对比上面两种循环的差异,就是少了一个CMP指令。

可是,这样也有点奇怪吧,难道,cmp指令,是可有可无的吗?

这里需要理解下BNE指令。


BNE指令,是个条件跳转,即:是“不相等(或不为0)跳转指令”。如果不为0就跳转到后面指定的地址,继续执行。

而“不相等(或不为0)”,是什么不相等,什么不为0?其实它判断的是CPSR中的 Z 标记。

具体关于 Z 标记怎么设置的?那就依赖于上一个执行指令了。


再看上一条指令,两种优化模式下编译的结果不同,分别是CMP与SUBS。


CMP比较指令,用于把一个寄存器的内容和另一个寄存器的内容或一个立即数进行比较,同时更新CPSR中条件标志位的值。

对于减法,本来有一个SUB。而SUBS的差异,就是多了一个S,它的作用就是会根据执行结果来更新CPSR中的 N、Z、C 和 V 标记。


简单总结一下,BNE的判断依赖于Z标志,而CMP与SUBS会影响标志位,具体是如何影响的,我也不想再深入研究了,大体了解到这里,基本能满足我的好奇心了。



再补充一句,就是关于BNE指令的耗时。

前面我们知道,跳转时它的耗时是3个机器周期。后面我又测试了一下,若不跳转,例如是最后一次比较,结果恰好为0,则顺序往下执行,此时,BNE指令的耗时是1个机器周期。


可见,汇编里面的内容是很深奥的,所幸,我不需要花太多时间去研究,在此,只是大体了解下,解解几个小疑惑即可。



关键字:STM32  汇编  延时

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic464655.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32-仿真调试时的SystemInit陷阱
下一篇:STM32-点灯程序

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

第38章 I2S—音频播放与录音输入—零死角玩转STM32-F429系列

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》及《I2S BUS》。若对I2S通讯协议不了解,可先阅读《I2S BUS》文档的内容学习。关于音频编译码器WM8978,请参考其规格书《WM8978_v4.5》来了解。38.1 I2S简介Inter-IC Sount Bus(I2S)是飞利浦半导体公司(现为恩智浦半导体公司)针对数字音频设备之间的音频数据传输而制定的一种总线标准。在飞利浦公司的I2S标准中,既规定了硬件接口规范,也规定了数字音频数据的格式。38.1.1 数字音频技术现实生活中的声音是通过一定介质传播
发表于 2019-09-19
第38章 I2S—音频播放与录音输入—零死角玩转STM32-F429系列

第50章 读写内部FLASH—零死角玩转STM32-F429系列

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。50.1 STM32的内部FLASH简介在STM32芯片内部有一个FLASH存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中,由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行,见图 501。图 501 STM32的内部框架图除了使用外部的工具(如下载器)读写内部FLASH外,STM32芯片在运行的时候,也能对自身的内部FLASH进行读写
发表于 2019-09-19
第50章 读写内部FLASH—零死角玩转STM32-F429系列

第49章 在SRAM中调试代码—零死角玩转STM32-F429系列

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、《Cortex-M3权威指南》、《Cortex-M4 Technical Reference Manual》(跟M3大部分是相同的,读英文不习惯可先参考《Cortex-M3权威指南》)。学习本章时,配合《STM32F4xx 中文参考手册》"存储器和总线结构"及"嵌入式FLASH接口"章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。49.1 在RAM中调试代码一般情况下,我们在MDK中编写工程应用后,调试时都是把程序下载到芯片的内部FLASH运行测试的,代码的CODE及RW-data的内容被写入
发表于 2019-09-19
第49章 在SRAM中调试代码—零死角玩转STM32-F429系列

第48章 MDK的编译过程及文件类型全解—零死角玩转STM32-F429

本章参考资料:MDK的帮助手册《ARM Development Tools》,点击MDK界面的"help->uVision Help"菜单可打开该文件。关于ELF文件格式,参考配套资料里的《ELF文件格式》文件。在本章中讲解了非常多的文件类型,学习时请跟着教程的节奏,打开实际工程中的文件来了解。相信您已经非常熟练地使用MDK创建应用程序了,平时使用MDK编写源代码,然后编译生成机器码,再把机器码下载到STM32芯片上运行,但是这个编译、下载的过程MDK究竟做了什么工作?它编译后生成的各种文件又有什么作用?本章节将对这些过程进行讲解,了解编译及下载过程有助于理解芯片的工作原理,这些知识对制作IAP
发表于 2019-09-19
第48章 MDK的编译过程及文件类型全解—零死角玩转STM32-F429

第47章 QR-Decoder-OV5640二维码识别—零死角玩转STM32-F429系列

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。关于开发板配套的OV5640摄像头参数可查阅《ov5640datasheet》配套资料获知。STM32F4芯片具有浮点运算单元,适合对图像信息使用DSP进行基本的图像处理,其处理速度比传统的8、16位机快得多,而且它还具有与摄像头通讯的专用DCMI接口,所以使用它驱动摄像头采集图像信息并进行基本的加工处理非常适合。本章讲解如何使用二维码识别库进行二维码的识别。47.1 二维码简介二维码,又称二维条码或二维条形码,二维条码是用某种特定的几何图形按一定规律在平面(二维方向
发表于 2019-09-19
第47章 QR-Decoder-OV5640二维码识别—零死角玩转STM32-F429系列

第44章 MPU6050传感器—姿态检测—零死角玩转STM32-F429系列

本章参考数据:《STM32F4xx参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。关于MPU6050的参考资料:《MPU-60X0寄存器》、《MPU6050数据手册》以及官方驱动《motion_driver_6.12》。本章讲解的内容跨领域的知识较多,若您感兴趣,请自行查阅各方面的资料,对比学习。44.1 姿态检测1.    基本认识在飞行器中,飞行姿态是非常重要的参数,见图 441,以飞机自身的中心建立坐标系,当飞机绕坐标轴旋转的时候,会分别影响偏航角、横滚角及俯仰角。图 441 表示飞机姿态的偏航角、横滚角及俯仰角假如我们知道飞机
发表于 2019-09-19
第44章 MPU6050传感器—姿态检测—零死角玩转STM32-F429系列

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved