Jump to content

İki mikrodenetleyici arasında haberleşme


şenol eker

Önerilen İletiler

İki mikrodenetleyici arasında haberleşme konusunda çok fazla "sıkıntı" çekildiğini görüyorum. Bu yüzden bu makaleyi yazmaya karar verdim.

Haberleşme isterse iki kıta arasında olsun isterse aynı pcb üzerindeki iki çip (PIC/AVR/STM) arasında olsun, haberleşmedir. ADSL modemden Wi-Fi'ye hatta modern telefon görüşmelerine kadar, tüm haberleşme "paketler" halinde yapılır. Çünkü en kolay ve düzgün olanı bu.

"Paket" dediğimiz adı üstünde, bir bütün. Peki neden bilgileri buradan ard arda gönderip karşıdan da ard arda almıyoruz?
Örneğin:
Birinci_mesaj
İkinci_Mesaj
3_Mesaj
Bunları ard arda gönderirsek, her bir mesajın nerede başlayıp nerede bittiğini bilemeyiz. Bu yüzden her bir mesajın başına / sonuna bir "işaret" koymalıyız. Örneğin işaret olarak * koymayı düşünelim.
Peki, mesajın içinde * varsa ne olacak?
Biz verileri doğrudan gönderiyorsak, her bir byte 0 ile 255 arasında değer alabilir. Dolayısı ile "başka amaçla kullanılmayan" diye bir şey yok. (ESC ile bu mümkün ama kafamızı karıştırmayalım; yolu yok diyelim) O halde nasıl ayıracağız bu mesajları?
Cevap, "paketleyerek". (packet)
Cevap, "zarfa koyarak", (envelope)
Cevap, "çerçeveye koyarak" (frame)
Öncelikle bir "protokol" kurmalıyız kafamızda. Protokol dediğimiz, neyi nasıl yapacağımızın tarifi.
Örnek olarak bir protokol uyduralım:
En başta bir "preamble" olsun. Paketin buradan başladığını biliriz. Preamble 0x55 0xAA olsun örneğin. (Bu sayının seçilme sebebi, binary olarak 01010101 10101010 şeklinde olması, bir ve sıfırlar ardışık)
Ardından yükümüzün (payload) kaç byte olduğunu verelim. Sabit bir şey göndereceksek buna gerek olmayabilir.
Sonra asıl göndereceğimiz veri. Yani "payload", yük.
En sonda da istersek bir kontrol toplamı (CRC) gönderebiliriz. Bir terslik olursa hatalı veri alınmasın diye.
Evet, "çerçeve"mizin yapısı bu olacak. Şimdi bunu yazalım da güzel görünsün:
 

Byte  İçerik        açıklama
00    0x55          Preamble ilk byte
01    0xAA          Preamble 2. byte
02    len           yük uzunluğu
03    payload(1)    yük
..    payload(..)   yük
len+2 payload(len)  yük son byte
len+3 CRC           yük CRC toplamı

Tabii bu bir kural değil. CRC koymayabiliriz örneğin, eğer aradaki iletişim çok sağlıklı ise ve hatalı bilgi gitmesi sistemimizde büyük sıkıntıya yol açmayacaksa. Çünkü pakete ilave edeceğimiz her byte, iletişim hızımızı düşürecek.

Örneğin uzunluğumuz sabit olabilir. O zman onu da kullanmayabiliriz. Örneğin 16 bitlik bir integer sayı gönderiyor olabiliriz. Bu durumda uzunluk sabittir. Verinin çok hızlı olmasını istiyorsak, örneğin preamble de 1 byte olabilir. Bu durumda paketimiz şöyle olacak:

Byte İçerik Açıklama
00   0x55   Preamble
01   yük-1  Yükün büyük byte'ı
02   yük-2  Yükün küçük byte'ı

Sonraki gönderide verinin nasıl gönderilebileceğine bakacağız.

(Devam Edecek)

Yorum bağlantısı
Sitelerde Paylaş

  • 4 months later...

Verileri karşı tarafa gönderirken, basit bir veri yapımız varsa, doğrudan seri porttan gönderebiliriz. Örneğin bir tane integer sayı ve tekn byte premable göndereceksek:

