Cortex-M4 Oject Oriented (OOP)

Başlatan Klein, 06 Ocak 2012, 20:22:29

Klein

Bir süredir gömülü sistemlerde OOP ile ilgileniyorum. OOP zaten çok uzun zamandır ilgimi çeken bir konuydu.
Ama hem zamansızlık hem de projelerde geriye uyumluluk vs.. gibi sebeplerden ciddi ciddi uğraşamadım. Uygulamalarım denemelerle sınırlı kaldı.

STM32F4 Discovery kiti gelince yeni bir başlangıç olur umuduyla OOP üzerine tekrar yoğunlaşmak istedim.
OOP temellerini biliyorum. Bilmediklerimi de zaman içinde öğrenirim sanırım.

C++ ile OOP uygulamaları yapmış arkadaşlara bazı sorularım var. Sorularım nasıl yaparım sorularından ziyade , OOP ruhunu anlamaya yönelik ,daha çok yorum ağırlıklı cevap beklentisi içinde olduğum sorular.

İlk projem ( daha doğrusu nesnem) bir COM port nesnesi olacak.
Bir Com Port nesnesi oluşturduğumda hangi uart kanalını seçtiysenm onun interruptlarını da ele geçirmem gerekiyor. Bunun için şöyle bir yol izlemeyi düşünüyorum.

her interrupt servisi için bir tane fonsiyon işaretçim olacak.

void (*interrupt_usart1_rx_service)(void);
void (*interrupt_usart2_rx_service)(void);
void (*interrupt_usart3_rx_service)(void);

bu servislere parmetre , dönüş değeri vs.. ekleyebilirim. ama bunlar sonraki iş. basit olsun diye böyle.

Ayrıca her interrupt için handler rutinini kafadan programa dahil edeceğim.

void USART1_IRQHandler()
{
	interrupt_usart1_rx_service();
} 

void USART2_IRQHandler()
{
	interrupt_usart2_rx_service();
} 

void USART3_IRQHandler()
{
	interrupt_usart3_rx_service();
}


COM PORT Nesnemi oluşturduğumda
diyelim ki usart kanalı olarak 2 seçtim.

interrupt_usart2_rx_service = comport_rx_event;

gibi bir yol izleyerek ( tabi c++ kurallarına uygun olarak) 

interrupt hizmetini ele geçirmiş olacağım.
Fonksiyon adresini belki vektör tablosundan alabilirim. Ama bu yol şimdilik daha kolay göründü gözüme.

Benim planım bu şekilde.
Ama bu plan OOP ruhuna aykırımıdır?
Bu işin doğru yolu nedir?
Önereceğiniz alternatif bir yol var mıdır?

Bir diğer sorum da sınıflar için kullanılacak isimler ile ilgili.
Delphi alışkanığı ile , genelde sınıflar için TComPort gibi başına T (Type) koyarak isimlendirme yapıyorum. C++ için kenel kabul görmüş bir jargon var mı?

Klein

#1
Yukarıdaki şey OOP değik farkındayım. Derdimi C ile daha rahat anlatabileceğim için böyle anlattım.
O zaman direk CPP ile gireyim.

Aşağıdaki sınıf daha önceki denemelerimden biri. Modbus RTU Master sınıfı.
Buradaki sorun şuydu.
uart verisini interrupt içerisinden alıyorum. İnterrupt içerisinden de rx_irq() veya tx_irq() rutinlerinden birini çağırıyorum.
Bu durumda İş nesneyi oluşturmakla bitmiyor tabi. Port değiştirdiğinde interrupt çağrısını diğer portun interruptuna taşı vs..
Şimdiki en önemli sorunum bu. Yukarıdaki plan da bu sorunu çözmek üzerine kurulu.

ekleme:  create olayının bu şekilde olmadığının farkındayım. ilk denemeleri yaparken böyle kolay geldi :)
Pascal'daki 'T' nin Torbodan geldiğini bilmiyordum Hep Type lduğunu düşünürdüm. Bu yüzden de C'de bile teni veri tiplerimin başına
typedef anlamında 'T' koyarım.


