THE BELL

Есть те, кто прочитали эту новость раньше вас.
Подпишитесь, чтобы получать статьи свежими.
Email
Имя
Фамилия
Как вы хотите читать The Bell
Без спама

Мы уже рассматривали таймер SysTick, который является частью ядра Cortex. Однако на этом всё не заканчивается. Таймеров в stm32 много, и они бывают разные. В зависимости от цели вам придется выбрать тот или иной таймер:

  • SysTick;
  • таймеры общего назначения (англ. general purpose timer) - TIM2, TIM3, TIM4, TIM15, TIM16, TIM17;
  • продвинутый таймер (англ. advanced timer) - TIM1;
  • сторожевой таймер (англ. watchdog timer).

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

Таймеры бывают разной битности: например, SysTick - 24-битный, а все таймеры, что имеются у нас в камне - 16-битные (т. е. могут считать до 2 16 = 65535), кроме WatchDog. Кроме того, каждый таймер имеет некоторое количество каналов, то есть, по сути, он может работать за двоих, троих и т. д. Эти таймеры умеют работать с инкрементными энкодерами, датчиками Холла, могут генерировать ШИМ (широтно-импульсная модуляция, англ. Pulse Width Modulation - о которой мы поговорим позже) и многое другое. Кроме того, они могут генерирувать прерывания или совершать запрос к другим модулям (например, к DMA - Direct Memory Access) по разным событиям:

  • переполнение (англ. overflow);
  • захват сигнала (англ. input capture);
  • сравнение (англ. output compere);
  • событие-триггер (англ. event trigger).

Если с переполнением таймера (точнее, достижением «0») нам всё понятно - мы рассматривали SysTick - то с другими возможными режимами работы мы еще не знакомы. Давайте рассмотрим их подробнее.

Захват сигнала

Этот режим хорошо подходит, чтобы измерять период следования импульсов (или их количество, скажем, за секунду). Когда на выход МК приходит импульс, таймер генерирует прерывание, с которого мы можем снять текущее значение счетчика (из регистра TIM_CCRx , где x - номер канала) и сохранить во внешнюю переменную. Затем придет следующий импульс, и мы простой операцией вычитания получим «время» между двумя импульсами. Отлавливать можно как передний, так и задний фронт импульса, или даже сразу оба. Зачем это нужно? Предположим, у вас есть магнит, который вы приклеили к диску на колесе, а датчик Холла - к вилке велосипеда. Тогда, вращая колесо, вы будете получать импульс каждый раз, когда магнит на колесе будет в той же плоскости, что и датчик Холла. Зная расстояние которое пролетает магнит за оборот и время можно вычислить скорость движения.

Также существует режим захвата ШИМ, однако это скорее особый способ настройки таймера, нежели отдельный режим работы: один канал ловит передние фронты, а второй - задние фронты. Тогда первый канал детектирует период, а второй - заполнение.

Режим сравнения

В таком режиме выбранный канал таймера подключается к соответствующему выводу, и как только таймер достигнет определенного значения, состояние вывода изменится в зависимости от настройки режима (это может быть «1» или «0», или состояние на выходе просто инвертируется).

Режим генерации ШИМ

Как понятно из названия, таймер в таком режиме генерирует широтно-импульсную модуляцию. Подробнее о таком режиме, а также о том, где его можно / стоит применять, мы поговорим в следующем занятии после рассмотрения захвата сигнала.

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

Режим Dead-time

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

В курсе мы будем использовать только «захват сигнала» и «генерацию ШИМ».

Добрый день. Сегодня набросаю первую статейку по таймерам в STM32. Вообще таймеры в STM32 настолько круты, что даже Шварцнегер нервно курит по крутости))) И изучать их придётся не в одной, и не в двух и не в трёх статьях. Но для начала не будем забивать себе сильно головы, а просто изучим первые простые таймеры и поработаем с ними.

В STM32 вообще существует три вида таймеров
1) базовые (basic timers)
2)общего назначения (general-purpose timers)
3)продвинутые (advanced-control timers)

Продвинутые таймеры самые крутые и в себе сочитают возможности двух предыдущих групп, плюс к этому ещё множество дополнительных функций типа работа с трёхфазными моторами и т.д. и т.п. До них нам ещё далеко, поэтому в данной части мы будем рассматривать работу с базовыми (basic timers).
Для начала давайте рассмотрим, какие есть таймеры на нашем процессоре STM32F407VG (вы смотрите про свои процессоры с которыми работаете)). В моём процессоре 14 таймеров — 12 — 16ти битных и 2 32 битных

