Forum: Mikrocontroller und Digitale Elektronik L3G4200D Register auslesen


von Florian K. (f-kae)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte das Gyroscope (L3G4200D - ich besitze das Breakout board von 
Pololu) gerne auslesen und die Daten dann auf einem Display ausgeben.

Ich nutze einen ATMEGA16 und programmiere in C / C++.
Verbinden möchte ich den Sensor per SPI.

Leider gelingt es mir nicht geeignete Tutorials oder Einsteigertipps 
hierzu zufinden. Auf der Pololu-Seite gibt es nur Arduino-Language 
Sample-Code. Arduino-Language möchte ich aber nicht nutzen auch wenn 
diese c-ähnlich oder was auch immer ist.

Meine konkrete Frage ist also wie ich programmiertechnisch auf die 
verschiedenen Register des Sensors zugreifen kann bzw. diese auslese und 
in Variablen abspeicher.

Simpelster Beispielcode in c wäre Klasse oder irgendwelche Hinweise wie 
ich mich darüber schlau lesen kann.

EDIT: Die grundsätzliche Kommunikation per SPI ist mir bekannt und hat 
auch schon mit einem anderen µC funktioniert, allerdings will ich nun ja 
verschiedene Register des Sensors auslesen bzw verändern, nur weiß ich 
nicht wie das funtioniert.
Anhang meiner bisherigen SPI-Kommunikation siehe SPI.c

von Florian K. (f-kae)


Lesenswert?

Funktioniert Die Kommunikation mit dem Sensor vielleicht so, dass ich 
wenn ich das Ctrl_Reg1 auslesen will, dafür folgendes vom Master senden 
muss:

1      0           100000
Read | no incre. | Ctrl_Reg1

Daraufhin sendet mir der Sensor das Register und ich kann es aus dem 
SPDR auslesen?

Ich kenne mich mit SPI leider noch so gutt wie garnicht aus und möchte 
meinen Sensor nicht gleich mit dem ersten "Befehl" zerstören. Ich wäre 
sehr dankbar für eine Reaktion!

von Dennis (Gast)


Lesenswert?

Florian K. schrieb:
> Ich kenne mich mit SPI leider noch so gutt wie garnicht aus

Also darfst du dich SELBST einarbeiten. Dauer: keine 15 Minuten. Im 
Datenblatt des Atmega ist ALLES piekfein beschrieben.

Florian K. schrieb:
> möchte
> meinen Sensor nicht gleich mit dem ersten "Befehl" zerstören

Also wenn du es schaffst, irrtümlich durch einen "falschen" Befehl den 
Sensor zu schrotten, dann kommst du in den "Hall of Flame" und ich kaufe 
dir einen neuen Sensor :-)

Jetzt aber im Ernst: nur Mut! Programmieren, testen, debuggen... so wie 
es auch die Großen machen.

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

Das ist recht einfach. Ich habe noch Code für den L3G4200D auf der 
Platte liegen (geschrieben für einen Atmega64 mit soft SPI). Den müsst 
ich aber erstmal raussuchen.

von Florian K. (f-kae)


Lesenswert?

@Dennis: Danke für deine Motivation, leider schaffe ich es dennoch nicht 
alleine aus den Datenblättern heraus schlau zu werden, ich möchte auch 
garnicht eine fertige Lösung für eine konkrete Aufgabenstellung, mir 
würde schon reichen das WHO_AM_I Register auszulesen und in einer 
Variable abzuspeichern.

