Joystick kormánykerék építés házilag

Egy kormányt építettem, ami egy számítógéphez köthető joystick és autós, kamionos szimulátorokhoz lehet használni. A kormány az USB HID szabvánnyal kompatibilis, külön meghajtóprogramot nem kell telepíteni sem Windows sem Linux vagy *BSD alá. Egy autóbontóban szereztem egy olcsó kormányt és a hozzá tartozó irányjelző kart (ami egy Daewoo Tico-ban volt). A kormány egy egyszerű műanyagcsöves tengelykapcsolón keresztül egy régi HP lézernyomtató enkóderét forgatja:
Joystick kormánykerék dobozolva Joystick kormány építés közben Joystick kormány tengelykapcsoló
Kormány joystick építhető potméterből is, de a potméterek általában 270 fokban forgathatók el, míg az enkóderrel készült joystick bármennyiszer körbefordítható. Ráadásul sokkal pontosabb is az enkóderes megoldás: egy teljes körülfordulásra 1800 jelet ad. Ez már nagyobb felbontás, mint amire nekünk szükségünk van, de ezen egy osztással könnyen lehet segíteni. Az ATmega8 forráskódja itt megnézhető és le is tölthető (bzr clone lp:atmega8-usb-hid-joystick).

Enkóder

A felhasznált enkóder (másnéven inkrementális jeladó) optikai tárcsája és az enkóder IC közelről:
Lézernyomtató lapadagoló enkóder Q9846 enkóder IC
Az enkóder jeleinek feldolgozásához csak két digitális bemenet kell: az egyiket érdemes egy megszakítást is generáló lábra kötni (nekem az ATmega8-nál ez a PD3/INT1). Ezen a lábon kell a felfutó és a lefutó éleket figyelni és a másik bemenet szintjét vizsgálni: ebből megállapítható, hogy merre forgatták a kereket. Ha a kereken α (alfa) szög távolság van két jel között, akkor az érzékelők közötti távolság = n * α + α / 2. Ahol n általában nullánál nagyobb, de kevesebb, mint ahány jelünk van a kereken.
C-ben így néz ki:

#define ENCODER0            _BV(PD3)
#define ENCODER1            _BV(PD5)

bool_t encoder_change = FALSE;
static int8_t encoder_cntr = 0;

/**
* Interrupt on rising/falling edge of PD3.
*/
ISR(INT1_vect)
{
    if (PIND & ENCODER0)
    {
        /* rising edge */
        if (PIND & ENCODER1)
        {
            encoder_cntr++;
        }
    }
    else
    {
        /* falling edge */
        if (PIND & ENCODER1)
        {
            encoder_cntr--;
        }
    }
    encoder_change = TRUE;
}

void ENCODER_init (void)
{
    encoder_change = FALSE;
    encoder_cntr = 0;

    /* PD3 and PD5 input for encoder */
    DDRD &= ~(ENCODER0 | ENCODER1);

    // Configure INT1 interrupt: interrupt on rising/falling edge
    MCUCR &= ~_BV(ISC11);
    MCUCR |= _BV(ISC10);
    GICR |= _BV(INT1);
}

Az enkóder pontosságát meg lehet kétszerezni, ha a mindkét lábon figyeljük a fel- és lefutó éleket. Az enkóder mellet egy DC motor is látható, ezt esetleg erővisszacsatolásra (force feedback, FFB) lehetne használni, de egyelőre ezzel nem foglalkozom, mert úgy tűnt másoknak sem sikerült könnyen FFB joystick-ot csinálniuk.

Pedál

A pedál lambériából és rétegelt lemezből van. A pedál rugózásáról egy nagyobb átmérőjű befőttesgumi és egy polifoam csomagolóanyagból tekert henger gondoskodik. A polifoam henger alatti ezüst színű csík egy házilag készült nyomásszenzor. A szenzor pontossága nem túl nagy, de pedálnak teljesen jó.
Joystick pedál Joystick pedál másik oldalról Joystick pedál belseje

Elektronika

ATmega8-al készült vezérlő elektronika egy korai fázisban és dobozba szerelt állapotban:
Joystick kormánykerék elektronika fejlesztés közben Joystick kormánykerék elektronika rögzítve, irányjelző kapcsoló bekötve
Az ATmega8-ban nincs USB vezérlő, a szoftveres V-USB-t használtam. A kapcsolási rajz a következő:
Joystick kormány kapcsolási rajza
A PB0..PB2 kimeneteket egy UC3717-es motormeghajtó IC vezérlésére terveztem, de jelenleg nem használja a szoftver.

USB HID

Az USB HID (Human Interface Device) szabványának alkalmazása teszi lehetővé, hogy különféle egerek, billentyűzetek, joystick-ok külön meghajtóprogram nélkül használhatók legyenek. Az ún. "Report Descriptor" nevű tömb írja le, hogy az eszköz milyen formátumban küldi el az állapotát (a report-ot), hogy az adott joystick hány tengellyel rendelkezik, hány gombja van és hogy ezeknek mi az értéktartománya. Így tulajdonképpen bármilyen joystick megvalósítható. A "HID Descriptor Tool"-lal lehet szerkeszteni ilyen "Report Descriptor"-t.

