Forum: Mikrocontroller und Digitale Elektronik Bosch BMA020 Zweierkomplement


von Nils B. (avr_noob)


Angehängte Dateien:

Lesenswert?

Hallo AVR-Gemeinde...

ich habe mir vor kurzem einen Bosch BMA020 Beschleunigungsmesser 
zugelegt und hab diesen auch schon mit I2C ansprechen können.

Das Problem an dem ich jetzt scheiter ist das Verarbeiten des 
Zweierkomplements.

Laut Datenblatt (s. Anhang) gibt der Sensor die Daten der drei Achsen in 
je zwei Registern aus. Für Achse X Register 0x02 für das niederwertige 
Byte, von dem nur Bit 6 und 7 benötigt werden, und Register 0x03 von dem 
das Gesamte Byte benutzt wird(für die anderen Achsen ensprechend andere 
Register)

Soweit so gut...damit ich jetzt mit den Daten was anfangen kann, muss 
ich die beiden Bytes zusammenfügen und zwar nach meinem Verständnis in 
eine 16bit Integer Variable, da der Sensor ja 10bit ausgibt.

Nach mehrfachem googeln bin ich der Meinung das man so machen kann:

gX = (data[2]<<2 | data[1]);

Wobei data[1] das niederwertige und data[2] das höherwertige Byte ist.

Um zu kontrollieren ob überhaupt was passiert, wandel ich den wert mit 
my_itoa um und gebe diesen auf dem LCD aus.

Jetzt meine Fragen bei denenen ich nicht weiter komme bzw. mir unsicher 
bin:

1. Kann man Zweierkomplemente so zusammenfügen??Und wenn ja, sind die
   Schiebewerte richtig??
2. Hab ich das richtig Verstanden, das der Sensor beim Auslesen, die
   Registeradresse automatisch erhöht??
3. Wie müsste ich die Integer Variable umrechnen, damit irgendwann G im
   Display steht??

Achso noch ein paar Infos:
- Prozessor ist Atmega32
- I2C von Peter Fleury
- Programieren mit AVR Studio
- Kommentierte main.c im Anhang.

Ich möchte keinen Fertigen Code haben, sondern stubser in die richtige 
Richtung um das ganze zu verstehen.

Gruß und vielen Dank vorweg

Nils

von Nils B. (avr_noob)


Lesenswert?

Achso ich hab vergessen den alten Versuchs - Schrott aus der Main zu 
löschen...einfach nicht Beachten....

von H.Joachim S. (crazyhorse)


Lesenswert?

> gX = (data[2]<<2 | data[1]);

eher nicht

gX = ((int)data[2]<<2 | data[1]>>6);

von Nils B. (avr_noob)


Lesenswert?

danke für die schnelle antwort, also muss ich das so verstehen,
da ich das data[2] byte zwei stellen nach links schiebe,damit ich platz 
für die beiden anderen bits mache, und data[1] sechs stellen nach rechts 
schiebe, damit ich die restlichen 6 "rausschmeisse"??

und wozu ist das (int) vor dem data[2] gut??

gruß nils

von Markus O. (pipimaxi)


Lesenswert?

du hast eine temporäre 16bit variable mit dem cast (int), verODERst das 
mit MSB, schiebst es um 2 nach links, damit die unteren 2 frei sind und 
verODERst das mit LSB, sechs nach rechts, damit die an die ersten 2 
stellen kommen

dummerweise wirst du somit aber keine signed variable erhalten

das vorzeichenbit ist ja immer das most significant bit, also das ganz 
linke.
Es genügt nicht das MSB von deinem wert um 6 nach links zu shiften, so 
würde das 2er Komplement nicht funktionieren

von Nils B. (avr_noob)


Lesenswert?

ok das hab ich soweit verstanden...aber was muss ich dafür nun tun, 
damit ich eine signed variable bekommen und ich auch negative Werte 
herausbekomme und dann die in Beschleunigung umwandeln kann??

von Markus O. (pipimaxi)


Lesenswert?

auf die Frage hab ich gewartet ^^
nu denn, du hast ja 10 bit auflösung.
da du den Acc nicht anders konfiguriert hast, geh ich ma von einem Range 
+-2g aus.
Demzufolge stellen ein diskreter Wert etwa 3,9mg dar.
Wenn du auf die unteren 2 Bit verzichten, also mit einer Auflösung von 
11,7mg leben kannst, schmeiß LSB weg und schieb das MSB direkt in eine 
signed 8bit varibale und werd glücklich.
Andernfalls könnte man es so machen wie bisher, also das verODERn und 
bitshiften der 2 Werte mit der int variable, das obere bit von MSB 
maskieren..müsste demzufolge an der Stelle 9 sein (wenn man 0 mitzählt), 
und anhand dessen ne kurze bedienung ob der "restliche" 9bit wert nun 
positiv oder negativ ist (...für die weniger schlauen: *(-1) wenn 
gesetzt)

