[Home] [< Prev: Таймеры в микроконтроллерах STM] [Next: Устройство таймера TIM1 >]

Таймер TIM1 с расширенным управлением

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

Оглавление
Таймер TIM1 с расширенным управлением (общие сведения)
   Введение
   Основные возможности таймера TIM1
   Простейший пример использования
Устройство таймера TIM1 
Устройство каналов таймера. Защитное отключение выходов 
Функции таймера TIM1 и примеры использования 
Регистры таймера TIM1 


Введение

Все "универсальные" таймеры (с расширенным управлением, общего назначения и базовые) имеют единый принцип построения и управления: они состоят из однотипных блоков и управляются с помощью унифицированного набора регистров (точнее, некоторого подмножества "максимального" набора; реализованный набор регистров конкретного таймера зависит от реализованного в нём набора функций). Регистры с одинаковым назначением имеют одинаковое расположение в адресном пространстве (у них одинаковые смещения относительно базового адреса таймера). То же касается и отдельных битов - положение битов с одинаковыми функциями в пределах регистра одинаково. Все "универсальные" таймеры описываются структурой TIM_TypeDef. Всё это обеспечивает хорошую совместимость таймеров "сверху вниз": более сложный таймер может быть задействован вместо более простого с минимальной модификацией кода.

Среди "универсальных" таймеров особое место занимает TIM1 - таймер с расширенным управлением. Он имеет наиболее сложное устройство и максимальные возможности. Изучив данный таймер, можно легко перейти к изучению и использованию любого другого таймера - достаточно будет уточнить в документации реализованный в конкретном таймере набор функций/битов/регистров (это будет некоторое подмножество такового в TIM1).

Хотя таймеры различных микроконтроллеров STM32 и даже различных семейств обладают высокой совместимостью, небольшие различия возможны. Для того чтобы разговор был более предметным, будем рассматривать устройство и возможности таймера TIM1 в микроконтроллерах из линейки STM32F100xx. Там где детали реализации могут отличаться в зависимости от исполнения микроконтроллера, будем подразумевать STM32F100RB (если ещё более точно, то STM32F100RBT6B - микроконтроллер в 64-выводном LQFP корпусе).

TIM1 может использоваться для решения множества разнообразных задач, включая измерение длительности импульсов входного сигнала (input capture), генерацию выходных сигналов (output compare - формирование сигнала схемой сравнения; PWM - генерация сигнала с широтно-импульсной модуляцией; complementary PWM - комплементарный ШИМ с формированием защитной паузы).

Минимальные значения длительности генерируемых импульсов и период сигнала могут составлять порядка десятков наносекунд. Максимальные величины практически неограниченны, что обеспечивается предделителем самого таймера и предделителями RCC (Reset and clock control - система, управляющая сбросом и тактированием периферийных устройств микроконтроллера), а также возможностью каскадного включения таймеров, когда один таймер управляется выходным сигналом другого таймера.

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

Основные возможности таймера TIM1

Перечислим некоторые возможности таймера TIM1:

Простейший пример использования

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

/*
 * Простейший пример использования таймера TIM1.
 * Используемый микроконтроллер: STM32F100RB.
 * Предполагается, что системная тактовая частота составляет 24 МГц;
 * к выводам PC8, PC9 подключены светодиоды (синий, зелёный) - через
 * токоограничивающие резисторы, зажигаются уровнем 1 на выводе MCU.
 *
 * Важное замечание:
 * VECT_TAB_SRAM символ должен быть определён при построении проекта,
 * если код будет выполняться из RAM и при этом будут использоваться
 * прерывания, иначе будет использоваться таблица прерываний,
 * размещённая в FLASH (смотрите также в файле <system_*.c> как
 * происходит модификация регистра SCB->VTOR: SCB->VTOR =...).
 */

#include "stm32f10x.h"

// При работе с таймером TIM1 могут быть реализованы и использоваться
// следующие обработчики прерываний:

// TIM1_BRK_TIM15_IRQHandler
// TIM1_UP_TIM16_IRQHandler
// TIM1_TRG_COM_TIM17_IRQHandler
// TIM1_CC_IRQHandler

// Описание функций, используемых для работы со светодиодами.
void init_led();
void toggle_blue_led();

// Обработчик прерывания от таймера TIM1, генерируемого при
// возникновении события обновления (совмещён с обработчиком
// прерывания от TIM16).
extern "C" void TIM1_UP_TIM16_IRQHandler()
{
    // Проверяем, произошло ли интересующее нас событие.
    if(TIM1->SR&TIM_SR_UIF)
    {
        // Сбрасываем флаг обработанного прерывания
        // (особенность данного регистра: запись в какой-либо бит 0
        // сбрасывает этот бит, запись в бит 1 не изменяет бит).
        TIM1->SR=~TIM_SR_UIF;
        // Выполняем действия (здесь: переключаем светодиод).
        toggle_blue_led();
    }
}

int main()
{
    init_led();

    // Включаем тактирование таймера TIM1.
    RCC->APB2ENR|=RCC_APB2ENR_TIM1EN;

    // Настраиваем NVIC для использования прерывания TIM1_UP_TIM16_IRQ.
    NVIC_SetPriority(TIM1_UP_TIM16_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
    NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);

    // Конфигурируем таймер.
    // По умолчанию, в качестве тактового сигнала счётчика используется
    // тактовый сигнал таймера (после
    // прохождения предделителя), получаемый с шины APB2, 24 МГц.
    // Устанавливаем требуемый коэффициент предделителя счётчика
    // (здесь получаем частоту тактового сигнала счётчика 1000 Гц).
    TIM1->PSC=24000-1;
    // Устанавливаем требуемое значение регистра автоперезагрузки
    // (здесь получим частоту генерации события обновления 1 Гц).
    TIM1->ARR=1000-1;
    // Разрешаем генерацию прерывания в ответ на событие обновления.
    TIM1->DIER|=TIM_DIER_UIE;
    // Разрешаем счёт.
    TIM1->CR1|=TIM_CR1_CEN;

    // В таймере TIM1 можно выбрать направление счёта
    // (даже во время счёта), по умолчанию, после сброса - счёт вверх,
    // для счёта вниз надо установить бит DIR в регистре TIM1->CR1.
    // TIM1->CR1|=TIM_CR1_DIR;

    /* Infinite loop */
    while(true);
}

// ****************************************
// Реализация для вспомогательных функций.
// ****************************************

// Конфигурирование портов ввода-вывода для управления светодиодами.
void init_led()
{
    // Включаем тактирование порта ввода-вывода C.
    RCC->APB2ENR|=RCC_APB2ENR_IOPCEN;

    // Конфигурируем выводы PC8 и PC9 как выходы
    // (push-pull out, 2 MHz max: CNF,MODE=00,10=0x2).
    GPIOC->CRH=GPIOC->CRH&~0xFF|0x22;
}

// Управление синим светодиодом:
// new_state=false - отключить;
// new_state=true - включить.
void toggle_blue_led(bool new_state)
{
    if(new_state)
        GPIOC->BSRR=GPIO_BSRR_BS8;
    else
        GPIOC->BRR=GPIO_BRR_BR8;
}

// Переключить синий светодиод в противоположное состояние.
void toggle_blue_led()
{
    toggle_blue_led(0==(GPIOC->ODR&GPIO_ODR_ODR8));
}

// Управление зелёным светодиодом.
void toggle_green_led(bool new_state)
{
    if(new_state)
        GPIOC->BSRR=GPIO_BSRR_BS9;
    else
        GPIOC->BRR=GPIO_BRR_BR9;
}
author: hamper; date: 2018-03-12
  @Mail.ru