SendChar (0x55);
SendChar (veri & 0xFF);
SendChar ((veri>>8) & 0xFF);

şeklinde gönderebiliriz. (Verimizin adı "veri" ve preamble 0x55)

Ama görece karmaşık bir verimiz varsa, bir veri yapısı içinde gönderebiliriz. Bir örnek olarak isim, soyisim ve boy bilgilerini göndermek isteyelim. Şüphesiz ki bunu da yukarıdaki gibi yapabiliriz. Yani tek tek gönderebiliriz. Ama çok genel olsun diye "tam tekmil" bir yapı verelim, İstemediğiniz yerleri çıkartabilirsiniz.

Önce bir union type tanımlayacağız. Hem gönderirken kolaylık olsun diye her byte'ı ayrı ayrı; hem de kullanımda kolaylık olsun diye uygun değişkenler şeklinde.

typedef union
{
  struct
  {
    char bytes[36];
  };
  struct
  {
    char preamble[2]
    char isim[16];
    char soyad[16];
    char boy;
    char checksum;
  };
}veri_type;
veri_type veri;

Sonra değerlerimizi koyacağız:

veri.preamble=0x55aa;
veri.isim=....
veri.soyad=....
veri.boy=180;

Daha sonra eğer kullanacaksak checksum hesaplayacağız:
 

veri.checksum=0;
for (i=2;i<35;i++) //preamble ve checksum hariç
{
  veri.checksum+=veri.bytes[i];
}



artık verimizi yollayabiliriz.

for (i=0;i<36;i++)
{
  SendChar(veri.bytes[i]);
}


Hepsi bu :)
 

Yorum bağlantısı
Sitelerde Paylaş

Peki gönderilen bu veriyi nasıl alabiliriz?

 Bunun en kolay yolu kesme kullanmak aslında. Ama kesme kullanmadan da yapabiliriz. İlk örneğimize göre yapacaksak:

while (GetChar<>0x55); //0x55 gelmesini bekliyoruz
veri=GetChar;
veri=veri + GetChar<<8;

İkinci örneğe göre yapacaksak:

typedef union
{
  struct
  {
    char bytes[34];
  };
  struct
  {
    char isim[16];
    char soyad[16];
    char boy;
    char checksum;
  };
}veri_type;
veri_type veri;
....
Bekle:
if (GetChar!=0x55) goto Bekle
if (GetChar!=0xAA) goto bekle
veri.checksum=0;
for (i=0;i<33;i++)
{
  veri.bytes[i]=GetChar;
  veri.checksum=veri.bytes[i];
}
veri.checksum-=GetChar;
if (veri.checksum!=0) goto Bekle;

Umarım anlaşılmıştır.
Eğer kesme içinde yapacaksak, bu sefer de sonlu durum makinesi kurmamız gerekir.

char state=0;
char tmp;
....
void Serial_ISR(void)
{
  tmp=GetChar;
  switch (state)
  {
    case 0:if (tmp==0x55)state=1;
      break;
    case 1:if (tmp==0xAA){state=2;}else{state=0;}
  break;
  case 33:
  default:
  if (state>33)
  {
    state=0;
  }else{
    veri.checksum=tmp;
    for (tmp=0;tmp<33;tmp++)
    {
      veri.checksum-=veri.bytes[tmp];
    }
    if (checksum==0)VeriGeldi=1;
    state=0;
  }
  }
  ResetIF;
}

Peki programımızda, bir "veri geldiğini" nereden biliriz?
"VeriGeldi" adlı bayraktan anlayacağız bunu da.
Sonlu durum makinesinin nasıl çalıştığını, ne olduğunu bilmiyorsanız, önce bunu öğrenmenizi öneririm.
Ayrıca yukarıdaki kodlar bir C lehçesi için yazılmadı, olayı anlatmak için yazıldı ve test edilmedi. Dolayısı ile copy+paste yapmanız için değildir, öğrenmeniz içindir. Zaten ben prensip icabı balık vermeye karşıyım.

Yorum bağlantısı
Sitelerde Paylaş

