你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

51 的精确延时函数

[复制链接]
gaosmile 发布时间:2021-2-22 22:03
序

对于某些对时间精度要求较高的程序,用 c 写延时显得有些力不从心,故需用到汇编程序。本人通过测试,总结了 51 的精确延时函数(在 c 语言中嵌入汇编)分享给大家。至于如何在 c 中嵌入汇编大家可以去网上查查,这方面的资料很多,且很简单。以 12MHz 晶振为例,12MHz晶振的机器周期为 1us,所以,执行一条单周期指令所用时间就是 1us,如 NOP 指令。下面具体阐述一下。

若要延时 1us,则可以调用_nop_();函数,此函数是一个 c 函数,其相当于一个 NOP 指令

使用时必须包含头文件 intrins.h

例如:

3 ?4 n/ A$ g; X" i- @8 b
#include <intrins.h>
4 H% o/ X5 R# Z! X. J' u#include <reg52.h>$ ?0 S" I! p, X3 L9 D7 @; ~: q
void main(void)4 w: \3 {$ F7 M
{
* r- p5 @0 }) c8 e% ]  P1 = 0x0;+ |$ ~; ~; f& B
  _nop_(); //延时 1us
7 t  x8 D6 W2 x+ z) s9 D6 K/ q! F: N  P1 = 0xff;
  N. V; d# l* ], H! o5 l+ r( b: y}' [' s5 Z7 i/ q3 ~$ ^+ U$ {: W
延时 5us,则可以写一个delay_5us()函数delay_5us()5 W! i# C8 Q3 t- Y7 P
{
+ ^+ P, \4 }9 q" d7 k; C  #pragma asm
; U1 ^& T3 ^+ L1 D. E/ d* Q  nop
/ B/ Z6 |! A3 V7 \& f" V  #pragma endasm# E# x' D! p6 w! {& g) w" J
}0 N7 C; ^- d* ^* ^

这就是一个延时 5us 的函数,只需要在需要延时 5us 时调用此函数即可。或许有人会问,只有一个 NOP 指令,怎么是延时 5us 呢?

答案是:

在调用此函数时,需要一个调用指令,此指令消耗 2个周期(即 2us);函数执行完 毕时要返回主调函数,需要一个返回指令,此指令消耗 2 个周期(2us)。调用和返回消耗了

2us + 2us = 4us。然后再加上一个NOP指令消耗 1us,不就是5us吗?

延时 10us。

我们编写一个 delay_10us()函数

