STM32 Часть 8–Таймеры общего назначения (+прерывания)
Хотел заняться АЦП, но похоже что перед этим нужно все таки попробовать в работе любой таймер, проще всего показалось использовать таймер общего назначения (General-purpose).
Основная задача которую будет выполнять счетчик это генерация прерываний через установленный промежуток времени.
Таймеры общего назначения
В состав микроконтроллера STM32F100RBT6B входят шесть таймеров общего назначения TIM2, TIM3, TIM4, TIM15, TIM16, TIM17 (и один таймер с расширенными настройками TIM1), честно говоря и таймеры общего назначения имеют немало настроек и возможностей.
Таймеры выделены в три группы по набору возможностей:
- TIM2, TIM3, TIM4
- TIM15
- TIM16, TIM17
Все таймеры имеют разрядность 16-бит, однако есть возможность сконфигурировать последовательное соединение двух таймеров (один в режиме “ведущий” другой в режиме “ведомый”).
Приведу для наглядности таблицу из документации просмотреть на офф сайте.
На первый взгляд самые простые за номерами 16 и 17, думаю с них и начнем.
Источником тактовых сигналов для таймера могут быть как внешние сигналы, так и сигналы тактового генератора МК:
Примечание: похоже на схеме ошибка, так как данный таймер может вести только инкрементный счет, а на схеме указана возможность и декремента.
при этом стоит обратить внимание что тактирование идет не напрямую от генератора, а через цепочку делителей коэффициент деления которых вы выбираете сами:
На данном этапе воспользуемся внутренним источником, для этого необходимо разрешить в модуле RCC прохождение тактового сигнала до соответствующего таймера, буду рассматривать таймер 17:
RCC->APB2ENR |= RCC_APB2ENR_TIM17EN;
далее необходимо установить предварительный делитель на необходимый коэффициент деления.
Я хочу использовать таймер для отсчета временных интервалов в 1 мс, а так как я по прежнему использую внутренний RC генератор с частотой 8 МГц, установим предварительный делитель равным 1000, таким образом значения счетчика будут инкрементироваться с частотой 8000 Гц:
TIM17->PSC = 1000 - 1;
Примечание: коэффициент деления равен единице при нулевом значении предварительного делителя, таким образом для получения коэффициента деления N, необходимо установить значения предварительного делителя в N-1.
а максимальное значение до которого будет считать таймер установим равным восьми:
TIM17->ARR = 8 - 1;
После того как таймер достигнет своего максимального значения необходимо что бы он уведомил об этом контроллер прерываний, для этого разрешим генерацию событий:
TIM17->DIER |= TIM_DIER_UIE;
Осталось только разрешить работу таймера и сброс по достижении максимального значения:
TIM17->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE;
Хочется надеяться, что и дальше будет всё так просто ;)
Контроллер прерываний
Описание контроллера прерываний вынесено в отдельный документ, так как он является стандартным блоком ядра Cortex-M3:
STM32F100xx Cortex-M3 programming manual
И не понятно по чему определения вынесены из заголовочного файла stm32f10x.h, пришлось брать заголовочный файл core_cm3.h от CMSIS.
Описание на русском можно почитать на gaw.ru
Я же не буду углубляться в его структуру и логику работы.
Для активации того или иного прерывания необходимо прописать соответствующий вектор в таблицу прерываний, установить приоритет и разрешить его работу.
Все прерывания от таймера 17 сгруппированы в одно событие, при возникновении которого контроллер прерываний сохраняет текущий контекст и передает управление обработчику прерываний.
У данного микроконтроллера к событиям таймера 17, добавлены еще два: от таймера 1 (TIM1 Trigger) и от Commutation (пока не смотрел что это за событие) и привязаны к 26 вектору.
Внесем изменения в нашу таблицу прерываний, т.е. пропишем адрес обработчика:
handler_tim1_trigger_and_communication_and_tim17, // 26
Так как контроллер является стандартным для всех микроконтроллеров с данным ядром, я решил использовать функции для работы с ним прописанные в заголовочном файле core_cm3.h.
Устанавливаем приоритет и разрешаем прерывания:
NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 15);
NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn);
Вроде ни чего не забыл, можно приступать к проверке.
Для проверки пропишем в обработчик небольшую логику:
void handler_tim1_trigger_and_communication_and_tim17(void)
{
if (TIM17->SR & TIM_SR_UIF)
{
TIM17->SR &= ~TIM_SR_UIF;
if (test_count == 0)
{
PIN_ON(LED_GREEN);
}
else if (test_count == 1000)
{
PIN_OFF(LED_GREEN);
}
if (++test_count >= 2000)
test_count = 0;
}
}
При входе в прерывания необходимо проверить источник и сбросить флаг (по крайней мере для таймера 17 указанно что данный флаг сбрасывается программно). Далее для теста я завел глобальную переменную test_count при каждом вхождении инкрементирую её и проверяю на ряд значений (0, 1000, 2000), при нулевом значении включаю зеленый светодиод, при значении 1000 выключаю и при достижении 2000 обнуляю. Задумка - просто мигать зеленым светодиодом с частотой 0,5 Гц.
Всю необходимую информацию вы можете найти на офф стайте:
STM32F100RB - STM32 Value Line 32-bit Microcontrollers
Проект с примером:
comments powered by Disqus