Мигаем диодами с помощью ШИМ ( PWM ) stm32f4 discovery [ arm ]

В этой статье речь пойдёт об использовании ШИМ контроллера. Применений этому режиму работы море, от банального моргания диодом, до управления двигателями и прочей электроникой.. Суть заключается в том, что при помощи широтно-импульсной модуляции и сглаживающей RC цепи можно получать аналоговое напряжение в диапазоне от логической еденицы до 0.

Мы же будем использовать таймер TIM4 нашего STM32F4 Discovery в качестве формирователя ШИМ сигнала (на буржуйском PWM — pulse width modulation).

Настроим и оставим как есть, чтобы диоды «моргали» с разной частотой.

При нажатии на пользовательскую кнопку на порте A0 будет производится переконфигурация таймера. Частота будет изменена…

Приступим.

Инклудим:

#include <stm32f4xx.h>
#include <stm32f4xx_gpio.h> 
#include <stm32f4xx_rcc.h> 
#include <stm32f4xx_tim.h> 
#include <stm32f4xx_exti.h> 
#include <misc.h> 
#include <stm32f4xx_syscfg.h>

Думаю тут всё понятно) Если нет идём в поиск по сайту.

Теперь инициализируем необходимую периферию:

void init_gpio() {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE); 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); 

    GPIO_InitTypeDef init; 
    init.GPIO_Mode = GPIO_Mode_AF; 
    init.GPIO_OType = GPIO_OType_PP; 
    init.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; 
    init.GPIO_Speed = GPIO_Speed_100MHz; 
    GPIO_Init(GPIOD,&init); 

    GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4); 
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4); 
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_TIM4);
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_TIM4); 
}

Тут всё должно быть ясно, за исключением разве что последних строк.

Мы переконфигурируем выходы 12,13,14,15 для их работы с таймером. Теперь сигнал формируемый таймером будет идти прямо на выводы контроллера.

Далее настроим таймер для работы в режиме ШИМ :

void tim_init(){ 
        TIM_TimeBaseInitTypeDef base_timer; 
        TIM_TimeBaseStructInit(&base_timer); 

        base_timer.TIM_Prescaler = 8400 - 1;   // делитель частоты 
        base_timer.TIM_Period = 10000;  // период
        base_timer.TIM_CounterMode = TIM_CounterMode_Up; // счёт вверх 
        TIM_TimeBaseInit(TIM4, &base_timer); 

        TIM_OCInitTypeDef oc_init; 
        TIM_OCStructInit(&oc_init);
        oc_init.TIM_OCMode = TIM_OCMode_PWM1;   // работаем в режиме ШИМ ( PWM ) 
        oc_init.TIM_OutputState = TIM_OutputState_Enable; 
        oc_init.TIM_Pulse = 500;   // частота шим 
        oc_init.TIM_OCPolarity = TIM_OCPolarity_High;  // положительная полярность 

        TIM_OC1Init(TIM4,&oc_init);   /// заносим данные в первый канал - порт D12 
        TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        //2 
        oc_init.TIM_Pulse = 5000; /// изменяем частоту шим 
        TIM_OC2Init(TIM4,&oc_init);  // настраиваем второй канал D13 
        TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        //3 канал - D14 
        oc_init.TIM_Pulse = 500; 
        TIM_OC3Init(TIM4,&oc_init); 
        TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        //4  - D15 
        oc_init.TIM_Pulse = 5000; 
        TIM_OC4Init(TIM4,&oc_init);
        TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        TIM_ARRPreloadConfig(TIM4,ENABLE); 
        TIM_Cmd(TIM4,ENABLE);   // запускаем счёт
}

Если не ясно с таймерами идём суда: простые таймеры stm32
Прерываение коментировать не буду. На сайте есть статья посвящённая прерываниям.

void init_it() { 
      EXTI_InitTypeDef   EXTI_InitStructure; 
      GPIO_InitTypeDef   GPIO_InitStructure; 
      NVIC_InitTypeDef   NVIC_InitStructure; 
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
      GPIO_Init(GPIOA, &GPIO_InitStructure); 
      SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0); // разрешаем внешнее прерывание по порту А 
      EXTI_InitStructure.EXTI_Line = EXTI_Line0; 
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
      EXTI_InitStructure.EXTI_LineCmd = ENABLE; 
      EXTI_Init(&EXTI_InitStructure); 
      NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; 
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; 
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
      NVIC_Init(&NVIC_InitStructure);
}

Обработчик прерывания:

void EXTI0_IRQHandler() { 
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) { 
        TIM_OCInitTypeDef oc_init; 
        TIM_OCStructInit(&oc_init); 
        oc_init.TIM_OCMode = TIM_OCMode_PWM1; 
        oc_init.TIM_OutputState = TIM_OutputState_Enable; 
        oc_init.TIM_Pulse = 5000; 
        oc_init.TIM_OCPolarity = TIM_OCPolarity_High; 
        TIM_OC1Init(TIM4,&oc_init); 
        TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        //2 
        oc_init.TIM_Pulse = 500; 
        TIM_OC2Init(TIM4,&oc_init); 
        TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        
        //3 
        oc_init.TIM_Pulse = 5000; 
        TIM_OC3Init(TIM4,&oc_init); 
        TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        
        //4 
        oc_init.TIM_Pulse = 500; 
        TIM_OC4Init(TIM4,&oc_init); 
        TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); 
        EXTI_ClearITPendingBit(EXTI_Line0);    
 } 
}

На изящность кода не претендую, хотел только показать что изменять частоту импольсов можно «на лету». Что и даёт все вкусности использования ШИМ в STM32.

Дело за малым:

int main(void){ 
    init_gpio(); 
    init_it(); 
    tim_init(); 
    while (1){ 
    } 
    return 0; 
}

Теперь можно смотреть результат.

Два диода моргают примерно раз в секунду, два — в 50 мс.

При нажатии кнопки они меняются местами.

Скачать пример.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий