forked from Acoucou/hugo-theme-stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
410 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
+++ | ||
author = "coucou" | ||
title = "51单片机的延时计算" | ||
date = "2022-09-26" | ||
description = "51单片机的延时计算" | ||
tags = [ | ||
"单片机" | ||
] | ||
|
||
+++ | ||
|
||
## 51单片机的延时计算 | ||
前言 | ||
正文 | ||
首先是时钟周期的算法:时钟周期(T)=1(秒)/晶振频率。 | ||
其次是机器周期:机器周期是由时钟周期组成的,机器周期是单片机完成一个基本操作所需要的时间。 | ||
1 传统的8051单片机: | ||
2 STC单片机: | ||
最后是指令周期:这个是单片机执行一条指令所需要的时间,它是由机器周期组成的。 | ||
总结 | ||
前言 | ||
我使用51,STC这一类的单片机做控制好几年,一直是使用现成的程序,在其上修修改改,以达到需求动作目的即可。从来都是不求甚解。想法既是如此,会用即可,了解那么多做什么。 | ||
此次又在做一项目,里面用到I2C通讯。本来是直接复制粘贴了事,却没想对里面的一个小小的延时函数起了兴趣,由于本人是基础功底只有5战斗力的渣渣,写写画画了一天才搞了个大体明白。 | ||
以前总是在看其他博主的文章,默默潜水。然而此次,突然就忍不住想写篇文章发表一下费尽心思的微不足道的心得体会。 | ||
|
||
正文 | ||
void Delay10us() //@12.000MHz | ||
{ | ||
unsigned char i; | ||
|
||
_nop_(); | ||
_nop_(); | ||
i = 27; | ||
while (--i); | ||
} | ||
上面这段代码是用STC-ISP软件中的软件延时计算器给出的,选用的是8051指令集STC-Y5,延时10us。 | ||
以前都是直接这么拿来用的,今天却突然想搞个明白,为什么代码要这么写。 | ||
|
||
于是查了各方资料。 | ||
从单片机计时的源头找起,它由下面几部分依次组成。 | ||
|
||
首先是时钟周期的算法:时钟周期(T)=1(秒)/晶振频率。 | ||
(比如:上面代码的时钟周期为1/12M(秒))。 | ||
这是单片机的基本时间单位。是由晶振震荡出来的,也叫震荡周期。 | ||
|
||
其次是机器周期:机器周期是由时钟周期组成的,机器周期是单片机完成一个基本操作所需要的时间。 | ||
关于机器周期,每种单片机可能都不太一样,我也只用过传统51和STC这两款,就拿此来对比下 | ||
|
||
1 传统的8051单片机: | ||
它的1个机器周期是由12个时钟周期组成的。 | ||
以12M晶振举例,它的一个机器周期就是:12(个时钟周期)*1(秒)/12MHz = 1(us) | ||
|
||
2 STC单片机: | ||
拿我常用的STC12C5A60S2这款单片机来讲,它可以有两个模式选择, | ||
一个是1T模式,在这个模式下STC单片机1个时钟周期就是1个机器周期; | ||
另一个是12T模式,这个模式下STC单片就和传统的8051单片机一样,12个时钟周期组成1个机器周期。 | ||
由此可见1T模式的速度就是12T模式的12倍。 | ||
以12M晶振为例,1T模式下就可以算得机器周期是: | ||
1(个时钟周期)*1(秒)/12Mhz = 1/12(us) | ||
|
||
最后是指令周期:这个是单片机执行一条指令所需要的时间,它是由机器周期组成的。 | ||
现在可以回到正文开头的代码中了。这个10us的函数是怎么得出来的呢? | ||
这个我之前查过很多资料,比如执行while语句需要多少个机器周期。赋值需要多少个周期。也就是查这个占用了我很大一部分时间。直到最后将上面的延时函数直接调到main函数中debug调试,才明白,问题其实很简单啊。 | ||
无论是执行什么语句,最终都会回到汇编上来,debug里单步调试,所有的指令周期就会明明白白了。 | ||
我用main函数直接调用延时函数,如下: | ||
|
||
void Delay10us() //@12.000MHz | ||
{ | ||
unsigned char i; | ||
|
||
_nop_(); | ||
_nop_(); | ||
i = 27; | ||
while (--i); | ||
} | ||
main | ||
{ | ||
Delay10us(); | ||
} | ||
|
||
我用的keil软件,将上述build之后,点击debug,开始调试 | ||
|
||
看图片上,开始debug,程序的起始就在C:0x0183 020171 LJMP Delay10us(C:0171), | ||
这里有个长转移指令LJMP,它要转移到C:0171行去执行Delay10us这个函数。 | ||
那执行LJMP这个指令需要多长时间呢,查找STC数据手册,在1T模式下,此条指令在单片机上运行需要4个时钟周期。 | ||
接下来,按单步调试F11键,如下图: | ||
|
||
程序成功转移到C:0171行,跳转到Delay10us函数中,此行程序执行NOP指令,空操作。查STC数据手册,NOP指令占用1个时钟周期。 | ||
接下来C:0172行,依然是NOP指令,1个时钟周期。 | ||
接下来C:0173行,此行执行 MOV R7,#0x1B,将立即数送入寄存器。是将27赋值给i。依然查手册,此条指令2个时钟周期。 | ||
继续: | ||
|
||
此时执行到while语句了,这里执行的指令时 DJNZ R7,C:0175,寄存器减1非0转移。此条指令执行1次4个时钟周期。 | ||
上面已经将寄存器填入27了,因此这条指令将执行27次。 | ||
继续: | ||
|
||
循环了27次,终于到0了,程序继续向下执行,此行指令RET,子程序返回。此条指令4个时钟周期。 | ||
继续: | ||
|
||
程序又回到了起点。 | ||
好了,可以计算一下此次延时的时间了。1个LJMP,4时钟;2个NOP,2时钟;1个MOV,2时钟;27个DJNZ,108时钟;1个RET,4时钟。 | ||
4+2+2+108+4=120。 | ||
单片机的时钟周期是:1(S)/12MHz = 1/12(us) | ||
此次延时的时间是:120 × 1/12(us)= 10(us) | ||
|
||
总结 | ||
其实并没有绝对的准确延时,上面只是理想化的状态,单片机的中断或者其他事件都可能影响到延时的。 | ||
另外,同样的STC单片机,同样的延时10us,同样的1T,官方给出的STC12系列和STC15系列的延时函数就不一样,STC12系列在延时函数内部要少两个NOP指令。debug对比,也是少量NOP,其他都一样。按照12系列和15系列的手册描述,他们的指令周期是相同的。那么他们的差别在哪?单片机内部结构的问题还是官方错误造成的误差? |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.