3 Eylül 2016 Cumartesi

5 - STM32F4 PWM Kullanımı

Merhaba Arkadaşlar,

                Sıradaki örneğimiz Stm32f4 ile PWM nasıl üretilir, bunun hakkında kısa bilgi ardından da kod paylaşımı yapılacaktır.
Öncelikle PWM frekansı üretmek için Timer’lara ihtiyaç duyarız ve bu yüzden timer ayarları yapmamız gerekir. Daha sonra timer’ın hangi kanalından ya da kanallarından pwm vereceğimizi belirtiriz.

Aşağıda ahngi timer’ın hangi pin ve dolayısıyla kanallardan pwm üretebileceğinin şeması bulunmaktadır. Bu şemaya bakarak çok rahat bir şekilde hangi timer’ı kullanıyorsak, hangi portun hangi kanalını kullanacağımızı bulabiliriz.


Yukarıdaki tabloya bakacak olursak, örneğin timer 8 kullanacaksak, kanal1 olarak PC6 veya PI5 pinini kullanmamız gerekir. Kanal2 olarak PC7 veya PI6 pinini kullanabiliriz. Aynı şekilde kanal3 ve kanal 4 için de aynı yorumları yapabiliriz.

1 – bütün timerların çalışma frekansı aynı değildir. Bunun sebebini bir önceki yazımızda vermiştik. Çünkü bazı timerlar APB1, bazıları da APB2 bus hattına bağlıdırlar. Bu hatlar farklı bus hızına sahip olduğu için timerların çalışma frekansları da farklı olacaktır. Aşağıda yine bus hattı tablosu verilmiştir.


2 – Bütün timerlar 16Bit Prescaler değerine sahiptirler.
3 – Timer6 ve Timer7 basit timer’lar demiştik. Bu yüzden bu timerlar pwm sinyali üretemezler.
4 - Timer2 ve Timer5 32bit timerlardır.
5 – Timer9 ve Timer12 iki kanal PWM üretebilirler.
6 – Timer10, Timer11, Timer13 ve Timer14 ise tek bir kanal PWM çıkışı verebilirler.
7 – Bir Timer’daki tüm kanallar aynı pwm frekansına sahiptirler. Sadece doluluk oranları farklı olabilir.

Artık kod yazma kısmına geçebiliriz:
Biz bu örnekte timer5’i kullanacağız. Timer5 32Bit timer, yukarıdaki tabloya bakarak kanal1’i kullanacağız ve bu yüzden de PA0 veya PH10 pinini kullanmamız gerekiyor. Biz PA0 pinini tercih ettik. 10Khz (10 000 Hz)’lik bir Pwm üretelim.

Bunun için Prescaler değeri belirlememiz gerekiyor. Prescaler değeri 16Bit olduğunu unutmayalım. O yüzden programın başına:

uint16_t PRSC_Degeri = 0; adında bir değişken tanımlayalım. Peki bu prescaler değerini nasıl buluruz:

PRSC_Degeri = (uint16_t)(SystemCoreClock/ counter clock)-1; formülü ile bulunur. Ancak Timer 5 APB1 bus hattına bağlı olduğu için SystemCoreClock’un yarısını almamız gerekiyor. O yüzden APB1 bus hattı için formül ise :

PRSC_Degeri = (uint16_t)((SystemCoreClock/2 )/ counter clock)-1; olarak söyleyebiliriz. Diğer APB2 hattı için yukarıda verilen ilk formül geçerlidir.

Hesaplamalarımızı yapalım:

PRSC_Degeri = (uint16_t)((168 Mhz/2 )/ 4Mhz)-1;  //clock frekansı 4 Mhz olsun(biz belirledik)
PRSC_Degeri = (21) – 1 = 20 olarak belirlenir. Ancak zaten biz bunu koda formül olarak koyacağımız için burada gerekli olan kısım bizim belirlediğimiz counter clock değeridir. Biz 4 Khz için 4Mhz’lik(4000000Hz) bir counter clock belirledir. Peki 10 Khz pwm sinyali için 4Mhz ‘lik clock frekansını ne yapacağız. Periyod değerini öyle bir değer vereceğiz ki PWM sinyalimiz 10 Khz olsun. 

O halde:

10 Khz = 4 Mhz / period formülü kullanılır.
Period = 400 değeri olması gerekmektedir.

Genel bir toplarsak yukarıdakileri: 10Khz için:

uint16_t PRSC_Degeri = 0;   //değişken tanımladık.
PRSC_Degeri = (uint16_t)((168 Mhz/2 )/ 4Mhz)-1
Period = 400                olarak belirledik. Hesap kitap işlerinden sonra başlayalım:


1 - Öncelikle gerekli kütüphane eklemelerini yapmamız gerek. Bunlar:

