一 单片机周期简介
首先,先从51单片机的周期说起,在单片机上面,有这么几种周期,分别为:晶振周期,时钟周期,机器周期和指令周期,它们的关系如下图
一个机器周期一般由12个晶振周期组成,比如对于晶振周期为12Mhz的单片机而言,其机器周期为1s/12Mhz * 12 = 1us。计算公式是1s/(晶振频率) * (12个晶振周期)
单片机执行不同指令时,消耗的机器周期因指令的复杂度而变化,范围在1~4个机器周期之间。此处的指令指汇编的指令,像MOV A,#data这类的语句执行时会消耗一个机器周期。
二 示例程序和分析
以下为延时子程序,实现的是5ms的延时
void delay(){
unsigned char i,j,k;
for(i = 1;i > 0; i--)
for(j = 98; j > 0; j--)
for(k = 24; k > 0; k--);
}
从keil中可以获得该延时子程序的汇编语句
C:0x0810 120833 LCALL delay(C:0833)
3: for(i = 1;i > 0; i--)
C:0x0833 7FC8 MOV R7,#T2CON(0xC8)
4: for(j = 98; j > 0; j--)
C:0x0835 7E62 MOV R6,#0x62
5: for(k = 24; k > 0; k--);
C:0x0837 7D18 MOV R5,#0x18
C:0x0839 DDFE DJNZ R5,C:0839
C:0x083B DEFA DJNZ R6,C:0837
C:0x083D DFF6 DJNZ R7,C:0835
6: }
C:0x083F 22 RET
LCALL调用延时子程序,然后跳到子程序。三个for循环语句被解释成DJNZ 不为0转移指令,以上汇编语句执行过程如下
进入延时子程序后,语句一路执行到第5条,然后自循环k次,接着执行第6句,判断R6此时是否为0,不为0,跳转到语句4,这个过程执行j次,接着执行第7句,判断R7是否为0,不为0,跳转到语句3,这个过程执行i次,最后RET跳转回主程序,从以上过程可以获得上述示例程序的执行时间为
{ 2 + 1 + (1 + (1 + 2k + 2)j + 2)i + 2 } * T (T为机器周期)
化简一下得到 { [(3 + 2k)j + 3 ]*i + 5 } * T
示例程序中 i =1, j = 98, k = 24 , 计算可得延时时间为5001 个机器周期,对于12MHZ的单片机而言即为5001us,即5ms,误差为 6us,将 i 的值进行替换,即可获得变换不同的延时时间,例如当 i = 200 时,计算得到时间为 1000205 us,亦即 1 s, 误差为 205us。
三 关于延时程序的实用代码
通过上文的公式,我遍历了一些i,j,k的值(晶振12Mhz),形成了几个较为常用的延时程序,如下
/*延时50ms,误差大于0us*/
void delay_50ms(){
unsigned char i,j,k;
for(i = 101;i > 0; i--)
for(j = 12; j > 0; j--)
for(k = 19; k > 0; k--);
}
/*延时200ms,误差大于3us*/
void delay_200ms(){
unsigned char i,j,k;
for(i = 78;i > 0; i--)
for(j = 13; j > 0; j--)
for(k = 97; k > 0; k--);
}
/*延时250ms,误差大于1us*/
void delay_250ms(){
unsigned char i,j,k;
for(i = 249;i > 0; i--)
for(j = 143; j > 0; j--)
for(k = 2; k > 0; k--);
}
/*延时333ms,误差大于2us*/
void delay_333ms(){
unsigned char i,j,k;
for(i = 203;i > 0; i--)
for(j = 11; j > 0; j--)
for(k = 73; k > 0; k--);
}
/*延时500ms,误差大于1us*/
void delay_500ms(){
unsigned char i,j,k;
for(i = 49;i > 0; i--)
for(j = 101; j > 0; j--)
for(k = 49; k > 0; k--);
}
/*延时1s,误差大于3us*/
void delay_1s(){
unsigned char i,j,k;
for(i = 98;i > 0; i--)
for(j = 101; j > 0; j--)
for(k = 49; k > 0; k--);
}
虽然延时误差仍然存在,而且采用的都是3层for循环(延时有限制范围),不过以上程序对于普通程序,对时间精度要求不严谨的应用场合还是可以得。