在 20 MHz(?) 时钟上增加 1 秒延迟



编辑:PIC 16F684

好的,我有一个简单的 3 LED 二进制时钟,计数范围为 0-7,并希望在每个灯打开之间添加大约 1 秒的延迟。

我已经计算出每个灯都需要处于一种循环中,我必须使用计数来测量刻度和翻转等。

我认为时钟是4MHz,这是手册的屏幕截图: https://i.stack.imgur.com/3A3sJ.png

以下是我的代码的相关摘录:

COUNT1 EQU 20h      ; Delay counter #1
COUNT2 EQU 21h      ; Delay counter #2

LOOP0
        MOVLW   TRIS_D0_D1      ; Move value defined in Constants to TRISA, to switch on LED 0.
        TRIS    PORTA           ;
        CLRF    PORTA           ; Clear all outputs.
        MOVLW   D0              ; Set the accumulator to the value of D0.
        MOVWF   PORTA           ; Move the accumulator to PORTA, to switch on LED 0.
    ; Using COUNTs to add a delay
        decfsz    COUNT1,1       ; Decrease COUNT1 by 1, and skip the next line if the result is 0.
        goto      LOOP0          ; If COUNT1 is 0, carry on. If not, go to LOOP0.   
        decfsz    COUNT2,1       ; Decrease COUNT2 by 1, and skip the next line if the result is 0.
        goto      LOOP0          ; If COUNT1 is 0, carry on. If not, go to LOOP0.

但是,我很确定我搞砸了时机,有人可以帮我一把吗?

假设:LOOP0处的代码是您希望每个延迟执行一次的代码,而不是在延迟期间尽可能多地执行。我还假设您正在设置COUNT1COUNT2某些内容 - 您发布的代码声明了两个"变量",但没有分配它们。

您目前拥有的代码将以 LOOP0 COUNT1 + COUNT2 次重复执行代码。这是因为每个循环都是独立的。这为您提供了 510 个周期的最大延迟。正如其他评论者所说,PIC16 大约每个周期执行一条指令,因此您需要延迟 1,000,000 个周期才能在 4MHz 下等待一秒钟。

如果我们考虑要等待196392周期的情况,我们基本上需要实现一个 16 位计数器。我们通过减少循环中的一个计数器来做到这一点。每次循环退出时,我们都会递减另一个计数器。当两个计数器都为零时,循环返回。下面是一个示例:

COUNT1 EQU 20h
COUNT2 EQU 21h
LOOP0
    ;do some stuff here
        ...
    ;delay loop starts here:
    ;assume COUNT1=0 and COUNT2=0
Delay_0
    decfsz COUNT1
    goto Delay_0
    decfsz COUNT2   ;COUNT1 = 0 so 0xff cycles have passed
    goto Delay_0
    goto LOOP0 ;both COUNT1 and COUNT2 = 0 - 196392 cycles have now passed
分支指令如果不跳过,则需要 1 个周期

,如果跳过,则需要 2 个周期。 goto总是需要 2 个周期,这意味着进行一次完整计数的实际时间为 767 个周期 (255 * 3 + 2(。我们可以将两者花费的时间计算为 ((255 * 3 + 2( + 3( * 255 + 2。

在Dos4Ever上有一个关于延迟例程的很好的解释。这将介绍延迟例程的工作原理以及如何计算延迟例程的计数器值和成本。

最后,如果您只想要剪切和粘贴延迟例程,PIClist 上的延迟例程生成器几乎是完美的。

上面的代码在假设两个计数器最初都设置为零的情况下有两个循环需要197119周期(而不是196392(,对吗?

我指的是代码:

  • Delay_0 decfsz COUNT1
  • 转到Delay_0
  • DECFSZ 计数2
  • 转到Delay_0

原因是与 count1 关联的内部循环将循环 255 次,这意味着 {255 次 3 个指令周期} 加上最终的 decfsz 需要额外的 2 个周期。因此,这是这个内部循环的第一次,相关的延迟(d1F(将是d1F = 255 * 3 + 2 = 767个周期。这一切都发生在我们到达 count2 的 decfsz 之前。接下来,其余的活动发生在我们达到 decfsz count2 时;它以 decfsz count2 开头,后跟 goto Delay_0,其中 'goto' 将调用另一个内部循环延迟(等于 d1F(。因此,这个由 decfsz count2、goto Delay_0 和 d1F 组成的三重组合将与 count2 索引值 255 相关联。然后我们继续获得更多的三重连击...使用索引 254,然后是索引 253...一直下降到 Count2 索引 1。这意味着我们得到了 255 个三重组合。最后,我们以最终的 decfsz count2(索引为 0(终止。最终的 decfsz count2 "指令"需要 2 个周期而不是 1 个周期。因此,延迟的第二部分是 (d1F+3(*255 + 2。"3"(指令周期(是由于正常循环期间的decfsz加上goto指令造成的。

因此,当我们把延迟的第一部分和第二部分放在一起时,我们得到:

    d2F = d1F + (d1F+3(x255 + 2 =
  • 767 + (767+3(x255 + 2 = 197119

现在,如果我们有多个循环,那么我们可以使用等式:

    d(n(_F = d(n-1(_F + {d(n-1(_F +
  • 3}x255 + 2 = 256xd(n-1(_F + 767

  • d(n(_C = d(n-1(_C + {d(n-1(_F + 3}x{count_n - 1} + 2

其中 d(n(_F 或 d(n-1(中的"F"_F表示所有计数器都用零值初始化的条件。d(n(中的"C"_C表示第n个循环的计数器使用我们最初选择的任何值初始化的条件。而'n'与第n个循环相关联。而'n-1'与第(n-1(个循环相关联。

因此,如果我们有 2 个循环,那么 d(1(_F 是由于循环 #1 具有"完整"周期数(即计数器 1 最初为 ZERO 或 256(引起的延迟;d(2(_F 是由于循环 #1 和循环 #2 引起的延迟,适用于计数器 1 和计数器 2 最初都等于 ZERO 或 256 的情况。

  • d(1(_C 是循环 #1 导致的延迟,用于使用我们最初选择的任何值初始化 count1 的情况。
  • d(2(_C 是循环 #1 和循环 #2 导致的延迟,用于使用我们最初选择的任何值初始化 count2 的情况。

请注意,count_n是第 n 个循环的初始计数器值。此外,如果特定计数器最初使用 ZERO 值初始化,则通常将该值视为"256"很方便。这当然是针对八位计数器的。例如,如果 count1 = 0,则将其视为 count1 = 256(而不是 0(很方便。

  • 我们还可以定义:d(0(_F = 0,d(0(_C = 0。

因此,对于 count1 = 1、count2 = 4 和 count3 = 2 的 3 循环系统,

  • d(1(_F = 256xd(0(_F + 767 = 256x0 + 767 = 767

  • d(1(_C = 0 + {0 + 3}x{1 - 1} + 2 = 2

  • d(2(_F = 256xd(1(_F + 767 = 256x767 + 767 = 197119

  • d(2(_C = d(1(_C + {d(1(_F + 3}x{4 - 1} + 2 = 2 +
  • {767+3}x3 + 2 = 2314

  • d(3(_F = 256xd(2(_F + 767 = 256x197119 + 767 = 50463231

  • d(3(_C = d(2(_C + {
  • d(2(_F + 3}x{2 - 1} + 2 = 199438

3 循环系统类似于:

  • Delay_0 decfsz count1
  • 转到Delay_0
  • Decfsz count2
  • 转到Delay_0
  • 德夫斯计数3
  • 转到Delay_0

梁健

最新更新