我需要一个用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;
答案 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.我不知道这对你来说是不是太小了。您可以通过多次调用延迟宏来解决此问题,甚至可能创建一个新宏来为您执行此操作。