Timeout => Zaman aşımı?
Eğer bir paket gönderilirken bir şekilde tamamlanmadı ise, bir sonraki paket bunun devamı gibi alınacağından o da bozuk alınmış olur.
Örneğin (örnek 3 byte paketimizi kullanıyorum)

0x55
0x15
0x24

olan paketimiz, bir iletişim sorunundan dolayı

0x55
0x15

şeklinde gitmiş olsun.
Bu durumda, bir sonra gelecek paketin ilk karakteri, bozuk paketin son karakteri imiş gibi algılanacak ve ardından gelen 2 byte değerlendirme dışı kalacaktır.
Dolayısı ile bozulmuş bir paket yüzünden, ardından gelen sapasağlam paket de ziyan olacaktır.
Bunu engellemek için eğer paketler arasında bir zaman boşluğu varsa, timeout kullanabiliriz.
Örneğin biz 100 milisaniyede bir paket gönderiyorsak ve iki byte arasındaki süre 0,1 milisaniye ise, örneğin 1 milisaniyelik bir timeout kullanabiliriz.
Yani, veri gelirken 1 milisaniyeden uzun bir kesilme olursa, almakta olduğumuz paketi iptal edip yeni bir paket paket beklemeye başlarız.
Böylece bozuk paketten sonra gelen sağlam paketi sorunsuz olarak alabiliriz.
Bunu yapabilmek için, bir timeout sayıcımız olması ve bunun bir timer kesmesi içinde artırılması gerekir.
örneğin:

int TimeoutCounter;


diye bir değişken tanımlayıp program init aşamasında buna sıfır değeri verelim:

TimeoutCounter=0;

Sonra bir timer kesmesinde (Örneğin 100 mikrosaniyelik bir kesmede) bu değişkeni bir arttıralım. Bu değişken 10 veya daha yüksek bir değer aldığında, 1 milisaniye aşılmış olacaktır.
 

if (TimeoutCounter<11)TimeoutCounter++;

Burda bir şart koyuyoruz ki, sayı devredip çok uzun süre sonra tekrar sıfır olmasın.
Sonlu durum makinemizde de bu durumu değerlendirelim:
"void Serial_ISR(void)" içinde, "tmp=GetChar;" ile "switch (state)" arasında bu değerlendirmeyi yapabiliriz:
 

if (TimeoutCounter>=10)state=0;
TimeoutCounter=0;

Böylece 1 milisaniyelik veya daha fazla bir kesinti olursa, state sıfırlanacağı için, sonlu durum makinemiz ardından gelecek bilginin yeni bir paket olacağını öngörerek, veri kaybını engellemiş olur.
Anlaşılmayan yer varsa lütfen yazın.

Yorum bağlantısı
Sitelerde Paylaş

  • 7 months later...
Guest Lara
Услуги консультации психолога.
Психолог онлайн Опытные психотерапевты и психологи.
Цены на услуги и консультации психолога.
Психотерапия онлайн! Психотерапия онлайн!

Індивідуальні консультації.
Консультация у психологов.
Yorum bağlantısı
Sitelerde Paylaş

Guest Hellen
patreon, shiba inu американская история ужасов 8
сезон hotmail sign in, wunschgutschein
Yorum bağlantısı
Sitelerde Paylaş

Guest Donte
facebook, fortnite tracker американская история ужасов выход серий gymshark, postbank online banking
Yorum bağlantısı
Sitelerde Paylaş

Guest Albertina
backmarket, netflix party смотреть сериал американская история ужасов dailymotion,
ookla
Yorum bağlantısı
Sitelerde Paylaş

Misafir
Bu konuyu yanıtla

×   Yapıştırdığınız içerik biçimlendirme içeriyor.   Biçimlendirmeyi Temizle

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Önceki içeriğiniz geri yüklendi.   Editör içeriğini temizle

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Yeni Oluştur...

Önemli Bilgilendirme

Facebook / Twitter / Google hesabınızla kolayca kaydolup cevap verebilir, soru sorabilir, istekte bulunabilirsiniz.
Devam etmeniz, forum kurallarını kabul ettiğiniz anlamına gelir.            Forum Kuralları