Microcontroller projects

Kleine USB-Bibliothek für ATMEL®-USB-AVRs® (|||)

Beschreibung

Die USB-Bibliothek entstand aus dem Wunsch heraus USB zu verstehen. Ziel war es ein absolut minimalistisches und verständliches Programm zur Kommunikation über USB mit AVR-USB-Controllern zu schreiben. Um die Bibliothek universeller einsetzbar zu machen wurde sie erweitert. Sie unterstützt nun die Controller: ATmega32u4, ATmega32u2, AT90USB162 und AT90USB1287, und kann leicht an andere Controller angepasst werden. Um keine Treiber unter Windows mehr installieren zu müssen kam die HID-Version dazu. Die Bibliothek arbeitet mit zwei 64-Byte Endpunkten (Buffern), kann aber leicht auf mehr Endpunkte erweitert werden.

Interessante und kostengünstige Boards um die Bibliothek zu nutzen sind zum Beispiel das Entwicklungsboard AT90USBKEY von ATMEL mit dem AT90USB1287 und das Mikrocontroller-Board Teensy 2.0 mit dem ATmega32u4. Natürlich können auch andere Boards mit den entsprechenden Controllern genutzt werden (z.B. Arduino Leonardo).

AT90USBKEY Teensy2 Teensy2

Die jeweilige Firmware (GPLv3) gibt es in Assembler, C und Bascom.

Die neue Version der Bibliothek lässt sich jetzt besonders leicht einbinden und programmieren. Die Bibliothek existiert in den zwei Varianten: HID (Datentransfer, Tastatur, Maus) und VENDOR (schneller Datentransfer, USB-EIA232).

Dokumentation zu USB und zur Firmware (pdf)

