【问题标题】:Writing a delay subroutine?编写延迟子程序?
【发布时间】:2011-02-27 17:55:37
【问题描述】:

我需要编写一个延迟子程序。它应该延迟大约 1 秒。它必须适用于 8051 环境,DS89C430 微控制器(11.0592 MHz XTAL)。这个子程序怎么写?

Delay1sec: ...
....
...
....
...

【问题讨论】:

    标签: assembly microcontroller 8051


    【解决方案1】:

    要获得在中断期间也有效的精确 1 秒延迟,您需要使用硬件计时器,而不是软件计时器。我建议您使用可用的板载计时器之一as suggested by 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


    软件延迟循环会浪费处理器时间并受到中断的干扰。话虽这么说,你可以用硬编码的方式来做。

    一种方法是了解每个机器周期的时钟周期数以及执行各种指令所需的机器周期数。根据data sheet,DS89C430通常每个指令字节使用一个机器周期,执行需要一个周期。每个指令的周期数在Ultra-High-Speed Flash Microcontroller User's Guide 中提供。

    由于您的晶振频率为 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 协助计算与确定循环计数器相关的计算。

    【讨论】:

      【解决方案2】:

      该微控制器具有三个连接到系统时钟(除以 12)的板载定时器(参见User's Manual 的第 11 节),因此只需对它们进行编程以在时间到期时产生中断。由于分频输入略低于 1 MHz,最大计数器为 16 位,因此您需要计算 14 个中断才能达到 1 秒(至少在我计算正确的情况下)。

      【讨论】:

      • (我没有读过手册,我对这个芯片也不是很熟悉,但是)你通常可以通过不使用计数器的全范围来获得一个更准确的定时器,即,它应该更准确地计数到 50,000 二十次(在 1MHz)而不是 65,535 大约十四次。
      • @Zooba:一般来说你是对的,虽然在这种情况下计数到 65,536 14 次给出的时间(幸运的是)确实非常准确(基于“大约一秒”,可能比他关心的更准确)。
      【解决方案3】:

      A) 引用一个硬件定时器。

      B) 引用一个 CPU 定时器。一些处理器有一个非常宽的计时器,即 64 位宽,它在时钟滴答声中运行。

      C) 软件循环。为获得最佳结果,代码和所有数据应驻留在具有可预测时序的内部存储器中。从 SDRAM 运行可能会导致时序问题。

      您不必计算装配周期即可。相反,您可以在引脚上“绘制”一个脉冲(在循环之前拉高,在循环之后拉低),使用逻辑分析仪测量脉冲宽度,然后更改循环计数以调整您的时序。为获得最佳效果,您应该补偿外部 CPU 时钟/晶振,通过使用频率计数器对其进行测量,然后补偿偏离中心频率,因为大多数便宜的晶振都不会死机。

      您可以通过使用计时器来计算循环时间来进行自我校准。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多