вівторок, 1 березня 2016 р.

Відлік часу без таймерів

Альтернативний спосіб відліку часу
Кам'яна сокира. (c) Wiki

Відмірювати час за допомогою таймерів -- найбільш зручний і коректний спосіб. Однак, іноді він може не підходити, з тих чи інших мотивів. Одна з альтернатив -- чисто програмна затримка. Іншими словами, контролер змушують якийсь час виконувати задані інструкції. Якщо потік виконання один і переривань не виникає, такий спосіб доволі точний (а з перерахунку вимог, щоб він був точним, видно основні його недоліки). Важливо, що на відміну від x86, час виконання інструкцій мікроконтролера значно більш передбачуваний.

Наведений нижче приклад реалізації базується на коді з Leaflab Maple IDE, аналогу оболонки Ардуїно для STM32. Увага: Приклад може бути неточним -- особливо ретельно я не тестував.

Update: Див. також розвиток теми в "Мікросекундні затримки та відлік мікросекунд для STM32".


//! Затримка в мікросекундах
inline static void delay_some_us1 (uint32_t dlyTicks) {
    // Взято з leaflabs Maple
    dlyTicks *= 6; 

    /* Невелика і не дуже точна поправка на час виклику функції */
    dlyTicks--;
    asm volatile("   mov r0, %[dlyTicks]          \n\t"
                 "1: subs r0, #1            \n\t"
                 "   bhi 1b                 \n\t"
                 :
                 : [dlyTicks] "r" (dlyTicks)
                 : "r0");
}

  • inline переконує компілятор вбудовувати цю функцію, щоб її виклик не займав зайвих тактів, збільшуючи похибку. static каже, що більше ніде, за межами цієї одиниці трансляції, вона не викликатиметься, вважається -- це збільшує шанс, що компілятор врахує прохання про inline (як відомо, даний специфікатор лише дорадчий -- може бути проігнорованим).
  • Підрахунок тактів кожної інструкції базується на "Cortex™-M3 Technical Reference Manual: 18.2. Processor instruction timings"
  • mov - 2 такти (увага, вона не крутиться в циклі)
  • subs - 1
  • bhi - 1+2 якщо відбувається перехід (а в циклі всі ітерації, крім останньої, він відбудеться), 1 якщо ні
  • Всього тривалість циклу -- 4 такти
  • 1 такт = 1/24 мкс = 41.6 нс; 1 мкс - 24 такти
  • 4 такти -- 0.167 мкс
  • Одна мікросекунда -- 1/0.167 = 6 ітерацій циклу. Тому, власне, на початку кількість мікросекунд множиться на 6.
  • Перед початком ітерацій віднімаємо від лічильника 1, щоб частково скомпенсувати час виконання тіла функції. На жаль, більш точної корекції досягнути складно -- кількість інструкцій в функції залежить від компілятора та його опцій, а час виконання може залежати і від контексту виклику.
  • Деякі ARM Cortex M мають апаратний лічильник тактів (схожий на Time Stamp Counter в x86) -- DWT. Детальніше про нього пише в офіційній документації: "8.3. DWT Programmers Model", та в обговореннях використання: 1, 2. Однак STM32F100RB його не містить :-(
При всіх недоліках такого підходу, іноді його може бути досить.

Немає коментарів:

Дописати коментар