18f4550, CCS C ve Java Gui Kullanarak Bootloader Özellikli USB HID Device Oluşturma

Bitirme ödevim için kullanmam gereken bir devreyi burada basit şekilde kalıcı hale getirmek istedim. Fakat şimdiden belirteyim ki elektronik bilgim çok iyi değildir bu yüzden bu yazıda sadece işime yaradığı için kullandığım ve öğrendiğim bilgileri yazmaya çalışacağım. Özellikle USB protokolleri hakkında henüz başlangıç seviyesindeyim. Peki bu devre tam olarak nedir ne işe yarayacak? Devreyi Pic 18f4550 mikrokontrolör ile oluşturacağız ve Pc’ye B Type Usb soketi ile bağlayacağız. Bu devrenin bootloader özelliği olacak yani Programlama Kartıma (Programlayıcıya) gerek kalmadan derlediğimiz hex kodlarını Pic’e yükleyebileceğiz. Devre üzerinde 2 tane led var bu ledleri kontrol edebileceğimiz bir GUI hazırlayacağız ve bunu Java’da oluşturacağız. Genelde bu GUI C# ile oluşturulmuş ben farklı olarak Java’da nasıl hazırlanacağını anlatacağım ki Windows işletim sistemine bağlı kalmayalım. Yazı boyunca kullandığım dosyaları ve kaynakları yazının sonunda bulabilirsiniz. Ayrıca PIC ve Java tarafındaki her dosyanın içinde kod satırlarını o dosyanın içinde tek tek anlatmaya çalıştım. Bu yüzden yazı boyunca kod dosyalırının içeriğini yüzeysel anlatacağım. Önce neler yapacağımız hakkında kısaca adımları tanımlayıp sonra tek tek adımları açıklayacağım.

Uygulama Adımları

Elektronik Devrenin Oluşturulması

Resim 1’de kurduğum devrede Crystali(20MHz) 2 tane 22pF kapasitörle Pic’in OSC1 ve OSC2 girişlerine bağladım. MCLR pinini 10k dirençle +5V’a, VUSB’yi ise 470nF kapasitör ile toprağa bağladım. RA4 pinine ise bir buton bağladım. Bu butona basılı tutup devreyi USB ile PC’ye bağladığınızda Bootloader Modunda çalışacak ve Hex dosyasını yükleyebileceğiz. Basılı tutmadan bağladığımızda ise çalışmasını istediğimiz kodlar devreye girecek. RB5 ve RB6 pinlerine ise birer Led bağladım. B tipi yani yazıcılarda olan o kareye benzeyen Usb Soketinin VCC yani +5V’ çıkışını doğrudan devreye kaynak olarak verdim. Ekstra bir güç kaynağı kullanmadım yani gücü bilgisayarın Usb çıkışından almış olduk. Yine aynı şekilde Usb Soketinin GND yani toprak hattını da doğrudan devrede kullandım. Soketin D- ve D+ uçları veri haberleşmesini sağlayacak bunları da Pic’in uygun girişlerine bağladım. Resim 1’de olmayan, Resim 3’de kendi oluşturduğum devrede göreceğiniz bir farklılık var. Pic’in VDD ve VSS pinleri arasına 100’er nF kapasitör bağladım. Bu sayede devre ilk çalıştığında meydana gelebilecek gerilim dalgalanmaları önlenmiş oldu.

Resim 1: 18f4550 ile oluşturacağımız devrenin şeması.

B Type Usb Soket’inin pinlerini ise Resim 2’de görebilirsiniz. Ben devreyi breadboardda kurdum ve soketi bağlamak için soket üzerindeki pinlere kablo lehimledim.

Resim 2: B Tipi Usb Soketi ve pinleri.

Resim 3: Devrenin tamamlanmış hali.

Devrenın Bootloader Olarak Hazırlanması

