Zitat Zitat von HaWe Beitrag anzeigen
hallo,
ich versuche erfolglos, einen Wii Nunchuck an Arduino (3.3V ARM Cortex) auszulesen. Ich habe 2 Nunchucks ausprobiert, beide verhalten sich identisch.
Vom I2C Scanner wird jeder einzelne bei Adresse 0x52 erkannt, sowohl bei 100kHz als auch bei 400kHz. Pullups sind jew. 4.7k, an den Nunchucks oder I2C Bus liegt es also nicht. (Auch alternativ testweise angeschlossener MCP6050 funktioniert auf i2c)

Spannungsversorgung über 3.3V und GND Buchse.

Seltsamerweise aber liefert der i2c-Scanner auch gleichzeitig I2C Devices an x01 x02 x03 x04 x05 x06 x07 (zusätzlich zu x52), die verschwinden, wenn er abgesteckt ist.

Es sind original-Nunchucks aus einem Wii-Set (IIRC).
Ich benutze zwar kein Arduino, hab aber schon einen Nunchuk (Clone) verschiedentlich zum Laufen gebracht. Wenn man einen funktionierenden I2C Master hat, ist das kein Hexenwerk. Ist halt ein I2C Device wie tausend andere auch. Ich zeig den Code mal in Portionen, damit ich dazwischen ein paar Erläuterungen machen kann.

Code:
#include "I2cMaster.h"

#define NUNCHUK_ADDR 0x52

typedef struct {
    int x;
    int y;
    int acc_x;
    int acc_y;
    int acc_z;
    int c_button;
    int z_button;
} nunchuk_data_t;

int NunchukInit(void);
int NunchukStart(void);
int NunchukRead(nunchuk_data_t *data);
Die Adresse ist natürlich 0x52, nur die Arduino-Jünger popeln da immer noch das R/W-Bit mit rein, weil die wohl die I2C-Spec nie gelesen haben oder weil ihnen die Aufteilung eines Bytes in verschiedene Bitfelder intellektuell zu anspruchsvoll ist. Hier wird nur die Struktur für das Ergebnis beschrieben. Das packt man gerne in ein Headerfile.
Code:
int NunchukInit(void) {
    I2cStart();
    if(I2cWriteAddress(NUNCHUK_ADDR, I2C_WRITE)){
        I2cStop();
        return -1;
    }
    if(I2cWriteByte(0x40)){
        I2cStop();
        return -1;
    }
    if(I2cWriteByte(0x00)){
        I2cStop();
        return -1;
    }
    I2cStop();
    return 0;
}
Das ist meine Init-Funktion. Hier werden dann die Funktionen aus meiner I2C lib verwendet. I2CStart() bzw I2CStop() erzeugt die entsprechenden Zustände auf dem Bus. I2cWriteAddress() setzt aus der Adresse und R/W das erste Byte des I2C Telegramms zusammen. Die Funktion liefert ACK/NAK zurück, wie es vom Bus kommt. Kommt NAK, wird die Sache abgebrochen. Bei I2cWriteAddress() heißt das typischerweise, es gibt kein Device auf dieser Adresse. So testen die I2C Scanner. Dann wird in das Nunchuk-Register mit der Adresse 0x40 eine 0 geschrieben, um den Nunchuk zu starten. Das findet man in Beispielen im Netz.

Das erste Byte nach dem Adressbyte bei einem Write setzt bei I2C typischerweise den Registerpointer im Device. Hier wird er auf 0 gesetzt.

Code:
int NunchukStart(void) {
    // set register address
    I2cStart();
    if(I2cWriteAddress(NUNCHUK_ADDR, I2C_WRITE)){
        I2cStop();
        return -1;       // nunchuk didn't respond
    }
    if(I2cWriteByte(0x00)){
        I2cStop();
        return -1;
    }
    I2cStop();

}
Die interessanten Register im Nunchuk liegen ab Adresse 0, daher muß NunchukStart() vor jedem Auslesen ausgeführt werden. Beim Interpretieren der gelesenen Daten gibt es ein Problem. Die Datenbytes sind "verschlüsselt". Es ist aber eine wirklich simple Funktion, die man auch im Netz findet und bei mir NunchukDecode() heißt.

Code:
int NunchukDecode(int data){
    return ((data ^ 0x17) + 0x17) & 0xff;
}

int NunchukRead(nunchuk_data_t *data) {
    int temp;

    I2cStart();
    if (I2cWriteAddress(NUNCHUK_ADDR, I2C_READ)) {
        I2cStop();
        return -1;       // nunchuk didn't respond
    }
    data->x = NunchukDecode(I2cReadByte(I2C_ACK));
    data->x -= 128;             // center the value
    data->y = NunchukDecode(I2cReadByte(I2C_ACK));
    data->y -= 128;
    data->acc_x = NunchukDecode(I2cReadByte(I2C_ACK)) << 2;
    data->acc_y = NunchukDecode(I2cReadByte(I2C_ACK)) << 2;
    data->acc_z = NunchukDecode(I2cReadByte(I2C_ACK)) << 2;
    temp = NunchukDecode(I2cReadByte(I2C_NACK));
    I2cStop();
// decode the last byte
    if(temp & 0x01)
        data->z_button = 0;
    else
        data->z_button = 1;
    if(temp & 0x02)
        data->c_button = 0;
    else
        data->c_button = 1;

    data->acc_x |= (temp >> 2) & 0x03;
    data->acc_y |= (temp >> 4) & 0x03;
    data->acc_z |= (temp >> 6) & 0x03;

    return 0;
}
Hier wird ein Byte nach dem anderen eingelesen, dekodiert und in die Struktur gepackt. Zum Schluß wird das letzte Byte gelesen und die Bits ausgewertet. Wenn ich mich recht erinnere, ist der Nunchuk pingelig, wenn das letzte Byte nicht mit NAK quittiert wird. Er bleibt dann hängen und blockiert den Bus.

Hier noch ein Kommentar zu einer der gezeigten Arduino-lib. Dort wird nie ACK ausgewertet sondern immer weiter gemacht. Das führt dazu, daß selbst wenn der BUS leer ist, immer 0xff bzw 255 gelesen wird. Die Pullups sorgen für eine 1 auf dem Bus. Wenn ich also lese: "ich bekomme immer 255" oder auch andere Zweierpotenzen, die sich aus Umrechnungen ergeben können, weiß ich, daß der Bus klemmt oder das Device nicht angesprochen wird ohne in den Code zu schauen oder das Scope anzumachen.

MfG Klebwax