PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Due Probleme mit I2C und GY-50



DanielSan
06.03.2017, 18:30
Hallo,

ich versuche aktuell mit mittelmäßigem Erfolg den Kreiselsensor GY-50 (L3G4200) an dem Arduino Due in betrieb zu nehmen. Mit dem I2C Scanner konnte ich erfolgreich die korrekte Adresse 0x69 auslesen. An meinem Nano funktioniert der Sensor tadellos und gibt mit folgendem Sketch...

#include <Wire.h>
#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
#define CTRL_REG5 0x24
int L3G4200D_Address = 105; //I2C address of the L3G4200D
int x;
int y;
int z;
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("starting up L3G4200D");
setupL3G4200D(2000); // Configure L3G4200- 250, 500 or 2000 deg/sec
delay(1500); //wait for the sensor to be ready
}
void loop() {
getGyroValues();// This will update x, y, and z with new values
Serial.print("X:");
Serial.print(x);
Serial.print(" Y:");
Serial.print(y);
Serial.print(" Z:");
Serial.println(z);
delay(100); //Just here to slow down the serial to make it more readable
}
void getGyroValues() {
byte xMSB = readRegister(L3G4200D_Address, 0x29);
byte xLSB = readRegister(L3G4200D_Address, 0x28);
x = ((xMSB << 8) | xLSB);
byte yMSB = readRegister(L3G4200D_Address, 0x2B);
byte yLSB = readRegister(L3G4200D_Address, 0x2A);
y = ((yMSB << 8) | yLSB);
byte zMSB = readRegister(L3G4200D_Address, 0x2D);
byte zLSB = readRegister(L3G4200D_Address, 0x2C);
z = ((zMSB << 8) | zLSB);
}
int setupL3G4200D(int scale) {
//FromJim Lindblom of Sparkfun's code
// Enable x, y, z and turn off power down:
writeRegister(L3G4200D_Address, CTRL_REG1, 0b00001111);
// If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2:
writeRegister(L3G4200D_Address, CTRL_REG2, 0b00000000);
// Configure CTRL_REG3 to generate data ready interrupt on INT2
// No interrupts used on INT1, if you'd like to configure INT1
// or INT2 otherwise, consult the datasheet:
writeRegister(L3G4200D_Address, CTRL_REG3, 0b00001000);
// CTRL_REG4 controls the full-scale range, among other things:
if (scale == 250) {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00000000);
} else if (scale == 500) {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00010000);
} else {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00110000);
}
// CTRL_REG5 controls high-pass filtering of outputs, use it
// if you'd like:
writeRegister(L3G4200D_Address, CTRL_REG5, 0b00000000);
}
void writeRegister(int deviceAddress, byte address, byte val) {
Wire.beginTransmission(deviceAddress); // start transmission to device
Wire.write(address);// send register address
Wire.write(val);// send value to write
Wire.endTransmission();// end transmission
}
int readRegister(int deviceAddress, byte address) {
int v;
Wire.beginTransmission(deviceAddress);
Wire.write(address); // register to read
Wire.endTransmission();
Wire.requestFrom(deviceAddress, 1); // read a byte
while (!Wire.available()) {
// waiting
}
v = Wire.read();
return v;
}


die erwarteten Zeilen auf der Seriellen Schnittstelle aus. Bei Bewegung des Sensors reagiert dieser wie erwartet. Die Werte ändern sich entsprechend, bis der Sensor keine weitere Beschleunigung erfährt.

starting up L3G4200D
X:0 Y:1 Z:0
X:0 Y:1 Z:0
X:0 Y:1 Z:0
X:0 Y:1 Z:0
X:0 Y:1 Z:0


Lade ich das gleiche Sketch in den Due bekomme ich folgende Ausgabe auf der Seriellen Schnittstelle. Die Werte reagieren auch auf Beschleunigungen aber ich erkenne keinen sinnvollen Zusammenhang.

starting up L3G4200D
X:65532 Y:65529 Z:65534
X:65535 Y:65526 Z:65533
X:1 Y:65529 Z:65533
X:1 Y:65526 Z:65535
X:65533 Y:65527 Z:65533
X:0 Y:65526 Z:65531