#include "stm32f4xx.h"                                               //stm32f4 kütüphanesi
#include "stm32f4xx_gpio.h"                    //GPIO kütüphanesi
#include "stm32f4xx_rcc.h"                       // RCC clock kütüphanesi
#include "stm32f4xx_tim.h"                      // timer kütüphanesi

2- Periph clock hatlarını aktifleştirelim:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);

3 – Timer’ın pin ayarlamalarını yapalım:

GPIO_InitTypeDef  GPIO_InitStructure;
               
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;   //Pinler analog function olarak belirlendi
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //Pin PushPull olarak ayarlandi
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;      //GPIO Bus hizi 100 MHz ayarlandi
GPIO_Init(GPIOA, &GPIO_InitStructure);  //Yukarida tanimlanan bilgileri GPIOA adresine ait oldugu belirtilir
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5); // timer5 PA0 pininden pwm verecek

4 – Timer ayarlarını yapalım:

TIM_TimeBaseInitTypeDef                        TIM_TimeBaseStructure;
               
PRSC_Degeri = (uint16_t)(SystemCoreClock/2 / 4000000)-1;  //yukarıda hesapladık aynısı
               
TIM_DeInit(TIM5);
 TIM_TimeBaseStructure.TIM_ClockDivision                 = 0;
TIM_TimeBaseStructure.TIM_CounterMode                   = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period                               = 400 - 1;             // 10 Khz için
TIM_TimeBaseStructure.TIM_Prescaler                           = PRSC_Degeri;
TIM_TimeBaseInit( TIM5,&TIM_TimeBaseStructure);

5 – PWM ayarlarını yapalım:

Eğer %20 dolulukta bir pwm sinyali istiyorsak bunun formülü ise:
Doluluk oranı = TIM_Pulse / Period formülü ile bulunur.

TIM_OCInitTypeDef      TIM_OCInitStructure;
               
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 80;                 //%20 doluluk oranı için 80/400 = %20
TIM_OC1Init(TIM5, &TIM_OCInitStructure);
               
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM5, ENABLE);

6 – Son olarak timeri aktifleştiririz.:

TIM_Cmd(TIM5, ENABLE);          // timerı aktifleştiririz.


Normalde biz bunların hepsini bir ana döngü içerisinde yazabiliriz. Ancak biraz daha kodların anlaşılır ve düzenli olması için fonksiyonlarla yazalım:

#include "stm32f4xx.h"                             // stm32f4 kütüphanesi
#include "stm32f4xx_gpio.h"                    // GPIO kütüphanesi
#include "stm32f4xx_rcc.h"                      // RCC clock kütüphanesi
#include "stm32f4xx_tim.h"                      // timer kütüphanesi

uint16_t PRSC_Degeri = 0;

void Periph_Config()
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
}

void Timer_GPIO_Config()
{
GPIO_InitTypeDef  GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;    //Pinler analog function olarak belirlendi
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //Pin PushPull olarak ayarlandi
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;       //GPIO Bus hizi 100 MHz ayarlandi
GPIO_Init(GPIOA, &GPIO_InitStructure);  //Yukarida tanimlanan bilgileri GPIOA adresine ait oldugu belirtilir
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5); // timer5 PA0 pininden pwm verecek
}

void Timer_Config()
{
TIM_TimeBaseInitTypeDef                        TIM_TimeBaseStructure;
PRSC_Degeri = (uint16_t)(SystemCoreClock/2 / 4000000)-1;  //yukarıda hesapladık aynısı
TIM_DeInit(TIM5);
 TIM_TimeBaseStructure.TIM_ClockDivision                     = 0;
TIM_TimeBaseStructure.TIM_CounterMode                   = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period                                   = 400 - 1;             // 10 Khz için
TIM_TimeBaseStructure.TIM_Prescaler                                             = PRSC_Degeri;
TIM_TimeBaseInit( TIM5,&TIM_TimeBaseStructure);
}

void PWM_Config()
{
TIM_OCInitTypeDef      TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 80;                 //%20 doluluk oranı için 80/400 = %20
TIM_OC1Init(TIM5, &TIM_OCInitStructure);    
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM5, ENABLE);
}

//Ana fonksiyon
int main(void)
{
                Periph_Config();
                Timer_GPIO_Config();
                Timer_Config();
                PWM_Config();
                               
  while (1)            //Sonsuz donguye girilir
                               {                                            
                                               TIM_Cmd(TIM5, ENABLE); //Timer5 aktifleştirilir
                               }
}

Osiloskoptan bakıldığında da PWM sinyalinin aşağıdaki gibi olduğunu göreceksiniz:

Frekans = 1/100us = 1/100*10^-6 = 10000 Hz
toplam periyod 100us, doluluk oranı = 20us = %20


Aşağıda kodun resmini bulabilirsiniz:


İyi Çalışmalar.

                                                                                                                            




Hiç yorum yok:

Yorum Gönder