Как мы видим на картинках к шине АРВ1 подключены таймеры TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM12
А к шине АРВ2 — TIM1, TIM8, TIM9, TIM10, TIM11
Теперь давайте рассмотрим картинку настройки нашего тактирования в программе CubeMX. Систему тактирования я ещё отдельно опишу, так как без неё никуда, но просто пока покажу как можно затактировать наши таймеры используя внутренний источник тактирования HSI.
Вот наша стандартная настройка тактирования без всяких перемножителей частот и т.д. Её мы и будем использовать.

А вот вариант ускорения работы)) Но советую шаловливыми ручёнками туда сильно не лазить, а то может уложить процессор на лопатки)) Это всё мы потом изучим и рассмотрим.

Итак, открываем Reference Manual на F4 серию микроконтроллеров, и начинаем курить мануал. ДА, в STM32 не всё так просто, поэтому товарищи учите английский, и читайте мануалы, потому что без этого будете долго искать что к чему. Я раньше как то очень тяжко к чтению документации относился (видать потому что задачи были простыми и мне хватало обычных примеров из интернета). Ну а теперь читаем… читаем…читаем…
Продолжим…
Итак таймеры 6 и 7 являются базовыми таймерами. Сидят они на шине АРВ1 как мы видим на картинке из reference manual.

Базовые таймеры 6 и 7 — 16ти битные, имеют настраиваемый предделитель от 0 до 65535. Для этих таймеров есть вот такие регистры доступные для чтения\записи.
Counter Register (TIMx_CNT) — счётчик
Prescaler Register (TIMx_PSC) — предделитель
Auto-Reload Register (TIMx_ARR) — регистр перезагрузки

Не будем сильно углубляться в подробности работы, так как там страниц 10 описания доступных нам регистров и т.д, нам хватит трёх — написанных выше
Итак, что это за регистры и для чего они нам нужны. Да вот для чего. Решили мы тут срочно помигать светодиодом, удивить товарищей AVR-щиков например, и говорим — а давай кто быстрее настроит мигание одним светодиодом с периодом пол секунды, а вторым с периодом в секунду тот и выиграл. (кстати можно проделать подобный эксперимент))))
Для того чтобы это нам реализовать нужно всего 5 шагов — 1
1) Запустить CubeMX и создать проект под наш контроллер.
2)в CubeMX выставить работу таймеров
3) сгенерировать проект и открыть его в Keil uVision
4)проинициализировать таймеры (по одной строчке на таймер)
5)прописать в прерывании каждого таймера код постоянного изменения состояния ножки к которой подключен светодиод.
Итак, давайте это рассмотрим более подробно. Первым делом запускам нашу программу CubeMX
и настраиваем наши 2 вывода PD12 и PD13 на вывод (ножки куда подключены светодиоды). Устанавливаем для них режим GPIO_Output, и режим Output Push_Pull.
Далее слева активируем наши базовые таймеры 6 и 7.

Теперь переходим в вкладку конфигурации. Как мы помним, мы не стали ничего менять в настройках частот для нашего процессора, поэтому у нас все шины тактируюся частотой -16МГц. Теперь исходя из этого, и исходя из того что нам нужно получить, давайте настроим наши значения предделителей и регистра автоперезагрузки.

Как мы помним, нам нужно чтобы один светодиод мигал с частотой 1Гц (период 1000мсек), а второй с частотой 2Гц (период 500 мсек) . Как нам это получить — да очень просто. Так как предделитель на СТМ32 можно ставить любой, то мы просто вычислим его значение
Итак частота у нас 16 000 000 тиков в секундку, а нужно 1000 тиков в секунду. Значит 16 000 000 \ 1 000 = 16 000. Это число минус 1 и вписываем в значение предделителя. То есть число у нас получается 15999.
Теперь наш таймер тикает с частотой 1000 раз в секунду. Далее, мы должны указать когда же нам нужно прерывание по переполнению. Для этого мы записываем нужное нам число в Counter Period (autoreload register).
То есть нам нужно получить одно прерывание в секунду, а как мы помним наш таймер тикает 1 раз в милисекунду. В одной секнуде — 1000 мсек — значит это значение и вписываем в регистр автоперезагрузки.
Для того, чтобы получить прерывание раз в пол секунды — записываем соответсвенно — 500.

