MCU'lar için C++ kullanlar?

Başlatan Elektroemre, 07 Ocak 2012, 16:07:58

Klein

:) yok hocam muhakkak fantezi için değil. Öğrendikçe bir sürü şey keşfediyorum ve her geçen gün daha da seviyorum.
Mesajımın hedefindeki kişiler, C++ kullanmak için gerçekten hiç gerekçesi olmayanlar.

yamak

Alıntı yapılan: Klein - 09 Ocak 2012, 17:14:45
Gömülü sistemlerde C++ kullanmak meselesini sadece ihtiyaç meselesine de indirgememek gerek. Belki C++ kullanmaya hiç ihtiyacımız olmayabilir. Ama onun getirdiği yenilikler, programlaya katacağı hoşluklar program yazmayı belki daha eğlenceli hale getirecek.

İnternet explorer kullanırken bir bağlantıyı yeni sekmede açmak için farenin orta tuşunu kullanıyorum. Sağ tuşa basıp yeni sekmede aç da diyebilirim. Orta tuşa ihtiyacım var mı? Yok! Ama böyle kullanmayı seviyorum.

C++ daki fonksiyon aşırı yükleme bile C++ kullanmayı isteğimiartırıyor. Şart mı değil. C ile, farklı şekillerde  halledebilirim. Ama bu özelliği kullanmak bana keyif veriyor.

Embedded programcılar için gerçek hayatta her zaman karşılaşmasak da karşılaşabileceğimiz bir örnek:

Bir I2C aygıtımız var. I2C kontrolünü software ile yapmak zorundayız. Bir sorun yok hemen bir program yazar hallederiz.
Peki bu aygıttan 2 tane lazım olursa ve biz bu aygıtı başka porta bağlamak zorunda kalırsak?
Sorun yok oturur 2. I2C rutinleri yazarız. Ya da rutinimizi parametrik hale getirir 2. I2C kontrolünü de yaparız.
Peki 3. 4. 5. 6. I2C ihtiyacı duyduğumuzda ne yapacağız?

Aynı şeyi C++ ile yapsaydık.
Bir tane I2C sınıfı tanımlardık.  O kadar.
Sonra bu sınıftan bir nesne oluşturup port ayarlarımızı yapardık iş bitti.
2. lazım olduğunda? bir nesne daha tanımla port ayarlarını yap kullan.
3. 4. 5. 6. 7. kaynakların yettiği sürece devam.

Bu işi C ile yapabiliyoruz. O zaman C++ gerekli mi? Değil. Ama kullanmak ister miyim? Evet.
Hocam burada anlattığınız şeyleri c'de de kütühane oluşturup yapabiliyoruz ama. Yanlış mıyım?

Klein

#17
Evet bazılarını C ile'de yapabilirsiniz. Ama C++'da olduğu kadar şık ve esnek olmaz. İhtiyaç meselesine indirgememek gerektiğini söylemem de bu yüzden.

Aşağıdaki kod yukarıda bahserriğim "i2c" sınıfı ve bu sınıftan nesne atamaya örnek kod.
Bu kodla işlemcimizin istediğimiz prtuna istediğimiz kadar i2c aygıt bağlayabiliyoruz. Bu işi c ile yapabiliriz.  ben C++ kodlarını koyuyorum. Bu işin C ile yapılmış halini de birisi koyarsa, C++ programcılık hayatımıza nasıl renkler katabilir daha iyi görebiliriz. Belki de C++ ile kod yazmaya hiç sıcak bakmayanlaın kafasında acaba? sorusunu uyandırabiliriz.  Amacım bununla yapılır , şununla yapılmaz gibi bir kıyaslama değil. C++ ile kod yazmanın benim hayatıma getirdiği kolaylıkları sizinle paylaşarak, biraz ilgi duymanızı sağlamak.

i2c sınıfının tanımı.
class i2c {
void delay(unsigned int usec);
void clk(void);
void start(void);
void stop(void);
void ack(void);
void init(void);
void write(unsigned char devdata);
unsigned char read(unsigned char ackchar);

unsigned char volatile __data13 *SCL;
unsigned char volatile __data13 *SDA;
unsigned char volatile __data13 *sda_dir;
unsigned char volatile __data13 *scl_dir;
unsigned char scl_mask;
unsigned char sda_mask;

public:
unsigned char read_byte(unsigned char i2caddr,unsigned char memadr);
void write_byte(unsigned char i2caddr,unsigned char memadr, unsigned char memdata);
void set_hardware( unsigned char volatile __data13 *sclport, unsigned char scl_pin,
                   unsigned char volatile __data13 *sdaport, unsigned char sda_pin,
                   unsigned char volatile __data13 *scl_direction_register,
                   unsigned char volatile __data13 *sda_direction_register);

  

};


i2c sınıfının üye fonksiyonları
#include "i2c.h"
#define ACK 0
#define NACK 1

const unsigned char port_mask[]={
                                    0x01,0x02,0x04,0x08,
                                    0x10,0x20,0x40,0x80,
                                    };

