Introduktion til vUSB - USB fra en billig AVR (Læst 6881x)

Offline kjoller

  • U=I*R
  • ***
  • Indlæg: 45
  • Antal brugbare Indlæg: 8
    • Vis profil
    • The Electronic Map Room
Introduktion til vUSB - USB fra en billig AVR
« Dato: Marts 05, 2015, 14:57:54 »


Standarden inden for hobby-segmentet er at vi kommunikerer fra microcontroller til computeren via en seriel forbindelse. I de fleste AVR development boards (fx Arduino) er der en USB-til-seriel adapter indbygget, og ellers kan man finde en billigt på ebay.

Nogle gange ville det dog være rart at kunne forbinde direkte fra microcontrolleren til pc’en. Min grund til at eksperimentere med USB var specifikt at bygge en USB game-controller til at spille arcade-spil. Selve dimsen blev jeg aldrig færdig med, men til gengæld blev jeg noget klogere i processen.

OBS: Software nævnt i denne artikel forudsætter at du ved hvordan du kompilerer avr-gcc, og indlæser dem på din microcontroller. Hvis ikke, så vil jeg gerne anbefale bogen “Make: AVR Programming” af Elliot Williams. Alternativt er der sikkert hjælp at finde på google.

Virtuel USB
USB er en relativt kompliceret måde at kommunikere på, og derfor vil man typisk anvende hardware-implementeret USB. Det kræver dog at ens microcontroller har det, og det betyder oftest en ret heftig stigning i pris. Et eksempel på en sådan microcontroller er Atmega32U4, som er at finde i en Arduino Leonardo. På Mouser.com koster denne kr. 42,91, hvor en Atmega168PB koster kr 9,54. (Og så kan man få en atmega328p til nærmeste ingenting, hvis man er villig til at gamble på AliExpress). Derfor kan man, hvis man er en nærigpind, og ikke er bleg for at rode med lidt semi-obskur kode, bruge en virtuel USB-stack til at ‘emulere’ hardwaren. Standarden for dette er V-USB.

Hvis du tilfældigvis har købt en billig AVR-programmer fra ebay, så er der en ret god chance for at du allerede har stiftet bekendtskab med V-USB. USBasp og vusbtiny er ret udbredte på markedet, og begge er bygget med V-USB

Hardware
V-USB kræver kun en lille smule mere end det man normalt sætter på en AVR. Nedenstående diagram er fra forsiden af V-USB-projektets hjemmeside, og viser en Attiny2313. Jeg har dog mest eksperimenteret med en Atmega238P, men forskellen burde være minimal. (Jeg forstå dog ikke hvorfor de ikke har en pull-up på reset, men nok om det).



På en Atmega328P skal der (som minimum) være:
  • En pull-up (10 kohm) på reset, evt en reset-knap til at hive den ned igen.
  • Afkoblingskondensatorer på VCC og AVCC

For at få V-USB til at køre, så skal følgende også være på plads:
  • En krystal (12MHz anbefalet) på XTAL1 og XTAL2 (inkl passende kondensatorer)
  • Forbindelse gennem en 68 ohm modstand fra INT0 til D+ på USB
  • Forbindelse gennem en 68 ohm modstand fra en GPIO til D- på USB
  • En måde at få enten VCC ned til 3.3V eller en måde at få D+/D- -signalerne ned til 3.3V
  • En 1.5 kohm pull-up til D-.

De bedste, mest stabile signaler fås ved at regulere spændingen ned til 3.3V. Ovenfor gøres det ved at sætte to dioder i serie, men en lille linær regulator ville nok være bedre. På min første udgave af mit eksperiment gjorde jeg det ved hjælp af to zener-dioder på D+ og D-. Det er en billig løsning, og den virker, men Ralph Doncaster (http://nerdralph.blogspot.dk/2015/01/usb-interfacing-for-avr-microcontrollers.html) har vist at det ikke nødvendigvis er den gode løsning.

Mit projekt
Jeg satte mig for at bygge en mere generel løsning, som kunne bruges til lidt af hvert. Sådan en slags development board. Hulbræt-udgaven så sådan ud med et gamepad-’shield’ ovenpå:


(Jeg mangler et billede af den uden shield på, det kommer lige så snart jeg kan grave den op af projektkassen)

Efter at det var begyndt at virke finpudsede jeg lidt på diagrammet og kom frem til dette:


Det endte med at jeg fik lavet en PCB hos http://dirtypcbs.com, der så sådan ud:


Flere billeder, beskrivelse og link til github-side findes her: http://lab.kjlr.dk/p/handystick

Software
Med hardwaredelen vel overstået er vi ved sagens kerne: Software. Jeg indrømmer blankt at noget af dette er semi-sort snak for mig, men man kommer langt med lidt copy-paste.

Konfiguration - usbconfig.h
V-USB sættes op i usbconfig.h. Der er en masse indstillinger man kan fifle med; jeg vil gennemgå de vigtigste her - men det er nok en god idé selv at undersøge hvad de gør, og om der er andre der gør noget vigtigt.

Den første sektion er hardware-opsætning, altså clock-hastighed og hvilken port og pins USB-data-linjerne er forbundet til:
Kode:
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) // Hardware-klokken i kHz. 
#define USB_CFG_IOPORTNAME D // Hvilken port. Jeg bruger D
#define USB_CFG_DMINUS_BIT 7 // Hvilken pin bruger D- - jeg har sat den på PD7
#define USB_CFG_DPLUS_BIT 2 //Hvilken pin bruger D+ - jeg har sat den på PD2