Pic için yazılacak kodları CCS C ile yazacağız ve bunun içi PCWHD IDE kullanacağız. IDE’yi kurduktan sonra C:\Program Files (x86)\PICC\Examples klasörü içerisinde ex_usb_bootloader.c diye bir dosya olacak. Bu dosya versiyona göre farklılık gösterir benim kullandığım IDE’nin versiyonu 4.140. ex_usb_bootloader.c’yi ide ile açtığımızda çıkan kodlarda herhangi bir değişiklik yapmamıza gerek yok. Bu yazıyı tamamen okuduktan sonra bu dosyayı komple okuyup nasıl çalıştığı hakkında bilgi edinmeniz faydalı olacaktır. PCWHD IDE’si ile ex_usb_bootloader.c dosyasını açtıysanız #include < ex_usb_common.h > kodunun bulunduğu satırda ex_usb_common.h yazısına sağ tıklayarak ‘Open File at Cursor’ seçeneğine basın ve ex_usb_common.h dosyasına erişin. Bu dosya konfigürasyon bilgilerini içeriyor. 71. Satırda “#define USB_HW_CCS_PIC18F4550 //CCS PIC18F4550 USB Development kit” kodunun olduğu satır açık fakat altındaki ve üstündeki bazı kodlar kapalı. Bu satır bizim 18f4550 Pic’ini kullandığımız için açık. Farklı bir Pic kullanacaksanız ona göre değiştirebilirsiniz. 105. satırdan itibaren aşağıdaki gibi bir kod var. Bunu inceleyelim;

NOT: Ben CCS firmasının IDE’si ile bunu hazırladım fakat kullandığımız PIC’in üreticisi olan Microchip firmasının kendi IDE’si olan MPLAB’i öğrenmenizi ve onu kullanmanızı tavsiye ederim.

ex_usb_bootloader.c dosyası ve include edilen konfig dosyaları vs. üzerinde değişiklik yapmanız gerekirse bunların hepsini farklı bir klasöre kopyalayıp orada düzenleme ve derleme işlemini yapın. Hatta dosyaların isimlerini üzerinde de değişiklikler yapın çünkü örneğin usb_cdc.h IDE’nin kendi kütüphaneleri içerisinde olduğu için direkt onu görecek ve ayarlarınız geçerli olmayacak. Bunu farkedemediğim için çok sorun yaşamıştım. Şimdi başka bir yerde bir klasör oluşturun ve ex_usb_bootloader.c dosyasını oraya kopyalayın. Bizim herhangi bir değişiklik yapmamıza gerek olmadığı için ex_usb_bootloader.c dosyasını açıp IDE’nin Compile sekmesi altında bulunan Compile butonuna basarak derliyoruz. C dosyasının bulunduğu klasörde bir .hex uzantılı dosya oluşacak. Bu dosyayı bir kereliğine Programlayıcı kullanarak Pic’e atmanız gerekiyor. İşte bu aşama sizde ne yapın edin ödünç alın birisinden ve hex dosyanızı Pic’e yazdırın.

USB ve USB Protokolü Hakkında Bilinmesi Gerekenler

Normalde herhangi bir Usb cihazını PC’ye taktığımzda ona özel Driver yükleriz. Bazen o da yetmez Usb 3.0 destekli bir cihaz ise PC’nin de Usb 3.0 destekli olmasını bekler. Usb Cihazı için özel driver oluşturmak bambaşka ve çok daha fazla tecrübe ve bilgi isteyen bir konu. Fakat bazı amaçlara hizmet eden Usb cihazları için standartlaşmış Device Class’ları mevcut. Bu Class’lar sayesinde o standarda uygun olarak hazırlanan driverları kullanarak cihazınızı kullanabilirsiniz. Device Class’lar ve bu Class’lara uygun hazırlanmış cihaz örneklerini burada görebilirsiniz. CCS C Compiler temel olarak CDC ve HID Device Class’larını destekliyor. Diğerleri hakkında bilgim yok. Fakat RS232 ile çalışmışlığınız varsa CDC Device Class’ı RS232 ile hemen hemen aynı şekilde çalışıyor. Hatta bunlar için oluşturulan terminaller birbirini destekliyor. HID ise daha hızlı. Ayrıca veri alışverişi yaparken paket paket işlem söz konusu ve biz bu Class’ı kullanacağız.

