В данной статье мы рассмотрим как использовать таймеры и задержки в FreeRTOS с помощью ESP32 и ESP-IDF. Также в статье приведено сравнение функции vTaskDelay(), предоставляемой FreeRTOS, с различными значениями задержки в миллисекундах.
Таймер высокого разрешения (ESP Timer), предоставляемый FreeRTOS, использует 64-битный аппаратный таймер. FreeRTOS также предоставляет программные таймеры, которые более удобны в использовании, однако они имеют некоторые недостатки. Для обратных вызовов таймера назначается задача с низким приоритетом, а максимальное разрешение устанавливается равным периоду такта RTOS. С помощью esp_timer генерируются периодические таймеры с разрешением в микросекунды и диапазоном в 64 бита. Более того, обратные вызовы таймера инициируются из задачи esp_timer с высоким приоритетом.

Прежде чем продолжить, убедитесь, что у вас установлена последняя версия VS Code и настроено расширение ESP-IDF:
- Установка ESP32 ESP-IDF на Windows и его интеграция с Visual Studio Code
- Установка ESP32 ESP-IDF на Linux Ubuntu
Также на нашем сайте вы можете прочитать статью про начало работы с ESP32 с использованием ESP-IDF.
Таймеры FreeRTOS для ESP32
Поскольку ESP-IDF основан на операционной системе FreeRTOS, мы начнем с подключения общей библиотеки FreeRTOS. Для использования функциональности таймера мы подключим заголовочный файл «freertos/timers.h».
|
1 |
#include "freertos/timers.h" |
Чтобы получить время с момента загрузки в микросекундах, мы используем функцию esp_timer_get_time(). Эта функция возвращает количество микросекунд с момента инициализации esp_timer, которая запускается до вызова функции app_main().
|
1 |
int64_t esp_timer_get_time(void) |
Код для получения времени таймера (время в микросекундах с момента загрузки)
Это полный код, который мы используем для вывода времени таймера в последовательный монитор. Функция esp_timer_get_time() вернет нам время в микросекундах с момента запуска приложения.
|
1 2 3 4 5 6 7 8 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { printf("Timer: %lld μs\n", esp_timer_get_time()); } |
Результаты показывают, что с момента запуска приложения прошло 169219 микросекунд.
Получение интервала времени между прерываниями (portTick_PERIOD_MS / portTICK_RATE_MS)
В операционной системе FreeRTOS используются различные расписания тактов. Это время между прерываниями. Некоторые из них предопределены, например, интервалы тактов между прерываниями. В FreeRTOS это определяется как portTick_PERIOD_MS. В более старой версии это определялось как portTICK_RATE_MS .
Это полный код, который мы будем использовать для вывода значений portTick_PERIOD_MS и portTICK_RATE_MS в последовательный терминал.
|
1 2 3 4 5 6 7 8 9 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { printf("portTICK_PERIOD_MS : %d\n", portTICK_PERIOD_MS); printf("portTICK_RATE_MS : %d\n", portTICK_RATE_MS); } |
В выходных данных указано, что параметры portTick_PERIOD_MS и portTICK_RATE_MS заданы как 10 миллисекунд. Это подходит для частоты 100 МГц, установленной в процессоре.
Задержка в тиках FreeRTOS
Далее мы покажем, как добавлять задержки с помощью FreeRTOS. Для генерации задержки мы будем использовать функцию «vTaskDelay». Она принимает параметр типа integer (uint32), который представляет собой константу TickType, определяющую количество тактов, которые нужно задержать. Это число соответствует такту, то есть частоте микросхемы. В функции vTaskDelay() получаемая задержка соответствует задержке конкретной задачи в количестве тактов.
|
1 |
vTaskDelay(1); |
В следующей программе мы измерим время работы таймера до и после задержки в 1 такт. Таким образом, мы сможем найти минимальную разницу для задержки выполнения задачи.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { long long int Timer1 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer1); vTaskDelay(1); long long int Timer2 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer2); long long int diff = Timer2 - Timer1; printf("Difference: %lld μs\n", diff); } |
В выходных данных мы видим два значения таймера (время в микросекундах с момента загрузки), полученные после задержки в 1 такт. Разница между ними составляет 5383 микросекунды. Как мы уже упоминали, период такта задан как 10 миллисекунд. Однако на выполнение потребовалось меньше 10 миллисекунд.
Задержка FreeRTOS в миллисекундах
В предыдущем примере мы рассмотрели функцию vTaskDelay() и измерили задержку в тактах. Однако чаще всего мы используем эту функцию для генерации задержек в миллисекундах.
Чтобы перевести такты в миллисекунды, мы разделим количество миллисекунд на portTICK_PERIOD_MS или portTICK_RATE_MS. Параметр portTICK_PERIOD_MS является частью директивы #defines. Он настраивается при компиляции скетча и по умолчанию устанавливается равным скорости работы микросхемы. Таким образом, деление 1 на количество тактов, в течение которых работает микросхема, даст нам миллисекунды.
|
1 |
vTaskDelay(1 /portTICK_PERIOD_MS); |
|
1 |
vTaskDelay(1 /portTICK_RATE_MS); |
В качестве альтернативы, мы также можем указать аргумент функции vTaskDelay() как pdMS_TO_TICKS() и указать количество миллисекунд в скобках.
|
1 |
vTaskDelay(pdMS_TO_TICKS(1)); |
Все три команды должны обеспечить задержку в 1 миллисекунду. Однако функция vTaskDelay() не очень точна для очень малых значений задержки. Давайте это проверим.
В приведенном ниже коде мы получаем значения таймера с задержкой в 1 миллисекунду.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { long long int Timer1 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer1); vTaskDelay(1/ portTICK_PERIOD_MS); long long int Timer2 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer2); float diff = Timer2 - Timer1; printf("Difference: %f ms\n", diff/1000); } |
Как видно из выходных данных, разница между двумя значениями таймера превышает 1 миллисекунду, поскольку задержка была слишком мала для идеальной работы. Нам удалось создать задержку приблизительно в 1,64 миллисекунды.
Давайте попробуем увеличить задержку до 1000 миллисекунд. Мы создадим задержку в 1000 миллисекунд, используя следующую строку кода:
|
1 |
vTaskDelay(1000/ portTICK_PERIOD_MS); |
Вот полный код, который мы используем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { long long int Timer1 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer1); vTaskDelay(1000/ portTICK_PERIOD_MS); long long int Timer2 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer2); float diff = Timer2 - Timer1; printf("Difference: %f ms\n", diff/1000); } |
Как видно из выходных данных, задержка между двумя значениями таймера теперь очень близка к 1000 миллисекундам. Следовательно, функция vTaskDelay() работает точнее при немного больших задержках.
Сравнение значений vTaskDelay() в миллисекундах
Теперь сравним задержки для 1, 10, 100 и 1000 миллисекунд, используя функцию vTaskDelay(). В приведенном ниже коде мы использовали программу, аналогичную предыдущему примеру. Однако теперь мы увеличили количество выводимых значений таймера с увеличением задержки. Таким образом, мы будем выводить значения таймера каждые 1 миллисекунду, 10 миллисекунд, 100 миллисекунд и 1000 миллисекунд. Эти значения будут использоваться для сравнения точности генерации задержки.
Вот полный код, который мы будем использовать:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/timers.h" void app_main() { printf("Timer for 1 millisecond\n"); long long int Timer1 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer1); vTaskDelay(1/ portTICK_PERIOD_MS); long long int Timer2 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer2); float diff1 = Timer2 - Timer1; printf("Difference: %f ms\n", diff1/1000); printf("\n"); printf("Timer for 10 milliseconds\n"); long long int Timer3 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer3); vTaskDelay(10/ portTICK_PERIOD_MS); long long int Timer4 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer4); float diff2 = Timer4 - Timer3; printf("Difference: %f ms\n", diff2/1000); printf("\n"); printf("Timer for 100 milliseconds\n"); long long int Timer5 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer5); vTaskDelay(100/ portTICK_PERIOD_MS); long long int Timer6 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer6); float diff3 = Timer6 - Timer5; printf("Difference: %f ms\n", diff3/1000); printf("\n"); printf("Timer for 1000 milliseconds\n"); long long int Timer7 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer7); vTaskDelay(1000/ portTICK_PERIOD_MS); long long int Timer8 = esp_timer_get_time(); printf("Timer: %lld μs\n", Timer8); float diff4 = Timer8 - Timer7; printf("Difference: %f ms\n", diff4/1000); } |
Как видно из результатов, увеличение задержки дает более точный результат по сравнению с меньшими значениями задержки. Это также было доказано в предыдущем примере.
54 просмотров