class modbus_rtu{
public:
  void create(
                unsigned int const_cycle,
                unsigned int const_poll_delay,
                unsigned int const_pretrans,
                unsigned int const_timeout
              );
  void set_hardware(
                     unsigned short volatile __data13 *tx_buffer,
                     unsigned char volatile __data13 *tx_en_port,
                     unsigned char rs485_txen_bit
                    );
  void loop(void);
  void rx_irq(unsigned char rx_char);
  void tx_irq(unsigned char ready_flag);
  void timer_irq(void);
  unsigned char write_task(unsigned char id, unsigned char func,unsigned int start, unsigned int count , unsigned int *buf);
private:
  unsigned char read(unsigned char id, unsigned char func, unsigned int start, unsigned int count );
  unsigned char preset(unsigned char id, unsigned char func, unsigned int start, unsigned int count, unsigned int *buf);
  unsigned char apply_task(unsigned char id, unsigned char func,unsigned int start, unsigned int count,unsigned int *buf);
  void reset_task(void);
  void pretrans_wait(void);
  unsigned char locate_data(unsigned int *buf);
  unsigned char send(void);
  void poll_wait(void);
  unsigned int get_expected_length(unsigned char function , unsigned char byte_count);
public:
  unsigned char error;
  unsigned int errcount;
  unsigned int rxcount;
  unsigned int txcount;
  unsigned char txrx_buf[20];
  unsigned int debug_counter;
private:
  unsigned short volatile __data13 *tx_buf;
  unsigned char volatile __data13 *txen_port;
  unsigned char rs485_txen_pin;
  unsigned int pretrans_time;
  unsigned int rxindex;
  unsigned char msgstate;
  unsigned int expectedlength;
  unsigned char tx_datalength;
  unsigned int poll_delay;
  unsigned int time_count;
  command_struct task;
  statement_struct loop_flow , tx_flow;
  unsigned char function;
//  unsigned int *buffer;
  unsigned int CYCLE_TIME;// microsecond
  unsigned int POLL_DELAY_CONST;// microsecond
  unsigned int PRETRANS_CONST;// microsecond
  unsigned int RS485_DELAY_CONST;// microsecond
  unsigned int TIMEOUT;// milisecond
  union{
      unsigned char flags;
      struct{        
        unsigned char rx_ok:1;
        unsigned char rx_enable:1;
        unsigned char rx_busy:1;
        unsigned char tx_ok:1;
        unsigned char tx_enable:1;
        unsigned char tx_busy:1;
        unsigned char tx_buf_empity:1;
        unsigned char modbus_busy:1;
      };
  };    
/******************************************************************************/
};

Klein

Hocam boşverin interrupt vs... bunlar zaman içinde çözülür. Biz biraz daha temelden başlayalım. Hatta destek verirseniz belki
Embedded OOPbaşlığı bile açılabilir.

Sorum şu.
Önceki mesajımda verdiğimi sınıf deklerasyonun siz olsaydınız nasıl yapardınız?  Hatalar neler?

mesela sizin deklarasyonlarınızda "Handle" var.  Benim aklıma gelmemişti.

Public değişken kullanmayıp, bunun yerine private değişkenler kullanıp; bunlara yazıp okumak için public yazma okuma fonksiyonları mı koyardınız?

Muhtemelen sınıfın içine doğrudan struc koymazdınız. Hatta belki struct bile kullanmaz bunlar için başka bir class tanımlardınız.

Çok büyük olmuş deyip daha küçük sınıflara mı bölerdiniz?

neleri private neleri public yapardınız?



Klein

bir soru daha.

sınıflarla ilgili bazı kontrolleri derleme anında yapıp derleme hatası üretebiliyor muyuz.
örn:

comport nesnem var.

bu nesneden 4 tane koymuşum program yazarken.  ama 3 tane de uart kanalım var. bir uarta ancak bir nesne bağlanabiliyor.
derleme esnasında programı yazana hoop arkadaş! sen bu nesneden en fazla 3 tane koyabilirsin. daha fazlası programa zarar verir diyebiliyor muyuz.

Klein

#4
Donanımla çalışacak birok nesne için zamanlama bilgisi hayati önem taşıyor.
zamanlama bilgisini almak için şık bir yöntem bulamadım.
Şu an  aşağıdaki gibi bir yolla işimi hallediyorum.

class myobject 
{
    int timeout;
    ...
    ...
public
    void timebase(void);
    ...
    ...

}

myobject::timebase(){
     if(timeout) timeout--;
}


myobj obj1,obj2,obj3,obj4;

void timebase_interrupt_service(void){
    obj1.timebase();
    obj2.timebase();
    obj3.timebase();
    obj4.timebase();
}


Bu yöntem hiç kullanışlı değil. Bir nesnenin tanımını kaldırdığınızda nesnenin timebase() metoduna yapılan çağrıları da silmeniz gerekiyor. ya da tersi.

Zamanlama bilgisi almak için daha kullanışlı bir yol , yöntem ne olabilir.

Klein