void i2c::set_hardware( unsigned char volatile __data13 *sclport, unsigned char scl_pin, 
                    unsigned char volatile __data13 *sdaport, unsigned char sda_pin, 
                    unsigned char volatile __data13 *scl_direction_reg, 
                    unsigned char volatile __data13 *sda_direction_reg )
{
  SCL = sclport;
  SDA = sdaport;
  sda_dir = sda_direction_reg;
  scl_dir = scl_direction_reg;
  scl_mask = port_mask[scl_pin];
  sda_mask = port_mask[sda_pin];
  init();
}

void i2c::init(void){
  *sda_dir |= sda_mask; // output
  *scl_dir |= scl_mask; // output
  stop();
}

void i2c::clk(void){
 *SCL |= scl_mask ;  
 *SCL &= ~scl_mask;
}

void i2c::start(void){
  *SCL |= scl_mask;
  *SDA &= ~sda_mask;
  *SCL &=~scl_mask;
}

void i2c::stop(void){
  *SDA &= ~sda_mask; 
  *sda_dir |=sda_mask; // output
  *SCL |=scl_mask;
  *SDA |=sda_mask;
}

void i2c::ack(void){
          *sda_dir &=~sda_mask; //input
          clk();
          *SDA |=sda_mask;
	       *sda_dir |=sda_mask; // output
}

void i2c::write(unsigned char devdata){
unsigned char I;
unsigned char sdata;
   *sda_dir |= sda_mask; // output
   sdata=devdata;
   for(I=0; I < 8; I++)
   {
      if(sdata & 0x80) *SDA |= sda_mask; else *SDA &= ~sda_mask;
	  clk();
      sdata <<=1;
   }
   ack();
};
   
unsigned char i2c::read(unsigned char ackchar){
unsigned char I;
unsigned char sdata=0;
    *sda_dir &=~sda_mask; //input
    *SDA |= sda_mask;
   for(I=0; I < 8; I++)
   {
      *SCL |= scl_mask;  
      sdata <<=1;
      if(*SDA & sda_mask ) sdata |=1;
	   *SCL &= ~scl_mask;
   }
   *sda_dir |= sda_mask; // output
   clk();
   return(sdata);
}



void i2c::write_byte(unsigned char i2caddr,unsigned char memadr, unsigned char memdata){
  start();
  write(i2caddr);
  write(memadr);
  write(memdata);
  stop();
}

unsigned char i2c::read_byte(unsigned char i2caddr,unsigned char memadr){
unsigned char eedata=0;
  start();
  write(i2caddr);
  write(memadr);
  start();
  write(i2caddr+1);
  eedata=read(ACK);
  stop();
  return(eedata);
}


kullanımı

#include <ior8c24_25.h> 
#include "i2c.h"


i2c i2c_eeprom;
i2c i2c_pot;
i2c i2c_device1;
i2c i2c_device2;

unsigned char x,y,z;

int main(){
i2c_eeprom.set_hardware(&p3,1,&p2,2,&pd3,&pd2);
i2c_pot.set_hardware(&p1,7,&p2,6,&pd1,&pd2);
i2c_device1.set_hardware(&p3,4,&p2,5,&pd3,&pd2);
i2c_device2.set_hardware(&p1,6,&p1,2,&pd1,&pd1);


i2c_eeprom.write_byte(0xA0, 12, 122);
x= i2c_eeprom.read_byte(0xA0, 12);
y= i2c_device2.read_byte(0xE0,x);
i2c_device1.write_byte(0xD0,y,x);
z=i2c_pot.read_byte(0xB0,156);

return 0;
}

Tagli

Bu arada, yanlış anlaşılma olmasın: Nesne tabanlı programlamayı çok seven biriyim. C'den önce Java öğrendim. C#'la da uğraşmışlığım var. Hayatta karşıma çıkan hemen her sorunu (özellikle de programlama sorunlarını) nesne mantığı ile düşünür ve çözüm ararım.

Ancak işletim sistemi olmadan nasıl malloc veya new yazabileceğimizi anlamış değilim. Benim bildiğim kadarıyla (ki burada aksinin söylendiğini görene kadar da bildiğimden oldukça emindim) boş alan isteyebilmek için, bu alanı isteyebileceğiniz bir otorite olması gerekir. Yani malloc bir alan istemidir ve arkada bu isteme cevap verebilecek bir işletim sistemi gerekir. Zaten malloc nadir de olsa null döndürebilir. Bunun anlamı, işletim sisteminin size istediğiniz alanı veremediğidir. Bu durumda ona göre işlem yapmak gerekir.

gerbay hocam, bunun nasıl mümkün olabileceğini anlatıp konuya açıklık getirebilirsen sevinirim.
Gökçe Tağlıoğlu

cicjoe

-IAR C Library Functions dokumani soyle diyor:
malloc void *malloc(size_t size)
Description
Allocates a memory block for an object of the specified size.
The availability of memory depends on the size of the heap. For more information
about changing the heap size, see the IAR C Compiler Reference Guide.

