Янв 292013
 

В этой статье рассмотрим метод формирования ШИМ (PWM) сигнала на выходе микроконтроллера. Я решил сразу не загружать вас всей информацией о работе каналов захвата/сравнения (Capture/Compare), а давать информацию небольшими порциями практических примеров. А всю теорию вынести в отдельную статью. Формирование PWM сигнала на выходе с помощью таймера является одним из режимов работы каналов Capture/Compare таймера. В этой статье будут рассмотрены минимально необходимые настройки таймера, которые позволят увидеть на выходе микроконтроллера сигнал с регулируемой длительностью импульсов.

Пример создавался для микроконтроллера серии STM32F100 и был проверен на плате STM32VL-DISCOVERY. В документе Reference Manual (RM0041) приводится описание процедуры инициализации таймера и задействованных для формирования PWM разрядов регистров:

  • Период сигнала задается через регистр TIMx_ARR. В этот регистр заносится значение, до которого будет считать таймер. Нужное значение в этом регистре для формирования необходимого периода сигнала можно посчитать по формуле, которая приведена в предыдущей статье STM32. General-Purpose Timers. Часть 1. Формирование временных интервалов. 
  • Длительность импульса задается через регистр TIMx_CCRx (CCR1-CCR4 – номер канала захвата/сравнения таймера, всего их может быть до 4). В моем примере (код см. ниже) период сигнала равен 1мс. При частоте тактирования 24МГц, которая по умолчанию является источником тактирования и для таймера, в регистре TIMx_ARR в этом случае должно быть число 23999. Тогда, чтобы сформировать PWM сигнал с коэффициентом заполнения (duty cycle) равным 10%, в регистр  TIMx_CCRx необходимо записать число 2400.
  • Может быть выбран один из двух режимов PWM1 или PWM2 с помощью разрядов OCxM регистра TIMx_CCMRx. Для режимов PWM значения в этих разрядах могут быть либо OCxM = 110 (режим PWM1), либо OCxM = 111 (режим PWM2). В режиме PWM1 канал активен пока TIMx_CNT<TIMx_CCRx при счете “вверх”, а при счете “вниз” активен пока TIMx_CNT>TIMx_CCRx. В режиме PWM2 все прямо противоположно. Пока на этом остановимся, поскольку эти режимы необходимо рассматривать более подробно, что я сделаю позже и в другой статье.
  • Необходимо разрешить предварительную загрузку регистра TIMx_CCRx (задает длительность импульса) установкой бита OCxPE в регистре TIMx_CCMRx. И предварительную загрузку регистра TIMx_ARR (задает период сигнала) установкой бита ARPE в регистре TIMx_CR1.
  • Можно задать полярность сигнала, для этого предусмотрен разряд CCxP регистра TIMx_CCER.
  • Подключение к выводу микроконтроллера канала формирования PWM разрешается установкой бита CCxE регистра TIMx_CCER.

Ниже код программы с комментариями. Частота тактирования таймера равна 24МГц. Сигнал PWM формируется на выходе канала 2 Capture/compare.

#include "stm32f10x.h"

int main()
{
  /*Инициализация GPIOA.
  Вывод PA1 настраивается для работы с выходом TIM2_CH2*/  
  RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN); //Тактирование порта GPIOA и альтернативных функций
  GPIOA->CRL |= GPIO_CRL_MODE1;//Максимальная скорость порта = 50 MHz
  GPIOA->CRL &= ~GPIO_CRL_CNF1;//Очистка бит CNF[1:0] для PA1 (поскольку после сброса мк задан режим "Input Floating")
  GPIOA->CRL |= GPIO_CRL_CNF1_1; //PA1 - выход Push-Pull в режиме альтернативной функции

  /*Инициализация таймера TIM2
  Для формирования сигнала ШИМ используется канал 2 (TIM2_CH2)*/
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//Тактирование таймера TIM2
  TIM2->CR1 |= TIM_CR1_ARPE;//Включен режим предварительной записи регистра автоперезагрузки
  TIM2->CCMR1 |= TIM_CCMR1_OC2PE;//Включен режим предварительной загрузки регистра сравнения
  TIM2->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1);//OC2M = 110 - PWM mode 1
  TIM2->ARR = 23999;//Период выходного сигнала T = 1mS
  TIM2->CCR2 = 19200;//Длительность импульса. В данном случае Duty cycle = 80%
  //TIM2->CCER |= TIM_CCER_CC2P;//Полярность выходного сигнала
  TIM2->CCER |= TIM_CCER_CC2E;//Выход канала захвата/сравнения включен
  TIM2->CR1 |= TIM_CR1_CEN;//Старт счета таймера

  while(1);
}

Для изменения полярности сигнала можно раскомментировать строку

//TIM2->CCER |= TIM_CCER_CC2P;//Полярность выходного сигнала

У данного таймера 4 независимых канала захвата/сравнения. Это значит, что можно сформировать 4 независимых сигнала PWM. Но, у этих сигналов будет одинаковым период, поскольку он задается в регистре TIMx_ARR, а этот регистр у таймера один. Также один и счетный регистр, поэтому выходные PWM сигналы всех каналов будут привязаны к одному моменту времени, т.е. синхронизированы.

А теперь картинки – осциллограммы с вывода PA1:

CC2P_0_DT_10

CC2P_0_DT_50

CC2P_0_DT_80

Что будет, если в регистр длительности импульса TIM2_CCR2 записать “1”?. Получим ШИМ сигнал с длительностью импульса равной всего одному периоду тактовой частоты (в данном случае около 40 наносекунд).

CC2P_0_CCR2_1

А теперь то же, но с другой полярностью сигнала (установлен бит CC2P в регистре TIM2_CCER):

CC2P_1_CCR2_1

CC2P_1_DT_10

CC2P_1_DT_80

Продолжение следует…

Другие статьи:

  4 Responses to “STM32. General-Purpose Timers. Часть 2. Формирование сигнала PWM.”

  1. то же переделал для таймера 16 и 17 — нет сигнала на выходе и все тут.

    /*Инициализация GPIOB.

    RCC->APB2ENR |= (RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN); //Тактирование порта GPIOB и альтернативных функций
    GPIOB->CRL |= GPIO_CRL_MODE7;//Максимальная скорость порта = 50 MHz
    GPIOB->CRL &= ~GPIO_CRL_CNF7;//Очистка бит CNF[1:0] для PA1 (поскольку после сброса мк задан режим «Input Floating»)
    GPIOB->CRL |= GPIO_CRL_CNF7_1; // — выход Push-Pull в режиме альтернативной функции

    //
    RCC->APB2ENR |= RCC_APB2ENR_TIM17EN;//Тактирование таймера
    TIM17->CR1 |= TIM_CR1_ARPE;//Включен режим предварительной записи регистра автоперезагрузки
    TIM17->CCMR1 |= TIM_CCMR1_OC2PE;//Включен режим предварительной загрузки регистра сравнения
    TIM17->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1);//OC2M = 110 — PWM mode 1
    TIM17->ARR = 299;//Период выходного сигнала
    TIM17->CCR2 = 100;//Длительность импульса.
    //TIM2->CCER |= TIM_CCER_CC2P;//Полярность выходного сигнала
    TIM17->CCER |= TIM_CCER_CC2E;//Выход канала захвата/сравнения включен
    TIM17->CR1 |= TIM_CR1_CEN;//Старт счета таймера
    */

  2. Спасибо большое! Мучился по другим примерам, и не работало. У вас копипастнул и всё запрыгало!