Mesela devremize yüklediğimiz ex_usb_bootloader.c dosyası CDC Device Class kullanarak hazırlanmış. Bunu şuradan anlayabilirsiniz; ex_usb_bootloader.c’de usb_cdc.h kütüphanesi include edilmiş adı üstünde CDC. usb_cdc.h içerisinde ise __USB_DESCRIPTORS__ tanımlanmamışsa yani konfigürasyon ayarları yapılmamışsa kendilerinin hazırladığı konfigürasyon dosyası olan usb_desc_cdc.h dosyasını include etmişler. usb_desc_cdc.h dosyasını açtığınızda bir çok tanımlamalar karşınıza çıkıyor. 59 ve 60. satırdaki #DEFINE USB_HID_DEVICE FALSE #DEFINE USB_CDC_DEVICE TRUE kodları herşeyi açıklıyordur sanırım? Bunun dışında Endpoint ayarları, interface ayarları, maximum alıp gönderilecek paketlerin byte cinsinden büyüklüğü… gibi bir çok ayarı yapmanız mümkün. Dosyadaki yorum satırlarını okuyarak ne işe yaradıklarını öğrenebilirsiniz.

CCS C Compiler ile GUI Tarafıyla Haberleşecek Kodların Yazılması

GUI kullanarak devre üzerinde tam olarak ne yapacağımızı açıklamakla başlayalım. Çünkü GUI’nin yolladığı komutlara göre devremiz tepki verecek. GUI çok basit olacak. Üç adet buton var biri LED1’i yakıp söndürecek, biri LED2’yi, sonuncusu ise her iki ledi de seri şekilde yakıp söndürecek. Tıkladığımız buton USB üzerinden PIC’e komutlar gönderecek bunlar; LED1’in yanması için led1-1, sönmesi için led1-0, LED2’nin yanması için led2-1, sönmesi için led2-0, LED’lerin sırayla yanması için cildir, sonra durması için ise sakin komutlarını gönderecek. Biz PIC tarafında bu komutlara göre tepkiler verdireceğiz.

Bu işlemleri gerçekleştirecek C kodlarımız için sadece iki adet dosyamız var. İlki main.c diğeri ise hid_config.h. main.c dosyasını aşağıda inceleyebilirsiniz her satıra açıklama yapmaya çalıştım. Genel olarak bahsedecek olursak ilk başta uygun kütüphaneleri özellikle usb_bootloader.h’yi include ediyoruz. usb_bootloader.h önceden PIC’e attığımız bootloader dosyaları bozulmaması için bir dosyadır ve CCS C Compiler’ın kendi içerisinde mevcut olan bir kütüphanedir. IDE’de bu satıra sağ tıklayıp ‘Open File at Cursor’ derseniz içeriğini inceleyebilirsiniz. TX ve RX kanallarının boyutlarını byte cinsinden tanımladık. Burası aslında 255byte’a kadar gider fakat PIC’e ve kanalların cinsine göre değişmektedir. Ben 64byte belirledim. USB noktasında çalıştıracağımız fonksiyonların tam listesini, açıklamalarını ve örneklerini CCS C Compiler’ın dökümantasyonunda bulabilirsiniz (62. sayfa). Sonrasını ise kodların yanına yazdığım yorum satırlarından takip edebilirsiniz.

