Небольшой пример по stm32: Таймеры и PWM

Осваиваю Армы, для одной из задач потребовалась генерация тактовой частоты в 32 kHz. Решил разобраться с PWM в stm32, поэтому выкладываю свой примерчик, кому-нибудь, да пригодится. Сразу оговорюсь — пишу с помощью библиотеки стандартной переферии — нравится она мне, да и размер флеша позволяет.
Итак, вот исодник с комментариями:

#include <stm32f10x.h>

void InitAll(void)
{
    // Подаем тактовую частоту на таймеры и порт A
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // Конфигурация порта
    GPIO_InitTypeDef GPIO_Config;
    // Пины 0, 6, 7
    GPIO_Config.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_6 | GPIO_Pin_7;
    // Альтернативная функция (в нашем случае - выход таймера), Push-Pull
    GPIO_Config.GPIO_Mode = GPIO_Mode_AF_PP;
    // Частота - 50 MHz
    GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;

    // Инициализируем порт A этой конфигурацией
    GPIO_Init(GPIOA, &GPIO_Config);

    // Конфигурация таймера
    TIM_TimeBaseInitTypeDef TIM_BaseConfig;
    // Конфигурация выхода таймера
    TIM_OCInitTypeDef TIM_OCConfig;

    // Запускаем таймер на тактовой частоте в 4800 kHz
    TIM_BaseConfig.TIM_Prescaler = (uint16_t) (SystemCoreClock / 4800000) - 1;
    // Период - 150 тактов => 4800/150 = 32 kHz
    TIM_BaseConfig.TIM_Period = 149;
    TIM_BaseConfig.TIM_ClockDivision = 0;
    // Отсчет от нуля до TIM_Period
    TIM_BaseConfig.TIM_CounterMode = TIM_CounterMode_Up;

    // Инициализируем таймер №3 (его выходы как раз на порту A)
    TIM_TimeBaseInit(TIM3, &TIM_BaseConfig);

    // Конфигурируем выход таймера, режим - PWM1
    TIM_OCConfig.TIM_OCMode = TIM_OCMode_PWM1;
    // Собственно - выход включен
    TIM_OCConfig.TIM_OutputState = TIM_OutputState_Enable;
    // Пульс длинной 75 тактов => 75/150 = 50%
    TIM_OCConfig.TIM_Pulse = 74;
    // Полярность => пульс - это единица (+3.3V)
    TIM_OCConfig.TIM_OCPolarity = TIM_OCPolarity_High;

    // Инициализируем первый выход таймера №3 (у HD это PA6)
    TIM_OC1Init(TIM3, &TIM_OCConfig);

    // Конфигурируем второй выход таймера
    TIM_OCConfig.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCConfig.TIM_OutputState = TIM_OutputState_Enable;
    // Пульс длинной 30 тактов => 30/150 = 20%
    TIM_OCConfig.TIM_Pulse = 29;
    // Ради эксперемента, меняем полярность (пульс - 0V).
    TIM_OCConfig.TIM_OCPolarity = TIM_OCPolarity_Low;

    // Инициализируем второй выход таймера №3 (PA7)
    TIM_OC2Init(TIM3, &TIM_OCConfig);

    // Как я понял - автоматическая перезарядка таймера, если неправ - поправте.
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM3, ENABLE);

    // Включаем таймер
    TIM_Cmd(TIM3, ENABLE);

    // Тоже самое, только теперь таймер №5 на частоте 10 kHz
    TIM_BaseConfig.TIM_Prescaler =  (SystemCoreClock / 10000) - 1;
    // Мигаем светодиодом 1 раз в секунду.
    TIM_BaseConfig.TIM_Period = 9999;
    TIM_BaseConfig.TIM_ClockDivision = 0;
    TIM_BaseConfig.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM5, &TIM_BaseConfig);

    TIM_OCConfig.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCConfig.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCConfig.TIM_Pulse = 4999;
    TIM_OCConfig.TIM_OCPolarity = TIM_OCPolarity_High;

    // Первый выход - PA0
    TIM_OC1Init(TIM5, &TIM_OCConfig);

    TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM5, ENABLE);

    TIM_Cmd(TIM5, ENABLE);
}