Итак — настроили, теперь можно смело генерировать наш проект. Сгенерировали, хорошо. осталось совсем чуток до момента мигания светодиодиками.
Открыли наш проект. У нас впринципе всё настроено и готово, только нужно запустить наши таймеры, так как хоть CubeMX всё за нас и делает — этим он уже не занимается. Итак- инициализируем
наши таймеры вот такими строчками

HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);

Именно в нём и находятся наши обработчки прерывания для наших таймеров
Вот обработчик прерывания для таймера 7

void TIM7_IRQHandler(void)
{
/* USER CODE BEGIN TIM7_IRQn 0 */

/* USER CODE END TIM7_IRQn 0 */
HAL_TIM_IRQHandler(&htim7);
/* USER CODE BEGIN TIM7_IRQn 1 */

/* USER CODE END TIM7_IRQn 1 */
}

Вписываем в обработчик прерывания то что мы хотим делать — а мы хотим в каждом прерывании менять состояние наших ножек к которым подключены свтеодиоды.
Используем вот такую конструкцию — HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13) ;

Собственно всё. Нажимаем F7, смотрим чтобы не было ошибок — и можем заливать всё это дел в наш подопытный процессор.
Ну и можем уже наслаждаться интересными перемигиваниями светодиодов.
Видео добавлю чуть позже, ну а пока как обычно правильна картинка. Ну и не забываем про благодарность))

В STM32 есть множество очень удобных и гибких в настройке таймеров. Даже у самого младшего микроконтроллера (STM32F030F4P6) есть 4 таких таймера.

8. Настроим проект - добавим нужные файлы

Чтобы использовать таймер, нам потребуется подключить файл библиотеки периферии stm32f10x_tim.c. Точно так же, правой кнопкой щёлкаем в Workspace (окно слева) по группе StdPeriphLib, Add –> Add files, файл LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c.