Mein Versuch sieht so aus und gibt mir leider nur 255 zurück, was dann 
wohl für 0b11111111 steht und nicht meinem WHO_AM_I Register entspräche! 
:(
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "MrLCD.h"
4
#include <stdlib.h>
5
6
void master_init (void);
7
void master_transmit (unsigned char data);
8
9
int WHO_AM_I = 0;
10
char WHO_AM_I_dez[8];
11
12
int main (void)
13
{
14
15
  master_init ();
16
  sei ();
17
  master_transmit(0b10001111);
18
  WHO_AM_I = SPDR;
19
20
  InitializeMrLCD();
21
  
22
  
23
  itoa(WHO_AM_I, WHO_AM_I_dez, 10);
24
25
  GotoMrLCDsLocation(1,1);
26
  Send_A_String(WHO_AM_I_dez);
27
  
28
  while(1)
29
  {  
30
31
  }
32
}
33
34
void master_init (void)
35
{
36
  DDRB |= 1 << (PB4) | 1 << (PB5) | 1 << (PB7);
37
  DDRB &= ~(1 << PB6);
38
  PORTB |= 1 << (PB4) | 1 << (PB7) | 1 << (PB0);
39
  SPCR |= 1 << (SPE) | 1 << (MSTR) | 1 << (SPR1);
40
  SPSR |= 1 << (SPI2X);
41
}
42
43
void master_transmit (unsigned char data)
44
{
45
  PORTB &= ~(1 << PB0);
46
  SPDR = data;
47
  while (!(SPSR & (1<<SPIF)));
48
  PORTB |= 1 << (PB0);
49
}

@Jo:  Ja ich bin sehr an Code interessiert :)

von Florian K. (f-kae)


Lesenswert?

Kann mir nicht vielleicht jemand weiterhelfen?

Wie gesagt mir reichen wirklich nur die nötigen Zeilen um das WHO_AM_I 
Register auf meinem AVR zu empfangen und abzuspeichern.

von Holger E. (holgere)


Angehängte Dateien:

Lesenswert?

Hallo Florian,
ich habe mal hier meine BASCOM-Dateien angehäng.

Was ich allerdings benutzt habe war das MinIMU-9 von Pololu
an ein PollinBoard mit ATmega88 drauf. Anschlüsse waren angelötet
mit 10kOhm Pullup-Widerständen gegen +5V.
Auf dem Board sind 3-Achsen Gyro L3G4200D und 3-Achsen Kompass / 
Beschleunigungssensor LSM303DLM.
Sollte mal für ein Mikrokopter sein, habe aber schon länger aus 
Zeitmangel
nichts mehr damit gemacht.

Ich hoffe du kannst was damit anfangen auch wenn es nur BASIC ist.

Und für alle anderen MitLeser:
-> Bitte verzeiht mein schlechten Programmierstil!
   (Aber es hat funktioniert!!)

Holger

von Florian K. (f-kae)


Lesenswert?

@Holger: Vielen Dank für deinen Beitrag, leider suche ich C-Code und SPI 
als Kommunikation. Leider kann ich daher mit deinen Dateien nicht viel 
anfangen.

Ich verzweifele bald :/

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

@ Florian,

sorry habs gestern vergessen. Wenn du dich noch bis heute Abend gedulden 
kannst - hab die Daten leider nicht auf meinem Arbeitsrechner.

Werde den Code heute abend posten - versprochen ;)

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

doppelt

von Jo D. (Firma: Jo) (discovery)


Angehängte Dateien:

Lesenswert?

So jetzt aber. Die Datei einfach einbinden und dann in der Main 
folgendes aufrufen um die Sensorwerte auszulesen:
1
//Buffer für High und Low Bytes
2
volatile uint8_t low = 0;
3
volatile uint8_t high = 0;
4
5
//Gyro Output
6
volatile int l3g_erg_x = 0;
7
volatile int l3g_erg_y = 0;
8
volatile int l3g_erg_z = 0;
9
10
11
// Pins für SPI konfigurieren
12
init_spi_l3g();
13
14
// GYRO aktivieren und konfigurieren
15
config_l3g();
16
17
////////////////////// READ GYRO DATA IN MULTIPLE READ MODUS ////////////////////
18
    send_byte_l3g(232); 
19
20
    low = read_byte_l3g();
21
    high = read_byte_l3g();
22
    l3g_erg_x = (int)(low + (high << 8));
23
24
    low = read_byte_l3g();
25
    high = read_byte_l3g();
