Задержка на x микросекунд в C для pic18f - PullRequest
2 голосов
/ 20 января 2011

Мне нужна точная функция задержки, написанная на 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 ]

6 голосов
/ 20 января 2011

Мне кажется из этого сегмента:

#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
1 голос
/ 20 января 2011

Код просто зацикливается на наборе nop -инструкций в течение заданного промежутка времени. Инструкция movlb используется для загрузки BSR (только 8-битный регистр, следовательно, сдвиг). Инструкция decfsz затем используется для уменьшения счетчика цикла и пропуска следующей инструкции, если результат равен нулю, чтобы выйти из цикла. Если следующая инструкция не пропущена, вызывается инструкция Jumpback (a goto), которая возвращается к началу цикла. Поскольку каждая инструкция на 18F имеет ширину два байта (двойные инструкции - четыре байта), вы должны вернуться на 12 строк назад для версии 32 МГц (5 nop с и decfsz).

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

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

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

...