delay_10us(){2 Y1 C: s" @8 n( c
#pragma asm/ x  x3 n& i, G
nop5 H& I( l5 G3 A2 q: s: R* Q
nop
# D# Q. W3 v0 T* u3 w. x6 l  U, x3 _nop
  p' y8 U# H6 C2 qnop7 }( t$ p" Y3 q, I' M
nop
! I- L' P+ Q5 v7 u9 K3 g4 E- anop
" \' Y7 {6 q6 Q% E6 q#pragma endasm
2 {" m: L, @8 k8 h/ ?}
2 L5 D% a) M$ l2 B& K7 B; U/ _+ M

这就是延时 10us 的函数。同延时 5us 函数一样,调用和返回消耗 4us,加上函数中的6 个 NOP 指令6us,正好是10us。

此时有人不禁要问那么,任意微秒时,函数应该怎么写呢?

看我慢慢道来:首先,延时任意微秒我暂时没有想到,但是,我可以延时任意偶数微秒或延时任意奇数微秒, 也就是说,需要两个函数,一个函数专门实现任意偶数的微秒级延时,另一个函数专门实现 任意奇数的微秒级延时。只要有了这两个函数在,不就可以延时任意的微秒了吗!

首先我们来实现任意偶数的微秒级延时:

void delay_even_us(unsigned char even){ //任意偶数的微秒级延时  v( R. H+ F7 d3 _5 _' w5 |
#pragma asm
6 P+ A7 L, e6 U* G1 mov a, r7 //为什么要用到 r7 呢,因为 r7 里面装的是函数的参数!!!
8 u- l0 ~" f7 u. n# W// ^_^ 这句消耗 1 个周期
0 T5 J% M5 b  K  y5 T! r' [& R0 c2 subb a, #10H //这句看完程序我再解释 这句消耗 1 个周期+ |1 W# B3 p5 X
3 mov b, #02H //这句看完程序我再解释 这句消耗 2 个周期
3 M4 G3 o8 S  {1 w9 ?; I" v- V$ F4 div ab // 这句意思是 a/b ,商放在 a 里,余数放在 b 里 稍, ^$ h5 i6 D5 N0 P: Q
//后解释 这句消耗 4 个周期
! P# v) D6 T( Q+ A( c& d3 E/ w5 mov r0, a //这句消耗 1 个周期
- m: B6 d6 J6 V1 d4 O6 nop //这句消耗 1 个周期
( t* u: ^; w, ?% A& ^# e. ]7 loop:  D$ _3 f: X# C! l) a
8 djnz r0, loop //不等于 0 跳转指令,也就是说 r0 中的值若不为 0 的话,4 Q5 I2 X$ b; o( h
//就跳转到 loop 处 这句消耗 2 个周期
' j! f6 j( T% o1 y3 e#pragma endasm" [0 @9 Z' N% b4 B
}
9 m' a3 @3 m$ M# y' N" V

下面我们来分析一下为何这样写:为了方便分析,我给句子编上了序号。我们以延时 100us为例(delay_even_us(100))。

首先减去调用和返回的 4 个周期(4us)。再减去参数传递所消耗的2 个周期。因为 c 函数参数传递到汇编是需要消耗周期的。一共消耗了 6 个周期。也就是消 耗了 6us,还剩下 100us-6us=94us。

然后再看我再程序上面注释的各语句消耗时间:

从 1 句到 5 句一共消耗了 10 个周期(不信你数数^_^)。还剩下 94us-10us=84us。

现在就看第 8 句了,这句应该消耗 84 个周期才能达到我们延时 100us。而这句每执行一次消耗 2 个周期,也就是说 r0 的值应该为 84/2=42。

那么,怎样达到 r0=42 的呢?我们从第 1 句开始分析:

第 1 句中,r7 为 c 传递过来的参数,此例子中为 100.执行完此句后 a 的值为 100;

第 2 句中,将 a=a-16 = 100-16=84。此句结束后 a 的值为 84;

第 3 句中,给 b 赋值为 2;

第 4 句中,用 a 来除以 b。结果商存入 a 中,余数存入 b 中,此句结束后 a 的值为 a=a/b = 84/2= 42;

第 5 句,将 a 值赋给 r0,此句结束后 r0 的值为 42。

于是乎, r0 的值为 42 这个目的达到了。结合前面的分析,此程序是不是延时了 100us 呢?

答案当然是 “是”了!

这个函数可以实现任意偶数微秒(>=18)的延时的,不信的话可以带一个值进去算的。至于为什么值必须>=18us,用不着我解释了吧。

任意奇数的微秒级延时:

void delay_odd_us(unsigned char odd){; n2 O- `2 Q3 z. V7 S
#pragma asm
/ v- J5 y8 ]! e; j6 b. d( n1 mov a, r7
# p# @$ p- a3 A2 subb a, #0fH& `. P2 S  T  ^7 V
3 mov b, #02H
/ _) Z. {6 F$ q3 T4 div ab
( I$ d; |: q$ Y7 h9 r& a9 S; y5 mov r0, a  o6 o3 V( U! F( A
6 loop1:( k# _1 D+ v# c6 n
7 djnz r0, loop1  w1 q1 O. i7 R/ {$ Y
#pragma endasm
7 q8 X4 \$ M  f! g6 O& W3 w}. z8 F! c7 W4 O% ]9 W& H

此即为任意奇数微秒的延时,和偶数延时一样的道理,不解释了。^_^

此函数的参数必须大于等于 17,请思考为什么?^_^


1 @" N: ^1 W9 P' H& y. Z$ v* }
赞 收藏 评论0 发布时间:2021-2-22 22:03

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版