Ещё нужно включить использование заголовка к этому файлу. Открываем stm32f10x_conf.h (правой кнопкой по названию этого файла в коде, «Open stm32f10x_conf.h». Раскомментируем строчку #include «stm32f10x_tim.h».

9. Добавим таймер

Задержка пустым циклом - это кощунство, тем более на таком мощном кристалле как STM32, с кучей таймеров. Поэтому сделаем эту задержку с помощью таймера.

В STM32 есть разные таймеры, отличающиеся набором свойств. Самые простые - Basic timers, посложнее - General purpose timers, и самые сложные - Advanced timers. Простые таймеры ограничиваются просто отсчётом тактов. В более сложных таймерах появляется ШИМ. Самые сложные таймеры, к примеру, могут сгенерировать 3–фазный ШИМ с прямыми и инверсными выходами и дедтаймом. Нам хватит и простого таймера, под номером 6.

Немного теории

Всё, что нам требуется от таймера - досчитывать до определённого значения и генерировать прерывание (да, мы ещё и научимся использовать прерывания). Таймер TIM6 тактируется от системной шины, но не напрямую а через прескалер - простой программируемый счётчик–делитель (подумать только, в СССР выпускались специальные микросхемы–счётчики, причём программируемые были особым дефицитом - а теперь я говорю о таком счётчике просто между делом). Прескалер можно настраивать на любое значение от 1 (т.е. на счётчик попадёт полная частота шины, 24МГц) до 65536 (т.е. 366 Гц).

Тактовые сигналы в свою очередь, увеличивают внутренний счётчик таймера, начиная с нуля. Как только значение счётчика доходит до значения ARR - счётчик переполняется, и возникает соответствующее событие. По наступлению этого события таймер снова загружает 0 в счётчик, и начинает считать с нуля. Одновременно он может вызвать прерывание (если оно настроено).

На самом деле процесс немного сложнее: есть два регистра ARR - внешний и внутренний. Во время счёта текущее значение сравнивается именно со внутренним регистром, и лишь при переполнении внутренний обновляется из внешнего. Таким образом, можно безопасно менять ARR во время работы таймера - в любой момент.

Код

Код будет очень похож на предыдущий, т.к. инициализация всей периферии происходит однотипно - за тем лишь исключением, что таймер TIM6 висит на шине APB1. Поэтому включение таймера: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Теперь заводим структуру типа TIM_TimeBaseInitTypeDef, инициализируем её (TIM_TimeBaseStructInit), настраиваем, передаём её в функцию инициализации таймера (TIM_TimeBaseInit) и наконец включаем таймер (TIM_Cmd).

TIM_TimeBaseInitTypeDef TIM_InitStructure; // Заводим структуру TIM_TimeBaseStructInit(&TIM_InitStructure); // Инициализация структуры TIM_InitStructure.TIM_Prescaler = 24000; // Предделитель TIM_InitStructure.TIM_Period = 1000; // Период таймера TIM_TimeBaseInit(TIM6, &TIM_InitStructure); // Функция настройки таймера TIM_Cmd(TIM6, ENABLE); // Включение таймера

Что за магические числа? Как мы помним, на шине присутствует тактовая частота 24МГц (при наших настройках проекта). Настроив предделитель таймера на 24000, мы поделим эту частоту на 24 тысячи, и получим 1кГц. Именно такая частота попадёт на вход счётчика таймера.

Значение же в счётчике - 1000. Значит, счётчик переполнится за 1000 тактов, т.е. ровно за 1 секунду.

После этого у нас действительно появляется работающий таймер. Но это ещё не всё.

10. Разберёмся с прерываниями

Окей, прерывания. Для меня когда–то (во времена PIC) они были тёмным лесом, и я старался вообще их не использовать - да и не умел, на самом деле. Однако, в них заключена сила, игнорировать которую вообще недостойно. Правда, прерывания в STM32 - ещё более сложная штука, особенно механизм их вытеснения; но об этом позже.

Как мы заметили раньше, таймер генерирует прерывание в момент переполнения счётчика - если включена вообще обработка прерываний этого прибора, конкретно это прерывание включено и сброшено предыдущее такое же. Анализируя эту фразу, понимаем что нам нужно:

  1. Включить вообще прерывания таймера TIM6;
  2. Включить прерывание таймера TIM6 на переполнение счётчика;
  3. Написать процедуру–обработчик прерывания;
  4. После обработки прерывания сбросить его.

Включение прерываний

Честно говоря, тут вообще ничего сложного. Первым делом включаем прерывания TIM6: NVIC_EnableIRQ(TIM6_DAC_IRQn); Почему такое название? Потому что в ядре STM32 прерывания от TIM6 и от ЦАП имеют одинаковый номер. Не знаю, почему так сделано - экономия, нехватка номеров или просто какая–то наследная штука - в любом случае, никаких проблем это не принесёт, потому что в этом проекте не используется ЦАП. Даже если в нашем проекте использовался бы ЦАП - мы могли бы при входе в прерывание узнавать, кто конкретно его вызвал. Практически все другие таймеры имеют единоличное прерывание.

Настройка события–источника прерываний: TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); - включаем прерывание таймера TIM6 по событию TIM_DIER_UIE, т.е. событие обновления значения ARR. Как мы помним из картинки, это происходит одновременно с переполнением счётчика - так что это именно то событие, которое нам нужно.

На текущий момент код таймерных дел таков:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6, &TIM_InitStructure); TIM_Cmd(TIM6, ENABLE); NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE);

Обработка прерываний

Сейчас запускать проект нельзя - первое же прерывание от таймера не найдёт свой обработчик, и контроллер повиснет (точнее, попадёт в обработчик HARD_FAULT, что по сути одно и то же). Нужно его написать.

Немного теории

Он должен иметь совершенно определённое имя, void TIM6_DAC_IRQHandler(void). Это имя, так называемый вектор прерывания, описано в файле startup (в нашем проекте это startup_stm32f10x_md_vl.s - можете сами увидеть, 126 строка). На самом деле вектор - это адрес обработчика прерывания, и при возникновении прерывания ядро ARM лезет в начальную область (в которую транслирован файл startup - т.е. его местоположение задано совершенно жёстко, в самом начале флеш–памяти), ищет там вектор и переходит в нужное место кода.

Проверка события

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

В нашей программе эта проверка будет выглядеть так: if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) - всё понятно, функция TIM_GetITStatus проверяет наличие указанного события у таймера, и возвращает 0 или 1.

Очистка флага UIF

