Продолжаем работу над программированием линейки контроллеров STM32F4 с использованием библиотеки LL.
Теперь давайте поработаем с таймерами.
С таймерами мы работаем уже давно и постоянно, также и с использованием библиотеки LL, но только общались мы с применением данной библиотеки с таймерами только применяя контроллеры серии F1. Теперь настала пора поработать и с серией F4.
Разница в таймерах между данными сериями не столь велика, поэтому, думаю, будет несложно. Тем более по архитектуре таймеров мы уже прошлись в уроке с применением библиотеки HAL, а по регистрам — при использовании библиотеки LL для серии F1 в уроке 147, поэтому в изучение аппаратной реализации нам углубляться на данном уроке сильно не придётся. Конечно же, если вдруг, встретится что-то новое в этом, мы обязательно такой вопрос изучим.
Плату мы по прежнему будем использовать STM32F429I-DISCOVERY.
Проект мы за основу возьмём с прошлого урока с именем LL_BLINK01 и присвоим ему имя LL_TIMER. Проект на основе существующего делается точно также, как и в случае использования IDE SystemWorkbench. Также данный процесс по пунктам я описал в текстовом файле, который лежит в архиве с проектом.
Откроем наш проект в Cube MX и включим там таймер 6, так как он один из самых простых по настройке
Настроим таймер на период 500 милисекунд и включим события по обновлению
Также включим глобальные прерывания
Можно, в принципе, немного назначить ниже приоритет данных прерываний (цифру ставим выше, чтобы назначить приоритет ниже, самый высокий — 0), чтобы они не толпились с прерываниями от системного таймера, иначе обычные задержки рискуют быть неточными, хотя и с одинаковыми приоритетами траблов замечено не было
Задействуем библиотеку LL под таймеры
Сгенерируем проект, откроем его в CubeIDE и для начала посмотрим, как происходит инициализация нашего таймера, для чего зайдём в тело функции MX_TIM6_Init, где сначала включается тактирование периферии при помощи функции LL_APB1_GRP1_EnableClock, вследствие чего устанавливается данный бит
Дальше назначается приоритет и включаются глобальные прерывания. Этот процесс мы пока не рассматриваем, так как это тема отдельного урока.
Затем заполняется структура и вызывается функция LL_TIM_Init, в теле которой выполняется ряд макросов и функций в зависимости от того, какой таймер. Рассмотрим только те условия, в которые мы попадём в случае использования нашего таймера.
Сначала считывается весь регистр CR1 в переменную
1 |
tmpcr1 = LL_TIM_ReadReg(TIMx, CR1); |
В условие макроса установки режима IS_TIM_COUNTER_MODE_SELECT_INSTANCE мы не попадаем.
Делителя у нас нет, поэтому в тело следующего условия if (IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx)) мы также не попадаем.
Далее идут вызовы функций без условий.
Первая функция LL_TIM_WriteReg(TIMx, CR1, tmpcr1) занесёт настройки в регистр CR1, считанные до этого и затем изменённые. Но так как мы никуда не попали, мы занесём в регистр то же, что и считали из него.
Далее с помощью вызова следующей функции данные из поля структуры Autoreload записываются регистр ARR
1 |
LL_TIM_SetAutoReload(TIMx, TIM_InitStruct->Autoreload); |
Регистр ARR полностью аналогичен одноимённому регистру из серии F1.
Далее устанавливается предделитель путём занесения данных из поля структуры в регистр PSC, также полностью аналогичный с одноимённым регистром серии F1, при помощи следующей функции
1 |
LL_TIM_SetPrescaler(TIMx, TIM_InitStruct->Prescaler); |
Следующее условие IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx) у нас также не выполнится, поэтому в его тело мы не попадём, , а дальше устанавливается бит UG в регистре EGR, так как мы выбрали в Cube обработку событий Update Event
1 |
LL_TIM_GenerateEvent_UPDATE(TIMx); |
Возвращаемся назад на уровень выше в функцию MX_TIM2_Init, где далее очищается бит ARPE в регистре CR1
1 |
LL_TIM_DisableARRPreload(TIM6); |
Далее в регистре CR2 устанавливаются или сбрасываются биты поля MMS также согласно установкам макроса, в нашем случае устанавливается бит MMS1, что означает выбор режима Update
1 |
LL_TIM_SetTriggerOutput(TIM6, LL_TIM_TRGO_UPDATE); |
И в заключении настройки таймера отключается режим MASTER/SLAVE сбросом бита MSM в регистре SMCR
1 |
LL_TIM_DisableMasterSlaveMode(TIM6); |
Переходим в функцию main, где сначала удалим весь пользовательский код из бесконечного цикла, а затем разрешим прерывания путём установки бита UIE в регистре DIER с помощью специальной функции библиотеки
1 2 |
/* USER CODE BEGIN 2 */ LL_TIM_EnableIT_UPDATE(TIM6); |
И далее непосредственно запускаем таймер, то есть заставляем его считать тики посредством установки бита CEN в регистре CR1
1 2 |
LL_TIM_EnableIT_UPDATE(TIM6); LL_TIM_EnableCounter(TIM6); |
Добавим пользовательский обработчик событий таймера, в котором для начала сбросим флаг прерывания, если таковой установлен
1 2 3 4 5 6 7 8 9 |
/* USER CODE BEGIN 4 */ void TIM6_Callback(void) { if(LL_TIM_IsActiveFlag_UPDATE(TIM6)) { LL_TIM_ClearFlag_UPDATE(TIM6); } } |
В данном условии при помощи функции LL_TIM_IsActiveFlag_UPDATE мы проверяем на установку бит UIF в регистре SR, затем при помощи функции LL_TIM_ClearFlag_UPDATE его же и сбрасываем.
Только наш обработчик сам по себе не запустится, его надо вызвать в основном обработчике, поэтому перейдём в файл stm32f4xx_it.c и для начала добавим для него прототип
1 2 |
/* USER CODE BEGIN PFP */ void TIM6_Callback(void); |
А затем мы его вызовем в функции TIM6_DAC_IRQHandler
1 2 |
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */ TIM6_Callback(); |
Вернёмся в файл main.c и добавим там глобальный пользовательский флаг для переключения текущего состояния светодиода
1 2 |
/* USER CODE BEGIN PV */ uint8_t tim6_flag; |
Далее в нашем обработчике TIM6_Callback в зависимости от состояния данного флага зажжём соответствующий светодиод, а другой погасим, а также переключим флаг
1 2 3 |
LL_TIM_ClearFlag_UPDATE(TIM6); if(tim6_flag & 0x01) {LED2_OFF(); LED1_ON(); tim6_flag = 0;} else {LED1_OFF(); LED2_ON(); tim6_flag = 1;} |
Соберём код, прошьём контроллер и увидим, что каждые полсекунды светодиоды на плате меняют состояние, то есть зажигаются и гаснут по очереди
Итак, на данном уроке мы научились работать с таймером контроллера линейки STM32F4, используя библиотеку LL.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F429I-DISCO
Смотреть ВИДЕОУРОК (нажмите на картинку)
а почему предыдущего урока нет?
Это происки врагов. Был дубль, удалил, а ссылки как раз наверно на него и были. Поправил. Простите за неудобства.