-EWARM Compiler Reference dokumani da soyle diyor:
Dynamic memory on the heap
Memory for objects allocated on the heap will live until the objects are explicitly
released. This type of memory storage is very useful for applications where the amount
of data is not known until runtime.
In C, memory is allocated using the standard library function malloc, or one of the
related functions calloc and realloc. The memory is released again using free.
In C++, there is a special keyword, new, designed to allocate memory and run
constructors. Memory allocated with new must be released using the keyword delete.

MCU uzerinde calisinca otorite biz oluyoruz. Yani birileri bizden malloc talebinde bulunabilir. Ancak soyle dusunelim. Bir fonksiyon icerisinde degisken tanimladik. int turunden olsun. o fonksiyon cagrilana kadar o degisken RAM'de degil. Cagrildiginda bir adres belirleniyor. Fonksiyon bitince de RAM'den siliniyor. Bu da aslinda dinamik bir adres atamasidir. Yani hepsini static girmeliyiz demissiniz, pek mumkun degil gibi gorunuyor.

cicjoe

Yani isletim sistemi dinamik yer saglayabiliyorsa, ve MCU uzerinde biz calisirken, aslinda MCU'nun isletim sistemi oluyorsak (yanlisim varsa duzeltin), bu dinamik yer saglama islemini kendimize yapabiliriz diye dusunuyorum.

Klein

Dinamik nesne oluşturma için yazdığım kod ve IAR'ın derleme sonunda ürettiği kod aşağıda. Kodu denemediğim için çalışıp çalışmadığını bilmiyorum.

int main(){
i2c *i2c_ptr;
i2c_ptr= new i2c;

i2c_ptr->set_hardware(&p3,1,&p2,2,&pd3,&pd2);
i2c_ptr->write_byte(0x80,x,15);

i2c_eeprom.set_hardware(&p2,6,&p3,1,&pd2,&pd3);
i2c_eeprom.write_byte(0xA0, 12, 122);


     12          int main(){
   \                     main:
   \   000000   7DBF         ADD.B   #-0x1, SP
     13          i2c *i2c_ptr;
     14          i2c_ptr= new i2c;
   \   000002   75C00A00     MOV.W   #0xa, R0
   \   000006   FD......     JSR.A   `??operator new`
   \   00000A   7305         MOV.W   R0, A1
     15          
     16          i2c_ptr->set_hardware(&p3,1,&p2,2,&pd3,&pd2);
   \   00000C   7DE2E600     PUSH.W  #0xe6
   \   000010   7DE2E700     PUSH.W  #0xe7
   \   000014   7DE2E400     PUSH.W  #0xe4
   \   000018   C302         MOV.B   #0x2, R0H
   \   00001A   C401         MOV.B   #0x1, R0L
   \   00001C   75C2E500     MOV.W   #0xe5, R2
   \   000020   7354         MOV.W   A1, A0
   \   000022   FD......     JSR.A   ??set_hardware
   \   000026   7DB6         ADD.B   #0x6, SP
     17          i2c_ptr->write_byte(0x80,x,15);
   \   000028   7CE200       PUSH.B  #0x0
   \   00002B   7CE20F       PUSH.B  #0xf
   \   00002E   0F....       MOV.B   x, R0H
   \   000031   74C080       MOV.B   #0x80, R0L
   \   000034   7354         MOV.W   A1, A0
   \   000036   FD......     JSR.A   ??write_byte
   \   00003A   7DB2         ADD.B   #0x2, SP
     18          
     19          i2c_eeprom.set_hardware(&p2,6,&p3,1,&pd2,&pd3);
   \   00003C   7DE2E700     PUSH.W  #0xe7
   \   000040   7DE2E600     PUSH.W  #0xe6
   \   000044   7DE2E500     PUSH.W  #0xe5
   \   000048   C301         MOV.B   #0x1, R0H
   \   00004A   C406         MOV.B   #0x6, R0L
   \   00004C   75C2E400     MOV.W   #0xe4, R2
   \   000050   75C4....     MOV.W   #i2c_eeprom, A0
   \   000054   FD......     JSR.A   ??set_hardware
   \   000058   7DB6         ADD.B   #0x6, SP
     20          i2c_eeprom.write_byte(0xA0, 12, 122);
   \   00005A   7CE200       PUSH.B  #0x0
   \   00005D   7CE27A       PUSH.B  #0x7a
   \   000060   74C10C       MOV.B   #0xc, R0H
   \   000063   74C0A0       MOV.B   #0xa0, R0L
   \   000066   75C4....     MOV.W   #i2c_eeprom, A0
   \   00006A   FD......     JSR.A   ??write_byte
   \   00006E   7DB2         ADD.B   #0x2, SP

cicjoe

hocam bilgiler icin tesekkurler, cok aciklayici olmus gercekten. elinize saglik.
yani arka planda mutlaka benzer algoritmalar kullaniyordur ancak C++'da bu yapi dile entegre sekilde değil mi?

Tagli

Bilgilendirmeler için teşekkürler arkadaşlar. Bir yanlışımı düzeltmiş oldunuz.

Konuyla ilgili Microchip'in bir uygulama notunu buldum: AN914
Gökçe Tağlıoğlu