Всё-таки, меня всегда умиляло название «таймер» для этих сложных штук в микроконтроллерах. Шутка ли: кроме срабатывания в строго заданный интервал, они имеют ещё до десятка дополнительных функций типа генерации ШИМ и подсчёта входящих импульсов. В микроконтроллерах STM32 таймеры настолько круты, что я посчитал нужным разбить их описание на несколько статей.
Таймеров в STM32 много, и они имеют разный набор возможностей, который делит их на 3 группы — базовые (basic timers), общего назначения (general-purpose timers) и продвинутые (advanced-control timers). Последние являются самыми навороченными и включают в себя функции остальных, и вдобавок имеют дополнительные функции для управления всякими трёхфазными двигателями — например, настройку dead-time.
Эта статья будет посвящена базовым таймерам STM32. Они значительно скромнее, согласно (можно скачать ):
16-битный счётчик с автоперезагрузкой.
16-битный программируемый делитель частоты: с 1 по 65535.
Схема синхронизации для запуска гравиЦАП.
Генерация прерывания и/или запроса DMA по переполнению счётчика.
Таймер действительно совсем базовый — только счётчиком умеет пощёлкивать, но и с таким можно сделать кое-что полезное. Программируемый делитель можно выбрать любой из указанного диапазона: хоть 666, если захочется. Это сильно упрощает настройку частоты счёта таймера по сравнению с теми же AVR (у них всего несколько возможных значений делителя). Например, у нас есть , на ней камень STM32F100RBT6B, работающий на частоте 24 МГц, и нужно инкрементировать счётчик каждую миллисекунду. Нет проблем: выставляем делитель в 24000, и всего делов. Предделитель, кстати, можно менять прямо в ходе работы таймера.
Переполнение происходит по достижении счётчиком специального числа (периода), которое настраивается в пределах с 0 по 65535. То есть, можно настроить таймер так, чтобы переполнение происходило через каждые 500 тиков, например. Период по умолчанию тоже можно менять в ходе работы таймера, но можно эту функцию отключить программно (даже не спрашивайте, зачем).
У нашего МК базовых таймеров два — TIM6 и TIM7. Задействуем первый для мигания светодиодами:
#include <stm32f10x.h>
#include <stm32f10x_gpio.h>
#include <stm32f10x_rcc.h>
/* В этом файле - всё для работы с таймерами */
#include <stm32f10x_tim.h>
/* В этом - для работы с NVIC */
#include <misc.h>
enum { BLUE_LED = GPIO_Pin_8, GREEN_LED = GPIO_Pin_9 };
void init_leds();
void init_timer();
int main()
{
init_leds();
GPIO_SetBits(GPIOC, BLUE_LED);
GPIO_ResetBits(GPIOC, GREEN_LED);
init_timer();
do __NOP(); while (1);
}
void init_leds()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpio;
GPIO_StructInit(&gpio);
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = BLUE_LED | GREEN_LED;
GPIO_Init(GPIOC, &gpio);
}
void init_timer()
{
/* Не забываем затактировать таймер */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/* Инициализируем базовый таймер: делитель 24000, период 500 мс.
* Другие параметры структуры TIM_TimeBaseInitTypeDef
* не имеют смысла для базовых таймеров.
*/
TIM_TimeBaseInitTypeDef base_timer;
TIM_TimeBaseStructInit(&base_timer);
/* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
base_timer.TIM_Prescaler = 24000 - 1;
base_timer.TIM_Period = 500;
TIM_TimeBaseInit(TIM6, &base_timer);
/* Разрешаем прерывание по обновлению (в данном случае -
* по переполнению) счётчика таймера TIM6.
*/
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
/* Включаем таймер */
TIM_Cmd(TIM6, ENABLE);
/* Разрешаем обработку прерывания по переполнению счётчика
* таймера TIM6. Так получилось, что это же прерывание
* отвечает и за опустошение ЦАП.
*/
NVIC_EnableIRQ(TIM6_DAC_IRQn);
}
void TIM6_DAC_IRQHandler()
{
/* Так как этот обработчик вызывается и для ЦАП, нужно проверять,
* произошло ли прерывание по переполнению счётчика таймера TIM6.
*/
if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
/* Очищаем бит обрабатываемого прерывания */
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
/* Инвертируем состояние светодиодов */
GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC) ^ (BLUE_LED | GREEN_LED));
}
}
Возможно, у вас возник вопрос: а где узнать конкретные константы для прерываний и имена обработчиков? Отвечу: константы смотреть нужно в stm32f10x.h, с учётом категории МК. Наш относится к Meduim density Value line (STM32F10X_MD_VL). Название обработчика обычно состоит из названия константы (без n) и слова Handler:
Огромное спасибо за старания. Подробного русскоязычного курса по этой платформе еще не было. Автор можно сказать благородным просветительским делом занят. Прикупил себе плату, буду по ходу дела изучать.
В целом очень много общего с AVR-ками. А возможностей гораздо больше.
Комментарии (3)
RSS свернуть / развернутьrealist
AndR
В целом очень много общего с AVR-ками. А возможностей гораздо больше.
Ozze
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.