Mikro denetleyiciler ile diğer cihazlar arasında veri alışverişi için sıklıkla asenkron haberleşme protokoluna -UART- başvuruyoruz. Genelde parazitsiz ve kısa mesafelerde sorun yaşanmasa da, yakın çevrede çalışan motorlar gibi parazit kaynakları var ise arada bir hatalı alınan karakterler olabiliyor. Benim CNC uygulamam da bunlardan birisi. Mikro denetleyicinin burnunun dibinde çalışan 500W lık bir fırçalı DC spindle motorundan, 3 tane güçlü adım motorunun kablolarından yayılan parazitler ortamı dolduruyor.
Bu durumlarda fiziksel seviyedeki önlemler, UART haberleşmeyi RS232, ya da daha iyisi RS485 üzerinden yürütmek. Buna ilaveten protokol seviyesinde de veri doğrulama yöntemlerine başvurmak gerekiyor.
Sadece kullanıcı ile text mesaj alışverişi gibi, arada bir gelen hatalı bir karakterin felakete yol açmadığı bir durumda bütün bunlara gerek görülmeyebilir. Ama, CNC ye giden bir hatalı koordinat bilgisi ortalığı bir anda savaş meydanına çevirebilir. Bu benim başıma geldiğinden dersimi aldım ve CNC-PC haberleşmesi sırasında veri doğrulama işlemlerine erken aşamalardan itibaren yer verdim.
CNC için kullandığım iki farklı yöntemi anlatacağım.
VERİYİ İKİ DEFA GÖNDEREREK KARŞILAŞTIRMA YÖNTEMİ
Bu ilk olarak kullandığım yöntem. Aynı satırı ard arda iki defa gönderip, alış tarafında bu iki paketi karşılaştırıyorum. Eğer fark yok ise sorun yok, gelen veri satırı kullanılabilir demektir.
Ama eğer ikinci gelen ilkinden farklı ise, gönderici tarafa bir sinyal göndererek aynı satırı tekrar göndermesini istiyorum. Tekrarlanan iki ardışık satır birbirinin aynı çıkana kadar bu çevrime devam ediyorum.
Bu basit ve güvenilir olmakla birlikte biraz verimsiz bir yöntem. Zira gelen veri doğru da olsa en az iki sefer gönderiliyor. Bu durum, sadece 15-20 karakterlik paketlerin alınıp verildiği CNC uygulamasında zaman kaybı açısından bir sorun oluşturmuyor, motorlar ilerlerken yeni verileri almak için bol bol vakit oluyor. Ama hızın önem kazandığı durumlarda biraz sıkıntılı olmaya başlıyor.
CHECK SUM İLE DOĞRULAMA YÖNTEMİ
Check sum yöntemi daha profesyonelce bir doğrulama yolu. Temelde gönderilen paketi oluşturan byte’ların toplamını alarak bu toplam bilgisinin de karşı tarafa iletilmesinden ibaret. Alıcı taraf da aynı toplama işlemini yaparak bulduğu sonucu gelen toplam bilgisi ile karşılaştırıyor. Eğer kendi hesapladığı toplam ile gelen toplam bilgileri birbirinin aynı ise veri doğru alınmış anlamına geliyor.
Checksum hesabının yapılması için çeşitli standartlar var. Bunlardan MD5 standardı Checksum bilgisini 128 bit olarak iletiyor. Benim bu kadar ileri doğrulama tekniklerine ihtiyacım yok, 8 bitlik bir checksum doğrulama kodu yeterli. Diğer checksum standartları için internette çok bilgi var. Bunlardan en basiti UART haberleşmesinde kullanılan tek bitlik “parity” kontrol yöntemi.
KULLANDIĞIM 8 BİT CHECK SUM DOĞRULAMA YÖNTEMİ
Checksum8-Modulo256 olarak anılan yöntemi kullanıyorum. Yöntem gayet basit, aşağıdaki gibi:
- Gönderilen her bir byte’ın toplamını al
- Toplamın MOD256 sini bul. (0xFF e bölününce kalan.)
- Bulunan değerin 2 ye tamamlayıcısını (2 nin komplementi) hesapla
- Bunun küçük 8 bitini iki ASCII karaktere çevir (UART üzerinden gönderebilmek üzere)
- Bu iki ASCII karakteri asıl verinin ardından alıcıya gönder.
Bu şekilde gönderilmiş olan iki karakteri, alıcı tarafta tekrar 8 bitlik bir tam sayıya (Integer) çevirerek checksum değerini geri kazanmış oluyoruz.
Alıcı taraf gelen karakterlerin toplamını hesaplayıp checksum değerini de buna ekleyince bulunacak toplamın alt 8 bitinin sıfır (0x00) çıkması gerekir. Eğer değilse veri alışı hatalı olmuştur, tekrar gönderilmesini isteriz.
Buna ilişkin Python fonksiyonum aşağıdaki gibi:
def stream(text, lineNr): # gelen satırın sonunda zaten '\n' yok ise ekleyelim, # alış tarafına satır sonunu '\n' ile bildiriyoruz if text.find('\n') == -1: text = text + '\n' print("ham text + newLine {}-".format(text)) # check sum hesabı ve iki karakterlik checksum'ın text sonuna eklenmesi toplam = 0 for c in text: toplam += ord(c) checksum = toplam % 256 # mod256 (0xFF e bölününce kalan) checksum = -checksum # checksum byte : 2 nin komplementi # aşağıdaki hexsum ve bytelar değişkenleri hata ayıklama amacı ile hexsum = '%2X' % (checksum & 0xFF) # '0x' karakterlerini sil bytelar = [hex(ord(c))[2:] for c in hexsum] # ASCII ye çevir print(bytelar) # ascii olarak bas,örn.0x17 için ['31','37'] checksum_chars = str(hexsum) # checksum Hex kodu bir byte olarak # başa komut başlangıç işareti olarak tilde '~' 0x7E, # sona da checksum karakterlerini ekliyoruz # (checksum değerini RSR232 den gönderilebilmek için # HEX den iki adet ASCII koda çeviriyoruz) # checksum doğruluğunun kontrolu print("toplam {0!s}".format(toplam)) result = (checksum + toplam) & 0xFF # bu test 0x00 sonucunu verir text_to_send = "{0}{1}{2}".format('~', text, checksum_chars) print("{0} sonuc:{1}".format(text_to_send,str(hex(result)))) seri.write(text_to_send.encode('ascii'))
CHECKSUM HESAPLAYICISI
Bu çalışma sırasında geliştirdiğimiz kodun doğruluğunu kontrol edebilmek için, kullandığımız text satırlarının doğru checksum değerlerine gereksinim oluyor. Bunun için internette çevrim içi hesaplayıcılar var. Benim yararlandığım hesaplayıcı aşağıdaki. :
ALIŞ TARAFINDAKİ İŞLEMLER
CNC de gönderdiğim satırlar tilde ‘~’ ile başlayıp, yeni satır ‘\n’ ile sonlanıyor. Checksum hesaplamasını ilk tilde’yi izleyen karakterden başlayıp, sondaki ‘\n’ dahil olmak üzere yapıyorum. Bu durumda checksum ile birlikte örnek bir veri paketi aşağıdaki gibi oluyor:
~G1 X0 Y0 Z0’\n’76
Burada checksum için toplamı alınan bölüm : G1 X0 Z0’\n’
STM32 üzerindeki alış ve checksum kontrol rutini aşağıdaki gibi :
/************* uart1_command_listener ************************* * By Selcuk OZBAYRAKTAR Eylül 2020 * * UART1 SeriPorttan gelen CNC komutunu buffer'a yerleştirir * CheckSum kontrolunu yapar. * CheckSum OK ise 1, aksi halde 0, * buffer taşması halinde 2 olarak döner * ******************************************************************/ uint8_t uart1_command_listener(uint8_t *buffer, uint8_t bufferSize) { uint8_t received; uint8_t uart1Index =0; uint8_t checksum_index =0; uint8_t checksum[2]; uint8_t completed = 0; uint8_t checksum_byte; uint8_t success = 0; uint16_t sum = 0, temp; uint8_t i; while (IsDataAvailable(&huart1)) { received = Uart_read(&huart1); if (checksum_index > 0){//checksum karakterini kaydet, sayacı ilerlet checksum[checksum_index - 1] = received; checksum_index++; } else if (uart1Index < bufferSize) { buffer[uart1Index] = received; uart1Index++; if (received == '\n'){//bunu izleyen iki karakter checksum checksum_index = 1; // checksum karakterlerini okumaya başla } } if (uart1Index == bufferSize) {// Buffer boyutu aşılmış success = 2; break; } if (checksum_index > 2) { // iki adet checksum karakteri de okunmuş checksum_byte = htoi(checksum[0])<<4;//Checksum'ı integer'e çevir checksum_byte += htoi(checksum[1]); completed = 1; } } if (success != 2) { //buffer taşmış checksum kontroluna gerek yok if (completed == 1) { //Checksum kontrolu // Toplamaya tilde'den sonraki karakterden başlıyoruz, while(buffer[i] != '~'){ if(i < (uart1Index-1)){//Tilde dışında en az bir karakter i++;} else { success = 2; break; } } if (success !=2) { i++; // ilk tilde'den sonraki karaktere geç while (i < uart1Index){ sum += buffer[i]; i++; } temp = (sum + checksum_byte); temp = temp & 0xFF; if (temp == 0x00) success = 1; else success = 0; } } } return success; }
SONUÇ
Böylece uzun zamandır kullanmakta olduğum aynı veriyi ard arda iki defa göndererek karşılaştırma yapma yöntemi yerine daha verimli olan Checksum kontrol yöntemine geçmiş oldum.
Bu doğrulama yöntemleri CNC için vazgeçilmez önemde. Zira binlerce satırlık veri aktarımı sırasında gelecek tek bir hatalı karakter, en ucuz altlatılan durumda o ana yapılan için boşa gitmesine yol açıyor, ama genellikle çok daha ağır sonuçlara, kesici uç ve CNC ye büyük hasarlar vermeye kadar gidiyor.