Настраиваем аппаратный ШИМ на микроконтроллере STM32f103 в среде программирования Keil uVision с помощью библиотеки HAL и CMSIS.

В данном уроке мы настроем работу ШИМ (сразу два независимых канала). Для настройки ШИМ возьмем два разных таймера TIM2 и TIM3. Два таймера взяты для того, что бы показать как работать с разными таймерами используя HAL. Для проверки работы ШИМ  у нас будет меняться  яркость свечения двух светодиодов. 
Для начала в программе STM32CubeMX активируем два таймера TIM2,  и так же само активируем таймер TIM3:
 
Таймер у нас будет тактироваться от системной шиной (в нашем случаи это 72 MHz):

 

 
Настройка таймера в нашем случаи заключается в настройке следующих параметров:
Prescaler -Предделитель (в нашем случаи оставляем 0);
Counter Period — Период (в нашем случаи мы ставим по максимуму 65535);
Counter Mode — направление счета (в нашем случаи в верх);
Остальные настройки не трогаем:
 
Выхода ШИМ следующие:
РА0 — TIM2
PA6 — TIM3
 
Текст программы(в данном случаи яркость светодиодов сама плавно увеличивается и уменьшается):
/* USER CODE BEGIN 1 */
uint32_t i,j;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);//включаем таймер №2
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);//включаем таймер №3
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
for(i=200;i<=65535;i++) // увеличиваем скважность
{
TIM2->CCR1=i;
TIM3->CCR1=i;
for(j=0;j<700;j++){}//задержка
}
for(i=200;i<=65535;i++) // уменьшаем скважность
{
TIM2->CCR1=65535-i;
TIM3->CCR1=65535-i;
for(j=0;j<700;j++){} // задержка
}
}
/* USER CODE END 3 */

Рассмотрим настройку ШИМ для третьего таймера, но уже с использованием библиотеки CMSIS (часть настроек мы оставим за библиотекой HAL — тактирование периферии, остальное настроим с помощью CMSIS.):

  1. Нам необходимо включить тактирование таймера на порту А.
  2. Включить необходимую ножку на выход.
  3. Настроить TIM3 на нужные нам параметры.
  4.  Запустить в основной программе алгоритм изменения работы ШИМ.

Прежде чем мы напишем код, нам нужно немного разобраться в настройках нашего таймера TIM3:

Для включения тактирования таймера используется команда: RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

TIM3->PSC  — делитель частоты.

TIM3->ARR — устанавливает число до которого должен досчитать таймер и выполнить обновление.

TIM3->CCR2 — начальное состояние выхода при включении канала на выход

TIM3->CCER — включаем нужный канал на выход, его параметры:TIM_CCER_CC1E — первый канал, TIM_CCER_CC2E — второй канал, а так же можем установить полярность сигнала при старте: TIM_CCER_CC1P (либо высокий либо низкий).

TIM3->CCMR1 — выбор режима работы ШИМ:

При работе канала в режиме сравнения (канал используется как выход таймера):
OC1M: данные биты определяют способ формирования выходных сигналов OC1 и OC1N:
000: при совпадении значений счётчика (TIMx_CNT) и регистра сравнения TIMx_CCR1 состояние выходов не изменяется. Таймер в этом режиме может использоваться для того, чтобы формировать интервалы времени в программе.
001: установить на выходе канала 1  при достижении счётчиком TIMx_CNT значения в регистре сравнения этого канала (TIMx_CCR1).
010: установить на выходе канала 0 при достижении счётчиком TIMx_CNT значения в регистре сравнения этого канала (TIMx_CCR1).
011:  сигнал на выходе переключается на противоположное значение, когда TIMx_CNT=TIMx_CCR1.
100: принудительно устанавливается низкий уровень сигнала OC1REF (независимо от значений в TIMx_CNT, TIMx_CCR1).
101: принудительно устанавливается высокий уровень сигнала OC1REF (независимо от значений в TIMx_CNT, TIMx_CCR1).
110: режим 1 PWM — при счёте вверх, состояние выбранного канала находится в активном состоянии пока TIMx_CNT<TIMx_CCR1.
111: режим 2 PWM — при счёте вверх,  состояние выбранного канала находится в неактивном состоянии пока TIMx_CNT<TIMx_CCR1.
CC2S — данные биты позволяют выбрать направление канала 2 (вход/выход), а также используемый источник сигнала при работе в режиме входа:
00: канал CC2 сконфигурирован как выход.
01: канал CC2 сконфигурирован как вход, IC2 подключено к TI2.
10: канал CC2 сконфигурирован как вход,IC2 подключено к TI1.
11: канал CC2 сконфигурирован как вход; IC2 подключено к TRC; этот режим работает, только если выбран внутренний триггерный вход (с помощью бита TS в регистре TIMx_SMCR).
OC1PE — управляет буферизацией регистра TIMx_CCR1:
0: буферизация регистра TIMx_CCR1 не используется, запись в
регистр TIMx_CCR1 может быть произведена в любой момент и новое
значение начнёт использоваться немедленно.
1: используется буферизация регистра TIMx_CCR1; операции чтения/
записи выполняются с буферным регистром, новое значение из буфера
передаётся в активный регистр (который и управляет работой таймера)
ARPE -включения режима предзагрузки для регистра ARR:
0: регистр TIMx_ARR не буферизируется (т.е. используется прямая запись в TIMx_ARR).
1: регистр TIMx_ARR буферизируется (при записи/чтении регистра TIMx_ARR происходит доступ к буферному для него регистру, после модификации буферного регистра, таймер продолжает работу в прежнем режиме до наступления события обновления, после чего содержимое буфера будет помещено в теневой регистр)
DIR — Направление счёта:
0: счёт вверх.
1: счёт вниз.
CEN — включение счётчика:
        0: счётчик отключён.
        1: счётчик включён.
Программный код:

/* USER CODE BEGIN 0 */
uint32_t i,j;
/* USER CODE END 0 */

/* USER CODE BEGIN 2 */

RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Разрешаем тактирование TIM3
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ; // Разрешаем тактирование порта
GPIOA->CRL &= ~ GPIO_CRL_CNF6_0;
GPIOA->CRL |= GPIO_CRL_CNF6_1;
GPIOA->CRL |= GPIO_CRL_MODE6;
TIM3->PSC = 0 ; // Делитель частоты — 1
TIM3->ARR = 65535; // Обновляем каждые 65535 тиков
TIM3->CCR1 = 0 ; // В начале – не светим
TIM3->CCER |= (TIM_CCER_CC1E);
TIM3->CCER &= ~TIM_CCER_CC1P;

TIM3->CCMR1 |= ( TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 ) ;
TIM3->CCMR1 &= ~ TIM_CCMR1_OC1M_0 ;

TIM3->CCMR1 &= ~ TIM_CCMR1_CC1S ;
TIM3->CCMR1 |= TIM_CCMR1_OC1PE ;

TIM3->CR1 |= TIM_CR1_ARPE ;
TIM3->CR1 &= ~TIM_CR1_DIR ;

TIM3->CR1|= TIM_CR1_CEN ;

/* USER CODE END 2 */

/* USER CODE BEGIN 3 */
 for(i=200;i<=65535;i++) {// увеличиваем скважность
TIM3->CCR1=i;
for(j=0;j<700;j++){}//задержка
}
for(i=200;i<=65535;i++) {// уменьшаем скважность
TIM3->CCR1=65535-i;
for(j=0;j<700;j++){} // задержка
}
}
/* USER CODE END 3 */