gruß

von Nils B. (avr_noob)


Lesenswert?

die auflösung ist mir erstmal egal...soll erstmal laufen.

also ich hab jetzt data[2] also das MSB(LSB muss ausgelesen werden) 
folgendermaßen:

gX = data[2];

zugewiesen.wobei gX ein signed char ist. dementsprechend sollte ich ja 
werte von -127 bis +127 bekommen, richtig??

Wo ich jetzt noch ein problem mir hab, ist die ausgabe auf dem LCD.

ich wandel mit

void my_itoa(int32_t zahl, char* string)

die ich hier im forum gefunden habe den wert in einen string um und gebe 
diesen dann aus.

aber da gX ja ein char ist und my itoa ja ein int32_t gibts da bestimmt 
wieder ein problem oder??

von Markus O. (pipimaxi)


Lesenswert?

sollte eigentlich kein Problem sein. Der Compiler könnte höchstens eine 
Warning ausgeben, dass der übergabeparameter von einem anderen typ ist, 
aber entweder machst du dann im funktionsaufruf einen typecast, also 
"my_itoa((int32_t)gX,string);" oder du deklarierst gX direkt schon als 
int32_t, was allerdings 3 verschenkte Bytes wären.

von Nils B. (avr_noob)


Lesenswert?

soo ich habe mir jetzt mal die z - achse betrachtet und die ausgeben 
lassen.

die werte machen sogar sinn, da der wert umgerechnet bei 1G liegt, nur 
ich bekomme keine negativen werte, wenn ich den chip umdrehe, ist der 
wert identisch, allerdings müsste er ja negativ sein...hat das wieder 
was mit dem msb und der auswertung zu tun??

von Karl H. (kbuchegg)


Lesenswert?

Nils B. schrieb:
> soo ich habe mir jetzt mal die z - achse betrachtet und die ausgeben
> lassen.
>
> die werte machen sogar sinn, da der wert umgerechnet bei 1G liegt, nur
> ich bekomme keine negativen werte, wenn ich den chip umdrehe, ist der
> wert identisch, allerdings müsste er ja negativ sein...hat das wieder
> was mit dem msb und der auswertung zu tun??

lass dir das ganze binär ausgeben, also auf Bitebene.
Dazu die original Registerinhalte auf Bitebene. Der Vergleich sollte für 
dich Licht in die Sache bringen.

(Irgendwann musst du ja mal lernen, wie man Bits aus mehreren Registern 
zusammensetzt)

von Markus O. (pipimaxi)


Lesenswert?

Wie sieht denn dein Programm bis jetzt aus? Also wie wertest du das MSB 
von der Z-Achse aus?

von Nils B. (avr_noob)


Lesenswert?

Eigentlich nicht viel anders als das für die x-Achse, ich habe nur mal 
den Wert aus Register 0x06 genommen und den auf dem Display dargestellt.

Was schon mal besser ist, dass es keine Werte mehr von 0...255 gibt, 
sondern max. bis 120, mehr G kann ich nicht erzeugen :). und wenn ich 
nach

Registerwert x 2 / 127 umstelle, kommt das mit dem Wert 60 den ich aus 
dem Register bekomme auch hin mit 1G, nur mit dem Vorzeichen hapert das 
noch.

von Markus O. (pipimaxi)


Lesenswert?

darf ich mal die funktion my_itoa sehen?

von Nils B. (avr_noob)


Lesenswert?

void my_itoa(int32_t zahl, char *string) {
  uint8_t i;

  string[11]='\0';                  // String Terminator
  if( zahl < 0 ) {                  // ist die Zahl negativ?
    string[0] = '-';
    zahl = -zahl;
  }
  else string[0] = ' ';             // Zahl ist positiv

  for(i=10; i>=1; i--) {
    string[i]=(zahl % 10) +'0';     // Modulo rechnen, dann den 
ASCII-Code von '0' addieren
    zahl /= 10;
  }
}

void lcd_numout(char *string, uint8_t start, uint8_t komma, uint8_t 
frac) {

  uint8_t i;            // Zähler
  uint8_t flag=0;       // Merker für führende Nullen

  // Vorzeichen ausgeben
  if (string[0]=='-') lcd_data('-'); //else lcd_data(' ');

  // Vorkommastellen ohne führende Nullen ausgeben
  for(i=start; i<komma; i++) {
    if (flag==1 || string[i]!='0') {
      lcd_data(string[i]);
      flag = 1;
    }
    else lcd_data(' ');         // Leerzeichen
  }

  lcd_data(',');                // Komma ausgeben

  // Nachkommastellen ausgeben
  for(; i<(komma+frac-1); i++) lcd_data(string[i]);

}

