

Сегодня поговорим про организацию задержек. Посмотрим какие задержки в STM32 лучше или хуже подходят для той или иной ситуации.
Ни одна программа для микроконтроллера не обходится без необходимости использовать задержку. Существует несколько вариантов построения функции задержки. Рассмотрим некоторые ниже.
Функция HAL_Delay
Одним из способов использования задержки, является функция STM32 HAL: HAL_Delay(uint32 delay). Данная функция использует системный таймер SysTick и настроена на 1 мс (миллисекунду). Если необходимо получить время меньше 1 миллисекунды, пишем свою функцию задержки.
ВНИМАНИЕ! Важно знать, что бы функция HAL_Delay работала в прерываниях, необходимо, что бы приоритет этого прерывания был ниже чем приоритет прерывания системного таймера. В противном случае программа зависнет в функции HAL_Delay. Причина в том, что функция проверяет значение счетчика uwTick, которое как раз таки и изменяется в прерывании системного таймера SysTick. И если приоритет у SysTick ниже или равен, то счетчик uwTick изменятся не будет.
Миллисекундная задержка
Как же нам получить время задержки меньше 1 миллисекунды? Давайте рассмотрим несколько способов.
Задержки for
Одним из вариантов организации задержки будет использования простого цикла for:
void delay(uint32_t time_delay) { uint32_t i; for(i = 0; i < time_delay; i++); }
Минусом такой задержки будет невысокая точность, а так же то, что придется постоянно подбирать переменную time_delay под новую частоту микроконтроллера. Так что, если высокая точность не нужна, то можно воспользоваться данным вариантом.
Функции задержки с таймером SYSTick
Использование системного таймера в своих целях для задержки нет ничего сложного. Ниже приведен код для настройки системного таймера.
//Настройка системного таймера на 1 миллисекунду RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); //Чтобы получить 1 микросекунду надо разделить на 1 000 000 //Объявим счетчик в прерывании SYS tick void SysTick_Handler() { msTicks++; }
Функция задержки на встроенном таймере
Здесь тоже все просто. Берем один из таймеров в микроконтроллере и настраиваем его срабатывание на 1 мкс. Затем пишем свою функцию задержки:
void usDelay(uint16_t useconds) { // Обнуляем счетчик таймера __HAL_TIM_SET_COUNTER(&htim4, 0); __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE); // ждем пока счетчик не достигнет заданного времени while(__HAL_TIM_GET_COUNTER(&htim4) < useconds) { // Ставим проверку, если вдруг счетчик переполниться if (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE) != RESET) { break; } } }
Перед использованием данного кода, не забудьте запустить этот таймер функцией
HAL_TIM_Base_Start(&htim4);
Пока просто используйте этот код, как настроить таймер STM32, разберем подробно в отдельной теме.
Функция задержки с использованием DWT
На модуле DWT подробно останавливаться не будем. Если коротко, то он предназначен для работы отладчика. Реализует возможности предоставляемые отладкой. Нам будет интересен его 32 разрядный счетчик. На его основе мы и реализуем свою миллисекундную задержку.
#define DWT_CONTROL *(volatile unsigned long *)0xE0001000 #define SCB_DEMCR *(volatile unsigned long *)0xE000EDFC void DWT_Init(void) { SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // разрешаем использовать счётчик DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk; // запускаем счётчик } void delay_micros(uint32_t us) { uint32_t us_count_tic = us * (SystemCoreClock / 1000000); // получаем кол-во тактов за 1 мкс и умножаем на наше значение DWT->CYCCNT = 0U; // обнуляем счётчик while(DWT->CYCCNT < us_count_tic); }
Для использования данной задержки, в начале функции main вызываем DWT_Init. И затем в нужном месте delay_micros с нужным временем задержки в микросекундах.