İkinci dosyamız ise hid_config.h. Adı üzerinde devremizi USB HID DEVICE yapacak konfigürasyon ayarlarının bulunduğu dosya. Bu dosyayı incelerken aslında ex_usb_bootloader.c’nin include ettiği usb_desc_cdc.h dosyasını da incelemenizi öneririm. Ayarlar benzerdir. Ayrıca kullanacağınız PIC’in USB donanımının özelliklerini de bilmeniz gerekmekte. 18f4550 hakkında detaylı bilgiyi kaynaklar kısmındaki Data Sheet’ten edinebilirsiniz. Tabi birde USB protokolü nasıl çalışmakta onun da iyi kavranması gerekiyor. Bunun için de USB HID PROTOCOL‘ünü incelemenizi öneririm.

Protokolün çalışma mantığından çok genel olarak bahsedeyim biraz. Endpoint diye tanımlanan çıkış noktaları bulunmakta. Bunların sayısı değişebiliyor. Her Endpoint’in de bir IN bir OUT diye iki yolu daha var. IN yolu PIC’den PC’ye çalışır OUT yolu ise PC’den PIC’e çalışır. Her Endpoint’in, IN-OUT yollarının, Interface’lerin vs. ayrı ayrı adresleri vardır. Yazılım kısımlarında bu adresler kullanılır. Endpoint’leri konfigüre ederek her Endpoint’in uzunluğunu, tipini (bulk,interrupt,control,disable…), adresini, kaç ms.’de bir çalışacağını, her çalıştığında ne kadar veri taşıyacağını ve daha başka özelliklerini de ayarlayabilirsiniz. hid_config.h dosyasında işimizi görecek ayarlamalar yapılmıştır. 162 ve 170. satırdan başlayan Endpoint ayarlarını incelemenizde fayda var fakat daha detaylı bilgiyi USB HID PROTOCOL‘ünden edinebilirsiniz ve ona göre düzenlemeler yapabilirsiniz. hid_config.h dosyasını da mümkün olduğu kadar yorum satırlarıyla açıklamaya çalıştım.

hid_config.h

Kodları tamamladıktan sonra main.c dosyasını Compile edelim. Sonuçta hata çıkmazsa (3,4 tane warning olabilir sorun bu sorun değil) Syntax hatası olmadan kodumuzu derledik demektir. main.c ile aynı klasörde hex dosyamız hazırdır. Artık kendi devremizi kullanarak bu hex dosyasını PIC’e yükleyebiliriz.

Devrenin PC’ye Bağlanması ve Bootloader Özelliğinin Kullanılması

Devremizin üzerindeki butona basılı tutarak devreyi usb kablosuyla bilgisayara bağlıyoruz. Takınca bilgisayar tepki verdikten sonra elinizi butondan çekebilirsiniz. İlk taktığınızda bilgisayar aygıtı tanımaya çalışacak fakat bulamayacak (bulursa sorun yok). Aygıt Yöneticisi/bulamadığını belirttiği aygıtı sağ tık ve Yazılımı Güncelleştir/ Sürücü yazılımı için bilgisayarımı tara/Bilgisayarımdaki aygıt sürücüleri listesinden seçmeme izin ver/Disketi Var/Gözat deyip yazının sonudaki pakette bulunan mchpcdc-V5.1.2600.7 klasörünün içindeki inf dosyasını gösterin ve devam edin. Versiyon uyumsuzluğuyla ya da sertifikayla ilgili uyarı verirse yine de devam edin sorun olmaz. Bu driver yazının başında bahsettiğim standart bir driver. Microchip firmasının oluşturduğu bir driver. Bu kullandığımızın versiyonu 5.1.2600.7. Dilerseniz Microchip Libraries for Applications kütüphanesini indirip son versiyonu çıkmış olursa onu da kullanabilirsiniz. Sürücüyü güncelledikten sonra Aygıt Yöneticisinde “Bağlantı noktaları (COM ve LPT)” grubunun altında “USB Serial Port – ADogan (COMX)” donanımını görüyorsanız işlem tamamdır. Artık sürücümüzü de tanıttık bootloader’ı kullanabiliriz. COMX kısmındaki X numarasını unutmayın.

