Bu yazımızda PWM uygulaması yapacağız. PWM (Pulse width modulation) motor hız kontrolü, ışık şiddeti ayarlama gibi pek çok uygulamada kullanılan bir yöntemdir. Temel mantığı, belli bir frekansta sürekli terslenen sinyalin, darbe boşluk oranını değiştirmektir. Aşağıdaki resimde darbe boşluk oranı değişimini görebilirsiniz.
Bu uygulamada NUCLEO kartı üzerinde bulunan ledi PWM ile sürerek ışık şiddetini ayarlayacağız.
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_tim.h" int main(void) { /* GPIO init */ // LED PIN AYARI RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // A portunun saat darbesini aktifleştidik. GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 5 numaralı pin için ayarlıyoruz. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // Pin çıkış tipini ayarladık. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Pinimizi alternatif fonksiyon olarak ayarladık. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // Pull Up direncini pasif yaptık. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // Pinin maksimum çalışma frekansını ayarladık. GPIO_Init(GPIOA, &GPIO_InitStructure); // Ayarları uyguluyoruz. GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_TIM2); // Ledin bağlı olduğu pine alternatif fonksiyon atadık. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; /* TIM2 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = (SystemCoreClock / 1000 ) - 1; // 1 KHz PWM frekansı ayarladık. TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* Channel 1 Configuration in PWM mode */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_Pulse = 1000; // Duty değeri TIM_OC1Init(TIM2, &TIM_OCInitStructure); // Kanal 1 i ayarladık. /* TIM2 counter enable */ TIM_Cmd(TIM2, ENABLE); /* TIM2 Main Output Enable */ TIM_CtrlPWMOutputs(TIM2, ENABLE); // PWM çıkışını aktifleştirdik. while (1) { } }// main sonu.
Daha önceki uygulamalarımızda ledin bağlı olduğu pini çıkış olarak ayarlamıştık. Bu sefer pine PWM uygulayacağımız için GPIO_Mode’u alternatif fonksiyon olarak ayarladık. Daha sonra GPIO_PinAFConfig() fonksiyonu ile pine uygulanacak olan alternatif fonksiyonu, timer 2 alternatif fonksiyonu olarak ayarladık.
STM32 serisi mikrokontrolörlerde neredeyse her pine birden fazla alternatif fonksiyon atanabilmektedir. Kullanmak istediğiniz pine atanabilen alternatif fonksiyonların neler olabileceğini ürünün kataloğundan öğrenebilirsiniz. Aşağıda STM32F401 serisine ait katalogtan bir parça görülmektedir. Ledin bağlı olduğu PA5 pinine atanabilen alternatif fonksiyonlar arasında timer 2 kanal 1 fonksiyonu olduğu için PWM ayarlarımızı buna göre yaptık.
TIM_TimeBaseStructure.TIM_Period = (SystemCoreClock / 1000 ) - 1; // 1 KHz PWM frekansı ayarladık.
Timer frekansını 1kHz olacak şekilde ayarladık.
TIM_OCInitStructure.TIM_Pulse = 1000; // Duty değeri TIM_OC1Init(TIM2, &TIM_OCInitStructure); // Kanal 1 i ayarladık. /* TIM2 counter enable */ TIM_Cmd(TIM2, ENABLE); /* TIM2 Main Output Enable */ TIM_CtrlPWMOutputs(TIM2, ENABLE); // PWM çıkışını aktifleştirdik.
Darbe boşluk oranını ve kanal 1 ayarladıktan sonra timerı ve pwm çıkışını aktifleştirdik.
Aşağıda bu uygulama ile PA5 pininde oluşan sinyal şekli görülmektedir.
Kit üzerindeki ledin hafif bir ışık yaydığını görebilirsiniz. Işık şiddetini arttırmak için ayar kısmındaki duty değerini arttırabilirsiniz. Fakat gerçek uygulamalarda bu değeri ana program içerisinde değiştirerek ışık şiddeti yada motor hızını ayarlamanız gerekir. Şimdi böyle bir örnek uygulama yapalım.
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_tim.h" void Set_Duty(uint32_t Duty); static __IO uint32_t TimingDelay; void SysTick_Handler(void); void Delay(__IO uint32_t nTime); void TimingDelay_Decrement(void); int main(void) { if (SysTick_Config(SystemCoreClock / 1000)) { /* Capture error */ while (1); } /* GPIO init */ // LED PIN AYARI RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // A portunun saat darbesini aktifleştidik. GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 5 numaralı pin için ayarlıyoruz. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // Pin çıkış tipini ayarladık. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Pinimizi output olarak ayarladık. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // Pull Up direncini pasif yaptık. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // Pinin maksimum çalışma frekansını ayarladık. GPIO_Init(GPIOA, &GPIO_InitStructure); // Ayarları uyguluyoruz. GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_TIM2); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; /* TIM2 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = (SystemCoreClock / 10000 ) - 1; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* Channel 1 Configuration in PWM mode */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_Pulse = 0; // Duty değeri TIM_OC1Init(TIM2, &TIM_OCInitStructure); /* TIM2 counter enable */ TIM_Cmd(TIM2, ENABLE); /* TIM2 Main Output Enable */ TIM_CtrlPWMOutputs(TIM2, ENABLE); int i; while (1) { for (i = 0; i <= 100; ++i) { Set_Duty(i); Delay(10); } for (i = 100; i > 0; --i) { Set_Duty(i); Delay(10); } } }// main sonu. void Set_Duty(uint32_t Duty) { TIM_SetCompare1(TIM2, (Duty * ((SystemCoreClock / 10000 ) - 1)) / 100); // Duty değerini ayarla. } /** * @brief This function handles SysTick Handler. * @param None * @retval None */ void SysTick_Handler(void) { TimingDelay_Decrement(); } /** * @brief Inserts a delay time. * @param nTime: specifies the delay time length, in milliseconds. * @retval None */ void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); } /** * @brief Decrements the TimingDelay variable. * @param None * @retval None */ void TimingDelay_Decrement(void) { if (TimingDelay != 0x00) { TimingDelay--; } }
Önceki uygulamadan farklı olarak bu uygulamada Set_Duty() isminde bir fonksiyon oluşturduk, ve ana programımızda bu fonksiyonu for döngüsü içerisinde çağırarak duty değerini değiştirdik. Timer kütüphanesi içerisinde bulunan TIM_SetCompare1() fonksiyonu ile duty değerini direkt olarak ilgili register adresine atadık. Uygulama sonucu kit üzerindeki ledin yavaşca parlaklığının arttığını ve azaldığını görebilirsiniz. Duty değişimin gözle görülebilir olması için daha önce gördüğümüz systick timer ile gecikme kullandık.
Proje dosyasını linkten indirebilirsiniz. –> coide_PWM_uygulamasi_1
Proje dosyasını linkten indirebilirsiniz. –> coide_PWM_uygulamasi_2