int main(void)
{
    InitAll();
 
    while(1)
    {
    }
    return 0;
}

В результате получилась вот такая картинка:

0 канал — первый выход, 1 — второй выход. Хорошо видна обратная полярность у второго канала.
  • +3
  • 04 июля 2011, 11:43
  • Kosyak
  • 1
Файлы в топике: stm32_pwm.zip

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

RSS свернуть / развернуть
Привет. На каком микроконтроллере это все проделал, если не секрет?
0
Следует заметить две неточности:
1. в конфигурационном файле, например, stm32_f10x_conf.h следует раскомментировать строчку: #include «stm32f10x_tim.h» или подключить эту библиотеку в main.
2. следует изменить порядок создания структур и инициализации RCC(сначала создавать структуры необходимых типов, а после — инициировать), иначе компилятор также будет выдавать ошибку, вроде Error[Pe268]: declaration may not appear after executable statement in block.
0
// Как я понял — автоматическая перезарядка таймера, если неправ — поправте.
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);

Это не перезарядка таймера. Это необходимость буферизировать мгновенное состояние счетчика по некому событию, дабы потом считать его не останавливая таймер от его работы. В данном примере не несет никакой пользы и можно пропустить.
0
Очень интересно — а что это за софт в котором сигналы видны?
0
Один из логических анализаторов подключаемых к ПК через порт ввода вывода (USB, LPT, RS-232...)
0
Есть ли у этих таймеров бит как на AVRовских — «FOC1A, FOC1B: служат для принудительного изменения состояния выходов OC1A и OC1B.»? Надо после выключения ШИМа (сбрасываю бит CEN) принудительно на TIMx_CH4 выставить «0″.
0
  • avatar
  • del
  • 12 июля 2013, 03:00
Никто не запрещает выключить альтернативную функцию на выводе (а на GPIO тем временем уже установлен низкий уровень задан) =)
+1
Добрый день.
Вижу, что много времени прошло, но может кому другому пригодится. Да, такая возможность есть, она выполняется выбором одного из режимов работы устройства захвата/сравнения, а именно:

Bits 6:4 OC1M: Output compare 1 mode
These bits define the behavior of the output reference signal, OC1REF, from which OC1 is derived.
OC1REF is active high whereas OC1 active level depends on the CC1P bit.
000: Frozen — The comparison between the output compare register TIM1_CCR1 and the counter
register TIM1_CNT has no effect on the outputs.
001: Set channel 1 to active level on match — OC1REF signal is forced high when the counter
register TIM1_CNT matches the capture/compare register 1 (TIM1_CCR1).
010: Set channel 1 to inactive level on match — OC1REF signal is forced low when the counter
register TIM1_CNT matches the capture/compare register 1 (TIM1_CCR1).
011: Toggle — OC1REF toggles when TIM1_CNT = TIM1_CCR1
100: Force inactive level — OC1REF is forced low
101: Force active level — OC1REF is forced high
110: PWM mode 1 — In up-counting, channel 1 is active as long as TIM1_CNT < TIM1_CCR1,
otherwise, the channel is inactive. In down-counting, channel 1 is inactive (OC1REF = 0) as long as
TIM1_CNT > TIM1_CCR1, otherwise, the channel is active (OC1REF = 1).
111: PWM mode 2 — In up-counting, channel 1 is inactive as long as TIM1_CNT < TIM1_CCR1,
otherwise, the channel is active. In down-counting, channel 1 is active as long as TIM1_CNT >
TIM1_CCR1, otherwise, the channel is inactive.

По сути, от PWM режима их отличает только 1 бит.
Данные взяты из даташита STM8, но в STM32 таймеры устроены также.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.