http://hotfile.com/dl/143124347/90cd576/PwmClass.rar.html
Nihayet ilk işe yarar sınıfımı bitirdim.
STM32 Timerlerinin Compare modüllerini kullanarak PWM sinyali üreten nesneye ait sınıf.
Yapılandırıcı, yıkıcı ve hata yönetimi şimdilik yok. Daha sonra ekleyeceğim.
C++ konusunda bilgi sahibi olan arkadaşların eleştirilerini bekliyorum.
sınıf deklerasyonu
stm32f4xx_CPwm.h
#include "STM32F4xx_GPIO.h"
#include "STM32F4xx_TIMER.h"
class CPwmOut{
private:
TIMER_TypeDef* Timer;
PORTIO_TypeDef* Port;
uint8_t channel_no;
uint8_t timer_no;
public:
// CPwmOut(TIMER_TypeDef *Tmr, uint8_t ch, uint16_t prsc, uint16_t cntr);
// ~CPwmOut();
// void *(error_manager)(uint8_t errcode);
void start(void);
void stop(void);
void polarity_active_hi();
void polarity_active_lo();
void timer_init(uint8_t tmr_no, uint8_t ch, uint16_t prsc, uint16_t cntr);
void port_init(PORTIO_TypeDef *port, uint16_t pin, uint8_t out_type, uint8_t speed);
void set_duty(uint32_t new_val);
void update_irq_enable();
void update_irq_disable();
void set_align_mode(uint16_t algnmode);
uint32_t get_duty();
};
stm32f4xx_CPwm.cpp
#include "STM32F4xx_PWM.h"
const uint32_t TMR_ADDR_LOOKUP[] = {TIM1_BASE , TIM2_BASE, TIM3_BASE, TIM4_BASE , TIM5_BASE, TIM6_BASE, TIM7_BASE ,
TIM8_BASE , TIM9_BASE, TIM10_BASE, TIM11_BASE , TIM12_BASE, TIM13_BASE, TIM14_BASE};
const uint8_t TMR_AF_LOOKUP[] = {PORT_AF1_TIM1 , PORT_AF1_TIM2,
PORT_AF2_TIM3, PORT_AF2_TIM4, PORT_AF2_TIM5, PORT_AF0_SYSTEM,PORT_AF0_SYSTEM,
PORT_AF3_TIM8, PORT_AF3_TIM9, PORT_AF3_TIM10, PORT_AF3_TIM11,
PORT_AF9_TIM12, PORT_AF9_TIM13, PORT_AF9_TIM14
};
const uint32_t TMR_APBEN_LOOKUP[] = { 0x00000001, 0x00000001, 0x00000002, 0x00000004, 0x00000008,
0x00000010, 0x00000020, 0x00000002, 0x00010000, 0x00020000,
0x00040000, 0x00000040, 0x00000080, 0x00000100
};
const uint32_t TMR_APB_ADDR_LOOKUP[] = {0x11,0x10,0x10,0x10,0x10,0x10,0x10,0x11,0x11,0x11,0x11,0x10,0x10,0x10};
#define TMR_CCMR_OFFSET 12
void CPwmOut::timer_init(uint8_t tmr_no, uint8_t ch, uint16_t prsc, uint16_t cntr){
uint32_t* APBEN_ADR = 0;
uint16_t* CCMR_ADR = 0;
timer_no = tmr_no-1;
channel_no = ch-1;
Timer = (TIMER_TypeDef *) TMR_ADDR_LOOKUP[timer_no] ;
APBEN_ADR = (uint32_t *)RCC + TMR_APB_ADDR_LOOKUP[timer_no];
*APBEN_ADR |= TMR_APBEN_LOOKUP[timer_no];
Timer->PSC = prsc; // Ön bölücü (Fck_PSC / (PSC_VAL+1))
Timer->ARR = cntr; // CCR1 ile karşılaştırılacak sayı.
CCMR_ADR = (uint16_t *)Timer + TMR_CCMR_OFFSET + (((channel_no)/2)*2);
*CCMR_ADR &= ~((uint16_t)0x00FF << ((channel_no % 2)*8));
*CCMR_ADR |= ((uint16_t) 0x0068 << ((channel_no % 2)*8)); // TMR_OCPE_PRELOAD_ENABLE, TMR_OCM_PWM_MODE1
Timer->CR1.ARPE = TMR_ARPE_NOTPRELOAD;
Timer->CR1.CEN = TMR_COUNTER_ENABLE;
Timer->EGR.UG = TMR_UG_UPDATE_REINIT;
}
void CPwmOut::port_init(PORTIO_TypeDef *port, uint16_t pin, uint8_t out_type, uint8_t speed){
Port = port ;
port->MODER.all &= ~(0x00000003 << (pin*2)); // PORT alternatif fonksiyon registeri temizle
port->MODER.all |= ((uint32_t)PORT_ALTERNATE_FUNC << (pin*2)); // PORT alternatif fonksiyon
port->OSPEEDR.all &=~(0x00000003 << (pin *2)); // PORT hız registerini temizle
port->OSPEEDR.all |= ((uint32_t)speed << (pin *2)); // PORT çıkış hızı
port->OTYPER.all &= ~(0x00000003 << (uint32_t)(pin *2)); // Çıkış tipi registerini temizle
port->OTYPER.all |= ((uint32_t)out_type << (pin *2)); // Çıkış tipi Push-Pull
port->AFR.all &= ~(0x0fULL << (pin *4)); // PORT Alternatif fonksiyon registeri temizle
port->AFR.all |= ((uint64_t)TMR_AF_LOOKUP[timer_no] << (pin *4)); // PORT TIMER ile ilişkilendirildi
}
void CPwmOut::start(void){
Timer->CCER.CCER |= (0x0001 << (channel_no * 4));
}
void CPwmOut::stop(void){
Timer->CCER.CCER &= ~(0x0001 << (channel_no * 4));
}
void CPwmOut::polarity_active_hi(){
Timer->CCER.CCER &= ~(0x0002 <<(channel_no *4)); // polarite
}
void CPwmOut::polarity_active_lo(){
Timer->CCER.CCER |= (0x0002 <<(channel_no *4)); // polarite
}
void CPwmOut::set_duty(uint32_t new_val){
Timer->CCR.CCR[channel_no] = new_val;
}
uint32_t CPwmOut::get_duty(){
return(Timer->CCR.CCR[channel_no]);
}
void CPwmOut::update_irq_enable(){
Timer->DIER.UIE = TMR_UIE_UPDATE_IRQ_ENABLE; // Interrupt açık her periyotta interrupt üretir.
}
void CPwmOut::update_irq_disable(){
Timer->DIER.UIE = TMR_UIE_UPDATE_IRQ_DISABLE; // Interrupt kapalı. Açılırsa her periyotta interrupt üretir.
}
void CPwmOut::set_align_mode(uint16_t alignmode){
Timer->CR1.CMS =alignmode;
}
Kullanımı
main.cpp
#include <stdio.h>
#include "STM32F4xx.h"
#include "STM32F4xx_GPIO.h"
#include "STM32F4xx_TIMER.h"
#include "STM32F4xx_CPWM.h"
unsigned char WAdr,RAdr;
char RxBuf[128];
char TxBuf[128];
unsigned char timer_update=0;
uint16_t pwm_demands[4];
CPwmOut pwm1 , pwm2 , pwm3 , pwm4;
/*********************************************************************************
CPU frekansi 168Mhz
AHB frekansi 84 Mhz
APB frekansi 42 Mhz
*********************************************************************************/
void SystemInit()
{
unsigned int i;
for (i=0;i<0x00100000;i++); // OSC oturtma ve kurtarma rutini
RCC->CFGR |= 0x00009400; // AHB ve APB hizlarini max degerlere set edelim
RCC->CR |= 0x00010000; // HSE Xtal osc calismaya baslasin
while (!(RCC->CR & 0x00020000));// Xtal osc stabil hale gelsin
RCC->PLLCFGR = 0x07402A04; // PLL katsayilarini M=4, N=168, P=2 ve Q=7 yapalim 168 Mhz
RCC->CR |= 0x01000000; // PLL calismaya baslasin (Rehber Sayfa 95)
while(!(RCC->CR & 0x02000000)); // Pll hazir oluncaya kadar bekle
FLASH->ACR = 0x00000605; // Flash ROM icin 5 Wait state secelim ve ART yi aktif edelim (Rehber Sayfa 55)
RCC->CFGR |= 0x00000002; // Sistem Clk u PLL uzerinden besleyelim
while ((RCC->CFGR & 0x0000000F) != 0x0000000A); // Besleninceye kadar bekle
RCC->AHB1ENR |= 0x0000001F; // GPIO A,B,C,D,E clock'u aktif edelim
GPIOD->MODER = 0x55550000; // GPIOD nin 15, 14, 13, 12, 11, 10, 9, 8 pinleri cikis tanimlandi (LEDler icin)
GPIOD->OSPEEDR= 0xFFFFFFFF; // GPIOD nin tum cikislari en yuksek hizda kullanacagiz
RCC->APB1ENR|=0x00000020; // Timer7 CLK'u aktif edelim (84 Mhz)
TIM7->CR1=0x0080; // Otomatik Reload
TIM7->PSC =4200; // Prescaler degerimiz 839, Count frekansimiz = fCK_PSC / (Yuklenen Deger + 1) 84E6 / (840) = 100 KHz
TIM7->ARR =2; // Counter, Decimal 1 olunca basa donsun. Her 20 mikrosaniye de bir timer int olusacak.
TIM7->DIER=0x0001; // Update Int enable
TIM7->CR1|=0x0001; // Counter Enable
NVIC->ISER[1] = 0X00800000; // NVIC de Timer 7 interrupta izin verelim
}
/*********************************************************************************
USART3 modulunu kullanarak asenkron haberlesme (Hata kontrolu yapilmiyor)
*********************************************************************************/
extern "C"{
void USART3_IRQHandler()
{
volatile int Sts;
Sts=USART3->SR;
RxBuf[WAdr]=USART3->DR;
WAdr=(WAdr+1)&0x7F;
}
void TIM7_IRQHandler(void)
{
TIM7->SR=0;
timer_update=1;
}
}
void UsartInit()
{
WAdr=0;RAdr=0;
// USART3 MODULUNU AKTIF HALE GETIRELIM
RCC->APB1ENR|=0x00040000; // USART3 Clk Enable (Rehber Sayfa 113)
RCC->APB1RSTR|=0x00040000; // USART3 Resetlendi
GPIOB->AFR[1]=0x07777700; // PB10..PB14 pinleri USART3 ile alakalandirildi (Hard Sayfa 49)
GPIOB->MODER|=0x2AA00000; // GPIOB 10..14 icin alternatif fonksiyon tanimi (Rehber Sayfa 148)
// USART3 MODULUNU AYARLAYALIM // 1 Start, 8 Data, 1 Stop, No parity (Default degerler)
RCC->APB1RSTR&=~0x00040000; // USART3 Reseti kaldiralim
// USART3->SR&=~0X03FF; // Status registeri silelim
USART3->BRR=0X1112; // 9600 Baud
USART3->CR1|=0x0000202C; // USART3 enable
NVIC->ISER[1]|=0x80; // NVIC da USART3 interrupta izin verelim
}
void SendChar(char Tx)
{
while(!(USART3->SR&0x80)); // TX Buffer dolu ise bekle (Rehber Sayfa 646)
USART3->DR=Tx;
}
void SendTxt(char *Adr)
{
while(*Adr)
{
SendChar(*Adr);
Adr++;
}
}
char DataReady()
{
return(WAdr-RAdr);
}
char ReadChar()
{
char Dat;
Dat=RxBuf[RAdr];
RAdr=(RAdr+1)&0x7F;
return(Dat);
}
// Rx ve TX pinlerini (GPIOB10 ve GPIOB11) birbirine baglarsaniz gonderdiginiz datalar geri gelecektir
int main()
{
unsigned int ledtime=0;
pwm1.timer_init(3,1,1,4096);
pwm1.port_init(PB,4,PORT_PUSH_PULL,PORT_HI_SPEED);
pwm2.timer_init(4,1,1,4096);
pwm2.port_init(PD,12,PORT_PUSH_PULL,PORT_HI_SPEED);
pwm3.timer_init(4,2,1,4096);
pwm3.port_init(PD,13,PORT_PUSH_PULL,PORT_HI_SPEED);
pwm4.timer_init(4,3,1,4096);
pwm4.port_init(PD,14,PORT_PUSH_PULL,PORT_HI_SPEED);
UsartInit();
SendTxt("PicProje C++ OOP Ornegi");
pwm1.start();
pwm2.start();
pwm3.start();
pwm4.start();
while(1){
if(timer_update){
timer_update=0;
if(++ledtime > 100){
ledtime =0;
GPIOD->ODR ^= 0x0000008000;
if(++pwm_demands[0] > 4096) pwm_demands[0]=0;
if(++pwm_demands[1] > 4096) pwm_demands[1]=0;
if(--pwm_demands[2] > 4096) pwm_demands[2]=4096;
if(++pwm_demands[3] > 4096) pwm_demands[3]=0;
pwm1.set_duty(pwm_demands[0]);
pwm2.set_duty(pwm_demands[1]);
pwm3.set_duty(pwm_demands[2]);
pwm4.set_duty(pwm_demands[3]);
}
}
}
}
C++ Bilmeyenler için kullanım açıklamaları.
Komple proje dosyaları linkte mevcut.
1- Aynen değişken tanımlar gibi CPwmOut sınıfından nesneler tanımlıyoruz.
örnek:
int a,b // normal değişken tanımlama.
CPwmOut a,b,c //nesne tanımlama.
2- main fonksiyonumuzun başında bu nesnelerin kullanacağı timer, kullanacağı kanal, bölücü değerleri gibi değerleri set ediyoruz.
pwm1.timer_init(Timer no , Ch No , Ön bölücü , Çözünürlük);
1.Paramerte Timer numarası. TIM1 için 1, TIM2 için 2 .... TIM14 için 14
2. parametre kullanacağımız kanal CH1 için 1 , CH2 için 2 CH3 için 3 CH4 için de 4
3. parametre timerimizin ön bölücü değeri. Eğer bus hızımız 84Mhz ise değeri 1 yaparsak counter hızımız 42MHz olur.
4. parametre PWM frekansını ve çözünürlüğünü belirleyen parametre. Örneğin: 10 bit çözünürlük istiyorsak , bu değerimiz 1024 olacak.
12 bit istersek 4096. İstersek de 15000 gibi bir değer verip 2'nin üssü olmayan bir çözünürlük de ayarlayabiliriz.
Bu değer aynı zamanda PWM frekansımızı da belirler. frekans = Counter hızı / çözünürlük olur.
Örnek : a.timer_init(3,1,1,4096);
b.timer_init(4,3,65,1000);
pwm1.port_init(Port , Pin , Çıkış tipi , Port hızı);
1. Parametre Çıkış alacağımız port. Bu portu rastgele belirleyemiyoruz. Hard:49 dan seçtiğimiz timerin hangi portlara çıkış verebildiğini
bulup, portu ona göre seçmemiz gerekiyor.
2. parametre Çıkış alacağımız pin. Bunu da Hard:49 dan buluyoruz.
3. parametre Portun çıkış tipi Push-Pull ya da Open-Drain olabiliyor. PORT_PUSH_PULL veya PORT_OPEN_DRAIN kullanarakseçebiliriz.
4. Parametre Portun çıkış hızı. Bu parametrenin PWM frekansı ile ilgisi yok. daha önce şamatalarda konuşulmuştu.
Örnek a.port_init(PB,4,PORT_PUSH_PULL,PORT_HI_SPEED);
b.port_init(PA,8,PORT_OPEN_DRAIN,PORT_LO_SPEED);
3- Artık PWM sayıcısını başlatabiliriz.
a.start();
b.start();
PWM değerini değiştirmek için.
a.set_duty( 100);
b.set_duty( 1000);
c.set_duty(0);
PWM polaritesini değiştirmek için
a.polarity_active_hi();
b.polarity_active_lo();
Terslenmiş PWM çıkışı olmayan timer kanalları ile , terslenmiş çıkış ve deadband PWM örneği.
1- Bu uygılamada Timer update kesmesi kullanılacak. Timer'in kesmesi NVIC registerlerinden aktif edilmeli.
Nesnemizin NVIC registerine müdahalesi yok.
2- Timer'i center align modda kullanacağız. bu da PWM frekansımızın yarıya düşmesi demek.
3- Deadband zamanı n/counter hızı olarak hesaplanır. n deadband için bizim belirleyeceğimiz sayı.
4- Kullanacağımız iki kanal da aynı timerin kanalı olmalı. Bu uygulamada Timer4 kanal 2 ve 4 ü kullanacağız.
#include "STM32F4xx.h"
#include "STM32F4xx_GPIO.h"
#include "STM32F4xx_TIMER.h"
#include "STM32F4xx_PWM.h"
uint16_t pwm=0;
CPwmOut pwm_hi,pwm_lo;
void SystemInit()
{
unsigned int i;
for (i=0;i<0x00100000;i++); // OSC oturtma ve kurtarma rutini
RCC->CFGR |= 0x00009400; // AHB ve APB hizlarini max degerlere set edelim
RCC->CR |= 0x00010000; // HSE Xtal osc calismaya baslasin
while (!(RCC->CR & 0x00020000));// Xtal osc stabil hale gelsin
RCC->PLLCFGR = 0x07402A04; // PLL katsayilarini M=4, N=168, P=2 ve Q=7 yapalim 168 Mhz
RCC->CR |= 0x01000000; // PLL calismaya baslasin (Rehber Sayfa 95)
while(!(RCC->CR & 0x02000000)); // Pll hazir oluncaya kadar bekle
FLASH->ACR = 0x00000605; // Flash ROM icin 5 Wait state secelim ve ART yi aktif edelim (Rehber Sayfa 55)
RCC->CFGR |= 0x00000002; // Sistem Clk u PLL uzerinden besleyelim
while ((RCC->CFGR & 0x0000000F) != 0x0000000A); // Besleninceye kadar bekle
RCC->AHB1ENR |= 0x0000001F; // GPIO A,B,C,D,E clock'u aktif edelim
NVIC_EnableIRQ(TIM4_IRQn);
}
extern "C" {
void TIM4_IRQHandler(){
pwm_hi.set_duty(pwm);
pwm_lo.set_duty(pwm-32); // 32 deadband için verdiğimiz değer. bu uygulamada 760ns
TIM4->SR=0;
}
}
int main()
{
pwm_hi.timer_init(4,1,1,4096); // counter frekansımız 42Mhz PWM frekansımız 10.25KHz
pwm_hi.port_init(PD,12,PORT_PUSH_PULL,PORT_HI_SPEED);
pwm_hi.update_irq_enable(); // aynı zamanlayıcıyı kullanan nesneler için sadece birinde aktif etmek yeterli.
pwm_hi.set_align_mode(TMR_CMS_ALIGNMODE1); // PWM frekansımız 5.12 KHz'e düştü.
pwm_lo.timer_init(4,3,1,4096);
pwm_lo.port_init(PD,14,PORT_PUSH_PULL,PORT_HI_SPEED);
pwm_l0.polarity_active_lo(); // bu kanalın polaritesini ters çevirdik.
pwm_lo.set_align_mode(TMR_CMS_ALIGNMODE1);
pwm_hi.start();
pwm_lo.start();
pwm= 300;
while(1);
}
(http://a1201.hizliresim.com/t/q/20d8z.png)
(http://b1201.hizliresim.com/t/q/20d9t.png)
Hocam, c ile derlenip düzgün çalışan basit led yakma işlemini ve timer7 işlemini, c++ çevirdim.
main.cpp dosyası oluşturdum, main.c dosyasında ne varsa main.cpp içine attım sonra, main.c dosyasını sildim.
Derlendi ve yükledim. Ancak sorunlu çalışıyor, timer arkaplanda neler yapıyorsa bilmiyorum, timeri kaldırdım düzeldi.
Sorunu çözmek önemli değil, bu yüzden kodları yazma gereği duymadım(Bülent hocanın timer7 kodları). Neden böyle birşey olur onu anlamadım. Dosyayı cpp yapınca ne değişir ki?
Bir sorum daha var.
Bir projenin bir kısmı cpp uzantılı dosyalardan oluşup, bir kısmı c uzantılı dosyalardan oluşuyorsa problem olur mu, anlaşabilirler mi?
Yukarıdaki örneği incelediğinde timer kesme rutininin başında bir tanım göreceksin.
extern "C" {
extern "C" { C++ ile kullanmanın ne anlamı var? C ile yazılmış fonksiyonları C++ link etmek için kullanılıyor ve yeri header dosyası.
Diye biliyorum ve öylede kullanıyorum.
http://www.cplusplus.com/forum/general/1143/ (http://www.cplusplus.com/forum/general/1143/)
Hocam çok teşekkür ederim düzeldi.
C ve C++ dillerini aynı projede kullanırken böyle yapmak gerekiyor demekki...
iyi günler c dili ile c++ dili arasındaki fark nedir neden c++ kullanmalıyız yada neden c kullanmalıyız acaba c++ ın bize kazandırdığı artılar nelerdir
Alıntı yapılan: zerolax - 05 Nisan 2014, 15:44:08
iyi günler c dili ile c++ dili arasındaki fark nedir neden c++ kullanmalıyız yada neden c kullanmalıyız acaba c++ ın bize kazandırdığı artılar nelerdir
c++ 'ın en büyük getirisi class'lar ile çalışabilmek yani OOP denilen programlama tekniğini kullanabilmek.
c++ ile değişken ve fonksiyon tanımlamaları daha kolay.
#include kullanımında da kolaylıklar var.
Tek tek yazmak çok uzun sürer.
Daha bir sürü yeni özellik var...