KEil'de C++ ile yazdığım kodu çalıştıramadım. IAR'da C/C++ seçimi opsiyon menüsünden yapılıyordu. Keil'de bulamadım böyle bir opsiyon. Derlemeye  çalıştığımda sınıf tanımlamalarına hata veriyordu. Ben de main.c dosyasının adını main.cpp olarak değiştirdim. Kod derleniyor. Sınıf tanımlamalarına hata vermiyor. Ama çalışmıyor.
Startup kodunun aşağıdaki bölümünde kalıyor.
DCMI_IRQHandler                                                            
CRYP_IRQHandler                                                    
HASH_RNG_IRQHandler
FPU_IRQHandler                                                 

                B       .   Burada takılıyor.

                ENDP

                ALIGN

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************


Keil'de c++ kullanmak için ayarlar nelerdir?

NaMcHo

Alıntı yapılan: Klein - 15 Ocak 2012, 03:59:26
KEil'de C++ ile yazdığım kodu çalıştıramadım. IAR'da C/C++ seçimi opsiyon menüsünden yapılıyordu. Keil'de bulamadım böyle bir opsiyon. Derlemeye  çalıştığımda sınıf tanımlamalarına hata veriyordu. Ben de main.c dosyasının adını main.cpp olarak değiştirdim. Kod derleniyor. Sınıf tanımlamalarına hata vermiyor. Ama çalışmıyor.
Startup kodunun aşağıdaki bölümünde kalıyor.
Merhabalar, bu sorunu çözebildiniz mi?

NaMcHo

#7
Alıntı yapılan: gerbay - 17 Ocak 2012, 14:47:42
B .


satırında. sonsuz döngülerde assembly kodu olarak "B ."    üretilir. Orada aynı satırda dönüp durur işlemci..

Merhabalar,
Hem C++'ın class yapısını kullanmak hemde ilk Z domeni uygulamamı yapmak(Doğrultulmuş sinüs) için bir kod yazdım IAR'da.
#include "stm32f4xx.h"

float rk=0.0,rk_1=0.0,rk_2=0.0,uk=0.0,yk=0.0,ek=1.0;
const float a=0.0628,b=0.9980;
float cikis=0;

class Dac{
private:
  
public:
  void hazirla(){
     GPIOA->MODER=0xA8000200;        // PA4 Analog.
     RCC->APB1ENR|=1<<29;            // DAC kanal1 için Saat sinyali verildi.
     DAC->CR=0x00000003;             // DAC1 ve Buffer aktif , tetikleme kullanılmıcak.
     DAC->DHR12R1=0;
  }
  void dor(float data){
     DAC->DHR12R1=(unsigned int)data;
  }
}dac1;

class zamanlayici{
private:
  
public:
  void hazirla(){
     RCC->APB1ENR|=0x00000020;       // Timer7 CLK'u aktif edelim (84 Mhz)
     TIM7->CR1=0x0080;               // Otomatik Reload                  
     TIM7->DIER=0x0001;              // Update Int aktif.
     NVIC->ISER[1] = 0X00800000;     // NVIC de Timer 7 interrupta izin verelim
     TIM7->CR1|=0x0001;              // Counter Enable
  }
  //fCK_CNT=fCK_PSC/(PSC[15:0]+1) formülüyle Counter girişindeki frekansı bulucaz
  //T=10^-6 için f=1MHz  1MHz=84MHz/(PSC+1) PSC=83
  //Şimdi 100KHz'lik bir örnekleme için kaça kadar sayılcagını belirlicez 100KHz=1MHz/(ARR+1) ARR=9
  void kur(unsigned int prescaler,unsigned int counter){
    TIM7->PSC=prescaler;            
    TIM7->ARR=counter;
  } 
};

void hazirla();

void TIM7_IRQHandler()
{
  TIM7->SR=0;
  static short sayac=0;
    rk=ek+2*b*rk_1-rk_2;
    uk=a*rk_1;
    rk_2=rk_1;
    rk_1=rk;
    cikis=(int)(uk*(4095.0/32.0));
    dac1.dor(cikis);
    if(sayac==1)
    {
      GPIOD->ODR=(1<<12);
      sayac=0;
    }
    else
    {
      GPIOD->ODR=(0<<12);
      sayac++;
    }
} 

int main()
{
  zamanlayici tim7; //zamanlayici sınıfı özelliklerine sahip tim7 nesnesi oluşturuldu.
  
  hazirla();
  dac1.hazirla();
  //DAC->DHR12R1=4000;
  tim7.hazirla();
  tim7.kur(83,9);
  while(1);
}

void hazirla(void)
{
//  RCC Ayarları
  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
     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 |= 0x00000009;     // GPIO A,D aktif.
     GPIOD->MODER=(1<<24);          //PD12 Çıkış olarak ayarlandı
     GPIOD->OSPEEDR=(3<<24);        //PD12 Max hız.
}

Kod işletilirken ilk TIM7 kesmesi geldiğinde B yazan kısımda kod çakılıp kalıyor...Sonra birde keildeki kodu derleyip kite yüklediğimde uygulamam sorunsuz çalıştı.
Keildeki Uygulama Kodu:
#include "stm32f4xx.h"