Hvis du bare gerne vil implementere allerede skrevet software, men på din egen hardware, så er dette muligvis det eneste du behøver at rette til. Hvis du gerne vil modificere lidt på det, eller skrive dit eget, så skal du også definere USB-klasse, samt det et vendor- og product-id, og du kan lave en tekst-beskrivelse af hvad produktet er.

Hvis du vil lave et HID (Human Interface Device, fx keyboard, mus eller joystick), så skal du også kigge på noget med længden af HID-description. HID devices er et specifikt og relativt omfattende emne, så jeg vil ikke gå dybere ind i det. Men du kan jo vælge at kigge på eksempel-softwaren til mit eksperiment ovenfor.

Vendor og Product ID
Hvis man skal lave et decideret produkt, og vil skrive på æsken at det er USB-kompatibelt, så skal man skaffe sig et vendor id, og man kan herefter bruge alle de product id’s. Vendor-id kræver et medlemskab hos USB.org (http://www.usb.org/developers/vendor/). Det koster dog USD 4.000 per år, så det er uden for mit hobby-budget. Så længe man arbejder med det på hobby-plan, så er der dog intet forgjort i at låne fra eksemplerne, eller endda tilfældigt finde på et - så længe man rammer et ikke-eksisterende produkt. Ellers kan det give rod med drivers.

Main-funktionen
Som i de fleste c-programmer er det main-funktionen der kører først, og det er dens opgave at kalde alle andre. Når al initialisering er overstået, er der typisk en uendeligt kørende løkke som denne:

Kode:
while(1) {
    usbPoll()
}

Dette er faktisk det eneste krav for en løkke; Man skal køre usbPoll-funktionen jævnligt for at sikre at det ‘administrative’ i USB-protokollen bliver overholdt. Man kan så sørge for at alt andet sker i interrupts. Jeg bruger i mit gamepad-eksempel følgende kode:
Kode:
while(1) {
    wdt_reset(); // Hold watchdog’en glad.
    _delay_ms(20); // Vent lidt for ikke at overdrive. Potentielt et problem for hardcore gamere.
    usbPoll(); // Administrativt USB-kald
    if ((reportBuffer.buttonMask!=readMask)&(usbInterruptIsReady())){ // Tjek om der er sket noget
      reportBuffer.buttonMask = readMask; // skriv ny buttonMask
      usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer)); // Send data afsted
    }
}

Jeg har en 2-sekunders Watchdog timer kørende, der genstarter softwaren, hvis noget låser, men ellers er det nye if-sætningen, og den heri indeholdte kode.

“reportBuffer” er den struktur, der bliver sendt over USB’en. Den indeholder en 8-bits størrelse ved navn buttonMask. Fra anden runde af løkken vil den indeholde det, der sidst er blevet sendt afsted.

Jeg har interrupts defineret på GPIO’erne til at finde ud af om der er blevet trykket på en knap. Hvis der er sket noget, så bliver resultaterne indlæst i readMask, der er af samme type som buttonMask. If-sætningen sammenligner reportBuffer.buttonMask med readMask for at se om der er sket noget med readMask siden sidst der blev sendt data. Hvis dette er tilfældet, så bliver reportBuffer.buttonMask opdateret, og resultatet bliver sendt til usbSetInterrupt-funktionen.

På den måde vil der, hver eneste gang man trykker eller slipper en knap, blive sendt data til computeren, og man kan hoppe, kravle og skyde i et eller andet spil :-)

Videre eksperimenter
Jeg ville rigtigt gerne forstå USB-protokollen og V-usb software-driveren mere i dybden, og det tror jeg sker bedst ved at eksperimentere med de forskellige eksempler man kan finde på http://www.obdev.at/products/vusb/. Jeg kunne f.eks godt tænke mig at se lidt på hvordan man kunne styre et standard 16x2 LCD gennem USB, eller måske en LED-cube. Jeg håber på at komme videre, og så vil jeg prøve at opdatere med dybere artikler.

Jeg håber at denne artikel har givet dig en lille smule indblik i hvordan V-USB virker, og at du har fået mod på at eksperimentere. Jeg kunne godt bruge lidt erfaringsudveksling.

PS:
Faktisk kom der en bonus med mit eksperiment ovenfor. Man kan installere USBAspLoader på den, en bootloader, der ved at holde en knap nede under reset emulerer en USBasp AVR-programmer, som kan programmere sig selv. Det gør at man kan lave en absurd billig arduino-klon, dog uden serial.  Det er derfor at der er to knapper på PCB'en; én til reset og én til at starte USBasp.
« Senest Redigeret: Marts 05, 2015, 19:05:18 af kjoller »