Второй шаг - очистка флага прерывания. Вернитесь к картинке: самый последний график UIF это и есть флаг прерывания. Если его не очистить, следующее прерывание не сможет вызваться, и контроллер опять упадёт в HARD_FAULT (да что же такое!).

Выполнение действий в прерывании

Будем просто переключать состояние светодиода, как и в первой программе. Разница в том, что теперь наша программа делает это более сложно! На самом деле, так писать гораздо правильнее.

If(state) GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); else GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); state = 1 - state;

Используем глобальную переменную int state=0;

11. Весь код проекта с таймером

#include "stm32f10x_conf.h" int state=0; void TIM6_DAC_IRQHandler(void) { if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM6, TIM_IT_Update); if(state) GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); else GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); state = 1 - state; } } void main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6, &TIM_InitStructure); TIM_Cmd(TIM6, ENABLE); NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); while(1) { } }

Архив с проектом таймера.

Ну и к слову, таймер умеет переключать ногу и сам, без прерываний и ручной обработки. Это будет наш третий проект.

Весь цикл:

1. Порты ввода–вывода

2. Таймер и прерывания

3. Выходы таймера

4. Внешние прерывания и NVIC

5. Ставим FreeRTOS

Post Views: 235

В любом современном контроллере есть таймеры . В этой статье речь пойдёт о простых (базовых) таймерах stm32f4 discovery .
Это обычные таймеры. Они 16 битные с автоматической перезагрузкой. Кроме того имеется 16 битный программируемый делитель частоты . Есть возможность генерирования прерывания по переполнению счётчика и/или запросу DMA.

Приступим. Как и раньше я пользуюсь Eclipse + st-util в ubuntu linux

Первым делом подключаем заголовки:

#include #include #include #include #include

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

Определим две константы. Одну для обозначения диодов, другую массив из техже диодов:

Const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; // все диоды const uint16_t LED = {GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_14, GPIO_Pin_15}; // массив с диодами

Скорее всего уже знакомая вам функция-инициализации периферии (то есть диодов) :

