2

我需要一个用 C 语言编写的精确时间延迟函数,它将 pic 程序的执行延迟给定的微秒数。我确实在 microchipc.com 上找到了一个使用 ASM 的示例,但代码只允许时钟速度高达 32000000。我的时钟速度需要为 64000000,但由于我不明白代码是如何工作的,我无法修改它做我需要的。谁能提供一些代码解释或建议如何实现类似的东西?

#if PIC_CLK == 4000000
  #define DelayDivisor 4
  #define WaitFor1Us asm("nop")
  #define Jumpback asm("goto $ - 4") 
#elif PIC_CLK == 8000000
  #define DelayDivisor 2
  #define WaitFor1Us asm("nop")
  #define Jumpback asm("goto $ - 4")
#elif PIC_CLK == 16000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop")
  #define Jumpback asm("goto $ - 4")
#elif PIC_CLK == 20000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop")
  #define Jumpback asm("goto $ - 6")
#elif PIC_CLK == 32000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop")
  #define Jumpback asm("goto $ - 12")
#else
#error delay.h - please define PIC_CLK correctly
#endif

#define DelayUs(x) { \
delayus_variable=(unsigned char)(x/DelayDivisor); \
asm("movlb (_delayus_variable) >> 8"); \
WaitFor1Us; } \
asm("decfsz (_delayus_variable)&0ffh,f"); \
Jumpback;
4

2 回答 2

6

在我看来,从这个部分:

#elif PIC_CLK == 16000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop")
  #define Jumpback asm("goto $ - 4")
#elif PIC_CLK == 20000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop")
  #define Jumpback asm("goto $ - 6")
#elif PIC_CLK == 32000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop")
  #define Jumpback asm("goto $ - 12")

即每增加 400 万PIC_CLK,您就需要另一nop条指令。

我没有使用早期的,因为它们只是在较低的时钟速度下使用缩放功能 - 因为你不能执行 a 的一半或四分之一nop,它们只是将循环计数减少到一半或四分之一并执行完整nop的多次。

因此,对于 6400 万(比上一条多 3200 万),您将需要另外 8nop条指令(3200 万除以 400 万),并且由于每条指令将跳转大小增加 2(PIC18F 的指令宽度为 2 字节) ,您应该能够使用以下内容:

#elif PIC_CLK == 32000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop")
  #define Jumpback asm("goto $ - 12")
#elif PIC_CLK == 64000000
  #define DelayDivisor 1
  #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop") \
                     asm("nop"); asm("nop"); asm("nop"); asm("nop"); \
                     asm("nop"); asm("nop"); asm("nop"); asm("nop");
  #define Jumpback asm("goto $ - 28")
#else
#error delay.h - please define PIC_CLK correctly
#endif

总之,这些是您需要为每个 PIC_CLK 值设置的值,以防下一代会更快:

PIC_CLK    Divisor  NOP count  Jump size
---------  -------  ---------  ---------
  1000000       16          1          4
  2000000        8          1          4
  4000000        4          1          4
  8000000        2          1          4
 16000000        1          1          4
 20000000        1          2          6
 24000000        1          3          8
 28000000        1          4         10
 32000000        1          5         12
 64000000        1         13         28
 96000000        1         21         44
128000000        1         29         60

或者,如果您想要大于或等于 1600 万的值的公式:

divisor = 1
nopcount = picclk / 4000000 - 3
jumpsize = nopcount * 2 + 2
于 2011-01-20T13:35:37.003 回答
1

代码只是在一组 - 指令上循环一段设定nop的时间。该movlb指令用于加载 BSR(只有一个 8 位寄存器,因此是移位)。然后该decfsz指令用于递减循环计数器并在结果为零时跳过下一条指令以跳出循环。如果没有跳过下一条指令,Jumpback则调用该指令 (a goto),它会跳回到循环的顶部。nop由于 18F 上的每条指令都是两个字节宽(双字指令是四个字节),因此对于 32MHz 版本(5 s 和 a ) ,您必须向后跳 12 行decfsz

现在,您可以按照 paxdiablo 的建议制作一个带有更多nops 的新版本,但是如果您无论如何只打算运行 @ 64MHz,那将占用一些不必要的空间。我认为你可以做类似的事情

#if PIC_CLK == 64000000
  #define WaitFor1NOP asm("nop")
  #define Jumpback asm("goto $ - 4")
#else
#error delay.h - please define PIC_CLK correctly
#endif

#define DelayUs(x) { \
delayus_variable=(unsigned char)(x*SOME_NUMBER); \
asm("movlb (_delayus_variable) >> 8"); \
WaitFor1NOP; } \
asm("decfsz (_delayus_variable)&0ffh,f"); \
Jumpback;

这里的 SOME_NUMBER 是nop循环达到 1µs @ 64MHz 所需的 s 数,根据 paxdiablo 的出色数学计算为 13。

编辑:

paxdiablo 引起我的注意,这个解决方案将限制延迟时间的范围超过他的,因为你可以传递给宏的最大数字是 unsigned char 的 1/13。无符号字符是 8 位,剩下 255/13 = 19。我不知道这对你来说是否太小。您可以通过多次调用延迟宏来解决此问题,甚至可能创建一个新宏来为您执行此操作。

于 2011-01-20T13:45:39.197 回答