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
Achso ich hab vergessen den alten Versuchs - Schrott aus der Main zu löschen...einfach nicht Beachten....
> gX = (data[2]<<2 | data[1]);
eher nicht
gX = ((int)data[2]<<2 | data[1]>>6);
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
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
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??
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ß
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??
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.
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??
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)
Wie sieht denn dein Programm bis jetzt aus? Also wie wertest du das MSB von der Z-Achse aus?
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.
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.
ok, die routinen scheinen ok zu sein, dann kanns nur am hauptprogramm liegen ;) also her damit :D
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 | }
|
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!!!!
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?
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
Das mit dem Komma ist schon merkwürdig...ich glaube das hat was mit der Verarbeitung durch my_itoa zu tun.
dein string ist auch kein string sondern ein einfaches Zeichen ein String wäre ein char-array, also mit [groesse] hinterher ;)
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.
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.
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??
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.
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.
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; |
> 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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.