• - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!
подписаться на RSS-ленту

STM32: Урок 6.1 - Базовые таймеры

Всё-таки, меня всегда умиляло название «таймер» для этих сложных штук в микроконтроллерах. Шутка ли: кроме срабатывания в строго заданный интервал, они имеют ещё до десятка дополнительных функций типа генерации ШИМ и подсчёта входящих импульсов. В микроконтроллерах STM32 таймеры настолько круты, что я посчитал нужным разбить их описание на несколько статей.

Таймеров в STM32 много, и они имеют разный набор возможностей, который делит их на 3 группы — базовые (basic timers), общего назначения (general-purpose timers) и продвинутые (advanced-control timers). Последние являются самыми навороченными и включают в себя функции остальных, и вдобавок имеют дополнительные функции для управления всякими трёхфазными двигателями — например, настройку dead-time.

Эта статья будет посвящена базовым таймерам STM32. Они значительно скромнее, согласно руководству по линейке STM32F100xx (можно скачать и тут):
  • 16-битный счётчик с автоперезагрузкой.
  • 16-битный программируемый делитель частоты: с 1 по 65535.
  • Схема синхронизации для запуска гравиЦАП.
  • Генерация прерывания и/или запроса DMA по переполнению счётчика.
Таймер действительно совсем базовый — только счётчиком умеет пощёлкивать, но и с таким можно сделать кое-что полезное. Программируемый делитель можно выбрать любой из указанного диапазона: хоть 666, если захочется. Это сильно упрощает настройку частоты счёта таймера по сравнению с теми же AVR (у них всего несколько возможных значений делителя). Например, у нас есть STM32VLDiscovery, на ней камень 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:

TIM6_DAC_IRQn → TIM6_DAC_IRQHandler
TIM7_IRQn → TIM7_IRQHandler

Ну, и так далее.
  • +3
  • 27 января 2012, 13:46
  • burjui

Комментарии (3)

RSS свернуть / развернуть
+
+1
Большое спасибо. Как раз закупил демо-плату с stm32, пока доставят, смогу её изучить работу по вашим статьям.
avatar

realist

  • 27 января 2012, 23:51
+
+1
Огромное спасибо, ваши уроки здорово помогают в освоении платы!=)
avatar

AndR

  • 28 января 2012, 10:46
+
+2
Огромное спасибо за старания. Подробного русскоязычного курса по этой платформе еще не было. Автор можно сказать благородным просветительским делом занят. Прикупил себе плату, буду по ходу дела изучать.
В целом очень много общего с AVR-ками. А возможностей гораздо больше.
avatar

Ozze

  • 30 января 2012, 18:13

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