2011-01-20 69 views
2

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

#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; 

回答

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每个附加的4万元增加,你需要另一个nop指令。

我没有使用早期的,因为他们只是使用缩放功能以较低的时钟速度 - 因为你不能执行nop的一半或四分之一,他们只是减少循环计数到一半或四分之一和执行完整的nop多次。因此,对于6400万(比上一次增加3200万),您需要另外8条指令(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 

或者,如果你wan T代表值大于或等于下式16万:

divisor = 1 
nopcount = picclk/4000000 - 3 
jumpsize = nopcount * 2 + 2 
+0

谢谢,我认为这可行,但由于我使用了不同的编译器,我现在不得不改变所有程序集的调用方式...... – Huggzorx 2011-01-20 15:18:59

1

的代码循环遍历的时间设定量的一组nop -instructions。指令movlb用于加载BSR(只有一个8位寄存器,因此是移位)。然后使用decfsz指令递减循环计数器,并在结果为零时跳过下一条指令以跳出循环。如果下一条指令未被跳过,则调用Jumpback指令(一个goto),该指令跳回到循环的顶部。由于18F上的每条指令都是两个字节宽(双字指令是四个字节),因此32MHz版本(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; 

这里SOME_NUMBER是你需要遍历根据paxdiablo的优秀数学达到1μs的@ 64MHz的,13 nop S上的号码。

编辑:

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

+0

这可能不是一个坏主意。 ifdef本身并没有编写更多的代码,因为它们没有被编译进去。你的解决方案使用的字节数比我的少24个字节,但是你需要小心,因为你付出的代价是缩短了等待时间。由于您将usecs乘以SOME_NUMBER(顺便提一句,它将为13),因此您可以传入的上限相应受到限制(达到额外24字节解决方案的1/13)。可惜13是素数,我们可能会妥协一些:-) – paxdiablo 2011-01-20 13:56:26