Hal delay stm32 не работает

ОБОРУДОВАНИЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ

Блог технической поддержки моих разработок

Урок 24. Организация коротких временных задержек в STM32. Библиотека DelayDWT.

В уроке научимся отрабатывать точные временные задержки малой длительности. Будем для этого использовать разные методы.

В уроках Ардино и в этом курсе, посвященном программированию STM32, я не устаю повторять, что программа должна выполняться параллельными процессами, операции производиться в фоновом режиме и т.д. Недопустимо, чтобы программа ”висела” в основном цикле, ожидая какого-либо события.

Но в некоторых ситуациях без коротких задержек не обойтись. Главное, чтобы они не блокировали программу на значительное время. Речь идет не об отладочных программах, а о реальных рабочих проектах. Примеров можно привести множество.

Один из самых типичных следующий. Часто возникает необходимость формировать задержку между сигналами при обращении к аппаратному устройству, модулю, микросхеме, подключенной к микроконтроллеру. Например, LCD-дисплей на базе контроллера HD4478 требует временные задержки между сигналами не менее 450, 800, 1000 нс и другие. В системах с Ардуино на ATmega328/168 изменение состояния цифрового вывода занимает не менее 4 мкс. О подобных задержках можно и не думать, а просто последовательно формировать сигналы.

STM32 более быстродействующий микроконтроллер. Если не принять специальных мер он переключит состояние выводов быстрее, чем допустимо и, например, дисплей работать не будет. Требуется искусственно формировать задержки. Бывает необходимо формировать не минимально допустимую задержку, а паузу с точно выдержанной длительностью.

Читайте также:  Пила штиль как настроить карбюратор

Попробую формализовать задачу. Урок посвящен способам формирования коротких точных временных задержек. Программа может при этом “зависать”. Если требуется формировать задержку не минимально-допустимую, а строго определенную, то могут быть даже запрещены прерывания.

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

HAL-функция задержки.

Упоминаю о ней формально. Функция простая. Все умеют ей пользоваться. Мы применяли ее в предыдущих уроках.

HAL_Delay

void HAL_Delay (uint32_t Delay)

Функция отрабатывает задержку, заданную в миллисекундах. Паузу менее 1 мс с помощью нее сформировать нельзя. Это главный недостаток функции. Миллисекундные импульсы не всегда можно считать короткими.

Для отсчета времени функция использует системные тики с периодом 1 мс, которые формирует системный таймер.

void SysTick_Handler(void)
<
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */

/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
>

Использование прерывания системного таймера.

Если функция HAL_Delay использует прерывание системного таймера, то почему бы нам не пойти тем же путем.

Создадим программный таймер в main.c

/* USER CODE BEGIN PV */
volatile uint32_t timer1=0; // программный таймер 1 мс
/* USER CODE END PV */

и в файле stm32f1xx_it.c

/* USER CODE BEGIN EV */
extern volatile uint32_t timer1; // программный таймер 1 мс
/* USER CODE END EV */

В обработчике прерывания системного таймера будем считать тики.

void SysTick_Handler(void)
<
/* USER CODE BEGIN SysTick_IRQn 0 */

Теперь в главном цикле мы можем считать значение нашего таймера, сбросить его.