Dazu noch die Funktion die alles auf dem LCD ausgibt. Beide aus dem 
Forum hier kopiert. Bei Analogen Werten die ich einlese und ausgeb is 
alles super.

von Markus O. (pipimaxi)


Lesenswert?

ok, die routinen scheinen ok zu sein, dann kanns nur am hauptprogramm 
liegen ;) also her damit :D

von Karl H. (kbuchegg)


Lesenswert?

Kannst du mal folgendes Programm ausprobieren
1
#include <avr/io.h>
2
#include <stdio.h>
3
#include <lcd-routines.h>
4
#include <util\delay.h>
5
#include "i2cmaster.h"
6
7
8
#define bma_read  0x71            // Lesezugriff BMA
9
#define bma_write 0x70            // Schreibzugriff BMA
10
11
int main()
12
{
13
  uint8_t data[7];
14
  int     gx;
15
16
  i2c_init();                  // I2C initialisieren
17
  lcd_init();                  // LCD Anwerfen
18
19
20
  while(1)
21
  {
22
    i2c_start_wait(bma_write);       // BMA schreibend Adressieren    
23
    i2c_write (0x02);                // Register 0x02 wählen
24
    i2c_stop();                      // Bus stop
25
 
26
    i2c_start_wait(bma_read);        // BMA lesend Adressiern
27
    for (unsigned char i=1; i<6; i++)
28
    {
29
      data[i] = i2c_readAck();
30
    }
31
    data[6] = i2c_readNak();
32
33
// Achtung: Alle Register sind gegenüber dem Datenblatt um 1 verschoben!
34
35
    data[1] = 0x00;
36
    data[2] = 0x7F;
37
38
    gx = (((int8_t)data[2]) << 2 ) | ( data[1] >> 6 );
39
  
40
    lcd_clear();                
41
    lcd_setcursor(2,1);
42
43
    {
44
      char buffer[40];
45
      
46
      sprintf( buffer, "2:0x%02x  3:0x%02x gx:0x%04x %d", data[1], data[2], gx, gx );
47
      lcd_puts( buffer );
48
    }
49
50
    _delay_ms(50);                // 50 ms Pause
51
  }
52
}


Es gibt dir die gelesenen Registerwerte, den Int der sich aus der 
Zusammensetzung ergibt und dessen Wert in Dezimal aus. Ich hab zwar die 
Hardware nicht da, aber im Simulator haben die simulierten Werte gut 
ausgesehen.

Falls du keine lcd_puts hast, hier ist eine
1
void lcd_puts( const char* s )
2
{
3
  while( *s )
4
    lcd_data( *s++ );
5
}

von Nils B. (avr_noob)


Angehängte Dateien:

Lesenswert?

So hier mal das akutell Program für Achse Z.
Auf dem Display wird ,0060 bzw. ein entsprechender Wert je nach Lage.

Für die anderen Achsen funktioniert das genau so.

Vielen Dank für die geduldige Hilfe!!!!

von Markus O. (pipimaxi)


Lesenswert?

nuja, da ich keinen BMA020 hier habe, kann ich das so direkt jetz nich 
ausprobieren ;)

es wird ein komma gleich schon mit ausgegeben? aber eigentlich hat der 
wert doch keine nachkommastellen nor?

von Nils B. (avr_noob)


Lesenswert?

Hallo Karl Heinz,

ich hab dein Programm mal Probiert und hab jetzt folgendes auf dem 
LCD(musste es ein bischen zurechtrücken, da ich nur 20 Zeichen habe)

0x00 0x7f 0x01FC 508

gruß Nils

von Nils B. (avr_noob)


Lesenswert?

Das mit dem Komma ist schon merkwürdig...ich glaube das hat was mit der 
Verarbeitung durch my_itoa zu tun.

von Markus O. (pipimaxi)


Lesenswert?

dein string ist auch kein string sondern ein einfaches Zeichen
ein String wäre ein char-array, also mit [groesse] hinterher ;)

von Nils B. (avr_noob)


Lesenswert?

So...nachdem ich das Program von Karl Heinz Probiert habe, hab ich 
wieder die dynamischen Werte aus dem Chip genommen, und siehe da es gibt 
negative und positive Zahlen :)

