对于pic18f,在C中延迟x微秒

时间:2011-01-20 13:10:17

标签: c assembly embedded delay pic18

我需要一个用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;

2 个答案:

答案 0 :(得分: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")

对于PIC_CLK每增加400万,您需要另外nop条指令。

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

所以,对于6400万(比上一个多了3200万),你需要另外8个nop指令(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

答案 1 :(得分:1)

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

现在,您可以按照paxdiablo的建议制作一个更多nop s的新版本,但是如果您只打算运行@ 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;

根据paxdiablo优秀的数学计算,SOME_NUMBER是你需要在64MHz时循环达到1μs的nop的数量,13。

编辑:

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