- İlk kez USB cihazı yaparken gereken akışı fiziksel kablolamadan PCB’ye, USB 2.0 hızlarına, protokol katmanlarına ve STM32 uygulamasına kadar baştan sona izlenebilir şekilde derliyor
- USB 2.0’ın temel bağlantısı +5V, GND, D+, D- olmak üzere dört hattan oluşur;
D+ ve D-, birlikte 1 bit taşıyan diferansiyel bir çifttir
- PCB’de diferansiyel çiftin uzunluk eşleştirmesi, birbirine yakın yerleştirilmesi ve empedansı önemlidir; ancak 12 Mbit/s full speed prototipler nispeten toleranslı çalışabilir
- Uygulamada NUCLEO-F103RB üzerinde
PA12, USB_DP; PA11, USB_DM olarak ayarlanır; CDC sanal COM portu olarak tanınan bir USB serial cihazı yapılır ve 1 girdisiyle LED yakılır
- STM32CubeIDE yaklaşımı, UI tabanlı kod üretimi ile boilerplate’in birleşmesi nedeniyle yük getirir; Linux tabanlı USB cihaz uygulamaları ise daha standartlaştırılmış API’ler ve ayrılmış bir yapı sunabilir
USB’yi ilk kez uygularken kapsam
- Amaç, bilgisayara takıldığında tanınan bir USB cihazını en basit biçimiyle baştan sona yapmaktır
- Burada cihaz, bilgisayarın işlevlerini genişleten çevre birimi anlamına gelir; bilgisayar ise host olarak adlandırılır
- Kapsam, USB cihazı ile host arasındaki fiziksel bağlantıdan host üzerinde USB cihazıyla etkileşen basit bir uygulamaya kadar uzanır
- Yetkin bir şartname açıklamasından çok, en basit uçtan uca USB cihazı yapım projesi ve referans kaynak dizini niteliğindedir
USB’nin temel kavramları
- USB, çeşitli elektronik cihazlar arasında veri alışverişi ve güç beslemesi sağlayan bir endüstri standardıdır
- USB bir seri veri yolu olduğu için bitler paralel değil, veri yolu üzerinden tek tek aktarılır
- Modern veri yolları çoğunlukla seri yapıdadır; USB, host ile device arasında bitlerin seri olarak değiş tokuş edildiği bir yöntem olarak anlaşılabilir
- USB yalnızca iki cihazı bağlayıp bit alışverişi yapan fiziksel bir standart değil, veri aktarımı ve güç iletimi için iletişim protokollerini de içerir
- Açıklamanın odağı USB 2.0 üzerindedir
USB kablolaması ve diferansiyel çift
- Tipik bir USB 2.0 bağlantısı en az dört hatla açıklanır
+5 V: host’un device’a güç sağladığı hat
D-, D+: birlikte çalışarak 1 bit taşıyan diferansiyel çift
GND: toprak
- Bazı bağlantılarda
ID gibi ek pinler olabilir; ancak uygulama yalnızca yukarıdaki dört hattı ele alır
-
USB-C hakkında dikkat edilmesi gerekenler
- USB-C de içeride diferansiyel çift kullanır; ancak ters yönde takıldığında da çalışması gerekmesi gibi yönlerden genel USB 2.0 açıklamasından ayrılır
- USB-C konnektörü kullanılması hızın ya da USB sürümünün belirli olduğu anlamına gelmez
- Bir USB-C cihazı USB 2.0 da olabilir, daha modern bir USB 3.0 cihazı da olabilir
- Sonraki uygulamada USB-C ayrıca ele alınmaz
-
Diferansiyel çiftin gürültüyü azaltma yöntemi
- Tek bir hat, gerilimi
GND ile karşılaştırarak bit değerini ifade eder; ancak uzun kablolu bağlantılarda gürültü etkisi artabilir
- Diferansiyel çift iki hat kullanır; bir hat
V, diğer hat -V taşır
- Alıcı taraf iki hat arasındaki gerilim farkına bakar
- İki hatta da aynı gürültü
Vn eklendiği varsayımıyla basitleştirirsek, (V + Vn) - (-V + Vn) = 2V olur ve gürültü iptal edilir
- Bu model çok basitleştirilmiş bir açıklamadır; diferansiyel çifti daha derinlemesine anlamak için Zach Peterson’ın Altium YouTube videosuna bakılabilir
- Video 15
PCB’de USB ile çalışırken
- Doğrudan donanım yapmayıp geliştirme kartı kullanıyorsanız PCB bölümünü atlayabilirsiniz; ancak USB konnektörünü PCB’ye yerleştirirken temel ilkeler faydalıdır
- USB konnektörü kütüphane bileşenlerinde yukarıda belirtilen pinler bulunur; diferansiyel çiftin de mikrodenetleyici veya SoC üzerindeki ilgili komşu pinlere yönlendirilmesi gerekir
- Diferansiyel çift yönlendirmesinin üç temel ilkesi vardır
D+ hattından çipin plus pinine giden trace ile diğer trace’in uzunluğunu eşitlemek
- İki trace’i birbirine çok yakın yerleştirmek
- Sinyale uygun empedansı dikkate almak
- İki hattın neredeyse aynı ortamdan geçmesini sağladığınız için, önceki basit modelde iki hattaki gürültünün aynı olduğu varsayılabilir
- Empedans hesabı, üreticinin hesaplayıcısına hedef empedans, ground plane ile mesafe, diferansiyel çift arasındaki mesafe gibi değerler girilerek gerekli trace genişliğini bulma şeklinde ele alınabilir
- Zach Peterson’ın diferansiyel çift ve USB yönlendirme videoları referans kaynak olarak dahil edilmiştir
USB 2.0 hızları
- USB 2.0 denildiğinde hız tek bir değere sabitlenmiş olmaz
- USB 2.0 örneğin iki hız seviyesine sahip olabilir
- full speed: 12 Mbit/s
- high speed: 480 Mbit/s
- Device ve host, bağlantı sırasında hangi hızın kullanılacağına karar vermelidir
- PCB üzerinde high speed’i kararlı biçimde elde etmek için empedansı eşleştirmek önemlidir
- Temel prototiplerde 12 Mbit/s full speed yeterli olabilir; USB konnektöründen çipe giden trace’ler kısaysa trace genişliği vb. konularda nispeten toleranslı olunabilir
Protokol ve yazılım katmanları
- USB yalnızca donanımı değil, host ve device taraflarındaki yazılım katmanlarını da birlikte değerlendirmeyi gerektirir
- Linux açısından USB’yi anlatan yaklaşık 45 dakikalık bir video önerilen kaynaklar arasındadır
- Bu video USB frame’lerini, endpoint’leri, configuration’ı ve tek bir device’ın birden fazla USB işlevini nasıl yerine getirebileceğini ele alır
- USB’yi cihazlardan oluşan bir ağ gibi düşünme bakışı önemlidir
- Host işletim sisteminin her USB cihazı için ayrı ayrı sürücü uygulaması gerçekçi olmadığından, işletim sistemi çeşitli USB device class’larını tanır
- mass storage device
- serial device
- diğer çeşitli sınıflar
- Uygulama cihazı, host açısından serial port cihazı olarak çalışır
STM32 ile USB serial port cihazı yapmak
- Uygulama cihazı, host’tan istek aldığında LED yakan basit bir USB cihazıdır
- Host bu cihazı serial port device olarak tanır
- Uygulama yöntemi genel olarak ikiye ayrılır
- USB destekli mikrodenetleyici kullanmak
- Linux çalıştırabilen bir SoC kullanıp işlerin çoğunu çekirdeğin halletmesini sağlamak
- Uygulamada basitlik için mikrodenetleyici yöntemi kullanılır
-
NUCLEO-F103RB kartı
- Geliştirme kartı olarak NUCLEO-F103RB kullanılır
- ABD’de Digikey gibi yerlerden 10 doların biraz üzerinde bir fiyata satın alınabilir
- Bu kart, birbirine bağlı iki bölümden oluşan bir yapıya sahiptir
- Küçük taraf programmer bölgesi, büyük taraf ise gerçekten programlanacak main MCU bölgesidir
- Kartın varsayılan USB bağlantısı main MCU’ya değil, programmer tarafındaki MCU’ya bağlıdır
- Bu programmer, ST-LINK protokolünü kullanarak bilgisayarla USB mesajları alışverişi yapar ve ardından main MCU’yu programlar
- Doğrudan STM32 tabanlı bir PCB yapıyorsanız karta yalnızca bir çip koyup USB üzerinden programladıktan sonra aynı USB portunu cihaz mantığı için de kullanabilirsiniz
- STM32 tabanlı PCB ve USB programlama için referans kaynak olarak Phil’s Lab videoları dahil edilmiştir
- Video 19
- Video 20
Gerçek USB portunun kablolanması
- Nucleo kartının varsayılan USB bağlantısı main MCU’ya bağlı olmadığından, main MCU’nun USB portunu doğrudan host’a bağlamak gerekir
- Programı yükledikten sonra ST-LINK programmer bilgisayardan ayrılır ve main MCU gerçek USB portu üzerinden güç alır
- Yazılım geliştirmek için STM32CubeIDE, karta yükleme işlemi için STM32CubeProgrammer kullanılır
-
Gerekli pinler ve ayarlar
- USB 2.0 cihazı yapmak için gereken pinler dörttür
- host’tan gelen 5V güç
GND
D+
D-
- NUCLEO-F103RB’de main MCU tarafındaki USB portundan güç almak için
JP5 jumper’ı değiştirilerek kartın external 5 V besleme alacak şekilde ayarlanması gerekir
- STM32CubeIDE’de pinler şu şekilde ayarlanır
PA12 → USB_DP
PA11 → USB_DM
- Bu karttaki çip, USB bağlantısı için harici 1.5 kΩ pull-up direnci gerektirir
- Direnç
PA12 hattını 3.3V’a pull-up eder
- Bu devre breadboard üzerinde kurulur
- MacBook Pro ile tek tek pinleri bağlamak için Amazon’dan bir USB breakout cable kullanılmış; terminal block çıkarılarak USB pinleri açığa çıkarılmış ve jumper wire ile Nucleo kartına ve breadboard’a bağlanmıştır
- Diferansiyel çift uzunluk eşleştirmesi ve empedans kontrolüne katı biçimde uyulmasa da bu uygulamada 12 Mbit/s bağlantı mümkün olmuştur
USB CDC yazılımının yazılması
- CubeIDE’de USB pinleri ayarlandığında clock ayarlarıyla ilgili bir bildirim çıkar; CubeIDE’nin bunu otomatik işleyeceği seçenek seçilebilir
Pinout & Configuration içindeki Middleware and Software Packs altında USB_DEVICE ayarlanır
- Uygulamada önemli mod şudur
Communication Device Class (Virtual Port Com)
- Bu ayarla Nucleo kartı, host açısından CDC serial port cihazı olarak çalışır
- Host, bu class bilgisine dayanarak custom device ile iletişim kurmak için uygun sürücüyü ayarlayabilir
- CubeIDE C kodu üretir;
main.c içinde şu başlatma çağrısı yer alır
MX_USB_DEVICE_Init();
- LED’i yakmak için
CDC_Receive_FS rutinine şu kod eklenir
/* USER CODE BEGIN 6 */
if (Buf[0] == '1') {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1);
}
- Bu HAL çağrısı, port A’nın pin 5’ine bağlı onboard LED’i yakar
- Adım adım referans video şöyledir
Flashlama ve çalıştırma
- ELF dosyası build edildikten sonra CubeProgrammer ile kod karta yüklenir
- Ardından programmer ayrılır ve kart, yukarıda açıklanan şekilde external 5V besleme kullanılarak kablolanır
- Kart açıldığında işletim sisteminin device manager’ında COM port veya Serial port benzeri bir öğe olarak görünmelidir
- CubeIDE’nin
USB_DEVICE middleware menüsünde Device Descriptor değerleri değiştirilerek işletim sisteminin device manager’ında gösterilecek custom device name ayarlanabilir
- Mac OS’te yeni cihaz
/dev dosya sistemi altında görünür
- Örnek yol:
/dev/tty.usbmodem497A0F6739561
- Linux’ta
/dev/ttyUSB0 gibi bir adla görünebilir
- Minicom ile serial device’a bağlanma örneği şöyledir
minicom --device /dev/tty.usbmodem497A0F6739561
- Bağlandıktan sonra klavyeden
1 girildiğinde Nucleo kartındaki yeşil LED yanar
STM32 yöntemi ile Linux yönteminin yazılım farkları
- Uygulamanın sonucu, mainstream işletim sistemleri tarafından tanınan bir USB serial port device oluşturmaktır
- STM32CubeIDE tabanlı yöntemde yazılım mühendisliği açısından rahatsızlıklar vardır
- UI menülerine tıklayarak çok miktarda boilerplate üretmek gerekir
InitUsbDevice(UsbClass.CDC) gibi kod içinde esnek biçimde parametreleştirilmiş bir kütüphane kullanma yaklaşımı değildir
- Üretilen kod kullanıcı koduyla sıkı biçimde birleştiği için code review zorlaşabilir
- Yeni sürüm çıktığında boilerplate’in nasıl güncelleneceği net değildir
- Mevcut ayar STM32 ekosistemine güçlü biçimde bağlıdır
- Linux’un USB device gibi davranma biçimi daha temiz bir yaklaşım olarak değerlendirilir
- Linux API’si daha sağlam ve standartlaştırılmıştır
- pseudo-file ve system call’larla etkileşime dayanabilir
- user space ile kernel space ayrılmıştır
- Linux, HAL katmanı gibi görülebilir
- Ancak hafif, ucuz ve üretimi kolay USB cihazlarına ihtiyaç duyulan durumlarda Linux SoC ağır kalabilir; birçok kullanım senaryosunda gereğinden fazla olabilir
- Sonuç olarak bare metal USB cihaz geliştirme için daha portable ve daha az opinionated bir framework olsaydı iyi olurdu
1 yorum
Hacker News yorumları
USB’ye giriş yazısı olarak çok iyi, ancak ST mikrodenetleyicileri kullanımına fazlasıyla ağırlık veriyor; güncel ESP32 ekosistemindeki gibi USB aygıtlarını kolayca bağlayıp kullanmaya kıyasla adımlar ve araç zinciri çok daha fazla görünüyor
Ayrıca diferansiyel çiftler, bizzat birkaç USB kartı yapmış biri olarak, yeni başlayanların çok endişelenmesi gereken bir konu değil; daha çok yüksek hızlı çalışmalarda önem kazanıyor
Arduino/ESP’de yaygın kullanılan USB denetleyici IC’leri ayrıntıların çoğunu hallettiği için, ilk aygıtını yapan birine hesap yaptırmak biraz fazla geliyor
Yeni başlayan biri için hızlı ve eğlenceli bir projeydi; en zahmetli kısmı ahşap kasayı oymaktı
Gerçek hesaplamaları neredeyse hiç yapmadım; kullandığım ECAD yazılımı diferansiyel çift yönlendirme ve empedans sinyal analizi araçları sunuyor
Yine de izleri çok kısa tutarsanız genellikle büyük bir sorun çıkmaz
Genelde yalnızca USB yerleşik mikrodenetleyiciler kullandım
Uzun zaman önce USB uygunluk testi yapmıştım; sık sorun gördüğüm kalemlerden biri ani akım testiydi
Temel olarak 5V tarafında çok fazla bypass kapasitörü olması durumuydu; yazıda bu kısmı görmedim sanırım
Yüksek hızlı dijital tasarıma odaklanmak kolay, ama uygunlukta bazen daha az havalı görünen kısımlar ayağa dolanıyor
Güncel standartlarda nasıl işlediğini bilmiyorum ama test hâlâ var gibi görünüyor; yazının kendisi iyiydi
https://compliance.usb.org/index.asp?UpdateFile=Electrical#:...
Birkaç transistörle akım sınırlayıcı yapılabilir ama sıcaklık koruması gibi işlevleri olan daha iyi entegre çözümler var gibi
USB-C konusunda yazıya ek olarak, CC pinlerini uygun dirençlere bağlamak gerekir
Aksi hâlde büyük olasılıkla çalışmaz
Ayrıca diferansiyel yönlendirme ve empedans USB 2.0’da o kadar büyük mesele değil
İz uzunluklarını benzer tutmak, makul şekilde doğrudan bağlamak ve mümkünse birbirine yakın tutmak yeterli
Uzunluk/iz genişliği ince ayarı, empedans kontrolü ve RF en iyi uygulamalarıyla aşırı uğraşmaya gerek yok; netleri bağlayın yeter
Birçok MCU’da USB PHY pinleri ile konnektör arasında seri direnç de gerekir
24mil yerine 20mil iz kullanmanız gerektiği için kartı yeniden yaptırmanız muhtemelen gerekmez; ancak USB 2.0 diferansiyel çift yerleşimi görece düşük stresli olsa da doğru yapmaya çalışmak iyi bir pratik olur
ARM işlemciler gibi lehimlemesi zor bileşenlerden endişe ediyorsanız, illa bu kadar büyük parçalar kullanmanız gerekmez
STM32 performans gerektiğinde iyi, ama küçük işler için daha küçük denetleyiciler daha uygun olabilir
Örneğin VUSB, küçük Atmel mikrodenetleyicilerde USB’yi bit-banging ile uygulayan bir kütüphanedir: https://www.obdev.at/products/vusb/index.html
Öğrencilere Linux çekirdek modülü programlamayı öğretirken kullandığım örnek kartın şeması da var: https://gitlab.cs.fau.de/i4/passt/passtboard-v2
Firmware: http://www.poempelfox.de/ds1820tousb/ ve https://gitlab.cs.fau.de/i4/passt/ds1820tousb
Arduino tarzı programlamayı tercih ediyorsanız, yalnızca birkaç include kütüphanesi satırıyla USB aygıtı gibi kullanılabilen çok sayıda kart da var; örneğin https://www.az-delivery.de/en/products/digispark-board
Örneğin NUCLEO-F429ZI var: https://www.st.com/en/evaluation-tools/nucleo-f429zi.html
Yazarın kullandığı NUCLEO-F103RB’ye çok benziyor, ancak yerleşik programlayıcı/hata ayıklayıcı için üstteki USB konnektörüne ek olarak altta da doğrudan mikrodenetleyiciye bağlı bir USB konnektörü var
ESD koruması gibi seçimlere bakmak isterseniz kart şemasını da indirebilirsiniz
Donanımsal USB içeren TSSOP-20 ve tekil fiyatı yaklaşık 0,81 dolar
Ancak yazılım tarafı biraz daha zahmetli olabilir
https://www.wch-ic.com/products/CH32V203.html?
MCU üzerinde bare-metal USB kodu yazmayı denedim; SPI veya I2C gibi basit dijital protokollerle karşılaştırınca epey sarsıcıydı.
Fiziksel katman ve veri bağlantı katmanı CAN’dan çok daha karmaşık sayılmaz, ama hemen ardından descriptor’lar, endpoint’ler ve driver yapılandırması duvarına çarpıyorsunuz.
USB en başından PC’ler için plug-and-play ekosistemi olarak tasarlanmış ve bu özelliği çok belirgin.
Mümkünse üreticinin sağladığı yazılımı olabildiğince kullanmak kesinlikle daha iyi bir yol.
Silik hatırladığım bir ipucu: yüksek throughput için bulk transfer kullanın, isochronous transferlere ise hiç bulaşmayın.
USB bir master/slave protokolü olduğu için maksimum throughput alınamıyorsa sebep genelde host olan PC tarafındadır.
Lisansı olan LGPL gereksinimlerinizle uyuyorsa libusb oldukça kullanımı kolay.
Üretici driver’ını kullanmıyorsanız donanımsal bir USB protokol analizörü gerçekten yararlı olur; USB in a NutShell de protokolü anlamak için iyi bir web kaynağı: https://www.beyondlogic.org/usbnutshell/usb1.shtml
STM32’de USB ile uğraşmışken sorayım: tek bir frame’de 64 bayttan fazla almayı nasıl destekleyeceğini bilen var mı?
Şu anda yazılımda 64 baytlık frame’leri tekrar tekrar işliyorum, ama daha büyüklerinin mümkün olduğunu biliyorum.
Sanırım en fazla 1 MB’a kadardı.
Sorun şu ki Reference Manual’da bu ayarlar normal register’lar olarak değil, bir tür sözde register olarak geçiyor.
Kolay bir workaround var mı merak ediyorum.
OTG olmayan USB çevre birimi için soruyorum.
Hangi STM32’den bahsettiğini bilmiyorum ama stm32g4 yalnızca full-speed destekliyor.
Çoğunlukla ESP32 kullanıyorum, ama birçok projeye uyan ucuz bir hack yöntemi var.
Çok hızlı ve kolay bir özel controller gerekiyorsa, atılmış bir USB klavyenin kontrol kartını söküp kullanmayı da düşünebilirsiniz.
Lehim yerine iletken yapıştırıcıyla kontaklara kablo tutturup glue gun ile sağlamca sabitlemek yeterli.
Spacebar girdisi gönderen, arcade button tabanlı tek düğmeli bir oyun controller’ını ucuza ama oldukça sağlam şekilde yapmıştım; debounce vb. her şey hallediliyor ve kod gerekmiyor.
USB 3 destekli geliştirme kartı var mı merak ediyorum.
Bir USB-C monitör sink’i prototiplemek istiyorum, ama USB üzerinden DisplayPort alabilecek performansta bir kart bulmakta zorlanıyorum.
Zaten DisplayPort sinyalini alabileceğiniz veya harici bir monitöre yönlendirebileceğiniz varsayımıyla tabii.
Bir USB billboard cihazı implemente edip doğru alternate mode’u sinyallemeniz yeterli.
Çalışması için aslında opsiyonel, ama hatırladığım kadarıyla spesifikasyon açısından gerekli.
Böylece USB-C fişinde DisplayPort sinyali çıkar; uygun AUX hatlarını DisplayPort konnektörüne bağlamanız yeterli olur.
Fiyatı da makul görünüyor.
2 yıl önce benzer bir şey aradığımda fiyatı bir basamak daha pahalıydı.
Raspberry Pi’yi PC’ye takılı tutup https://github.com/xairy/raw-gadget ile sanal USB cihazı oluşturuyorum.
Şimdilik bunu proprietary yazılımı kandırmak için bir MTP kamerayı emüle etmekte kullanıyorum.
Raspberry Pi Zero ve Linux kernel’in composite USB özelliğiyle birkaç USB cihazı prototiplemiştim.
En azından storage ve serial cihazları oldukça kolay çalıştırabildim.
Raspberry Pi’de composite kernel modülünü başlatan bir shell script gibi bir şeye ihtiyaç var; boilerplate kodu kernel dokümantasyonunda bulabilirsiniz.
pikvm de ilginç bir proje.
PC’ye bağlandığında USB bağlantısı yalnızca klavye ve fare gibi çalışmakla kalmıyor, sistemi boot etmek için kullanılabilecek bir USB drive da olabiliyor.
Kurulum işleri için epey ilginç bir yöntem.