Ich habe jetzt drei Vermutungen entweder passt die I2C Geschwindigkeit beim Due nicht. Er Taktet ja höher und steht evtl. standardmäßig auf 400khz oder mehr? So das der Sensor damit nicht klar kommt. Oder irgendwas passt mit meinen Variablen nicht weil die Werte verdächtig nah am "überlauf" -> 2^16 sind. Oder es gibt Probleme mit der Versorgungsspannung 3,3V <-> 5V. Der Nano läuft mit 5V und der Sensor wird mit 5V versorgt. Der Due läuft mit 3,3V und der Sensor wird entsprechend mit 3,3V versorgt. Am Due habe ich SDA an Pin 20 und SCL an Pin 21 angeschlossen.

Könnt ihr mir helfen?
Vielen Dank.

Gruß Daniel

HaWe
07.03.2017, 00:17
hallo,
als erstes: ändere mal überall in deinem Code int um in int16_t.
Es könnte sein, dass beim Due int 32bit lang ist, im Unterschied zu AVRs
Serial.println (sizeof(int) );
gibt dir darüber Auskunft.
Die Serial class hat da auch so generell einige Macken mit der Ausgabe, wenn ich mich recht erinnere.

Falls es daran nicht gelegen hat:

Laut http://www.hotmcu.com/3-axis-gyrol3g4200d-p-171.html ist supply voltage 1.8 bis 3.6 V, da sind eher die 5V zu hoch. Die Due 3.3V müssen also passen.
i2c ist bei allen Arduinos standardmäßig 100kHz, egal welcher Prozessortakt. Laut http://www.haoyuelectronics.com/Attachment/GY-50/L3G4200D.pdf unterstützt er aber auch 400kHz, mehr macht ein Due soweit ich weiß sowieso nicht.


edit:

Der Due hat bereits eigene interne bzw. Platinen-Pullups an i2c-0 verbaut (IIRC, ca. 10k oder iwas in der Art, die Angaben waren für mich etwas verwirrend);
hast du noch weitere von dir aus dazu gebaut?
Dann könnte das der Grund sein, weshalb es nicht funktioniert.

Ansonsten hat der Due i2c-1 keine Due-eigenen Pullups, also quasi nackt, wie TWI am Nano oder Uno.
wenn das bis hier hin noch vergeblch war, muss man weiter sehen.

DanielSan
07.03.2017, 19:38
Hi,

also ich habe alle int in int16_t geändert.
Serial.println(sizeof(int)); gibt "4" aus -> 4byte pro int richtig? Also war das mit int16_t schonmal nicht verkehrt.

Mein Code sieht jetzt so aus:

#include <Wire.h>
//Arduino 1.0+ only