USB debuggolás Linux alatt

Linux alatt az usbmon nevű programmal lehet a nyers USB csomagokat "elkapni", ehhez az usbmon kernel modult kell beilleszteni:

$ sudo modprobe usbmon
$ sudo usbmon
ebfb4380 5.363547 S Ii:2:016:1 -:8 4 <
ebfb4380 5.371494 C Ii:2:016:1 0:8 4 =
    04009600
ebfb4380 5.371511 S Ii:2:016:1 -:8 4 <
ebfb4380 5.427542 C Ii:2:016:1 0:8 4 =
    04009600
ebfb4380 5.427569 S Ii:2:016:1 -:8 4 <
ebfb4380 5.435497 C Ii:2:016:1 0:8 4 =
    04009600
ebfb4380 5.435520 S Ii:2:016:1 -:8 4 <
ebfb4380 5.467531 C Ii:2:016:1 0:8 4 =
    04009600

Ha GUI-n szeretnénk az alábbi adatokat látni, akkor a wireshak-ot érdemes használni. Ezt a programot nem root-ként kell futtatni, hanem sima felhasználóként és a debug könyvtár jogosultságait érdemes átállítani:
$ sudo chmod -R 777 /sys/kernel/debug
$ wireshark

Ha már valamennyire működőképes az USB eszköz és felismeri a rendszer, akkor az evtest nevű programmal lehet tesztelni:
$ sudo /usr/bin/evtest /dev/input/by-id/usb-Peter_s_Steering_wheel_ivanovp@gmail.com-event-joystick
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x4242 product 0xbeef version 0x101
Input device name: "Peter's Steering wheel"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 288 (BTN_TRIGGER)
    Event code 289 (BTN_THUMB)
    Event code 290 (BTN_THUMB2)
    Event code 291 (BTN_TOP)
    Event code 292 (BTN_TOP2)
    Event code 293 (BTN_PINKIE)
    Event code 294 (BTN_BASE)
    Event code 295 (BTN_BASE2)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value     -1
      Min     -127
      Max      127
      Flat      15
    Event code 1 (ABS_Y)
      Value      0
      Min     -127
      Max      127
      Flat      15
    Event code 2 (ABS_Z)
      Value      0
      Min     -127
      Max      127
      Flat      15
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
Event: time 1420958150.109931, type 3 (EV_ABS), code 0 (ABS_X), value 0
Event: time 1420958150.109931, -------------- EV_SYN ------------
Event: time 1420958150.221926, type 3 (EV_ABS), code 0 (ABS_X), value 1
Event: time 1420958150.221926, -------------- EV_SYN ------------
Event: time 1420958150.317923, type 3 (EV_ABS), code 0 (ABS_X), value 2
Event: time 1420958150.317923, -------------- EV_SYN ------------
Event: time 1420958150.437931, type 3 (EV_ABS), code 0 (ABS_X), value 3
Event: time 1420958150.437931, -------------- EV_SYN ------------
Event: time 1420958150.909932, type 3 (EV_ABS), code 0 (ABS_X), value 2
Event: time 1420958150.909932, -------------- EV_SYN ------------
Event: time 1420958154.205927, type 3 (EV_ABS), code 0 (ABS_X), value 1
Event: time 1420958154.205927, -------------- EV_SYN ------------
Event: time 1420958154.269926, type 3 (EV_ABS), code 0 (ABS_X), value 0
Event: time 1420958154.269926, -------------- EV_SYN ------------
Event: time 1420958154.333931, type 3 (EV_ABS), code 0 (ABS_X), value -1
Event: time 1420958154.333931, -------------- EV_SYN ------------
Event: time 1420958154.429931, type 3 (EV_ABS), code 0 (ABS_X), value -2
Event: time 1420958154.429931, -------------- EV_SYN ------------
Event: time 1420958154.709927, type 3 (EV_ABS), code 0 (ABS_X), value -1
Event: time 1420958154.709927, -------------- EV_SYN ------------

Másik tesztelő a jstest, ami Arch Linux alatt a joyutils csomagban van. A jscal-lel lehet kalibrálni.
$ jstest /dev/input/by-id/usb-Peter_s_Steering_wheel_ivanovp@gmail.com-joystick
Joystick (Peter's Steering wheel) has 3 axes and 8 buttons. Driver version is 2.1.0.
Testing ... (interrupt to exit)
Axes:  0:     0  1: 25673  2:     0 Buttons:  0:off  1:off  2:off  3:off  4:off  5:off  6:off  7:off

A jstesk-gtk-val pedig GUI-n mutatja a joystick paramétereit, ezzel is lehet kalibrálni.