//Not: void TIM7_IRQHandler() fonksiyonunu yazma bakalım ne olucak ? TIM7 interruptı aktif iken

float rk=0.0,rk_1=0.0,rk_2=0.0,uk=0.0,yk=0.0,ek=1.0;
const float a=0.0628,b=0.9980;
float cikis=0;
short sayac=0;

void hazirla(void);

void TIM7_IRQHandler()
{
  TIM7->SR=0;
 // static short sayac=0;
    rk=ek+2*b*rk_1-rk_2;
    uk=a*rk_1;
    rk_2=rk_1;
    rk_1=rk;
    cikis=(int)(uk*(4095.0/32.0));
    DAC->DHR12R1=(unsigned int)cikis;
    if(sayac==1)
    {
      GPIOD->ODR=(1<<12);
      sayac=0;
    }
    else
    {
      GPIOD->ODR=(0<<12);
      sayac++;
    }
} 

int main()
{
  hazirla();
  while(1);
}

void hazirla(void)
{
//  RCC Ayarları
  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
     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 |= 0x00000009;     // GPIO A,D aktif.
     GPIOD->MODER=(1<<24);          //PD12 Çıkış olarak ayarlandı
     GPIOD->OSPEEDR=(3<<24);        //PD12 Max hız.

	 //DAC Ayarları
	 GPIOA->MODER=0xA8000200;        // PA4 Analog.
     RCC->APB1ENR|=1<<29;            // DAC kanal1 için Saat sinyali verildi.
     DAC->CR=0x00000003;             // DAC1 ve Buffer aktif , tetikleme kullanılmıcak.
     DAC->DHR12R1=0;

	 //TIM7 Ayarları
	    RCC->APB1ENR|=0x00000020;         // Timer7 CLK'u aktif edelim (84 Mhz)
	    TIM7->CR1=0x0080;                      // Otomatik Reload
	    TIM7->PSC =83;                   
	    TIM7->ARR =9;                       
	    TIM7->DIER=0x0001;                     // Update Int enable
	    NVIC->ISER[1] = 0X00800000;        // NVIC de Timer 7 interrupta izin verelim
	    TIM7->CR1|=0x0001;                    // Counter Enable
}


IAR'daki bu sorunun sebebi ne olabilir?

Uygulama Videosu :

Klein

Onlarca kez kontrol ettim  ama yönetmediğim bir interrupt yok.
Hatta bütün interruptları kapatıp , tek bir interrupt ile deniyorum. Ama aynı. Interrupt geldiği anda program orada takılıp kalıyor. Interrupt handler rutinine hiç girmiyor.   

NaMcHo

#9
Merhabalar,
Sorunu hallettim.

Alıntı Yap
If you use C++, an interrupt function could look, for example, like this:

extern "C"
{
   __irq __arm void IRQ_Handler(void);
}

__irq __arm void IRQ_Handler(void)
{
}
Böle yapınca sorun düzeliyor B satırında interrupt rutini takılıp kalmıyor.

EWAM DevelopmentGuide.Enu Dökumanında sayfa : 61'de yazıyor.

İyi çalışmalar.

Klein

Ben de Keil'deki sorunu benzer şekilde çözdüm.

bütün interrupt hanler rutinlerini

extern "C"{

}

bloğu içine aldım. 
STM32F4xx.h içerisinde
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

tanımı var. ama yine de derleyici kendisi blok içine almıyor.  __cpluplus define edilmiyor olabilir diye kendim
#define __cplusplus  yaptım. redefine uyarısı verdi.


z

Klein bahsettigin sorunu gormemiz icin hazir proje klasorunu upload edebilirmisin?

Bir ara startup koduyla fazlasiyla hasir nesir olmustum. Sorunu oradan cozerim diye dusunuyorum.
Bana e^st de diyebilirsiniz.   www.cncdesigner.com

Klein

Hocam kodlar linkte.
http://hotfile.com/dl/143048245/a26a4f7/cpp_bug.rar.html

Interrupt Handler rutinlerinin olduğu bölümdeki 
extern "C" {

}

bloğunu kaldırdığınızda sorun başlıyor.
Her yazdığım programda interrupt handler kodunu bu bloğun içine alıp yola devamedebilirim. Ama bana bu iş böyle olmamalı gibi geliyor.


z

Projeyi verdigin halde de derledim, //extern "C"{  ve sondaki //} blogu kaldirip da derledim sifir hata ve uyari ile derledi.

Bana e^st de diyebilirsiniz.   www.cncdesigner.com

Klein

Derlerken uyarı vermiyor. Çalışma anında sorun oluyor.