26
    l3g_erg_y = (int)(low + (high << 8));
27
28
    low = read_byte_l3g();
29
    high = read_byte_l3g();
30
    l3g_erg_z = (int)(low + (high << 8));
31
32
    complete_l3g();
33
    /////////////////////////////////////////////////////////////////////////////////

von Florian K. (f-kae)


Lesenswert?

Vielen Dank schon einmal ich werde es mir sofort anschauen! :)
Was genau bedeutet diese Zeile Code?
1
if ( byte & (1<<(7)) )

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

Florian K. schrieb:
> Vielen Dank schon einmal ich werde es mir sofort anschauen! :)
> Was genau bedeutet diese Zeile Code?
>
1
> if ( byte & (1<<(7)) )
2
>

Ich schau mir das Bit Nummer 7 in der Variablen Byte an und setze 
demgemäß meinen Ausgangspin auf 1 oder 0 je nachdem ob das Bit 1 ist 
oder 0 ist. Das Ganze wird 8 mal durchgeführt bis das ganze Byze 
rausgesendet wurde. Der Code ist deshalb etwas lang geraten. Prinzipiell 
kann man den ganzen Spaß auch in einer For-Schleife machen, dann spart 
man sich eine Menge Zeilen.

PS:

Deine Ausgangspins musst du natürlich noch entsprechend an dein Design 
anpassen. Das vergaß ich oben zu erwähnen.

von Florian K. (f-kae)


Lesenswert?

Ok ich hab es verstanden, ich nahm naiver weise an, das der AVR am 
SPI-BUS zumindest die Chip-Select Leitung selbst steuert und ich direkt 
eine 8-BIT Zahl senden kann.

Naja aber vielen vielen dank! Du hast mir damit sehr geholfen, heute 
habe ich keinen Kopf mehr das anzupassen aber ich werde morgen versuchen 
die Register auszulesen und teile dann nochmal mein Ergebnis mit :)
Genau danach habe ich nun Zwei Tage gesucht :D

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

> Ok ich hab es verstanden, ich nahm naiver weise an, das der AVR am
> SPI-BUS zumindest die Chip-Select Leitung selbst steuert und ich direkt
> eine 8-BIT Zahl senden kann.

Zur Harware SPI des AVR kann ich nichts sagen. Der Code von oben ist 
Soft-SPI - d.h an allen Portpins verwendbar.

Auf jeden Fall viel Erfolg.

von Florian K. (f-kae)


Lesenswert?

OK das erklärt vermutlich so einiges :/

Dann bin ich weiterhin auf der Suche nach Hilfe!

von Eumel (Gast)


Lesenswert?

Florian K. schrieb:
> SPI-BUS zumindest die Chip-Select Leitung selbst steuert und ich direkt
> eine 8-BIT Zahl senden kann.

Wieso sollte er das tun? Wäre doch vollkommen sinnlos.

von Eumel (Gast)


Lesenswert?

Aber erzähl doch mal welche Stelle im Datenblatt des Atmega16 dir 
Probleme bereitet?

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

Florian K. schrieb:
> OK das erklärt vermutlich so einiges :/
>
> Dann bin ich weiterhin auf der Suche nach Hilfe!

Bist du denn auf die Hardware SPI angewiesen? Die brauchst du doch nur, 
wenn du zeitkritische Anwendungen machst.

von Florian K. (f-kae)


Lesenswert?

@Eumel:
Ich meine überall zu lesen das folgende Zeilen ausreichen um ein Byte 
auzulesen:
1
char ReadByteSPI(char addr)
2
{
3
  SPDR = addr;      //Load byte to Data register
4
  while(!(SPSR & (1<<SPIF)));   // Wait for transmission complete 
5
  addr=SPDR;
6
  return addr;
7
}

Wobei addr zum Beispiel = 0b10001111 ist um das Register WHO_AM_I 
auszulesen.
Und als Rückgabewert dann 0b11010011 ausgeben sollte

Ich erhalte allerdings immer als Rückgabewert 255.

