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
}
}
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