Void init_leds(){ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // разрешаем тактирование GPIO_InitTypeDef gpio; // структура GPIO_StructInit(&gpio); // заполняем стандартными значениями gpio.GPIO_OType = GPIO_OType_PP; // подтяжка резисторами gpio.GPIO_Mode = GPIO_Mode_OUT; // работаем как выход gpio.GPIO_Pin = LEDS; // все пины диодов GPIO_Init(GPIOD, &gpio);

Функция инициализатор таймера:

Void init_timer(){ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // включаем тактирование таймера /* Другие параметры структуры TIM_TimeBaseInitTypeDef * не имеют смысла для базовых таймеров. */ TIM_TimeBaseInitTypeDef base_timer; TIM_TimeBaseStructInit(&base_timer); /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */ base_timer.TIM_Prescaler = 24000 - 1; // делитель 24000 base_timer.TIM_Period = 1000; //период 1000 импульсов TIM_TimeBaseInit(TIM6, &base_timer); /* Разрешаем прерывание по обновлению (в данном случае - * по переполнению) счётчика таймера TIM6. */ TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); TIM_Cmd(TIM6, ENABLE); // Включаем таймер /* Разрешаем обработку прерывания по переполнению счётчика * таймера TIM6. это же прерывание * отвечает и за опустошение ЦАП. */ NVIC_EnableIRQ(TIM6_DAC_IRQn); }

Я прокомментировал код, так-что думаю всё ясно.
Ключевыми параметрами тут являются делитель (TIM_Prescaler) и период (TIM_Period) таймера. Это параметры, которые собственно и настраивают работу таймера.

К примеру, если у вас на STM32F4 DISCOVERY тактовая частота установлена в 48МГц, то на таймерах общего назначения частота 24МГц. Если установить делитель (TIM_Prescaler) в 24000 (частота счёта = 24МГц/24000 = 1КГц), а период (TIM_Period) в 1000, то таймер будет отсчитывать интервал в 1с.

Обратите внимание, что всё зависит от тактовой частоты. Её вы должны выяснить точно.

Так же отмечу, что на высоких частотах переключение светодиода по прерыванию существенно искажает значение частоты. При значении в 1МГц на выходе я получал примерно 250КГц, т.е. разница не приемлима. Такой результат видимо получается из-за затрат времени на выполнение прерывания.

Глобальная переменная - флаг горящего диода:

U16 flag = 0;

Обработчик прерывания, которое генерирует таймер. Т.к. этоже прерывание генерируется и при работе ЦАП, сначала проверяем, что сработало оно именно от таймера:

Void TIM6_DAC_IRQHandler(){ /* Так как этот обработчик вызывается и для ЦАП, нужно проверять, * произошло ли прерывание по переполнению счётчика таймера TIM6. */ if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { flag++; if (flag>3) flag = 0; /* Очищаем бит обрабатываемого прерывания */ TIM_ClearITPendingBit(TIM6, TIM_IT_Update); GPIO_Write(GPIOD, LED); // зажигаем слудующий диод } }

Функция main:

Int main(){ init_leds(); init_timer(); do { } while(1); }

Цикл оставляем пустым. Счётчик выполняет свою работу асинхронно, а прерывание на то и прерывание, чтобы не зависеть от выполняемой в данный момент операции.

Собственно, поэтому давайте сразу же переходить к программированию. Возьмем любой из базовых таймеров микроконтроллера STM32F3 , произведем его минимальную настройку и попытаемся сгенерировать прерывания через равные промежутки времени. Максимально простой пример 😉

Итак, из Standard Peripheral Library нам понадобятся парочка файлов, в которых реализовано взаимодействие с регистрами таймеров:

#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x.h" /*******************************************************************/ TIM_TimeBaseInitTypeDef timer; /*******************************************************************/

Минимальная инициализация таймера выглядит следующим образом. Кстати заодно настроим одну из ножек контроллера на работу в режиме выхода. Это нужно всего лишь для того, чтобы мигать светодиодиком 😉

/*******************************************************************/ void initAll() { // Тактирование - куда ж без него RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE) ; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE) ; // На этом выводе у нас синий светодиод (STM32F3Discovery) gpio.GPIO_Mode = GPIO_Mode_OUT; gpio.GPIO_Pin = GPIO_Pin_8; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, & gpio) ; // А вот и долгожданная настройка таймера TIM2 TIM_TimeBaseStructInit(& timer) ; timer.TIM_Prescaler = 7200 ; timer.TIM_Period = 20000 ; TIM_TimeBaseInit(TIM2, & timer) ; /*******************************************************************/

Тут стоит уделить внимание двум непонятно откуда взявшимся числам – 7200 и 20000 . Сейчас разберемся что это 😉 Таймер у меня тактируется частотой 72 МГц . Prescaler, он же предделитель, нужен для того, чтобы эту частоту делить) Таким образом, получаем 72 МГц / 7200 = 10 КГц . Значит один “тик” таймера соответствует (1 / 10000) секунд , что равняется 100 микросекундам. Период таймера – это величина, досчитав до которой программа улетит на обработчик прерывания по переполнению таймера. В нашем случае таймер дотикает до 20000 , что соотвествует (100 * 20000) мкс или 2 секундам. То есть светодиод (который мы зажигаем и гасим в обработчике прерывания) будет мигать с периодом 4 секунды (2 секунды горит, 2 секунды не горит =)). Теперь с этим все понятно, продолжаем…

В функции main() вызываем функцию инициализации, а также включаем прерывания и таймер. В цикле while(1) кода и того меньше – он просто пуст 😉

/*******************************************************************/ int main() { __enable_irq() ; initAll() ; TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE) ; TIM_Cmd(TIM2, ENABLE) ; NVIC_EnableIRQ(TIM2_IRQn) ; while (1 ) { } } /*******************************************************************/

Все, осталось написать пару строк для обработчика прерываний, и дело сделано:

/*******************************************************************/ void TIM2_IRQHandler() { TIM_ClearITPendingBit(TIM2, TIM_IT_Update) ; if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_8) == 1 ) { GPIO_ResetBits(GPIOE, GPIO_Pin_8) ; } else { GPIO_SetBits(GPIOE, GPIO_Pin_8) ; } } /*******************************************************************/

Прошив программу в контроллер, наблюдаем мигающий синий светодиод, следовательно программа функционирует верно! В принципе на этом все на сегодня, такая вот получилась краткая статейка)

THE BELL

Есть те, кто прочитали эту новость раньше вас.
Подпишитесь, чтобы получать статьи свежими.
Email
Имя
Фамилия
Как вы хотите читать The Bell
Без спама