Merhabalar, bu yazımda uzun süredir aklımda olan fakat uygulaması bu güne kısmet olan LPC1343 ile gerçekleştirmiş olduğum USB HID uygulamasından bahsedeceğim LPC1343 NXP firmasının üretmiş olduğu Cortex-M3 çekirdeğine sahip 32 bit mikrodenetleyicidir. LPC1343, 8 bit mikrodenetleyicilerden 32 bit mikrodenetleyicilere yumuşak bir geçiş yapmak isteyenler için ideal bir deneteleyicidir.
Çevresel birimleri ve donanım yapısı 8/16 bit denetleyicilere benzerlik göstermektedir. Bu sayede fazla yabancılık çekmeden ARM Cortex denetleyicilerle çalışmaya başlayabilirsiniz. Benzer şekilde aynı yapıda olan LPC11xx (Cortex-M0) denetleyicilerde tercih edilebilir.
LPC serisi dentleyicilere başlangıç için temel seviyede olan LPCxpresso geliştirme kitlerini kullanabilirsiniz. LPCxpresso kitleri ST’nin üretmiş olduğu Discovery kitlere benzer yapıda ve amaçlı düşük maliyetli geliştirme kitleridir. Pratik ve kullanışlı yapısı sayesinde kısa sürede uygulama geliştirmeye başlayabilirsiniz. LPCxpresso kitlerde gördüğüm olumsuzluk ise programlama/debug işlemleri için Keil IDE’sini desteklememektedir. Daha önce paylaştığım “LPCXPRESSO Kit (LPC1343) USB Boot Loader Denemesi” yazımda LPC1343 ve USB özelliğinden bahsetmiştim. Bu yazıyı okuyarak daha detaylı bilgi edinebilirsiniz.
USB HID Özelliği
LPC1343 denetleyicisinin öne çıkan ve hoşuma giden bir özelliği de USB (device) çevre biriminin bulunmasıdır. Buna ek olarak HID ve MSC sınıfı uygulamalar için gerekli hazır fonksiyonları dahili ROM’u içerisinde barındırmaktadır. Bu sayede fazladan kod yazmadan, fonksiyon işaretçi tanımlayarak ROM içerisindeki hazır fonksiyonlara erişebilir ve uygulamanızı USB üzerinden haberleşemeye hazır hale getirebilirsiniz.
Gerçekten çok kullanışlı bir özellik kullanıcıya bir sürü ayarlar ile uğraştırmadan USB özelliği kullanabilme imkanı sunuyor. Bu sayede uygulamalarınızda eski ve daha yavaş olan rs232 haberleşmesi kullanmak yerine USB kullanarak bilgisayar ile iletişime geçebilirsiniz.
USB haberleşmesi farklı çeşitlerde olabilmektedir. Her haberleşme türünün farklı kullanım alanı, avantaj ve dezavantajları vardır. USB haberleşmesi başlı başına teferruatlı bir konu olduğu için bu yazımda sadece USB HID sınıfından biraz bahsedip uygulamamızı anlatacağım.
HID Sınıfı
HID (Human Interface Device/ İnsan Arabirim Cihazı) sınıfı kullanımı ve anlaması diğer sınıflara göre daha basit bir USB iletişim çeşididir. Kullandığımız usb fare klavye gibi birçok donanımlar HID cihaz olarak haberleşmektedir.
HID cihazların kolaylık sağlayan bir özelliği de işletim sistemleri tarafından otomatik olarak sürücüye gerek kalmadan tanınması ve kullanılmasıdır. HID sınıfı tek seferde maksimum 64 byte veri alma ve gönderme paketlerini desteklemektedir.
Elektronik devrelerinizi bilgisayar ile haberleştirmek için HID sınıfı ideal bir yöntemdir. Seri/paralel port bağlantılarının günümüz bilgisayarlarından yavaş yavaş kaldırıldığını düşünürsek, USB iletişime geçmek zorunluluk haline gelmeye başlamıştır.
LPC1343 USB Uygulaması
LPC1343 USB uygulamasını yazarken Nxp tarafından yayınlanmış olan örnek uygulamadan faydalandım. Uygulama bilgisayara HID cihaz olarak bağlanıp 2 byte veri gönderir ve 2 byte veri alır. Alınan 2 byte verinin bir byte’ı uygulamada bulunan PWM değerini ayarlamak için diğer byte ise kit üzerinde ki LED’i yakıp söndürmek için kullanılır. Benzer şekilde gönderilen 2 byte’ın bir byte’ı ADC’den okunan değeri göndermek için diğer byte ise kit üzerinde bulunan LED’in durumunu göndermek için kullanılır.
Uygulama hem HID kullanımına hemde LPC denetleyiciler üzerinde ADC, PWM ve port işlemlerinin kullanımına örnek olmaktadır.
Uygulamanın kodları Keil üzerinde derlenmiştir. LPCxpresso Keil’i desteklemediği için Debug işlemi yapılamamaktadır. Keilde üretilen .axf uzantılı program dosyası LPCxpresso’nun kendi IDE’si üzerinden LPC1343’e yazılmıştır.
Keil USB HID uygulamasını derlemeden önce eğer derlemediyseniz kodlar içerisinde bulunan cmsis_lib projesini derleyip cmsis_lib.lib kütüphane dosyasını üretmelisiniz. USB uygulaması cmsis kütüphanesini kullandığı için aksi halde cmsis_lib kütüphanesi bulunmadı diye derleme hatası alırsınız.
/**************************************************************** * Isim : LPC1343 USB Hid Demo Uygulamasi * * Yazar : Erhan YILMAZ * * E-posta : [email protected] * * Tarih : 14.01.2013 * * Sürüm : 1.0 * ****************************************************************/ #include "lpc13xx.h" /* LPC13xx definitions */ #include "usb.h" #include "usbdesc.h" #include "rom_drivers.h" #include "config.h" #define EN_TIMER32_1 (1<<10) #define EN_IOCON (1<<16) #define EN_USBREG (1<<14) USB_DEV_INFO DeviceInfo; // USB aygit bilgilerini içeren yapi tanimlanir HID_DEVICE_INFO HidDevInfo; // HID aygit bilgilerini içeren yapi tanimlanir ROM ** rom = (ROM **)0x1fff1ff8; // ROM'daki USB fonksiyonlar1n1 çagirmak için isaretci tanimlanir. void setPWM(void); void ADC_Init(void); uint16_t ADC0_Oku(); uint16_t ADC_Data; /* * Get HID Input Report -> InReport */ void GetInReport (uint8_t src[], uint32_t length) { ADC_Data = ADC0_Oku(); src[0]=(uint8_t)((ADC_Data>>2)&0xff); // ADC sonucunu gönder // LED durum bilgisini gönder if(LPC_GPIO0->DATA&(1<<7)) src[1]='E'; else src[1]='H'; } /* * Set HID Output Report <- OutReport */ void SetOutReport (uint8_t dst[], uint32_t length) { // Alinan veriye göre PWM'i ayarla LPC_TMR16B1->MR0 = dst[0]*100; // Alinan veriye göre LED'in durumunu belirle if(dst[1]=='E') LPC_GPIO0 -> DATA |= (1<<7); else LPC_GPIO0 -> DATA &= ~(1<<7); } int main (void) { /* for delay loop */ volatile int n; HidDevInfo.idVendor = USB_VENDOR_ID; // Üretici ID'si (NXP) HidDevInfo.idProduct = USB_PROD_ID; // Üretici ürün ID'si HidDevInfo.bcdDevice = USB_DEVICE; HidDevInfo.StrDescPtr = (uint32_t)&USB_StringDescriptor[0]; HidDevInfo.InReportCount = 2; // PC'ye gönderilecek byte sayisi (max 64 byte) HidDevInfo.OutReportCount = 2; // PC'den alinacak byte sayisi (max 64 byte) HidDevInfo.SampleInterval = 0xFF; // Veri gönderme alma araligi (ms) HidDevInfo.InReport = GetInReport; // Veri gönderme fonksiyonu gösterilir. HidDevInfo.OutReport = SetOutReport; // Veri alma fonksiyonu gösterilir. DeviceInfo.DevType = USB_DEVICE_CLASS_HUMAN_INTERFACE; DeviceInfo.DevDetailPtr = (uint32_t)&HidDevInfo; /* Enable Timer32_1, IOCON, and USB blocks (for USB ROM driver) */ LPC_SYSCON->SYSAHBCLKCTRL |= (EN_TIMER32_1 | EN_IOCON | EN_USBREG); /* Use pll and pin init function in rom */ (*rom)->pUSBD->init_clk_pins(); /* insert a delay between clk init and usb init */ for (n = 0; n < 75; n++) {} (*rom)->pUSBD->init(&DeviceInfo); /* USB Initialization */ (*rom)->pUSBD->connect(TRUE); /* USB Connect */ LPC_GPIO0 -> DIR |= 1<<7; //GPIO0_7 pini çikis olarak ayarlandi. ADC_Init(); setPWM(); __WFI(); // Kesme gelene kadar bekle while (1); } // USB kesme fonksiyonu void USB_IRQHandler() { (*rom)->pUSBD->isr(); } // PWM Ayarlari void setPWM(void) { LPC_SYSCON->SYSAHBCLKCTRL |= (1<<8); // Timer1 clock'u aç LPC_IOCON->PIO1_9 |= 0x01; // Timer1_16 MAT0 - set bit 1 LPC_TMR16B1->MCR = 0x0400; // Reset at MR3 LPC_TMR16B1->MR0 = 7500; // Match register 0 = 7500 = 2%5 duty cycle ilk deger %25 LPC_TMR16B1->MR3 = 10000; // Match register 3 - cycle length LPC_TMR16B1->PWMC = 0x01; // PWMC register -MAT0 is PWM. LPC_TMR16B1->TCR = 1; // timer1'i aç } // ADC Ayarlari void ADC_Init(){ LPC_SYSCON->PDRUNCFG &= ~(0x1<<4); // Disable Power down bit to the ADC block. LPC_SYSCON->SYSAHBCLKCTRL |= (1<<13); // Enable AHB clock to the ADC. LPC_IOCON->JTAG_TDI_PIO0_11 = 0x02; // Select AD0 pin function LPC_ADC->CR = (10<<8); //CLKDIV=10 ADC Clock=PCLK/(CLKDIV+1)=48MHz/11=4.36MHz LPC_ADC->CR &= 0xFFFFFF00; // clear channel selection LPC_ADC->CR |=(1 << 0); //Kanal 0 seç } // ADC0 Kanalini Oku uint16_t ADC0_Oku(){ uint32_t regval; LPC_ADC->CR |= (1 << 24); // Çevrimi baslat. while ( 1 ) // wait until end of A/D convert { regval = LPC_ADC->DR0; // read result of A/D conversion if ( regval & (0x80000000) ) break; } LPC_ADC->CR &= 0xF8FFFFFF; // stop ADC now return (uint16_t)((regval >> 6)&0x03ff); }
Yukarıda uyuglamamızın C kodları görülmektedir. Kodlar basit be anlaşılır biçimde yazılmıştır. Ana program başlangıç ayarlarını, ADC, PWM, Port ve USB ayarlarını yaptıktan sonra uygulama wfi komutu ile işlemciyi askıya alarak kesme gelmesini bekler. USB kesmesi geldiğinde veri alma işlemleri yapılır. Alınan verilere göre PWM ve LED’in durumu düzenlenir. Aynı şekilde gönderilen veri içerisine ADC bilgisi ve LED’in durumu eklenerek bilgisayara iletilir. Uygulamayı istediğiniz gibi geliştirip kendinize özel uygulamalar yazabilirsiniz.
Bilgisayar Tarafı HID Arayüz Programı
Uygulamamızın mikrodenetleyici kısmı hazırdır. Bu aşamadan sonra bilgisayar tarafında HID uygulaması yazılmalıdır. İnternet üzerinde HID ile ilgili birçok örnek uygulamalar bulunmaktadır. HID iletişimi standart olduğu için bir kaç değişiklik ile uygulamayı kendi uygulamanıza uyumlu hale getirebilirsiniz.
HID uygulamalarında önemli nokta bilgisayar tarafında ve mikrodenetleyici tarafından tanımlanan VID(Vendor ID/Üretici Kimliği) ve PID(Product ID/Ürün Kimliği) numaralarının aynı olmasıdır. Bu sayede HID uygulaması USB üzerinde bağlı olan cihazı tanıyıp haberleşmektedir. Uygulamamızda VID numarası olarak NXP’ye ait olan VID (0x1FC9) numarası ve NXP’nin bu uygulama için belirlediği PID (0×0003) numarası kullanılmıştır. Belli bir ücret karşılığında kendinize ait USB VID numarası alabilirsiniz.
Yukarıda resimde bilgisayar uygulamamıza ait arayüz programı görüntüleri görülmektedir. Arayüz programı visual studio geliştirme ortamı üzerinde C# dili ile yazılmıştır. HID kütüphanesi olarak buradaki yazıda Yusuf YAMAK arkadaşımızın bahsettiği UsbLibrary.dll kütüphanesi kullanılmıştır. UsbLibrary.dll dosyasını uygulamanıza dahil ederek bir kaç basit fonksiyonu kullanarak uygulamanıza bağlı olan HID cihaz ile iletişime geçebilirsiniz.
Arayüz programı çalıştığında eğer USB cihaz ile bağlantı yoksa ekranda “Bağlantı yok elemanlar gösterilmez” yazısı yazdırılır. Bağlantı sağlanınca uygulama ekranı üzerinde elemanlar görülür. Uygulamada kullanılan trackbar elemanı sayesinde PWM değeri ayarlanarak LPC1343e gönderilir. Gönderilen PWM değerine göre LPC1343 PWM çıkışına bağlı olan LED’in parlaklık kontrolü yapılır. Benzer şekilde LPC1343 tarafından gönderilen ADC bilgisi progress bar elemanı kullanılarak kullanıcıya gösterilir. Uygulama üzerinde bulunan LED butonu ile LPC1343 GPIO0.7 pinine bağlı olan LED’in durumu değiştirilir. Program üzerindeki radiobutton elemanı ile LED’in durumu LPC1343 tarafından okunarak ekranda gösterilir.
USB HID uygulamasının anlaşılmasına yönelik güzel bir uygulamadır. Kodları inceleyerek veri alma gönderme ve bağlantı işlemlerinin nasıl yapıldığını kavrayabilir ve uygulamayı geliştirerek kendinize özel olarak düzenleyebilirsiniz. Aşağıda uygulamaya ait C# kodlarını görebilirsiniz.
/**************************************************************** * Isim : LPC1343 USB Hid Demo Uygulaması C# Arayüz Programı * * Yazar : Erhan YILMAZ * * E-posta : [email protected] * * Tarih : 14.01.2013 * * Sürüm : 1.0 * ****************************************************************/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using UsbLibrary; namespace USB_HID_Deneme { public partial class Form1 : Form { //int sayac = 0; byte[] out_data = new byte[3]; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { this.usbHidPort1.CheckDevicePresent(); toolStripStatusLabel1.Text = "Bağlantı Kesildi"; PWM_Bar.Visible = false; ADC_Bar.Visible = false; ADC_Label.Visible = false; PWM_Label.Visible = false; LED_buton.Visible = false; radioButton1.Visible = false; label1.Text = "Bağlantı yok elemanlar gösterilmez."; } protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); usbHidPort1.RegisterHandle(Handle); } protected override void WndProc(ref Message m) { usbHidPort1.ParseMessages(ref m); base.WndProc(ref m); // pass message on to base form } private void usbHidPort1_OnDataRecieved(object sender, DataRecievedEventArgs args) { if (InvokeRequired) { try { Invoke(new DataRecievedEventHandler(usbHidPort1_OnDataRecieved), new object[] { sender, args }); } catch { } } else { byte[] In_data; In_data = args.data; ADC_Bar.Value=(int)(In_data[1]); ADC_Label.Text="ADC="+Convert.ToString(In_data[1]); if (In_data[2] == (byte)'E') radioButton1.Checked = true; else radioButton1.Checked = false; //sayac++; //PWM_Label.Text = Convert.ToString((char)In_data[2]) + " Alım Sayısı=" + Convert.ToString(sayac); } } private void usbHidPort1_OnSpecifiedDeviceArrived(object sender, EventArgs e) { toolStripStatusLabel1.Text = "Baglanti Kuruldu"; PWM_Bar.Visible = true; ADC_Bar.Visible = true; ADC_Label.Visible = true; PWM_Label.Visible = true; LED_buton.Visible = true; radioButton1.Visible = true; label1.Text = " "; } private void usbHidPort1_OnSpecifiedDeviceRemoved(object sender, EventArgs e) { if (InvokeRequired) { Invoke(new EventHandler(usbHidPort1_OnSpecifiedDeviceRemoved), new object[] { sender, e }); } else { toolStripStatusLabel1.Text = "Bağlanti Kesildi"; PWM_Bar.Visible = false; ADC_Bar.Visible = false; ADC_Label.Visible = false; PWM_Label.Visible = false; LED_buton.Visible = false; radioButton1.Visible = false; label1.Text = "Bağlantı yok elemanlar gösterilmez."; } } private void usbHidPort1_OnDeviceRemoved(object sender, EventArgs e) { if (InvokeRequired) { Invoke(new EventHandler(usbHidPort1_OnDeviceRemoved), new object[] { sender, e }); } else { toolStripStatusLabel1.Text = "Bağlantı Kesildi"; PWM_Bar.Visible = false; ADC_Bar.Visible = false; ADC_Label.Visible = false; PWM_Label.Visible = false; LED_buton.Visible = false; radioButton1.Visible = false; label1.Text = "Bağlantı yok elemanlar gösterilmez."; } } private void usbHidPort1_OnDeviceArrived(object sender, EventArgs e) { toolStripStatusLabel1.Text = "Bağlantı kuruldu"; PWM_Bar.Visible = true; ADC_Bar.Visible = true; ADC_Label.Visible = true; PWM_Label.Visible = true; LED_buton.Visible = true; radioButton1.Visible = true; label1.Text = " "; } private void button1_Click(object sender, EventArgs e) { if (radioButton1.Checked) out_data[2] = (byte)'H'; else out_data[2] = (byte)'E'; out_data[1] = (byte)((100-PWM_Bar.Value) & 0xff); if (usbHidPort1.SpecifiedDevice != null) usbHidPort1.SpecifiedDevice.SendData(out_data); } private void PWM_Bar_Scroll(object sender, EventArgs e) { PWM_Label.Text = "PWM=" + Convert.ToString(PWM_Bar.Value); out_data[1] = (byte)((100-PWM_Bar.Value) & 0xff); if (usbHidPort1.SpecifiedDevice != null) usbHidPort1.SpecifiedDevice.SendData(out_data); } } }
Yukarıda şekilde uygulamamızın çalışma görüntüsü görülmektedir. Uygulamada ADC için ADC0 girişi PWM çıkışı için ise GPIO1.9 pini kullanılmıştır. PWM değeri ile LED’in parlaklığının değiştiği görülebilir. Benzer şekilde potansiyometre ile gerilim değerini değiştirerek bilgisayar tarafında analog değerin değiştiği gözlemlenebilir. Aynı şekilde kit üzerinde bulunan GPIO0.7 pinine bağlı LED’in durumu bilgisayar uygulaması tarafından değiştirilebilir. USB soketi LPC1343’ün USB pinlerine varsayılan bağlantı ile bağlanmıştır.
Son olarak uygulamaya ait çalışma videosunu youtube bağlantısını kullanarak izleyebilirsiniz. Uygulama ait kodları (proje halinde)
Uygulama ile ilgili takıldığınız soruları sorabilirsiniz elimden geldiğince yanıtlamaya çalışırım. Bir sonraki uygulamada görüşmek üzere…
Şifre-Pass: 320volt.com
Yayım tarihi: 2013/01/16 Etiketler: amr sample, arm projeleri, lpc1343, lpc1343 usb hid, lpcxpresso kit, usb hid
Belli bir ücret karşılığında kendinize ait USB VID numarası alabilirsiniz. Bu kısmı anlayamadım. Normalde gerek yok değil mi? Ticari amaçlar için mi VID numarası almak gerekiyor?
Denemeler için gerek yok çünkü NXP’nin VID numarası kullanılıyor. Ayni şekilde diğer mikrodenetleyici firmalarının da kendilerine ait VID numaraları var. Dediğiniz gibi ticari uygulamalarda kendinize ait VID numarası almanız gerekiyor. 1000$ gibi ücreti vardı yanlış hatırlamıyorsam.
1000$ ücreti vermeden VID ayarlanabilir aslında. 1000$ ciddi bi rakam. bunu yapacak olan olursa buraya yazsın döner ve yardımcı olabilirim VID konusunda.
LPC nin mikrodenetleyicileri hoşuma gitti. Aslında daha hiç arm programlamadım. Sadece pic programlamıştım.
Benim sorum (belki komik gelecek size ama cahilliğime verin) LPC nin M3 lerinden çok M4 leri hoşuma gitti (DSP özelliğinden dolayı) ama M3 ile başlayıp M4 ‘e geçiş yapmak istiyorum. Çekirdekler arası geçişlerde çok zorlanırmıyım. Birde M4 serisindede HID i bu kadar kolay kullanabileceğimiz bir model var mı?