Задержка микросекунды LPC1768 / ARM Cortex-M3 - PullRequest
1 голос
/ 26 июня 2011

Я пытаюсь реализовать микросекундную задержку в среде с голым металлическим плечом (LPC1768) / GCC.Я видел примеры, которые используют SysTimer для генерации прерывания, которое затем выполняет некоторый подсчет в C, который используется в качестве временной базы

https://bitbucket.org/jpc/lpc1768/src/dea43fb213ff/main.c

Однако на системных тактовых частотах 12 МГц Iне думаю, что это будет очень хорошо масштабироваться до микросекундных задержек.Обычно процессор тратит все свое время на обслуживание прерывания.

Можно ли запросить значение SYSTICK_GetCurrentValue в цикле и определить, сколько тактов происходит в микросекундах и выходить из цикла после количества тактов?превышает расчетное число?

Я бы предпочел не использовать отдельный аппаратный таймер для этого (но будет, если нет другого выбора)

Ответы [ 3 ]

4 голосов
/ 26 июня 2011

Один из способов - использовать цикл для создания задержки, как показано ниже.Вам нужно откалибровать свой коэффициент.Более универсальным подходом является вычисление коэффициента при запуске на основе некоторой известной временной базы.

#define CAL_FACTOR ( 100 )

void delay (uint32_t interval)
{
  uint32_t iterations = interval / CAL_FACTOR;

  for(int i=0; i<iterations; ++i)
  {
    __asm__ volatile // gcc-ish syntax, don't know what compiler is used
    (
      "nop\n\t"
      "nop\n\t"
      :::
    );
  }
}
3 голосов
/ 13 июля 2011

Для этого вы можете использовать SYSTICK внутри процессора ARM.Просто запрограммируйте его, чтобы он подсчитывал каждые 1 мкс или меньше, если у вас достаточно тактовой частоты, и выполняйте цикл пока не истечет значение вашей задержки.Например:

void WaitUs(int us) {

    unsigned int cnt; 

    while(us-- >0) {
       cnt = STK_VAL; // get systick counter, ticking each 500nS
       while( (STK_VAL-cnt) < 2); // repeat till 2 ticks
    }
}

Имейте в виду, что это пример, вам необходимо отрегулировать его для встречного опрокидывания.

3 голосов
/ 26 июня 2011

Первые прерывания не нужны для такого рода вещей, вы можете опрашивать таймер, нет необходимости перебивать прерывания. Да, причины, по которым в этих примерах используются прерывания, но это не означает, что это единственный способ использовать таймер.

Ответ Гая Сиртона звучит здраво, но я предпочитаю ассемблер, так как могу управлять им точно по такту (при условии, что на пути нет прерываний или других предметов). Таймер обычно проще, хотя, поскольку код немного более переносим (измените тактовую частоту процессора, и вам придется перенастроить цикл с помощью таймера, иногда все, что вам нужно сделать, это изменить код инициализации, чтобы использовать другой прескалер или измените одну строку в поисках вычисленного значения) и разрешите прерывания и тому подобное в системе.

В данном случае, хотя вы говорите о 12 МГц и одной микросекунде, это 12 инструкций, да? Положите в 12 нопс. Или переходите к какому-либо ассемблеру с 10 или 8 шайбами, чтобы компенсировать сбрасывание конвейера на двух ветвях. Таймер и прерывания сжигают более 12 циклов команд в служебной информации. Даже опрос таймера в цикле будет небрежным. Контурный цикл тоже подойдет, вам нужно понять затраты на ветку и настроить на это:

delay_one_ms:
mov r0,#3
wait:
sub r0,#1 @cortex-m3 means thumb/thumb2 and gas complains about subs.
bne wait
nop  @might need some nops to tune the loop accurately
nop
bx lr

Вызовите эту функцию, что 30 миллионов раз в цикле, используя светодиод gpio или выход uart и секундомер, и увидите, что мигания с интервалом 30 секунд.

ldr r4,=uart_tx_register_address
mov r5,#0x55
again:
ldr r6,=24000000
str r5,[r4]
top:
bl delay_one_ms
sub r6,#1
bne top
str r5,[r4]
b again

На самом деле, поскольку я принимал 2 такта на ветвь, тестовый цикл имеет 3 такта, предполагается, что задержка составляет всего 12 тактов, поэтому 15 тактов на цикл, 30 секунд - 30 000 000 микросекунд, в идеале 30 миллионов циклов, но мне нужно 12 / 15-е число петель для компенсации. Это гораздо проще, если у вас есть осциллограф, чья временная база несколько точна, или, по крайней мере, настолько точна, насколько вы хотите, чтобы эта задержка.

Я сам не изучал стоимость филиала ARM, иначе я бы прокомментировал это. Вероятно, это два или три часа. Таким образом, значение mov равно единице, а sub - умножению числа циклов на единицу, скажем, в два раза больше числа циклов. Два для филиала, чтобы получить здесь два для возвращения. 5 + (3 *) + петли NOPS = 12. (3 * петли) + nops = 7 циклов - 2, а nops - 1, да? Я думаю, что объединить несколько nops вместе гораздо проще:

delay_one_ms:
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    bx lr

Возможно, вам придется записать еще несколько инструкций, временно отключающих прерывания, если вы их используете. Если вы ищете «хотя бы» одну микросекунду, не беспокойтесь об этом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...