Şimdi amacımız hex dosyamızı PIC’e USB üzerinden göndermek. Bunun için bize terminal tarzı bir araç lazım. Ben SecureCRT 7.2.1 (non-integrated) 32Bit kullandım. 30 günlük deneme sürümünü linkten indirebilirsiniz. Daha başka alternatifler olursa yine buraya eklerim. SecureCRT programını indirip, kurup, açtıktan sonra sol tarafta Session Manager penceresi olacak oradaki Sessions grubunda sağ tıklayarak New Session diyoruz. Protokol olarak Serial seçiyoruz (Bootloader tarafımız CDC olarak hazırlandı çünkü) next diyoruz, Port kısmına COMX kaçsa onu seçiyoruz ve ileri diyerek bitiriyoruz. Sessions grubu altında oluşturduğumuz Session’a çift tıklıyoruz. Açılan pencerenin başlığında yeşil bir tik işareti varsa tamamdır sorunsuz bağlandık. CDC ile oluşturduğunuz devrenizi bu terminal üzerinden de kullanabilirsiniz. Şimdi Transfer sekmesinden Send ASCII’yi seçiyoruz. Devreye yüklemek istediğimiz hex dosyasını seçiyoruz. 5-10 sn içerisinde hex PIC’e yazılacak ve PIC kapanacak ya da yeniden başlayacak. Dolayısıyla Session’da hata alacağız çünkü bağlantı kopmuş olacak. Yani sorun yok başarıyla hex dosyamızı PIC’e atmış olduk. Burada yükleyeceğiniz hex dosyasını oluştururken usb_bootloader.h kütüphanesini eklediğinize emin olun yoksa bootloader kodlarını bozabilirsiniz ve bootloader özelliğini kullanamayabilirsiniz. Bunun için ex_usb_bootloader.c ile oluşturduğunuz hex dosyasını programlayıcı kullanarak PIC’e tekrar yazmanız gerekecek… Hex dosyamızı attığımıza göre artık GUI kullanarak bu devreyi kontrol etme aşamasına geçebiliriz.

Java GUI’nin Hazırlanması

GUI’yi oluşturduğum ‘Maven’ dosyaları yazının en altında bulunan paketin içerisindeki ‘PicControlMaven’ klasöründe mevcut. O kodları maven ile çalıştırabilirsiniz. USB iletişimini sağlamak için libusb kütüphanesini kullandım. libusb tabanlı usb4java kütüphanesini ise libusb’yi Java’da kullanabilmek için, dependency ve propertylerinden faydalanmak için kullandım. Ayrıca libusb bir çok programlama dilinde kullanılmak üzere hazırlanmış C tabanlı bir kütüphanedir. Farklı platformlarda çalışmak için kolay bir kütüphane bence. Fakat çok detaylı bir dokümantasyonu yok bu yüzden kaynaklar kısmında verdiğim örnek projeleri incelemeniz daha çok işe yarayacaktır. src\main\resources\javax.usb.properties dosyasını unutmayın bu property olmadan kütüphaneyi çalıştıramıyorsunuz. Bu ayar ile javax.usb’nin Usb Host Manager’ına usb4java’yı implement ediyoruz. Bu dosyada isteğe göre usb oturumundaki timeout veya iki sorgu arasında geçecek süreyi de ayarlayabilirsiniz bunların detayları için usb4java/Configuration sayfasını inceleyebilirsiniz.

