USART Üzerinden Struct Göndermek

Başlatan kimlenbu, 04 Haziran 2014, 17:06:18

muhittin_kaplan


kantirici

Alıntı yapılan: gerbay - 05 Haziran 2014, 14:33:21
hocam onu float ve double için yazdım, eğer struct a float, double da koyup gönderdiyseniz size ancak "bravo!" diyebilirim.

Hocam şimdi kafam karıştı. Struct içinde float tanımlayıp uarttan gönderelim mi göndermeyelim mi?



Elektroemre

#17
O zaman kendimizce uart için basit bir paket yapısı oluşturalım:

typedef struct 
{
    /**  Header  **/
    uint32_t Sync;         // Mesela 0x55AA0000 olabilir
    uint16_t PayloadLen;   // 0..65535
    uint16_t HeaderCrc16;  // Header icin Crc
    
    /** Esas Data **/
    uint8_t* Payload;      // Esas Data
    uint16_t PayloadCrc16; // Esas Data icin Crc
    
}UartPack_t;


Bu struct konunun başında konuşulan esas data struct'ı değil. Esas göndermek istediğimiz data katarı ya da struct'ı bu struct içersindeki payload pointer'ına type cast yapıp bildireceğiz.

Sonra Packet gönderme ve alma rutinleri yukarıdaki bu struct ile çalışacak ve payload'ın doğru şelkilde paket senkronizasyonu yapılarak gönderilip alındığını garanti edecek. Payloadın içeriği herhangi bir şey olabilir 'şu karaketeri kullanmak yasak' gibi bir şey söz konusu değil. Senaryo böyle olsun.

Bizde "Alt katmanda neler oluyor?" , "Paketin başını sonunu doğru kestirdik mi?" gibi sıkıntılar yaşamadan üst katmanlarda uygulamamızı yazalım.

Mesela CAN'de ya da USB HID'de paket gönderip paket alıyoruz. Gerisi bizi hiç mi hiç ilgilendirmiyor. UART'tada benzer rahatlığı yaşayabileceğimiz hale getirmemiz çok güzel olur.

Vaktim oldukça yazmaya çalışacağım.

Tagli

Bence olayı 9 bitlik veri paketleri ile çözmek daha rahat olur. Böylece paket başları net bir şekilde belirlenir. ARM'ı bilmiyorum ama seri port modülü olan tüm PIC'lerde 9 bit ve "address detect" özelliği var. Etkinleştirildiğinde, 9. biti 1 olmayan tüm veriler yok sayılıyor. Bus üzerinde birden fazla slave cihaz olan uygulamalarda çok mantıklı.

9. bitte bozulma olursa işler karışır gerçi, onun da önlemini almak lazım.
Gökçe Tağlıoğlu

AsHeS

#19
Konuşulanlara ek olarak şöyle bir yöntemde olabilir paketin başına STX , sonuna EOT karakteri koyacağımızı varsayalım. Sorun şu paketin içinde bunlar varsa ne olacak onu da şöyle aşalım ESC karakterini remap karakteri olarak belirleyelim ve mesajın içindekileri yenileri ile remap edelim.
Diyelim
struct __packed msg_packet_s
{
      uint32_t  mp_fly_me;
      uint16_t  mp_fly_high;
      uint8_t    mp_fly_more;
};

struct msg_packet_s msg_packet;

msg_packet.mp_fly_me = STX;
msg_packet.mp_fly_high = EOT;
msg_packet.mp_fly_more = ESC;


mesajlarını taşısın gönderilecek mesajın başı sonu şöyle olacaktır.
STX (0x00 0x00 0x00 ESC 0x01)  (0x00 ESC 0x02) (ESC 0x03)  EOT
Tabi hem verici hem alıcı tarafta mesaj çıkışından ve alışından önce pre_process mekanizması olmalıdır.
Ayrıca worst case durumu olan bütün mesajın bu karakterlerden oluşması durumu için çıkış bufferının boyutunu struct'ın 2 katı seçmekte yarar var.  uint8_t out_buff[2*sizeof(struct msg_packet_s) +2]

Edit: Mesajın içine checksum da konulabilir ama dikkat çekmek istediğim nokta paket içeriği değil dışarıya çıkış şeklidir.

Klein

#20
Alıntı yapılan: Elektroemre - 05 Haziran 2014, 13:53:24
Hocam UART ile 3.5 byte süresini tespit edip frameleri ayrıştırma olayı açıkçası sıkıntılı olur.