@ Jo:
Ja ich möchte wirklich gerne den SPI-BUS verwenden!

von Eumel (Gast)


Lesenswert?

Stichwort: Dumybyte senden.

von Eumel (Gast)


Lesenswert?

Um es nochmal etwas deutlicher zu sagen. Du hast nocht nicht ganz 
verstanden wie SPI funktioniert. Deine Funktion ReadByteSPI sendet ein 
Byte an deinen Sensor und wartet, dass der Controller mit Senden fertig 
ist. Allerdings musst du danach noch ein Byte nachschieben. Dein Sensor 
verhält sich nämlich so wie jeder SPI slave. Die machen NICHTS von 
alleine.
Also: du schickst 0b10001111 an den Sensor. Der weiß jetzt: "Aha, er 
will mein WHO_AM_I Register auslesen" Der Sensor legt dann das 
entsprechende Byte in sein Schieberegister. Allerdings kommt das da von 
alleine nicht raus, der Master muss es rausschieben. Also sendest du 
noch ein Dummybyte hinter. Der Wert dieses Bytes ist EGAL, es dient nur 
dazu die von dir gewollten Daten aus dem Sensor rauszuschieben.
Richtig wäre also:

char ReadByteSPI(char addr)
{
  SPDR = addr;      //Load byte to Data register
  while(!(SPSR & (1<<SPIF)));   // Wait for transmission complete
 SPDR = addr;      //Load byte to Data register, wir jetzt als Dummybyte 
benutzt!!!!!
  while(!(SPSR & (1<<SPIF)));   // Wait for transmission complete
  addr=SPDR;
  return addr;
}

das sollte funktionieren.

von Eumel (Gast)


Lesenswert?

Ach mist, vielleicht zwischendurch noch cs toggeln. Schau mal ins 
Datenblatt deines Sensors.

von Florian K. (f-kae)


Lesenswert?

Vielen Vielen Dank!!!!!
Nun klappt es endlich!
Damit zauberst du mir doch glatt ein lächeln aufs Gesicht.

Also wie gesagt vielen Dank!!! :D

von Eumel (Gast)


Lesenswert?

Schön, dass es klappt. Wichtig ist auch, dass du verstanden hast wieso!

Schau dir z.b. das hier: 
http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf
nochmals an.

von Hans C. (hans55)


Lesenswert?

Holger E. schrieb:
> Hallo Florian,
> ich habe mal hier meine BASCOM-Dateien angehäng.
> ....
>
> Ich hoffe du kannst was damit anfangen auch wenn es nur BASIC ist.
>
> Und für alle anderen MitLeser:
> -> Bitte verzeiht mein schlechten Programmierstil!
>    (Aber es hat funktioniert!!)
>
> Holger

Holger, genau dieses Beisp. für den minIMU-9 mit BASCOM hab ich gesucht!
Es funktioniert wunderbar!
DANKE!!!!!

Gruß
  Hans

von Alex (Gast)


Lesenswert?

Hi,

ich bin auch grad daran, den L3G4200D auszulesen. Mache das aber über 
I2C...
Hab alles korrekt angeschlossen und die Kommunikation gelingt auch. Also 
Das WhoAmI bekomm ich korrekt zurück.

Wenn ich aber nun die 3 Achsen auslesen möchte, erhalte ich nicht die 
erwarteten Wert zurück.

Hier zunächst mal der Code:
1
//*** IC starten ***
2
    L3G4200D_PowerUP(250);
3
4
//*** Achsen auslesen ***
5
    L3G4200D_GetAxis();
6
7
    //*** Temperatur auslesen ***
8
    L3G4200D_GetTemp();
9
10
//Konfig:
11
short L3G4200D_x, L3G4200D_y, L3G4200D_z;
12
short L3G4200D_Temp;
13
14
void L3G4200D_PowerUP (int scale) {
15
    I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL1, 0b00001111);      //PowerUP, Enable X, Y, Z
16
    I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL2, 0b00000000);      //HPF disable