İlk olarak src\main\java\UsbHelper.java dosyasını inceleyerek başlayalım. Bu class usb bağlantısını kurarak veri alışverişi işlemini yapacak metotlara sahip. ‘findDevice(UsbHub hub, short vendorId, short productId)’ metodu PIC’e hazırladığımız kodlarda tanımladığımız vendorID, productID ile birlikte UsbService nesnesi alarak o idlere sahip donanımı buluyor. Ve bize bir UsbDevice türünde nesne veriyor. Bu nesne bizim devremizin bilgilerine sahip. ‘stringToHex(int length, String sData)’ metodu ise parametre olarak verilen string nesnesini length uzunluğundaki bir byte dizisine yerleştiriyor açıkta kalan byteları da 00 yapıyor. Bunun sebebi usb üzerinden veri gönderirken veriyi byte dizisi şeklinde gönderecek olmamızdır. UsbHelper constructer’ı ise herhangi UsbHelper nesnesi oluşturulduğu anda findDevice metodunu çalıştırıp cihaz bağlı mı değil mi onu kontrol edecek ve eğer bağlıysa device nesnesini tanımlayacak, hemen arkasından 00 bytelarıyla dolu bir kontrol verisi gönderecek hata oluşursa zaten terminalde uyaracaktır. Hata olmazsa device nesnemizi rahatlıkla kullanabileceğiz.

Gelelim ‘veriYolla(String sData)’ metoduna. GUI tarafından butona tıklandığında USB’ye bu metot ile veri aktarılacak. Veri almada da vermede de temel adımlar şu şekildedir; device nesnesinden device’da tanımlanmış olan configuration bilgilerini al, interface ‘bilgilerini’ al, interface’i talep et, interface üzerinden işimize yarayacak olan endpoint nesnesini iste (doğrudan adres de verebiliriz, tek seferde hepsini de isteyebiliriz), endpoint nesnesinden descriptor’ı al (endpoint hakkında bilgiler bunda tutuluyor.), yine endpoint nesnesinden pipe’ı al -pipe: o endpoint üzerinden verinin iletileceği hat-, pipe’ı aktif et, veriyi senkronize et (gönder veya al), pipe’ı kapat, interface’i geri gönder. Çalışma sırası bu ‘genel’ olarak bu şekilde olmalı. Şimdi yorum satırlardaki yorum alanlarını takip ederek UsbHelper classını inceleyebilirsiniz.

UsbHelper classında HexFormatter diye bir class kullandık. Bu class libusb örneklerinde vardı. Onların oluşturduğu bir class. Biz onu byteları terminalde göstermek için kullandık. Bu class zaten temel programlama mantığıyla yazılmış kodlar. Konumuzla alakası olmadığı için detayına girmiyorum. Main classı da Swing ve Awt ile oluşturduğum form sayfasını tutuyor. Bu da GUI konusuna girdiği için orayı da size bırakıyorum. Main classında incelememiz gereken tek yer butonlara eklediğimiz listenerlar. Kullandığım butonlar JToggleButton olduğu için ItemListener kullandım. Aşağıdaki LED1 butonunda buton SELECTED durumunda ise usbHelper.veriYolla metodu üzerinden devremize led1-1 komutu gidecek, değilse led1-0 gidecek. Bu kadar.

Usb iletişimi ile ilgili daha fazla örnekler için libusb’nin örnek Java projesinin içindeki tüm örnekleri incelemenizi tavsiye ederim. Devremizin Java projesini oluşturup derlediğinizde GUI sorunsuz oluştuysa artık devreyi test edebilirsiniz. Devreyi USB ile bağlarken devre üzerindeki butona basmadan bağlayın. Yeni donanım bulundu diyecek. Kendisi driver yüklemezse bootloader modunda kurduğunuz driverı tekrar aynı şekilde kurabilirsiniz fakat HID driver olduğu için kendisi direk tanıması lazım. Devreyi PC’ye tanıttıysanız java projesini derleyin ve butonlara tıklayarak test edebilirsiniz. Aşağıdaki videoda nasıl test ettiğimi gösterdim… İlk defa USB kullanarak böyle bir çalışma yaptım. O yüzden yanlış ya da eksik aktardığım bir bilgi varsa uyarırsanız düzeltirim. Çok basit şekilde örneklendirip anlatmak ve kısa tutmak istedim umarım işe yaramıştır.

Dosyalar ve Kaynaklar