Die Dokumentation steht unter der Creative Commons Lizenz (http://creativecommons.org)

Die Dokumentation wird gerade überarbeitet. Hier die älteren Versionen:

Die Firmware (prinzipielle Funktionsweise)

Die gesamte Arbeit zum USB-Bus wird mittels Interrupt-Service-Routinen abgewickelt. Alle USB-Routinen und Unterprogramme befinden sich in der USB-Bibliothek. In der neuen Version reicht es die USB-Bibliothek im Hauptprogramm einzubinden und in der Initialisierungsphase das Unterprogramm USBINI aufzurufen. Danach werden dann mittels je eines 64 Byte Buffers (Feld-Variable) Daten an den USB-Bus übergeben beziehungsweise vom Bus abgeholt. Die beiden Buffer entsprechen den beiden Endpunkten EP1 (zum Senden) und EP2 (Empfang). Werden mehr Endpunkte benötigt, so kann die Bibliothek leicht auf bis zu 6 Endpunkte erweitert werden.

Zur Kommunikation werden sowohl beim Senden wie auch beim Empfang nur je 3 Variablen benötigt. Folgend ein Auszug der Assembler-Software zur Übergabe zweier Byte des A/D-Wandlers an den Bus. Die Anzahl der zu sendenden Byte wird in der Variablen EP1_CNT hinterlegt. Die Daten landen im Buffer (EP1_BUF). Durch Setzen der Variablen EP1_Flag wird dem Bus mitgeteilt, dass Daten bereitstehen.

Daten senden


        ldi     Tmp1,2          ;ADC liefert 2 Byte!
        sts     EP1_CNT,Tmp1
        ...
        lds     Tmp1,ADCL       ;ADC-Wert im EP1-SRAM Buffer abspeichern
        sts     EP1_BUF+0,Tmp1
        lds     Tmp1,ADCH
        sts     EP1_BUF+1,Tmp1
        ...
        ldi     Tmp1,1          ;EP1 Flag setzen weil Daten vorhanden
        sts     EP1_FLAG,Tmp1
        

Im folgenden Auszug der Bascom-Software werden 8 Byte von der Tastatur aus gesendet:


  Ep1_cnt = 8                           '8 bytes 
  Disable Interrupts  
  Ep1_buf(1) = 0                        'modifier byte
  Ep1_buf(2) = 0                        'reserved byte = 0
  Ep1_buf(3) = 0                        'stop pressing key
  Ep1_buf(4) = 0                        
  Ep1_buf(5) = 0                        
  Ep1_buf(6) = 0                        
  Ep1_buf(7) = 0                        
  Ep1_buf(8) = 0                        
  Enable Interrupts
  Ep1_flag = 1                          'set EP1 flag
  

Daten empfangen

Auch beim Empfang reichen die drei Variablen EP2_CNT, EP2_BUF und EP2_Flag. Das Flag teilt mit wenn Daten zur Verfügung stehen. Im Zähler steht wie viele Byte vom PC gesendet wurden. Die Daten stehen dann im Buffer bereit. Hier ein Auszug aus der C-Software, wo ein Byte empfangen wird.:


    if (Ep2_flag == 1)
    {
      blinkflag = (Ep2_buf[0] & 0x40);  // Blinkflag ausmaskieren
      PORTB |= (Ep2_buf[0] & 0x0F); // LEDs mit Maskierung setzen
      PORTB &= (Ep2_buf[0] | 0xF0); // LEDs mit Maskierung loeschen
      Ep2_flag = 0; //Flag zuruecksetzen
    }
    

HID

Die HID-Klasse ermöglicht eine Kommunikation unter Windows ohne spezifische Treiber! Die Übertragungsgeschwindigkeit (64 kByte/s) reicht für die meisten Anwendungen. Für den Datentransfer stehen zwei Endpunkte (Buffer) mit je 64 Byte zur Verfügung, die in festen Zeitabständen (1ms, Interrupt-Transfers) abgefragt werden.

Firmware

Die Firmware gibt es gleich drei mal. Beim Programm zur Datenkommunikation fragt der PC Daten über Endpunkt 1 (IN d.h zum PC) den Wert des A/D-Wandlers an (2 Byte). Über den Endpunkt 2 (OUT d.h. vom PC) sendet der PC ein Byte mit Zustandsinformationen für die LEDS (5 Bit). Mit der entsprechenden PC-Software können so LEDs geschaltet werden und der Wandler-Wert dargestellt werden. Zusätzlich gibt es zwei Beispielprogramme zur Emulation einer Maus oder einer Tastatur. Diese benötigen natürlich keine PC-Software. Die Mausemulation funktioniert besonders gut mit dem kleinen Joystick des AT90USBKEY. Mit der Tastaturemulation und hochohmigen Pull-Up-Widerständen lassen sich PCs vielfältig steuern (google: "makey makey").

Für ausführlichere Erklärungen siehe "Firmware prinzipielle Funktionsweise".

In der Assemblerversion 1.2 waren noch zwei Fehler. Aktuell für Assembler ist jetzt Version 1.3. Danke an Thomas Tahsin-Bey.

Die PC-Software

PC-Software

Die Software benutzt auf der PC Seite die Betriebssystem-Treiber der HID Klasse. Ein PC-Programm in C++ ( Qt (GUI)) ermöglicht die Kommunikation mit dem Controller. Unter Windows und Linux wird die hidapi von Alan Ott genutzt. Unter Linux kann sowohl die libusb-Version von hidapi wie auch die hidraw-Version genutzt werden (siehe Quellcode). Der Quellcode lässt sich am einfachsten mit dem Qt-Creator kompilieren.

Downloads

Eine sehr einfache Möglichkeit um unter Linux (zum Beispiel Raspberry Pi) oder Windows auf den Controller zuzugreifen bietet Python. Hier ein Beispielcode um die 4 LEDs zu setzen und den A/D-Wandler einzulesen:


# Communication with HID or VENDOR device (ex. teensy2) using pyUSB
# git clone https://github.com/walac/pyusb.git 
# cd pyusb
# sudo python setup.py
# Windows needs libusb! (http://sourceforge.net/projects/libusb-win32)
Vendor_ID = 0x3EB #(ATMEL)
#Product_ID = 0x01 #usb_small_lib "vendor")
Product_ID = 0x02 #usb_small_lib "hid")

import usb.core, time
interface = 0
dev = usb.core.find(idVendor = Vendor_ID,idProduct = Product_ID)
if dev is None:
    print "device not found"
# only linux: uncomment if device not free (error resource busy)   
"""if dev.is_kernel_driver_active(interface) is True:    
    dev.detach_kernel_driver(interface)        
    usb.util.claim_interface(dev, interface)        
    dev = usb.core.find(idVendor = Vendor_ID,idProduct = Product_ID)"""
dev.set_configuration()
# communication code 
msg = [0x0F,0,0] # endpoint2 (0x02) = 0x0F (light 4 LEDs stop blinking)
print "Number of Bytes written to the device: ",dev.write(0x02, msg) 
res = dev.read(0x81, 2) # read 2 Byte (ADC) from endpoint1 (0x81)
print "The voltage is: ",((res.tolist()[0]+res.tolist()[1]*256)*5.0/1024)," Volt"

Soll das Programm ohne root Rechte ausgeführt werden, so ist im Verzeichnis /etc/udev/rules.d eine Text-Datei mit folgendem Inhalt zu erstellen:

# weigu.lu usb_small_lib_vendor
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="0001", GROUP="plugdev", MODE="0666"
# weigu.lu usb_small_lib_hid
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="0002", GROUP="plugdev", MODE="0666"

Der Name der Text-Datei muss mit ".rules" enden, zum Beispiel: 15-usb_small_lib.rules

VENDOR

Will man größere Datenmengen schneller (bis 1216 kByte/s) übertragen, so kann man Bulk-Transfers nutzen. Allerdings ist die Bus-Bandbreite nicht garantiert, so dass diese Lösung für zeitkritische Anwendungen nicht geeignet ist. Am Bus sollten nicht zuviele Geräte angeschlossen sein.

Firmware

Für die Bulk-Transfers wird eine VENDOR-Klasse (herstellerspezifische Klasse) genutzt. Auch hier stehen zwei Endpunkte (Buffer) mit je 64 Byte zur Verfügung. Die Firmware zur Datenkommunikation funktioniert genau so wie bei der HID-Firmware. Über den über Endpunkt 1 (IN d.h zum PC) wird der Wert des A/D-Wandlers (2 Byte) geliefert. Über den Endpunkt 2 (OUT d.h. vom PC) sendet der PC ein Byte mit Zustandsinformationen für die LEDS (5 Bit)). Zusätzlich zur normalen Datenkommunikations-Firmware gibt es auch eine experimentelle Firmware zur Emulation einer seriellen Schnittstelle (EIA232).