/* USER CODE BEGIN WHILE */
while (1) <
timer1=0; while(timer1 // пауза 250 мс
HAL_GPIO_TogglePin(Led13_GPIO_Port,Led13_Pin); // инверсия светодиода

Светодиод мигает 2 раза в секунду.

Использование аппаратных таймеров.

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

Я создал проект Lesson24_1. Настроил систему тактирования на 72 мГц, вывод светодиода PC13 “развернул” на выход.

Настроил таймер 2 на режим простого счетчика со значением предделителя 71, регистра перезагрузки 65535.

Таймер 2 тактируется от синхросигнала шины APB1. У нас его частота 72 мГц. С учетом предделителя частота тактирования таймера будет 1 мГц или период 1 мкс. Запускаем таймер.

HAL_TIM_Base_Start(&htim2); // запуск таймера 2

Теперь он постоянно считает импульсы и его можно использовать для формирования коротких задержек.

TIM2->CNT= 0; while(TIM2->CNT // пауза 5 мкс

Следующая программа формирует импульс низкого уровня длительностью 5 мкс и периодом 20 мкс.

Led13_GPIO_Port -> BSRR = Led13_Pin // сброс бита
TIM2->CNT= 0; while(TIM2->CNT // пауза 5 мкс

Led13_GPIO_Port -> BSRR = Led13_Pin; // установка бита
TIM2->CNT= 0; while(TIM2->CNT // пауза 15 мкс

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

__disable_irq (); // запретить прерывания
Led13_GPIO_Port -> BSRR = Led13_Pin // сброс бита
TIM2->CNT= 0; while(TIM2->CNT // пауза 5 мкс
Led13_GPIO_Port -> BSRR = Led13_Pin; // установка бита
__enable_irq (); // разрешить прерывания

Время задержки отсчитывается от оператора

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

TIM2->CNT= 0; // начало отсчета задержки

while(TIM2->CNT // ожидание окончания задержки

Загрузить проект с примерами урока можно по ссылке Lesson24_1.

Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

Использование DWT-счетчика.

У микроконтроллера STM32 есть еще один скрытый таймер. Это элемент DWT-модуля (Data Watchpoint and Trace Unit), узла предназначенного для отладки системы.

Если забыть об остальных узлах модуля, то для нас DWT-таймер это 32х разрядный счетчик, значение которого увеличивается на единицу с каждым тактовым импульсом микроконтроллера. Для платы STM32F103C8T6 максимальная частота составляет 72 мГц, период 13,88 нс.

У DWT-счетчика есть адрес в пространстве памяти STM32 (0xE0001004). Значение счетчика можно считать и изменить. Перед использованием DWT-таймера необходимо разрешить его работу.

#define DWT_CYCCNT *(volatile uint32_t *)0xE0001004
#define DWT_CONTROL *(volatile uint32_t *)0xE0001000
#define SCB_DEMCR *(volatile uint32_t *)0xE000EDFC

SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // разрешение счётчика
DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk; // запуск счётчика

Теперь можно формировать задержку по принципу из предыдущих примеров урока.

DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT // задержка 5 мкс (5 * 72 )

Следующая программа формирует импульс низкого уровня длительностью 5 мкс и периодом 20 мкс.

SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // разрешение счётчика
DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk; // запуск счётчика

Led13_GPIO_Port -> BSRR = Led13_Pin // сброс бита
DWT->CYCCNT = 0; while(DWT->CYCCNT // задержка 5 мкс (5 * 72 )

Led13_GPIO_Port -> BSRR = Led13_Pin; // установка бита
DWT->CYCCNT = 0; while(DWT->CYCCNT // задержка 15 мкс (15 * 72 )

Проект с примером Lesson24_2

Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

К недостаткам способа следует отнести необходимость выполнения следующих действий:

  • выполнить в программе определения и установки регистров DWT;
  • вычислять константу для каждого времени задержки с учетом частоты тактирования микроконтроллера.

Библиотека DelayDWT.

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

void initDelayDwt(void)

Функция инициализации DWT счетчика. Разрешает работу счетчика, устанавливает по умолчанию время задержки 1 мкс. Должна быть выполнена до использования других функций библиотеки.

initDelayDwt(); //инициализация DWT-счетчика

void delayNs(uint32_t ns)

Формирование задержки, заданной в наносекундах.

Конечно, функция не обеспечивает разрешающую способность 1 нс. Но, даже для платы STM32F103C8T6, у которой командный цикл равен 13,88 нс, функция способна отрабатывать десятые доли микросекунды. Для более быстрых плат STM32 разрешающая способность будет еще меньше.

delayNs(5000); // задержка 5000 нс

inline void delaySetNs(void)

Формирование задержки, заданной в наносекундах, с заранее рассчитанным значением тиков.

В предыдущей функции значение задержки пересчитывается в число тиков DWT-счетчика. Используются 2 операции деления и одна умножения. Это занимает достаточно много времени и искажает конечную длительность задержки. Функция delaySetNs использует заранее вычисленное с помощью функции setDelay число тиков.

Также значительное время тратится на вызов самой функции. Поэтому она объявлена inline (встроенной) функцией. В результате время задержки отрабатывается значительно точнее.

setDelay(5000); // предварительный расчет задержки 5000 нс
delaySetNs(); // задержка 5000 нс

void setDelay(uint32_t ns)

Предварительный расчет числа тиков из значения задержки.

void delayMks(uint32_t mks)

Формирование задержки, заданной в микросекундах.

delayMks(5); // задержка 5 мкс

Для использования библиотеки DelayDWT необходимо произвести обычные действия, описанные в уроке 14.

  • Создаем каталог Libraries в корневой папке проекта и копируем туда папку с библиотекой DelayDWT.
  • В Atollic TrueStudio:
  • Подключаем библиотеку.

/* USER CODE BEGIN Includes */
#include «DelayDWT.h»

  • Правой кнопкой мыши нажимаем на папку DelayDWT в проекте, Add/Remove Include Path -> OK.
  • Правой кнопкой по Libraries, Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder -> Apply.
  • Надо не забыть инициализировать DWT-счетчик функцией initDelayDwt().

Для сравнения я реализовал цикл из 2х задержек с использованием разных функций.

initDelayDwt(); //инициализация DWT-счетчика
__disable_irq (); // запретить прерывания
setDelay(5000); // предварительный расчет задержки 5000 нс

Led13_GPIO_Port -> BSRR = Led13_Pin // сброс бита
//delayMks(5);
//delayNs(5000);
delaySetNs();

Led13_GPIO_Port -> BSRR = Led13_Pin; // установка бита
//delayMks(5);
//delayNs(5000);
delaySetNs();
>

В идеальном случае частота сигнала на выводе PC13 должна быть 100 кГц, период 10 мкс. У меня получилось.

Функция Частота Период
delayNs(5000) 84,81 кГц 11,79 мкс
delaySetNs() 96,78 кГц 10,33 мкс

Функция delaySetNs отрабатывает время точнее. С учетом того, что в цикле по 2 вызова функций задержки, а также требуется время для перехода на начало цикла и переключение вывода, длительность импульса выдерживается достаточно точно.

Загрузить библиотеку DelayDWT можно по этой ссылке.

Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

Пример работы с библиотекой Lesson24_3.

Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!

Вроде тема незначительная, но как мне кажется, получился очень полезный практичный урок.

В следующем уроке я собираюсь рассказать о подключении к STM микроконтроллерам знакосинтезирующих LCD индикаторов. Представлю свою библиотеку.

Источник

HAL_Delay () застрял в бесконечном цикле

Я застрял с функцией HAL_Delay (). Когда я вызываю эту функцию HAL_Delay (), управление застревает в бесконечном цикле. В поисках проблемы я обнаружил это

В этом конкретном комментарии, в котором говорится, и я цитирую: «Существует проблема с файлом компоновщика, используйте прилагаемый. Вам необходимо сопоставить два банка памяти отдельно, поэтому сначала SRAM1 96 КБ, а затем SRAM2 32 КБ. Я думаю, что об этом следует сообщить как об ошибке в CubeMX, поскольку он создает плохой файл компоновщика. » и есть два файла с расширением .ld.

Я ищу, как использовать эти файлы в моем проекте ИЛИ любой другой лучший вариант решения этой проблемы.

PS . Я использую плату обнаружения stm32l476, Cubemx 5.0.0 и Atollic True Studio.

ИЗМЕНИТЬ

В моем проекте используется связь RS485, откуда я беру данные, и у меня есть две задачи с этими данными, отображать их на дисплее MAX7219 и отправлять в Интернет с помощью модуля sim800 gsm.

Код, в котором застрял элемент управления. обратите внимание, что эта функция вызывается только при выполнении задач GSM.

Эта функция записывает In Delay в терминал, но Out Delay не отображается. Но у меня также есть таймер, который запускается каждые 2 секунды для отображения данных на MAX72219.

После залипания регулятора эта функция всегда срабатывает через 2 секунды. Таким образом, вывод, что каким-то образом контроль застрял в HAL_Delay() .

IMP THING

Проблема возникает каждый раз, но нет определенного времени, т.е. элемент управления может зависнуть через 5 минут, 10 минут или 15 минут. Он не зависает от конкретной функции. Функции могут быть разными. то есть иногда он может зависнуть от имени функции getIMEI() , а иногда и мне get service provider

2 ответа

Все функции задержки и тайм-аута реле HAL на счетчике увеличены в обработчике SysYick. Если вы используете любую из этих функций в другом прерывании, вы должны убедиться, что приоритет прерывания SysTick выше, чем приоритет прерывания, обработчик которого вызывает эти функции. В противном случае обработчик SysTick никогда не вызывается, и вы попадете в бесконечный цикл, поскольку счетчик никогда не будет увеличиваться.

Исправление:

Резюме:
Увеличьте приоритет SysTick_Handler NVIC (уменьшив его числовое значение NVIC, которое находится в диапазоне от 0 до 15).

Детали :
То, что @P__J__ говорит в своем ответе здесь, правильно, и я тоже подозреваю, что это ваша проблема. Чтобы исправить это, вам нужно сделать так, чтобы ваше прерывание SysTick имело приоритет NVIC (Nested Vectored Interrupt Controller) выше , чем любые другие прерывания, которые выполняют вызовы HAL, которые могут зависеть от увеличения системного тика. Сюда входят все вызовы HAL с тайм-аутом, например, а также задержки HAL. Более высокий приоритет NVIC означает, что вы должны сделать его более низким числовым значением, поскольку наивысший приоритет NVIC равен 0, а самый низкий — 15 для микросхем STM32 в конфигурации по умолчанию.

Чтобы установить приоритеты NVIC в STM32CubeMX 5, перейдите в «Распиновка и конфигурация» -> «Ядро системы» -> (щелкните маленькую стрелку вверх / вниз, чтобы перейти на страницу, на которой отображается NVIC), затем нажмите «NVIC -> Уменьшить» «Приоритет приоритетного обслуживания» должен быть ниже (более высокий приоритет, чем) любых других ISR, полагающихся на вызовы HAL.

Вот скриншот. Обратите внимание, что вы также можете перейти на этот экран, нажав кнопку «Просмотр системы» рядом с «Просмотр выводов», а затем нажав «NVIC» в разделе «Ядро системы».

Дополнительная информация о HAL_IncTick(); :

Из файла «stm32f4xx_it.c» вы увидите, что SysTick_Handler ISR вызывает HAL_IncTick(); :

Если вы нажмете Ctrl + щелчок по нему (по крайней мере, в System Workbench / Eclipse), чтобы перейти к реализации HAL_IncTick() , вы увидите следующее, что дает дополнительную информацию в комментариях:

Эта функция HAL_IncTick() находится внутри файла «. STM32Cube_FW_F4_V1.19.0 / Drivers / STM32F4xx_HAL_Driver / Src / stm32f4xx_hal.c», который также содержит функцию HAL_InitTick() чуть выше HAL_IncTick() . Его комментарии очень проницательны:

Обратите особое внимание: часть, в которой говорится:

Необходимо соблюдать осторожность, если HAL_Delay () вызывается из периферийного процесса ISR,
Прерывание SysTick должно иметь более высокий приоритет (численно более низкий)
чем периферийное прерывание. В противном случае процесс ISR вызывающего абонента будет заблокирован.

Это именно то место, где я это узнал.

Обязательно бегайте по коду и иногда просматривайте функции и документацию внутри самого исходного кода ST HAL, чтобы найти подобное скрытое понимание. Сделайте это, конечно, в дополнение к ссылкам на следующие основные документы:

Ключевые документы STM32 для вашего чипа в порядке приоритета (наиболее важные в первую очередь):

Источник

Оцените статью