STM32F4 C++ Örneği

Başlatan Klein, 22 Ocak 2012, 04:25:56

Klein

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]);			 
		}
	}
		
} 
}

Klein

#1
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();




Klein

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





Gökhan BEKEN

#3
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?
Özel mesaj okumuyorum, lütfen göndermeyin.

Klein

Yukarıdaki örneği incelediğinde timer kesme rutininin başında  bir tanım göreceksin.

extern "C" {

arm-beginner

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/

Gökhan BEKEN

Hocam çok teşekkür ederim düzeldi.
C ve C++ dillerini aynı projede kullanırken böyle yapmak gerekiyor demekki...
Özel mesaj okumuyorum, lütfen göndermeyin.

emre salman

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

Gökhan BEKEN

#8
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...
Özel mesaj okumuyorum, lütfen göndermeyin.