Задержка на 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).

Теперь вы можете последовать совету Паксиабло и сделать новую версию с более nops, но это заняло бы некоторое ненужное пространство, если вы все равно собираетесь работать на частоте 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 - это число nops Вы должны зацикливаться, чтобы достичь 1 мкс при 64 МГц, 13 в соответствии с превосходной математикой Паксдиабло.

РЕДАКТИРОВАТЬ:

paxdiablo обратил мое внимание на то, что это решение ограничит диапазон времени задержки больше, чем его, поскольку наибольшее число, которое вы можете передать макросу, составляет 1/13 от того, что входит в неподписанный символ. Беззнаковый символ равен 8 битам, что оставляет нам 255/13 = 19. Я не знаю, слишком ли это мало для вас. Вы можете обойти это, вызвав макрос задержки несколько раз, возможно, даже создав новый макрос, который сделает это за вас.

Другие вопросы по тегам