17
    I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL3, 0b00001000);      //Date Ready
18
    switch (scale) {
19
        case 250:
20
            I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL4, 0b00000000);
21
            break;
22
23
        case 500:
24
            I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL4, 0b00010000);
25
            break;
26
27
        default:
28
            I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL4, 0b00110000);
29
            break;
30
        }
31
32
    I2C_WriteToDevice(i2c_fd, L3G4200D_REG_CTRL5, 0b00000000);
33
    usleep(1500);
34
35
}
36
37
//Daten holen:
38
void L3G4200D_GetAxis (void) {
39
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_X_L, 1, i2c_buffer);
40
    short L3G4200D_x_l = i2c_buffer[0];
41
    //printf("0: %d\n", i2c_buffer[0]);
42
43
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_X_H, 1, i2c_buffer);
44
    short L3G4200D_x_h = i2c_buffer[0];
45
    //printf("0: %d\n", i2c_buffer[0]);
46
47
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_Y_L, 1, i2c_buffer);
48
    short L3G4200D_y_l = i2c_buffer[0];
49
    //printf("0: %d\n", i2c_buffer[0]);
50
51
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_Y_H, 1, i2c_buffer);
52
    short L3G4200D_y_h = i2c_buffer[0];
53
    //printf("0: %d\n", i2c_buffer[0]);
54
55
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_Z_L, 1, i2c_buffer);
56
    short L3G4200D_z_l = i2c_buffer[0];
57
    //printf("0: %d\n", i2c_buffer[0]);
58
59
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_Z_H, 1, i2c_buffer);
60
    short L3G4200D_z_h = i2c_buffer[0];
61
    //printf("0: %d\n", i2c_buffer[0]);
62
63
    L3G4200D_x = (L3G4200D_x_h << 8) | L3G4200D_x_l;
64
    L3G4200D_y = (L3G4200D_y_h << 8) | L3G4200D_y_l;
65
    L3G4200D_z = (L3G4200D_z_h << 8) | L3G4200D_z_l;
66
67
    printf("X: %d\nY: %d\nZ: %d\n", L3G4200D_x, L3G4200D_y, L3G4200D_z);
68
}
69
70
//Temperatur holen
71
void L3G4200D_GetTemp (void) {
72
    I2C_ReadFromDevice(i2c_fd, L3G4200D_REG_OUT_TEMP, 1, i2c_buffer);
73
    L3G4200D_Temp = i2c_buffer[0];
74
    printf("T: %d\n", L3G4200D_Temp);
75
}

Als Ergenis bekomme ich dann:
X: -5
Y: -104
Z: 31
T: 17

X: -25
Y: -101
Z: 63
T: 17

X: 5
Y: -110
Z: 31
T: 17

X: 5
Y: -78
Z: 31
T: 17

X: -37
Y: -141
Z: 15
T: 17

X: -15
Y: -53
Z: 42
T: 17

X: -20
Y: -77
Z: 43
T: 17

X: -14
Y: -82
Z: 41
T: 17

X: 28
Y: -60
Z: 21
T: 16

X: -20
Y: -83
Z: 50
T: 17

X: 10
Y: -95
Z: 31
T: 17

Wie man sieht schwanken die Achsen ziemlich stark, was nicht sein kann, 
da der Sensor nur auf dem Tisch liegt. Die Temperatur ist zwar relativ 
stabil, jedoch viel zu niedrig...

Hat hierzu jemand eine Idee, was das Problem sein könnte?
In der Konfig ein Register vergessen oder ähnliches?

von Alex (Gast)


Lesenswert?

Hallo nochmal,

wie ich mittlerweile herausgefunden habe ist der Temperatursensor nicht 
für die Umgebungstemperatur, sondern für die IC-Temperatur gedacht.

Die Achsenwerte scheinen doch zu stimmen, da der Gyro die Werte in °/s 
ausgibt. Und wenn der Sensor nur liegt, bleiben die Werte natürlich 
gleich, bis auf die üblichen Schwankungen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.