Задержка на x микросекунд в C для pic18f
Мне нужна точная функция задержки, написанная на 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 ответа
Мне кажется из этого сегмента:
#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")
что за каждые дополнительные 4 миллиона увеличение PIC_CLK
тебе нужен другой nop
инструкция.
Я не использовал более ранние, так как они просто используют функцию масштабирования на более низких тактовых частотах - поскольку вы не можете выполнить половину или четверть nop
они просто уменьшают количество циклов до половины или четверти и выполняют полное nop
это много раз.
Таким образом, для 64 миллионов (что на 32 миллиона больше, чем в прошлом) вам понадобится еще восемь nop
инструкции (32 миллиона, разделенные на 4 миллиона), и, поскольку каждая из них увеличивает размер перехода на 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
Или, если вы хотите получить формулы для значений, больших или равных 16 миллионам:
divisor = 1
nopcount = picclk / 4000000 - 3
jumpsize = nopcount * 2 + 2
Код просто зацикливается на множество nop
-инструкции за определенное время. movlb
Инструкция используется для загрузки BSR (только 8-битный регистр, следовательно, сдвиг). decfsz
Затем команда используется для уменьшения счетчика цикла и пропуска следующей инструкции, если результат равен нулю, чтобы выйти из цикла. Если следующая инструкция не пропущена, Jumpback
Инструкция называется (а goto
), который возвращается к началу цикла. Поскольку каждая инструкция на 18F имеет ширину два байта (двойные инструкции - четыре байта), для 32-мегагерцовой версии вам нужно отскочить на 12 строк назад (5 nop
с и decfsz
).
Теперь вы можете последовать совету Паксиабло и сделать новую версию с более nop
s, но это заняло бы некоторое ненужное пространство, если вы все равно собираетесь работать на частоте 64 МГц. Я думаю, что вы могли бы просто сделать что-то вроде
#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
s Вы должны зацикливаться, чтобы достичь 1 мкс при 64 МГц, 13 в соответствии с превосходной математикой Паксдиабло.
РЕДАКТИРОВАТЬ:
paxdiablo обратил мое внимание на то, что это решение ограничит диапазон времени задержки больше, чем его, поскольку наибольшее число, которое вы можете передать макросу, составляет 1/13 от того, что входит в неподписанный символ. Беззнаковый символ равен 8 битам, что оставляет нам 255/13 = 19. Я не знаю, слишком ли это мало для вас. Вы можете обойти это, вызвав макрос задержки несколько раз, возможно, даже создав новый макрос, который сделает это за вас.