Assembly 写一个延迟子程序?
我需要写一个延迟子程序。它应该延迟大约1秒。它必须适用于8051环境,DS89C430微控制器11.0592 MHz XTAL。如何编写这个子程序Assembly 写一个延迟子程序?,assembly,microcontroller,8051,Assembly,Microcontroller,8051,我需要写一个延迟子程序。它应该延迟大约1秒。它必须适用于8051环境,DS89C430微控制器11.0592 MHz XTAL。如何编写这个子程序 Delay1sec: ... .... ... .... ... 该微控制器有三个板载定时器(参见第11节)连接到系统时钟除以12,因此需要对它们进行编程,以便在时间到期时生成中断。由于分频输入刚好低于1MHz,最大计数器为16位,如果我计算正确,至少需要计算14次中断才能达到1秒。要获得在中断期间也能工作的精确1秒延迟,需要使用硬件计时器,而不是
Delay1sec: ...
....
...
....
...
该微控制器有三个板载定时器(参见第11节)连接到系统时钟除以12,因此需要对它们进行编程,以便在时间到期时生成中断。由于分频输入刚好低于1MHz,最大计数器为16位,如果我计算正确,至少需要计算14次中断才能达到1秒。要获得在中断期间也能工作的精确1秒延迟,需要使用硬件计时器,而不是软件计时器。我建议您使用一个可用的车载计时器 这是一种涉及内置计时器和计数计时器溢出的方法。由于运行计时器默认每12个时钟周期更新一次,以保持与8051的兼容性,因此将每秒更新921600次。一个乘法位告诉我们从0到46080的计数需要50毫秒,这也告诉我们我们可以在19456启动一个16位计时器,等待它溢出20次以延迟1秒* 代码可能如下所示:
CLR T0M ; set timer 0 to use a divide-by-12 of
; the crystal frequency (default)
MOV R0,TMOD ; set timer 0 to 16-bit mode without
ORL R0,#01h ; affecting the setup of timer 1
MOV TMOD,R0
LCALL Delay1sec ; call the delay subroutine
Delay1sec:
MOV R0,#20d ; set loop count to 20
loop: CLR TR0 ; start each loop with the timer stopped
CLR TF0 ; and the overflow flag clear. setup
MOV TH0,#4Ch ; timer 0 to overflow in 50 ms, start the
MOV TL0,#00h ; timer, wait for overflow, then repeat
SETB TR0 ; until the loop count is exhausted
JNB TF0,$
DJNZ R0,loop
RET
Delay1sec: ; <------------------------------+
; LCALL Delay1sec ; 3 cycles |
MOV R2,#42d ; 2 cycles |
MOV R1,#00d ; 2 cycles |
MOV R0,#00d ; 2 cycles |
loop: DJNZ R0,loop ; 4 cycles <-- l1 <- l2 <- l3 Delay1sec
DJNZ R1,loop ; 4 cycles <---------+ | |
DJNZ R2,loop ; 4 cycles <---------------+ |
RET ; 3 cycles <---------------------+
注意:示例中不包括指令执行时间的开销。
*数学是如何分解的:
11059200 / 12 = 921600
0.05 * 921600 = 46080
65536-46080=19456=0x4C00
软件延迟循环浪费处理器时间,并受到中断的干扰。也就是说,你可以用硬编码的方式来做
一种方法涉及了解每个机器周期的时钟周期数以及各种指令执行所需的机器周期数。根据,DS89C430通常对每个指令字节使用一个机器周期,并且需要一个执行周期。中提供了每条指令的周期数
由于您的晶体频率为11.0592 MHz,因此您的例程需要延迟11059200个时钟周期。这通常是通过已知长度的嵌套循环完成的,然后包括任何额外的循环设置以及可能的子程序调用和返回指令*
该函数可以如下所示:
CLR T0M ; set timer 0 to use a divide-by-12 of
; the crystal frequency (default)
MOV R0,TMOD ; set timer 0 to 16-bit mode without
ORL R0,#01h ; affecting the setup of timer 1
MOV TMOD,R0
LCALL Delay1sec ; call the delay subroutine
Delay1sec:
MOV R0,#20d ; set loop count to 20
loop: CLR TR0 ; start each loop with the timer stopped
CLR TF0 ; and the overflow flag clear. setup
MOV TH0,#4Ch ; timer 0 to overflow in 50 ms, start the
MOV TL0,#00h ; timer, wait for overflow, then repeat
SETB TR0 ; until the loop count is exhausted
JNB TF0,$
DJNZ R0,loop
RET
Delay1sec: ; <------------------------------+
; LCALL Delay1sec ; 3 cycles |
MOV R2,#42d ; 2 cycles |
MOV R1,#00d ; 2 cycles |
MOV R0,#00d ; 2 cycles |
loop: DJNZ R0,loop ; 4 cycles <-- l1 <- l2 <- l3 Delay1sec
DJNZ R1,loop ; 4 cycles <---------+ | |
DJNZ R2,loop ; 4 cycles <---------------+ |
RET ; 3 cycles <---------------------+
让我们看看数学是如何分解的**:
l1=4*256=1024个周期
l2=1024+4*256=263168个周期
l3=263168+4*42=11053224个周期
Delay1sec=11072668+3+2+2+3=11053236个周期
11053236个周期*1/11059200秒/周期=999.461毫秒
*如有必要,可以省略子程序调用和返回指令。
**我使用Microsoft Excel来帮助计算与确定循环计数器有关的数据。参考硬件计时器 B参考CPU定时器。一些处理器有一个非常宽的计时器,即64位宽,以时钟为单位运行 C软件循环。为了获得最佳结果,代码和所有数据都应该驻留在具有可预测定时的内部内存中。从SDRAM运行可能会导致计时问题 这样做不需要计算装配周期。相反,你可以在引脚上画一个脉冲,在环路前拉高,在环路后拉低,用逻辑分析仪测量脉冲宽度,然后改变环路计数来调整你的计时。为了获得最佳效果,您应该使用频率计数器测量外部CPU时钟/晶体,然后补偿偏离中心频率的情况,因为大多数便宜的晶体不会死机
您可以通过使用计时器来计算循环计时来进行自我校准。我没有阅读手册,也不太熟悉该芯片,但通常通过不使用计数器的全量程,可以在多次中断时获得更精确的计时器,即,在1MHz频率下数到50000二十次应该比数到65535大约十四次更准确。@Zooba:一般来说,你是对的,不过在这种情况下,数到65536十四次会给出一个时间,幸运的是,根据大约一秒钟的时间,它确实非常准确,可能比他所关心的更准确。