#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
#define CTRL_REG5 0x24
int16_t L3G4200D_Address = 0x69; //I2C address of the L3G4200D
int16_t x;
int16_t y;
int16_t z;
void setup() {

Wire1.begin();
Serial.begin(9600);
Serial.println("starting up L3G4200D");
setupL3G4200D(2000); // Configure L3G4200- 250, 500 or 2000 deg/sec
Serial.println(sizeof(int));

delay(1500); //wait for the sensor to be ready
}
void loop() {
getGyroValues();// This will update x, y, and z with new values
Serial.print("X:");
Serial.print(x);
Serial.print(" Y:");
Serial.print(y);
Serial.print(" Z:");
Serial.println(z);
delay(100); //Just here to slow down the serial to make it more readable
}
void getGyroValues() {
byte xMSB = readRegister(L3G4200D_Address, 0x29);
byte xLSB = readRegister(L3G4200D_Address, 0x28);
x = ((xMSB << 8) | xLSB);
byte yMSB = readRegister(L3G4200D_Address, 0x2B);
byte yLSB = readRegister(L3G4200D_Address, 0x2A);
y = ((yMSB << 8) | yLSB);
byte zMSB = readRegister(L3G4200D_Address, 0x2D);
byte zLSB = readRegister(L3G4200D_Address, 0x2C);
z = ((zMSB << 8) | zLSB);
}
int16_t setupL3G4200D(int16_t scale) {
//FromJim Lindblom of Sparkfun's code
// Enable x, y, z and turn off power down:
writeRegister(L3G4200D_Address, CTRL_REG1, 0b00001111);
// If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2:
writeRegister(L3G4200D_Address, CTRL_REG2, 0b00000000);
// Configure CTRL_REG3 to generate data ready interrupt on INT2
// No interrupts used on INT1, if you'd like to configure INT1
// or INT2 otherwise, consult the datasheet:
writeRegister(L3G4200D_Address, CTRL_REG3, 0b00001000);
// CTRL_REG4 controls the full-scale range, among other things:
if (scale == 250) {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00000000);
} else if (scale == 500) {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00010000);
} else {
writeRegister(L3G4200D_Address, CTRL_REG4, 0b00110000);
}
// CTRL_REG5 controls high-pass filtering of outputs, use it
// if you'd like:
writeRegister(L3G4200D_Address, CTRL_REG5, 0b00000000);
}
void writeRegister(int16_t deviceAddress, byte address, byte val) {
Wire1.beginTransmission(deviceAddress); // start transmission to device
Wire1.write(address);// send register address
Wire1.write(val);// send value to write
Wire1.endTransmission();// end transmission
}
int16_t readRegister(int16_t deviceAddress, byte address) {
int16_t v;
Wire1.beginTransmission(deviceAddress);
Wire1.write(address); // register to read
Wire1.endTransmission();
Wire1.requestFrom(deviceAddress, 1); // read a byte
while (!Wire1.available()) {
// waiting
}
v = Wire1.read();
return v;
}


Die Ausgabe von diesem Code:

starting up L3G4200D
4
X:3 Y:-12 Z:-1
X:-1 Y:-11 Z:1
X:2 Y:-14 Z:-1
X:1 Y:-13 Z:-3
X:-1 Y:-11 Z:0
X:2 Y:-14 Z:255
X:-2 Y:-12 Z:-4
X:2 Y:-12 Z:-5
X:2 Y:-12 Z:-4
X:-3 Y:-8 Z:0


Die Werte zappeln immernoch liegen aber jetzt schon besser und reagieren tendenziell korrekt auf Beschleunigung um die jeweilige Achse. Ab und zu haben sie aber doch starke ausreisser ("Z:255").

Das ganze hängt im Moment am SCL1/SDA1 ohne externe Pullups. Sobald ich Pullups verwende (2,2kOhm und 10kOhm getestet) funktioniert die I2C Kommunikation gar nicht mehr. Egal ob am SCL0/SDA0 oder SCL1/SDA1. Versorgungsspannung ist 3,3V (an VCC am Sensor angeschlossen).
Schließe ich den Sensor an den Nano an (ohne externe Pullups), ist alles bestens.

Muss ich die Pins noch irgendwie konfigurieren? Wie schalte ich die internen Pullups ab? Geht das überhaupt?

Danke.

EDIT: Oh man... Mir ist gerade aufgefallen, das in der Software vom Nano steht das alle Werte durch 10 geteilt werden. Kein Wunder das die da viel ruhiger sind. Ich habe also nur noch ein einziges Problem, die ausreisser auf 255.

HaWe
07.03.2017, 20:35
jaja, die Arduino-APi...
vor lauter Schiss vor fehlender backwards- Konpatibilität schrecken die davor zurück, endlich mal die ganzen APIs, libs und cores auf vernünftige und sichere Füße zu stellen bezüglich neuer stdint Datentypen (und auch bezüglich thread-safety)
Da ist extrem viel verkorkst. :roll:

zu den Pullups:
nein, wenn das jetzt soweit beim Due funktioniert, brauchst du keine zusätzlichen Pullups. Die eingebauten lassen sich aber auch nicht abschalten beim Due.
Eher wundert es mich, dass das überhaupt auf dem Nano ohne Pullups funktioniert... :roll:

Das mit den 255 scheint für mich ein Byte-Überlauf-Fehler zu sein.
Entweder, quick-and-dirty: mach ne if- Scheife dazwischen, und wenn da 255 steht, setz es auf null.

Eher scheint aber ein lib-Fehler dahinter zu stecken, mich erschreckt auch etwas dein Start-Kommentar

//Arduino 1.0+ only

