3

编辑:PIC 16F684

好的,我有一个简单的 3 LED 二进制时钟,从 0 到 7 计数,并且想在每次打开灯之间添加大约 1 秒的延迟。

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

认为时钟是 4MHz,这是手册的截图:http: //i.imgur.com/tJatQ.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.

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

4

2 回答 2

4

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

您目前拥有的代码将在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 上的延迟例程生成器非常完美。

于 2012-04-30T18:08:59.383 回答
1

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

我指的是代码:

  • Delay_0 decfsz COUNT1
  • 转到延迟_0
  • decfsz COUNT2
  • 转到延迟_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 的延迟,循环数为“完整”(即,counter1 最初为零或 256);d(2)_F 是由于循环 #1 和循环 #2 导致的延迟,在 counter1 和 counter2 最初都等于零或 256 的情况下。

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

请注意,count_n 是第 n 个循环的初始计数器值。此外,如果一个特定的计数器最初是用零值初始化的,那么将该值视为“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
  • 转到延迟_0
  • decfsz 计数2
  • 转到延迟_0
  • decfsz 计数3
  • 转到延迟_0

肯尼·梁

于 2013-06-13T00:16:01.003 回答