Микроконтроллеры STM8. Таймеры, часть 1.

Микроконтроллеры STM8. Таймеры, часть 1.

Здравствуйте,
Сегодня мы с вами рассмотрим таймеры в микроконтроллерах STM8.
Так как таймеры, это обширная тема, то я решил разбить ее на пару статей для удобства восприятия и удобства написания. Таймеры у STM8 понавороченей и посложней, чем у AVR, но и возможностей предоставляют побольше. Рассмотрим общие характеристики таймеров, и по ходу будем сравнивать их с аналогами у AVR.
Итак, в микроконтроллерах STM8 есть три типа таймеров:
— Таймер с расширенным управлением (Advanced control) — TIM1;
— общего назначения (General purpose) — TIM2,TIM3,TIM5;
— базовые (Basic) — TIM4,TIM6.
Основные свойства таймеров можно посмотреть в таблице 30 на странице 129 Reference Manual`а. Если бы я мог сверстать таблицу – я бы ёё перевел, а пока ограничимся скриншотом из даташита и комментариями к нему.
Timers
Разбор таймеров начнем с Таймера1. Из таблички явно видно, что Таймер1 имеет наибольшее количество всяких удобных фич, что делает его самым удобным, но и в то же время самым сложным – в документации ему посвящено 80 страниц. Посмотрим на его блок-схему.
Timer1
По сравнению с AVR сразу бросается в глаза возможность выбора произвольного предделителя входной частоты (колонка Prescaler factor) и наличие входа останова. Выбор предделителя не из ряда степеней двойки позволяет гибко конфигурировать таймер. Кроме того, у AVR максимальное значения предделителя у всех таймеров — 1024, а у STM8, как мы видим из таблички – 65535 у Таймера1. Это позволяет нам просто отмерять длительные временные отрезки. Например: при частоте тактирования в 16 МГц и предделителе 1024 у шестнадцатибитного Таймера1 в AVR время, за которое он досчитает от 0 до 0xFFFF составляет (1/16 000 000)*1024* 65 535= 4,19424 с. Используя Таймер1 в STM8 и максимальный предделитель 65536 получаем: (1/16 000 000)*65536* 65 535= 268,43136 с., что составляет почти четыре с половиной минуты. Еще более длинную задержку можно получить, используя Repetition counter. Это такой модуль, который считает события таймера. Если его настроить таким образом, чтобы он считал переполнения таймера, то мы получим еще один предделитель с максимальным значением 255. Стоит также заметить, что Repetition counter считает вниз от 255 по умолчанию (можно выбрать любое значение меньше 255) до 0.
Вход останова можно использовать в задачах управления двигателем, когда таймер формирует управляющие импульсы для обмоток, а на вход останова подключен датчик температуры или датчик тока. Тогда при перегрузке двигателя автоматически происходит его отключение.
Таймер1 имеет четыре канала захвата-сравнения. Что это такое и как их можно использовать? В режиме захвата, эти каналы позволяют измерить длительность импульса на входе по каждому из каналов. При работе в режиме сравнения можно генерировать различные задержки, а также, формировать ШИМ-сигнал.
У Таймера1 можно выбрать источник тактирования. Кроме очевидного тактирования от основной частоты микроконтроллера можно выбрать тактирование от внешнего источника, а также тактирование от других таймеров. В этом случае таймер-источник тактового сигнала выступает либо в виде источника синхронизации, либо в виде дополнительного предделителя Таймера1.
Все конфигурационные регистры Таймера1 мы рассматривать не будем, их слишком много, ограничимся основными. Информацию по всем настройкам и конфигурационным регистрам Таймера1 можно найти в Reference Manual начиная со страницы 181.
Регистр TIM1_CNTRTimer1 Counter. Счетный регистр Таймера1. Это 16-битный регистр, в котором хранится текущее значение счетчика Таймера1. Доступ к этому регистру производится через регистры TIM1_CNTRH и TIM1_CNTRLCounter high и Counter low соответственно, причем сначала обращение (как в режиме чтения, так и в режиме записи) должно производится сначала к старшему регистру (TIM1_CNTRH), а потом – к младшему (TIM1_CNTRL). То же самое относится и к другим 16-битным регистрам в STM8S.
Регистр TIM1_PSCRTimer1 Prescaler. Регистр предделителя Таймера1. Как и TIM1_CNTR – это 16-битный регистр, и обращение к нему происходит аналогичным образом, через регистры TIM1_PSCRH и TIM1_PSCRL. Значение, TIM1_PSCR определяет, на сколько делится входная частота по формуле fCK_CNT = fCK_PSC / (PSCR[15:0]+1), где fCK_CNT – частота, поступающая на таймер, fCK_PSC – частота, поступающая на делитель.
Регистр TIM1_CR1Control register 1. Регистр управления 1. В нем находятся основные конфигурационные биты Таймера1.
TIM1_CR1
Установка бита CEN запускает таймер.
Установка бита UDIS запрещает прерывания Таймера1.
Бит URS отвечает за выбор событий, при которых возникнет прерывания от Таймера1. Если этот бит установлен, то прерывание возникнет только при переполнении счетчика. При значении URS = 0 прерывание, кроме обычного способа, может также вызываться модулем внешнего тактирования, или программно, установкой бита UG в регистре TIM1_EGR.
При установленном бите ORS Таймер1 переходит в режим one pulse mode: он считает до переполнения и останавливается. Для продолжения работы требуется перезапуск таймера.
Бит DIR определяет направления счета:
0 – счет вверх, от 0 до 0xFFFF;
1 – счет вниз, от 0xFFFF до 0.
Биты CMS[1..0] – выбор режима счета; возможные значения:
00 – таймер считает вверх или вниз в зависимости от состояния бита DIR;
01 – таймер считает вверх, потом вниз. Флаг совпадения с регистром сравнения выставляется при счете вниз;
10 — таймер считает вверх, потом вниз. Флаг совпадения с регистром сравнения выставляется при счете вверх;
11 — таймер считает вверх, потом вниз. Флаг совпадения с регистром сравнения выставляется как при счете вверх так и при счете вниз;

Регистр TIM1_CR2Control register 2. Регистр управления 2.Этот регистр отвечает за настройку модуля захвата-сравнения. Детально мы рассматривать его пока не будем.
Регистр TIM1_SR1Status register 1. Регистр статуса 1. Основной регистр статуса, в нем выставляются флаги прерываний, которые необходимо анализировать при обработке прерываний. Рассмотрим его подробней:
TIM1_SR1
Флаг UIF становится равным единице, когда возникает переполнение счетчика и соответствующее прерывание разрешено.
Флаги ССxIF выставляются при совпадении значения счетчика таймера со значением, записанным в регистре TIM1_CCRx, если таймер работает в режиме сравнения. В режиме захвата эти флаги выставляются при появлении импульса на ножке ICх, при этом текущее значение счетчика таймера записывается в регистр TIM1_CCRx.
Флаг BIF выставляется при появлении высокого уровня на входе останова Таймера1.
Регистр TIM1_SR2Status register 2. Регистр статуса 2. Этот регистр отвечает за настройку модуля захвата-сравнения. Детально мы рассматривать его пока не будем.
TIM1_SR2
Регистр TIM1_IERInterrupt enable register. Регистр разрешения прерываний.
TIM1_IER
Бит UIE разрешает прерывание по переполнению.
Биты CCхIE разрешают прерывания по захвату-сравнению на соответствующем канале.
Бит BIE разрешает прерывание по входу останова.
Регистр TIM1_EGREvent generation register. Регистр генерации событий.
TIM1_EGR
Интересный регистр – установка в нем флагов приводит к вызову вручную прерываний, которые вызываются автоматически, в результате работы таймера. Например, установка флага BG вызовет прерывание по входу останова. Биты этого регистра сбрасываются автоматически, нет необходимости очищать их вручную
Остальные регистры мы рассмотрим позже.
Для начала настроим Таймер1 в самом простом и очевидном режиме – режиме счета вверх от 0 до 0xFFFF (65535). По срабатыванию прерывания происходит переключение состояния светодиода. Заодно и изучим, как оформляются прерывания. Для этого необходимо всего лишь обнулить конфигурационные регистры, разрешить прерывание по переполнению и включить таймер.
Итак, немного о прерываниях в STM8S. Вектора прерываний находятся в UBC (User boot code) и начинаются с адреса 0х0080000. Эта область памяти усердно защищена от случайной записи, ее размер настраивается и ее можно использовать, например, для хранения бутлоадера. По сравнению с AVR, векторов прерываний гораздо меньшее количество, на каждое периферийное устройство их максимум два. Это означает, что при обработке прерывания необходимо анализировать, какое из возможных событий вызвало его.
По умолчанию, прерывания в STM8 отключены. Разрешение прерываний производится командой RIM (Reset Interrupt Mask). Отключаются прерывания командой SIM (Set Interrupt Mask).
Важный момент: флаги возникновения прерываний, которые были установлены автоматически при вызове прерывания не сбрасываются автоматически при входе в обработчик прерывания. Это значит, что при обработке прерываний необходимо очищать флаг, вызвавший прерывание, иначе обработчик прерывания будет постоянно выполнятся.
В IAR для STM8S прерывания оформляются так:

#pragma vector = <номер вектора>
__interrupt void <имя обработчика>(void)
{
	//код обработчика
}

Номер вектора берем из файла iostm8s105s6.h, который мы подключим к нашему проекту. Список векторов находится в самом конце этого файла. Имя обработчика выбирается произвольно, исходя из правил языка Си и здравого смысла.

#include "iostm8s105s6.h" // подключение заголовочного файла с объявлениями регистров, масок и битов
__interrupt void TIM1_OVR_UIF_handler(void);
void init(void)
{
  PD_DDR_bit.DDR0 = 1;   // Ножка PD0 конфигурируется на вывод
  PD_CR1_bit.C10 = 1;   // Выход типа Push-pull
  PD_CR2_bit.C20 = 1;   // Скорость переключения - до 10 МГц.
  
  //Настройка Таймера1
  // Синхронизация как ведущий с периферией отключена
    TIM1_CR2 = 0;
  // Синхронизация как ведомый с периферией отключена
    TIM1_SMCR = 0;
    // Внешнее тактирование отключено
    TIM1_ETR = 0;
    // Прерывание по обновлению счетного регистра разрешено
    TIM1_IER = MASK_TIM1_IER_UIE;
    // Предделитель - 0
    // ВАЖНО!!!
    // Порядок установки предделителя - старший регистр, потом младший
    TIM1_PSCRH = 0;
    TIM1_PSCRL = 0;

  // Режим непрерывного счета по возрастанию
  // Прерывание по переполнению разрешено и таймер запущен
    TIM1_CR1 = (MASK_TIM1_CR1_URS+MASK_TIM1_CR1_CEN);
}  



int main( void ) // Основная программа
{

  init();
  asm("rim"); //Разрешаем прерывания
  while(1)      // Бесконечный цикл
  {
   asm("nop");	//Пустая операция
  }  
    

}

// Вектор прерывания по обновлению или переполнению Таймера1
#pragma vector = TIM1_OVR_UIF_vector
__interrupt void TIM1_OVR_UIF_handler(void)
{
  // Проверка, что же вызвало прерывание
  if (TIM1_SR1_UIF==1)
  {
    TIM1_SR1_UIF = 0;             // Очистка флага прерывания по обновлению
    PD_ODR ^= MASK_PD_ODR_ODR0;   // Переключение уровня напряжения на ножке на противоположное
                                  // при помощи операции Исключающее ИЛИ (XOR)

  }  
}

Особого пояснения программа не требует, стоит только сказать, что в основном цикле используется функция asm(«nop»), которая выполняет пустую операцию. Она находится там для удобства отладки. С этой программой можно провести несколько экспериментов. Например, изменим значение предделителя в строках на какие-то другие значения:

    TIM1_PSCRH = 0;
    TIM1_PSCRL = 100;

Мы видим, что наш светодиод стал моргать реже. Теперь посмотрим, что же будет, если нарушить порядок доступа к 16-битному регистру.

    TIM1_PSCRL = 100;
    TIM1_PSCRH = 0;

Светодиод моргает с частотой, с которой он моргал при предделителе равным нулю. Откроем окно Watch (Viev -> Watch), и введем в поле Expression имена регистров TIM1_PSCRL и TIM1_PSCRH. Мы видим, что, несмотря на то, что мы, вроде бы, записали свое значение предделителя и видим их значения, на самом деле запись в регистр не произошла.
Советую самостоятельно провести еще один эксперимент и проверить, что же произойдет, если не очищать флаг TIM1_SR1_UIF в теле прерывания.
На сегодня все, а в следующий раз мы продолжим разбираться с таймерами.

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

RSS свернуть / развернуть
Стоит также заметить, что Repetition counter считает вниз от 255 по умолчанию (можно выбрать любое значение меньше 255) до 0.
Возможно правильнее «можно выбрать любое значение меньше 256»
0
Advanced timer может как «продвинутый таймер» лучше будет звучать?
0
А не нужно добавить еще строку вида:
CLK_PCKENR1 = 0x80
?
Ведь, если мне не изменяет память, тактирование таймера по умолчанию отключено.
0
Страница 67 reference manual'a:
After a device reset, all peripheral clocks are enabled.
Вот в STM32 периферия по умолчанию отключена от тактирования.
0
Точно. Это я ступил. Просто разбираюсь с STM8L-серией, а у них по умолчанию все отключено. Видимо для энергосбережения.
0
Для выполнения этой же задачи для серии STM8L необходимо лишь добавить CLK_PCKENR2 = 0x20 или еще что-то?
0
  • avatar
  • Onic
  • 11 февраля 2012, 22:43
у меня вопрос, счётчики асинхронные там?
а то в AVR гемор полный с этими синхронными счётчиками…
больше четверти тактовой ядра не подашь.
и частотметр простой не сделаешь.
0
всю линейку AVR этой хернёй погубили, поставив триггер на входе счётчиков.
Есть часовой но я не думаю что он потянет.
0
При установленном бите ORS — может все же OPM?
0
А предделитель надо сбрасывать перед пуском таймера, как в АВРках или нет
???
0
Чет не пойму, мучаю таймер 1 осциллом, и как-то так получается, что таймер на прескаллер совсем внимания не обращает. что он равен 0, что 0xffff. Отчего так может быть?
0
Ну, во первых, в таблице говорится про значения от 1 до 65535. Не факт, что нолик он примет. А во вторых, ты соблюдаешь порядок записи в 16-битный регистр? В самом конце статьи пример, очень похожий на описываемое тобой поведение.
0
Да это все понятно. Нет, значения писал абсолютно разные, старшим байтом вперед, значения в регистр заносятся (судя по отладчику). Вообще что-то с моим контроллером чудеса творятся, надо перекинуть попробовать. Стэк срывать стало на пустом месте.
0
Почти целый день пробовал разобраться с таймером 4, но чета он у меня не захотел запускаться, решил попробовать таймер 1 с теми же настройками. В бесконечном цикле копирую значение TIM1_CNTR в переменную, которую видно в отладчике, переменная постоянно увеличивается, значит, таймер тикает, пробую запустить таймер4:
TIM4_CNTR = 0;
	TIM4_PSCR = 1;
	TIM4_CR1 = MASK_TIM4_CR1_CEN;
	
	
	while(1)              // Бесконечный цикл
	{
		gh = TIM4_CNTR;
	}

переменная gh постоянно остается равна 0, иногда, почему то равна 1. В чем проблема?
0
Привет! Классные статьи про МК! Но у меня такой вопрос: как нужно искать апликейшен нотэ(AN) на сайте СТ.КОМ?? У меня нифига не получается-документов там целая куча, но не то, что нужно. Что нужно забивать в строке поиска, чтобы найти АН на таймеры, GPIO и т.д. Поделись, пожалуйста, опытом)
0
Вот тут слева в разделе Resources можно найти всю доступную информацию. АН на порты и таймеры — ты серьезно?) Такие вещи берутся из даташита(в случае ST это reference manual). Если же речь о SPL, то с ней поставляется chm файл где все функции разжеваны, а также все исходники открыты для изучения.
0
Во, спасибо. Почему-то референс мануал не смотрел, смотрел даташит. Нашел, что хотел.)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.