das klingt nach nem ziemlich alten IDE/API-Bug. Inzwischen haben wir ja 1.8.2 mit etlichen neuen AVR- und ARM-cores.
Ich kann mich erinnern, da gab es mal was mit dem Null-Byte in irgend einer seriellen bzw. TWI-Lib.

Mit welcher IDE arbeitest du?
Im Zweifel würde ich nach neuen Libs suchen und mit wenigstens halbwegs aktuellen IDEs arbeiten (1.5.x oder 1.6.x aufwärts)


PS,
nur zur Warnung:
ist zwar hier nicht mit dabei, aber denk dran:
Bei AVR ist char immer signed char,
beim ARM (Due, Zero) wird char vom Compiler aber als unsigned char übersetzt.
Das liegt an den cores und lässt sich auch nicht ändern.
Also immer explizit signed oder unsigned vor die chars schreiben, wenn du sowohl mit AVRs als auch mit ARMs rumwerkelst.
ich persönlich würde auch "byte" immer durch "uint8_t" ersetzen, das ist krisenfester.

Außer bei chars, die für die Serial-Class verwendet werden:
Hier darf immer nur char stehen, wenn Buchstaben gemeint sind, niemals signed oder unsigned davor, oder gar int8_t oder uint8_t.
Dass liegt wieder an den verkorksten und veralteten und inkompatiblen überladenen Serial-Methoden.
Grausam, aber so ist das eben bei Arduino.
Aber die Entwickler sind zu blöd, das zu kapieren, und ändern nichts.

Klebwax
07.03.2017, 21:00
... Ich habe also nur noch ein einziges Problem, die ausreisser auf 255.

Dezimal 255 sind binär 0b11111111, also alles 1. Der Zustand des I2C Busses, wenn niemand ihn treibt, ist durch die Pullups ebenfalls 1. Die 255 könnte also bedeuten, daß der Slave garnicht antwortet.

Ob der Slave sich angesprochen fühlt, erkennt man am ACK, daß der Slave beim Senden der Adresse liefern muß. Was kommt da bei dir, wenn du einen Ausreißer hast?

MfG Klebwax

DanielSan
07.03.2017, 22:17
Danke für euren Input. Ich arbeite mit 1.8.x die libs halte ich mit der IDE aktuell. Der Code ist sicher schon alt aber für einen ersten Funktionstest ist/war der brauchbar. Interessant ist das der nano die ausreisser nicht hat.

Im Datenblatt habe ich auch was vom Status der Messwerte gesehen. Die einzelnen Register gucke ich mir morgen noch mal genau an. Vielleicht "verschluckt" sich da irgendwas...

Auf die anderen Punkte gehe ich morgen nach der Arbeit ein. :-)

HaWe
07.03.2017, 23:50
@ klebwax:
wie kriegst du die acks bei der Wire Class?

ich kenne da jetzt nur
begin()
requestFrom()
beginTransmission()
endTransmission()
write()
available()
read()
SetClock()
onReceive()
onRequest()

das ist ja das einzige Handwerkszeug, das die Arduino IDE/API zur Verfügung stellt...

Immerhin gäbe es dann auch die Möglichkeit, bei val=255 den Wert einfach zu verwerfen, den alten zu verwenden, und auf den nächsten zu warten.
Der Due ist da vlt einfach etwas zickiger als der AVR.
Hat du den aktuellsten Due board/core Treiber ?

Klebwax
08.03.2017, 01:30
@ klebwax:
wie kriegst du die acks bei der Wire Class?

Keine Ahnung, ich programmiere selbst. Da ACK bzw. NAK das einzige Fehlersignal bei I2C ist (dafür wird sogar pro Byte ein zusätzliches Bit übertragen), sollte man es beachten. Ob die Erfinder der "Wire Class" das getan haben, weiß ich nicht, sollte aber in der Doku stehen.

MfG Klebwax

DanielSan
08.03.2017, 05:52
Guten Morgen,

Ich habe gerade noch das hier gelesen:
"The endTransmission returns an error, that can be checked to see if the device did acknowledge.
The requestFrom returns the number of received bytes, if something is wrong the number is most likely zero."

Bis später.