1. 概要
延时在嵌入式系统中,无处不在。延时的作用是等待固定的时间后,执行某个操作。
如,控制LED闪烁,如果延时很小或者没有延时,人眼无法看到闪烁的效果。
2. 原理
CPU在延时处理中,执行指定的时间。在时间未到之前,不能返回或被打断。
3. 实现方式
实现方法有两种:硬件延时,软件延时。
3.1. 软件延时
许多情况下,系统的定时器资源有限,或者用作其他用途,这时可以选择使用软件延时的方法。软件延时,通常使用循环体实现。
3.1.1. 汇编延时
汇编语言延时特点是准确,当是不灵活。
在软件中,每执行一条指令都是要消耗时间的。那么,一条指令的执行时间是确定的吗?对于时钟固定的系统,答案是肯定的。时钟是嵌入式系统的“小心脏”。以51单片机12MHz晶振为例,12MHz的意思是单片机1s内可以执行(12M/12)=1M个机器周期,51单片机规定每个机器周期需要12个时钟震荡周期。换句话说,每个机器周期的时间=1/1M=1um。不同的汇编指令,消耗的时间是机器周期的整数倍。
指令 功能 机器周期数 时间(um)
MOV 数据传送 1 1
NOP 空操作 1 1
DJNZ 循环移位 2 2
LCALL 调用 2 2
RET 返回 2 2
因此,使用汇编语句可以实现很精确的延时。汇编实例分析,50ms延时子程序
DEL : MOV R7, #200 ①
DEL1: MOV R6, #125 ②
DEL2: DJNZ R6, DEL2 ③
DJNZ R7, DEL1 ④
RET ⑤
①:MOV R7, #200 ,单周期指令,执行一次,时间1us
②:MOV R6, #125,单周期指令,由于④,循环执行200次,时间200us
③:DJNZ R6, DEL2,双周期指令,由于②和④,循环执行125*200次,时间2*125*200us
④:DJNZ R7, DEL1,双周期指令,由于①,循环执行200次,时间2*200us
⑤:RET,双周期指令,只执行一次,时间2us
总时间=①+②+③+④+⑤=1+200+2*125*200+2*200+2=50603≈50ms
3.1.2. C 语言延时
C 语言实现的延时程序就相对灵活一些。
C 语言最终编译为汇编语言,就可以知道程序的执行时间了。
可以在C文件中使用NOP()语句实现。(假设:晶振12MHz,一个机器周期1us.)
例1:
void delay_10us(void)
{
_NOP_();
_NOP_();
_NOP_();
_NOP_();
_NOP_();
_NOP_();
}
分析:上面函数中使用了6个NOP()语句,每句执行时间为1us;调用delay_10us函数时,执行LCALL指令(2个机器周期,2us),函数退出时执行RET指令(2个机器周期,2us),执行该函数共消耗10us时间。
例2:
void delay(unsigned char n)
{
while(--n);
}
如果调用delay(1),keil C对应的汇编代码为
C:0x0005 7F01 MOV R7,#0x01
C:0x0007 111D ACALL delay1(C:001D)
C:0x001D DFFE DJNZ R7,delay1(C:001D)
C:0x001F 22 RET
分析:传递参数1,函数调用2,函数返回2,此三个为固定开销,还有DJNZ的2个
实际延时= 1+2+2+n*2
例3:
void delay_500_ms(void)
{
unsigned char i, j, k;
for(i = 15; i > 0; i--)
for(j = 202; j > 0; j--)
for(k = 81; k > 0; k--);
}
对应的汇编代码
DEL: MOV R7, #15
DEL1: MOV R6, #202
DEL2: MOV R5, #81①
DJNZ R5, $①
DJNZ R6, DEL2
DJNZ R7, DEL1
RET
分析:
一层循环n:R5*2 = 81*2 = 162us DJNZ 2us
二层循环m:R6*(n+3) = 202*165 = 33330us DJNZ 2us + R5赋值 1us = 3us
三层循环: R7*(m+3) = 15*33333 = 499995us DJNZ 2us + R6赋值 1us = 3us
循环外: 5us 子程序调用 2us + 子程序返回 2us + R7赋值 1us = 5us
延时总时间 = 三层循环 + 循环外 = 499995+5 = 500000us =500ms
计算公式:延时时间=[(2*R5+3)*R6+3]*R7+5
3.1.3. 软件延时步骤
- 确定系统时钟
- 计算指令周期
- C语言,分析对应的汇编代码
- 根据指令消耗的机器周期数,计算延时时间
3.2. 硬件延时
硬件延时就是使用定时器了。
对于晶振12MHz,一个机器周期1us的51单片机而言,最长的延时时间为216=65536us,若定时器采用工作方式2,可以实现精确延时。步骤,配置定时器工作方式,设置计数值,启动定时器,等待溢出,停止计数器。
void delay_1ms(void)
{
TMOD = 0x01; /*定时器0工作在模式1下(16位计数器)*/
TH0 = 0xfd; /*(65536-1000)/256*/
TL0 = 0x65; /*(65536-1000)%256*/
TR0 = 1; /*启动定时器*/
while(TF0 == 0); /*计数器溢出后TF0由0变1*/
TR0 = 0; /*停止定时器*/
}
65.536ms内的延时程序
void delay_us(int n)
{
TMOD = 0x01;
TH0 = (65536-n)/0xff;
TL0 = (65536-n)%0xff;
TR0 = 1;
while(TF0 == 0);
TR0 = 0;
}
3.2.1 硬件延时步骤
- 选择定时器
- 配置定时器工作方式
- 计算计数值,设置相关寄存器
- 启动定时器
- 等待溢出
- 停止定时器
参考
单片机延时方法总结
51单片机Keil C延时程序的简单研究