Die Bascom-Version wurde zusammen mit Jean-Claude FELTES erstellt. Danke für die Hilfe :-).

Für ausführlichere Erklärungen siehe "Firmware prinzipielle Funktionsweise".

In der Assemblerversion 1.2 waren noch zwei Fehler. Aktuell für Assembler ist jetzt Version 1.3. Danke an Thomas Tahsin-Bey.

Die PC-Software

Ein PC-Programm in C++ ( Qt (GUI)) ermöglicht die Kommunikation mit dem Controller (siehe Screenshot bei HID). Der Quellcode lässt sich am einfachsten mit dem Qt-Creator kompilieren.

Python bietet ebenfalls eine sehr einfache Möglichkeit auf die Bibliothek zuzugreifen. Das gleiche Python Programm wie oben ermöglicht auch den Zugriff auf die Vendor_Bibliothek. Es muss dazu nur die Produkt ID geändert werden.

Die VENDOR-Bibliothek benötigt im Gegensatz zu HID Treiber unter Windows. Der Zugriff erfolgt über die "libusb" sowohl unter Linux, wie auch unter Windows. Die Software benutzt die freie Bibliothek "libusb", welche bei Linux-Rechnern meist schon installiert ist. Für Windows-Rechnern findet man eine "libusb-win32" bei sourceforge.net. Praktisch ist bei dieser Version ein Tool um die benötigte Treiberdatei (".inf" Datei) zu erstellen (Datei inf-wizard im Unterverzeichnis bin). Diese Datei muss nach dem Anstecken einmal ausgeführt werden, damit das Gerät richtig erkannt wird. Unter Windows 64 Bit scheint die Installation der "libusb" Schwierigkeiten zu bereiten. Hier nutzt man dann ev. besser die HID-Klasse.

Downloads