Bu bahsettiğiniz senkronizasyon byte'ları ve sonrasında gelen 0 değerleri, gönderdiğiniz içerikle tesadüf olarak denk gelince ne yapılıyor?

Sistem gelen data buffer'ında senkronizasyon byte'larını arıyor,
daha sonra senkronizasyon byte'ından sonraki diğer parametrelere bakıp değerler tutarlıysa "tamam abi burası paketin başı" kararı veriyor,
yok tutarlı değilse "paketin başı zannettik ama değilmiş normal data galiba" deyip diğer senkronizasyon byte'larını aramaya devam ediyor.

Durum bu mudur hocam?
Yok tam öyle değil.  3.5 byte  senkronizasyon baytı değil.  sadece bekleme.  eğer 3 byte boynca veri gelmiyorsa, paket alınmıştır. bu kadar.  Eğer 3.5 byte beklemeden sonra veri alınmışsa bu yeni paketin verisidir. 
Ama bu yöntem Master-Slave çalışmaya daha uygun bir yöntem.   
Modbus-RTU kullanan Her yeni cihazımda bir kaç gün süren haberleşme testi yaparım. Haberleşme testi yapılırken cihazın diğer fonksiyonları da çalışır durumda olur. Menülere girer çıkarım, input output fonksiyonlarını test ederim.
Test şartları genellikle şöyledir.
Baud 256000
Data bit 8
parite yok
stop bit 1
master 10 byte paket gönderip slave cihazdan 250 byte uzunluğında bir paket ister.
Slave paketi gönerir. Paket içeriğinde her türden  canlı veri ve sabit veri bulunur. 
Eğer  paket doğru alınmışsa   başarı sayacı bir artar,  CRC hatası, paket boyu  hatası , zaman aşımı hatası  ve benzeri hata durumlarında  hata sayıcısı bir artar.
cevap paketinden sonra 10ms beklerim ve yeni sorguyu gönderirim. 

Bu şekilde  bir kaç gün test ederim.  Eğer  şebeke gürültüsü konusunda cihazı  zorlamazsam ,  hata sayıcısının arttığını hiç görmedim. 
Eğer çok vahşi gürültü testleri yaparsam ,  40-50 bin pakette bir hatalı paket aldığım olur. Bunu normal sayarım.
Ama bu vahşi test bazen işlemciyi uçurmaya  kadar gider. Hatta bilgisayarın USB, Bluetooth gibi aygıtları bile uçar.

Ama uygulama , bu şartlar altında bile bu hatayı kaldıramayacak bir hata olursa o zaman bu yöntemin sıkıntılı olduğundan bahsedebiliriz.
Belki de bu hata bile yöntemden değil , benim donanımda veya yazılımda yaptığım hatalardan kaynaklanıyordur.


mesaj birleştirme:: 05 Haziran 2014, 20:11:39

Alıntı yapılan: muhittin_kaplan - 05 Haziran 2014, 14:28:06
Konu Güzel. yeni bir öğrenme Kaynağı,
Hocam Kullandığım Yöntemden bahsedeyim
bilgileri sprintf ile bir katar yapıp usart tan yolluyorum. tabiki ardına CR LF ekliyorum.

bu struct göndermek arasında ne gibi bir iyilik/kötülük oluşturur ? Sonuçta usart ByteByte Göndermeyecek mi ?
Evet sonuta byte byte gönderecek. ama Structların esas gücü faklı veri tiplerini kullanabiliyor olmanda  yatıyor.
diyelim ki
Standart bir tx arrayın var.   göndermen ve alman gereken bir çok tipte    verin var.
örneğin
float offset;
float span;
unsigned long int  counter;
char name[16];
float  AnalogValue;
unsigned int DeviceCount;
unsigned int aaaaaaaaaa;


şimdi sana bunları  seri porttan gönder ve karşı tarafta da birleştir desem, struct kullanmadan nasıl yaparsın?

Ben sana struct ile nasıl yapılacağını göstereyim , sen bana struct kullanmadan nasıl yapılacağını.

typedef struct
{
      float offset;
      float span; 
      unsigned long int  counter;
      char name[16];
      float  AnalogValue;
      unsigned int DeviceCount;
      unsigned int aaaaaaaaaa;
}TValues;

TValues  values;
/* gönderme */
Gönder( (char *) values, sizeof(values));

/* alma */
char *rxbuffer = values;
al( rxbuffer);



