我需要编写一个延迟子程序。它应该延迟大约 1 秒。它必须适用于 8051 环境,DS89C430 微控制器(11.0592 MHz XTAL)。我该如何编写这个子程序?
Delay1sec: ...
....
...
....
...
我需要编写一个延迟子程序。它应该延迟大约 1 秒。它必须适用于 8051 环境,DS89C430 微控制器(11.0592 MHz XTAL)。我该如何编写这个子程序?
Delay1sec: ...
....
...
....
...
要获得在中断期间也有效的精确 1 秒延迟,您需要使用硬件计时器,而不是软件计时器。我建议您使用Jerry Coffin 建议的可用板载计时器之一。
这是一种涉及内置计时器和计数计时器溢出的方法。由于运行定时器默认每 12 个时钟周期更新一次,以保持与 8051 的兼容性,因此它将每秒更新 921,600 次。一点乘法告诉我们从 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
注意:示例中不包括指令执行时间的开销。
* 数学如何分解:
11059200 / 12 = 921600
0.05 * 921600 = 46080
65536 - 46080 = 19456 = 0x4C00
软件延迟循环会浪费处理器时间并受到中断的干扰。话虽这么说,你可以用硬编码的方式来做。
一种方法涉及了解每个机器周期的时钟周期数以及执行各种指令所需的机器周期数。根据数据手册,DS89C430 通常每个指令字节使用一个机器周期,执行需要一个周期。超高速闪存微控制器用户指南中提供了每条指令的周期数。
由于您的晶体频率为 11.0592 MHz,因此您的例程将需要延迟 11,059,200 个时钟周期。这通常通过已知长度的嵌套循环来完成,然后包括任何额外的循环设置以及可能的子程序调用和返回指令*。
该函数可能如下所示:
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 + 2 + 3 = 11053236 个周期
11053236 个周期 * 1/11059200 秒/周期 = 999.461 毫秒
* 子程序调用和返回指令可根据需要省略。
** 我使用 Microsoft Excel 协助进行与确定循环计数器相关的计算。
该微控制器具有三个连接到系统时钟(除以 12)的板载定时器(参见用户手册的第 11 节),因此只需对它们进行编程以在时间到期时产生中断。由于分频输入略低于 1 MHz,最大计数器为 16 位,因此您需要计算 14 个中断才能达到 1 秒(至少如果我计算正确的话)。
A) 引用一个硬件定时器。
B) 引用一个 CPU 定时器。一些处理器有一个非常宽的定时器,即 64 位宽,它在时钟滴答声中运行。
C) 软件循环。为获得最佳结果,代码和所有数据应驻留在具有可预测时序的内部存储器中。从 SDRAM 运行可能会导致时序问题。
您不必计算装配周期即可。相反,您可以在引脚上“绘制”一个脉冲(在循环之前拉高,在循环之后拉低),使用逻辑分析仪测量脉冲宽度,然后更改循环计数以调整您的时序。为了获得最佳结果,您应该通过使用频率计数器对其进行测量来补偿外部 CPU 时钟/晶体,然后补偿远离中心频率,因为大多数廉价晶体都不会死机。
您可以通过使用计时器来计算循环时间来进行自我校准。