das einzige was mich noch wundert, das die Werte im negativen Bereich 
bis -20 gehen und im positiven bis 265. obwohl ich den chip nur halte 
und nicht aktiv beschleunige.

von Karl H. (kbuchegg)


Lesenswert?

Nils B. schrieb:
> So...nachdem ich das Program von Karl Heinz Probiert habe, hab ich
> wieder die dynamischen Werte aus dem Chip genommen, und siehe da es gibt
> negative und positive Zahlen :)

Ach. Ich hab vergessen meine Hardgecodeten Werte ruaszuwerfen, mit denen 
ich die Umrechnung kontrolliert habe :-)

>
> das einzige was mich noch wundert, das die Werte im negativen Bereich
> bis -20 gehen und im positiven bis 265. obwohl ich den chip nur halte
> und nicht aktiv beschleunige.

Na ja.
Sieh dir die Register an, die vom Chip ausgelesen wurden. Dann weisst 
du, ob die Werte schon vom Chip so kommen, oder ob zwischendruch was 
passiert.

Genau aus dem Grund lasse ich mir ja die Urwerte, so wie ich sie das 
erste mal in die Finger kriege ausgeben: Damit ich weiß, ob ich bei mir 
einen Fehler habe, oder ob mein Programm grundsätzlich stimmt und die 
ausgelesenen Werte falsch sind.
Du hast ein LCD! Benutze es um dir alle für die Fehlersuche relevanten 
Faktoren ausgeben zu lassen.

von Nils B. (avr_noob)


Lesenswert?

Ich werd da gleich mal gucken.

Eine Frage hab ich noch, was meinst du mit

"Achtung: Alle Register sind gegenüber dem Datenblatt um 1 verschoben!"

und wieso hast du das gemacht??

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Wenn der Fehler im Wertebereich noch da ist?? Dann tippe ich darauf, das 
der Chip die absolute Beschleunigung anzeigt. Da geht die normale 
Gravitation mit ein.

von Karl H. (kbuchegg)


Lesenswert?

Nils B. schrieb:
> Ich werd da gleich mal gucken.
>
> Eine Frage hab ich noch, was meinst du mit
>
> "Achtung: Alle Register sind gegenüber dem Datenblatt um 1 verschoben!"
>
> und wieso hast du das gemacht??

Das hab nicht ich gemacht. Das hast du so gemacht.

Laut Datenblatt stehen die für dich interessanten Werte in den 
Chip-Registern 0x02 und 0x03. Durch die Art und Weise, wie du dein 
Auslesen organisiert hast, landet das IC-Register 0x02 bei dir in 
data[1] und das IC-Register 0x03 in data[2].  Und weil ich da 
drübergestolpert bin, hab ich mir da einen Kommentar dazugemacht. Denn 
wenn wer eine for Schleife macht

   for( i = 1; i <= .....

die anstelle von mit 0 mit 1 anfängt und anstelle von < ein <= 
beinhaltet, dann klingeln bei mir die Alarmglocken und ich seh mir 
genauer an, was da gemacht wurde.

von Werner B. (werner-b)


Angehängte Dateien:

Lesenswert?

Ein kommentierter Änderungsvorschlag.

Werner

von Werner B. (werner-b)


Lesenswert?

Habe ein paar Fehler gefunden.
1
i2c_write (0x02);
2
...
3
gx = (int16_t)(data[0]) >> 6;
4
gy = (int16_t)(data[1]) >> 6;
5
gz = (int16_t)(data[2]) >> 6;
soll heißen
1
i2c_write(addr);
2
...
3
gx = ((int16_t)data[0]) >> 6;
4
gy = ((int16_t)data[1]) >> 6;
5
gz = ((int16_t)data[2]) >> 6;

von Nils B (Gast)


Lesenswert?

> das einzige was mich noch wundert, das die Werte im negativen Bereich
> bis -20 gehen und im positiven bis 265. obwohl ich den chip nur halte
> und nicht aktiv beschleunige.

ich hab das mysterium um den Wertebereich gelöst...ist mir ja schon fast 
peinlich die Auflösung zu geben...das Display war zu Ende,dadurch ist 
die dritte Stelle durch das "-" nach Hinten weg :D

Jetzt gehts von -200 -> +265

Den rest bekomme ich auch in den griff...hoffe ich...

Aber erstmal vielen Dank für die Hilfe und den Code!!!

Gruß Nils

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Ich habe gerade das Datenblatt überflogen. Da finde ich aber nix von 
Kompensation der Schwerkraft! Macht das Teil beim Einschalten eine 
Kompensation aufgrund der momentanen Ruhelage?

Es gab die Sache hier wohl schonmal bei einem AD-Chip.

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.