Elektroemre

@gerbay hocam,

O struct data struct'ı değil, bizim paket gönderme alma alt fonksiyonları ile çalışmak için tanımladığımız struct.

muhittin_kaplan

@Klein
Hocam

sprintf(str,"GX%dGY%dGZ%dAX%dAY%dAZ%dS\r\n",GyrX,GyrY,GyrZ,AccX,AccY,AccZ);
USART_puts(USART2,str);


ile PC ye gönderir
Oradada STRING MANIPULATION la ayırırım.
(GX ile GY arasına Al Şuraya At
GY ile GZ arasını al Buraya At gibi
)


Klein

Hocam basit örnek olsun diye hiç bir şey koymadım. Farklı veri türleri olması yeterliydi.
Genellikle  Modbus-RTU kullanıyorum.  İlla ki özel bir protokol kullanmam gerekirse ( ki çook nadir) ,  Teslimat bilgileri, paket içeriğine ilişkin bilgiler  ve paket sıra numarası gibi alanlar bulunuyor. Çok nadiren de zaman senkronizasyonu yaptığım oluyor. 

Klein

Alıntı yapılan: muhittin_kaplan - 05 Haziran 2014, 22:22:55
@Klein
Hocam

sprintf(str,"GX%dGY%dGZ%dAX%dAY%dAZ%dS\r\n",GyrX,GyrY,GyrZ,AccX,AccY,AccZ);
USART_puts(USART2,str);


ile PC ye gönderir
Oradada STRING MANIPULATION la ayırırım.
(GX ile GY arasına Al Şuraya At
GY ile GZ arasını al Buraya At gibi
)



:) Bir ayır bakalım.
Zaten olamayacağını iddia ettiğm için değil , ortaya çıkan kodla , Struct kullanılan kod arasındaki farka bakıp ,  neden Struct kullanmalıyız sorusunun cevabını görmen için istedim.
Ha bu işin daha başlangıcı. bazen 250 baytlık  paket gönderdiğim oluyor. Bunu işini zorlaştırmak için söylemiyorum.

Tabi yapıları  sadece haberleşmeyle sınırlandırma.  Kodunu daha okunaklu yapması , değişkenleri guruplandırabilme, aynı tip yapıyı bir çok kere kullanabilme , casting ile her türlü diziye atabilme gibi bir çok avantajı var.
Hatta mümkün olduğunca global değişkenlerinin tümünü eşitli yapılar içerisinde guruplandır.
Hatta yapıyı buradaki gibi diziye çevirip bir yere transfer etmeyeceksen,  bazı durumda fonksiyon işaretçileri kullanarak , structuna özel fonksiyon çağrısı yapabilirsin. Velhasıl Pointer ve Struct olmadan C nin tadı ıkmaz.

İşin  başka bir ayrıntısı da  yapı (Struct) ile sınıfların (Class)  benzerliği. Eğer Yapıları iyi anlar ve iyi kullanabilirsen , C++ için çok iyi bir başlangıç yapmış olursun.

muhittin_kaplan

#25
Hocam, PC de ufak değerlerde pek fark yaratmıyor. Yoğun Veri trafiğinde tek seferde parse edilebilecekken yoğun bir String ayırma işlemiyle uğraşılır. Haliyle PC yi yorar, MCU yu düşenemiyorum bile.

mesaj birleştirme:: 05 Haziran 2014, 23:46:03

Alıntı yapılan: gerbay - 05 Haziran 2014, 22:44:09
muhittin hocam,

sen de kızılderililer gibi duman  ile haberleşiyormuşsun ;)

Daha Nelerle Haberleştiğimi Görseniz.

mufitsozen

Arkadaslar dayanamadim araya girecegim,  kusura bakmayin.

Desturrr!!!!.... Yine okumaniz icin bir referans verecegim. ;D kufur yok, kotu soz yok... :P

UART uzerinden frame olarak bilgi gondermek icin sultan palamut devrinden beri byte stuffing kullanilir(di, 7E ve 2D xor filan falan) ama bununda bazi dezavantajlari vardi. Yakin zamanda gelistirilen bir algorithma ile bu sorunlar cozuldu.

Sayin @klein, @muhittin_arslan ve sevgili @gerbay bazilari akil yasta degil bastadir dersede bazen akil hem yasta hemde bastadir cunki yasli olanlar ayni zamanda baslida olabilir  :P

