Mega 2560 + BMA020 + I2C

Seid gegrüßt,

nachdem ich seit längerem nun schon mit der MultiWii rumspiele habe ich mir jetzt erstmal ein nacktes Arduino Mega 2560 geholt.
Nun hatte ich hier auch noch einen BMA020 von ELV rumfliegen.

CSB + UIN + Pullup = VCC
SDO + GND = GND
SCK = I2C SCL
SDI = I2C SDA

So, habe mir dieses dann erstmal mit Breadboardkabeln versehen.

VCC geht auf 3.3V am Arduino Mega.

Als Programmcode nutze ich folgenden:

 #include <Wire.h>

#define BMA020_ADDR 0x38

byte data[6];          //Byte field for incoming i2c data
int bma020_x;          //Current G on X direction
int bma020_y;          //Current G on Y direction
int bma020_z;          //Current G on Z direction
boolean bma020_x_new;  //True = Last X value was new
boolean bma020_y_new;  //True = Last Y value was new
boolean bma020_z_new;  //True = Last Z value was new


void setup() {
    Wire.begin();
    Serial.begin(19200);
    Serial.println("Init start...");
    initBMA020();
    Serial.println("Init done!");
}


void loop() {
    //Read new Data from BMA
    readBMA020(BMA020_ADDR);
    
    //Show output
    Serial.print("X: ");
    Serial.print(bma020_x);
    Serial.print("   Y: ");
    Serial.print(bma020_y);
    Serial.print("   Z: ");
    Serial.print(bma020_z);
    Serial.print("   Betrag: ");
    Serial.print(norm3dim(bma020_x, bma020_y, bma020_z));
    Serial.println();
    delay(200);
}


void initBMA020() {
  byte control;  
  
  //Set SPI 4 Wire mode
  writeRegister(0x38, 0x15, 0x80);
  
  //Range Settings
  // Range to 2g --> 255 in datafield is ~1g 
  // Range to 4g --> 127 in datafield is ~1g 
  // Range to 8g --> 63  in datafield is ~1g 
  
  //Set Range + Bandwidth
  control = readRegister(0x38,0x14);
  control = control & 0xE0;        // save bits 7,6,5
  control = control | (0x02 << 3); // Range to 8g (10)  
  control = control | 0x00;        // Bandwidth 25 Hz 000
  writeRegister(0x38, 0x14, control);
  
  Serial.println(readRegister(0x38,0x15));
  delay(50);
  Serial.println(readRegister(0x38,0x14));
  delay(50);
  
}

byte readRegister(byte addr, byte reg){
  byte val = 0x00;

  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();

  Wire.requestFrom(addr, (byte)1);
  return Wire.read();
}

void writeRegister(byte addr, byte reg, byte value){
  byte val = 0x00;
  Wire.beginTransmission(BMA020_ADDR);
       
  Wire.write(reg);
  Wire.write(value);

  Wire.endTransmission();
}



void readBMA020(byte addr) {
  int i;
  
  for (i = 0; i < 6; i++) {
    data[i] = readRegister(addr, 0x02+i);
  }
  
  //Parse data information
  bma020_x = ( (int)data[1] << 8) + (int)data[0];
  bma020_y = ( (int)data[3] << 8) + (int)data[2];
  bma020_z = ( (int)data[5] << 8) + (int)data[4];

  //Extract "new data Bits"
  bma020_x_new = bma020_x & 0x0001;
  bma020_y_new = bma020_y & 0x0001;
  bma020_z_new = bma020_z & 0x0001;
  
  bma020_x = bma020_x >> (6);  
  bma020_y = bma020_y >> (6);
  bma020_z = bma020_z >> (6);  
}  

int norm3dim (int a, int b, int c) {
  double total;
  total = sq((double)bma020_x) + sq((double)bma020_y) + sq((double)bma020_z);
  //return total;
  return (int)(sqrt(total));
}

Ich bin soweit auch ganz zufrieden, weil es so halbwegs klappt. Aber jetzt kommen wir zu meinen Problem.

Zum einen bin ich noch sehr "ungeübt" in i2c kommunikation. Leider ist auch für mich die Dokumentation der I2C Funktionen in der Wire.h "dürftig".
Mir fehlt einfach die Analogie zum normalen Mikrocontroller I2C.

Anbei das Datenblatt zum BMA020:

--> Seite 34/35: I2C Kommunikation
--> Seite 9: Registerbeschreibung
--> Seite 21: Format der X, Y und Z-Register

Meine erste Frage: Ist das readRegister() + writeRegister() bezogen auf den BMA020 richtig implementiert? Oder sind diese Funktionen eher "glückstreffer"?

Nun zum eigentlichen Problem:
Ich lasse mir über die Konsole die gemessenen Werte ausgeben. Meine X, Y und Z Register für die aktuelle beschleunigung ist lt. Datenblatt 10 Bit groß. 10 Bit ==> von -512 bis +511
Der BMA020 habe ich auf 8g gestellt. d.h. 8g ~ 511, das bedeutet 1g ~ 63.

Und das klappt auch soweit. Setze ich den BMA auf 4g ist 1g ~ 127. Auf 2g ist 1g ~ 255

Lege ich den Sensor mit 8g vor mir flach hin erhalte ich folgende Ausgabe:
Flach hingelegt bekomme ich folgende Ausgabe:

X: 4 Y: -4 Z: 62 Betrag: 62

Der Betrag hier sieht auch ganz plausibel aus. (Ich gehe davon aus meine Betragsrechnung korrekt angesetzt ist)

Drehe ich den Sensor komplett auf den Kopf erhalte ich:

X: 4 Y: -1 Z: -63 Betrag: 63

Auch hier bin ich noch vollkommen zufrieden.

Neige ich nun die Y-Achse des Sensors richung "Anziehungskraft" erhalte ich (Schrott?!)

X: 3 Y: 58 Z: -4 Betrag: 58

Ist diese "Toleranz" zwischen den einzelnen Achsen "normal"?

Oder Versemmel ich es irgendwie mit der Umrechnung der Werte?

Habe den Sensor in Schrumpfschlauch "Verschweißt". Ist das evtl. eine Störquelle für diesen Effekt?

Mit freundlichen Grüßen
Chris2go

Der Kommunikations-Teil sieht von der Funktion her korrekt aus. :slight_smile:

Was du vielleicht machen kannst ist die untere read-Methode so umschreiben, dass sie gleich alle Daten ausliest:

void readRegister(byte reg, byte* data, byte length)
{
  Wire.beginTransmission(BMA020_ADDR);
  Wire.write(reg);
  Wire.endTransmission();

  Wire.requestFrom(BMA020_ADDR, length);

  for(int i = 0; i < length; i++)
       data[i] = Wire.read();
}

Dann musst du ein ein Array erstellen, das die Daten aufnimmt:
byte data[6];

Und das an die Methode übergeben:
readRegister(0x02, data, 6);

Das ist aber Geschmackssache :slight_smile: Bei write geht das laut Datenblatt komischerweise nicht so und er will für jedes Byte eine Adresse.

Außerdem übergibst du bei read die Device-Adresse, aber bei write steht sie mit beginTransmission(BMA020_ADDR) fest drin. Das sollte man konsistent machen. Da die bekannt ist und sich nicht ändert, braucht man den Parameter nicht.

Das sind aber wie gesagt mehr Schönheitsfehler. Zu funktionieren scheint es auch so wie es ist.