bu konuya caktirmadan okuyup ne oldugunu anlamaya calisan arkadaslar C bilginiz yeteri kadar iyi ise sizde sayin @klein, @muhittin_arslan ve sevgili @gerbay gibi cok bilgili ve tecrubeli arkadaslara birseyler  soyleme/gosterme firsatiniz var fekaaattt elinizi cabuk tutun bu @gerbay denilen canavar simdi hemen bu konuyu arastirip 8 mcuda 4 degisik compiler ile derleyip, assemblerli cross listingi uzerinden performans analizi bile yapar! demedi demeyin.. ;)

Alıntı YapFraming packets transmitted on a UART serial link (such as RS-232 or RS-485) is a common problem in embedded programming. The typical approach is to put framing characters at the beginning and end of the packet. If a framing character occurs in the packet, it is escaped using a technique called byte stuffing. This sucks for a few reasons, but there's a better way. The recently developed algorithm called (cikartim, bu makaleyi okuyun hazira konmayin  :P ) is a great solution that should be in every embedded software engineer's toolbox.

First, a bit of background. I'm working on a project where I have to transmit data between two microcontrollers using RS-422. RS-422 is a differential signalling standard that is often used in place of RS-232 to connect two UARTs when electrical isolation and/or noise immunity are required.

The question came up, how do we assemble packets on the transmitter and detect packet boundaries on he receiver? (devami asagidaki linkte, tabii googledan bulabileceginiz yaklasik 20,000 referansida okuyabilirisiniz. wikipediayi soylemiyorum bile...)

Yani uzun sozun kisasi COBS diyorum!

http://www.jacquesf.com/2011/03/consistent-overhead-byte-stuffing/


Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

Elektroemre

@mufitsozen hocam gerçekten güzel algoritmaymış teşekkürler. Birde buna CRC ekledik mi tadından yenmeyecek :)

kantirici

#28
@mufitsozen hocam teşekkürler.


Algoritma ile ilgli http://stuartcheshire.org/papers/COBSforSIGCOMM/ buradaki sayfanın en sonunda verilen kodları şunlar;

Source Code Listings

/*
 * StuffData byte stuffs "length" bytes of
 * data at the location pointed to by "ptr",
 * writing the output to the location pointed
 * to by "dst".
 */

#define FinishBlock(X) \
    (*code_ptr = (X),  \
    code_ptr = dst++,  \
    code = 0x01)

void StuffData(const unsigned char *ptr,
unsigned long length, unsigned char *dst)
    {
    const unsigned char *end = ptr + length;
    unsigned char *code_ptr = dst++;
    unsigned char code = 0x01;

    while (ptr < end)
        {
	if (*ptr == 0) FinishBlock(code);
	else
	    {
	    *dst++ = *ptr;
	    code++;
	    if (code == 0xFF) FinishBlock(code);
	    }
	ptr++;
	}
    FinishBlock(code);
    }
Listing 1. COBS Encoding in C

/*
 * UnStuffData decodes "length" bytes of
 * data at the location pointed to by "ptr",
 * writing the output to the location pointed
 * to by "dst".
 */

void UnStuffData(const unsigned char *ptr,
unsigned long length, unsigned char *dst)
    {
    const unsigned char *end = ptr + length;
    while (ptr < end)
	{
	int i, code = *ptr++;
	for (i=1; i<code; i++) *dst++ = *ptr++;
	if (code < 0xFF) *dst++ = 0;
	}
    }


Burada veri olarak  char hamveri[10]={10,9,8,7,6,5,1,2,3,4}; giriyorum ve çıkışta

0B-0A-9-8-7-6-5-1-2-3 bilgisini alıyorum. 

Yularıdaki kod paket yapısını tam olarak oluşturmuyor mu?


mufitsozen

Alıntı yapılan: gerbay - 06 Haziran 2014, 13:43:49
Müfit Bey,

her zaman söylüyorum; "sizden hepimizin öğreneceği çok şey" var diye.. 

keşke kitap(lar) yazsanız hem teknik hem teknik olmayan..

Vayt canavar gelmis!   ;D

sevgili @gerbay once sen Picproje Hangout'a cik, sonra teknik kitap yazma konusunu ciddi olarak konusalim(ornek kodlari kim yazacak acaba?)   
Subram bunu sende duy, not al hemen! :P


<kisiye ozel mesaj>gerbay bu mesaj icin onceden konustugumuz gibi 100TL hesabina bugun yatar. Sen beni ovmeye devam et. :P

Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.