NOT FINISHED YET!!! My first try to get a self built fully working Quadrocopter based on an mbed, a self built frame and some other more or less cheap parts.

Dependencies:   mbed MODI2C

Files at this revision

API Documentation at this revision

Comitter:
maetugr
Date:
Sat Oct 13 08:59:03 2012 +0000
Parent:
4:e96b16ad986d
Child:
6:179752756e9f
Commit message:
?bergang von LCD auf PC-Terminal

Changed in this revision

BMP085/BMP085.cpp Show annotated file Show diff for this revision Revisions of this file
BMP085/BMP085.h Show annotated file Show diff for this revision Revisions of this file
HMC5883/HMC5883.cpp Show annotated file Show diff for this revision Revisions of this file
HMC5883/HMC5883.h Show annotated file Show diff for this revision Revisions of this file
Terminal-Emulation/terminal.cpp Show annotated file Show diff for this revision Revisions of this file
Terminal-Emulation/terminal.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BMP085/BMP085.cpp	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,147 @@
+
+#include "mbed.h"
+#include "BMP085.h"
+
+//I2C Adresse
+#define BMP085_ADRESS 0xEE
+
+#define xpow(x, y) ((long)1 << y)
+
+
+// Constructor
+// -----------------------------------------------
+BMP085::BMP085(PinName sda, PinName scl) : i2c_(sda, scl)
+    {
+    Init();
+    }
+    
+
+// Temperatur und Druck auslesen und berechnen
+// -----------------------------------------------
+void BMP085::Update () 
+    {
+    long P, UTemp, UPressure, X1, X2, X3, B3, B5, B6;
+    unsigned long B4, B7;
+
+    twi_writechar(BMP085_ADRESS, 0xf4, 0x2e);
+    // Wait at least 4.5ms
+    wait(0.005);
+    UTemp = twi_readshort(BMP085_ADRESS, 0xf6);
+    
+    X1 = ((UTemp - AC6) * AC5) >> 15;
+    X2 = (MC << 11) / (X1 + MD);
+    B5 = X1 + X2;
+    Temperature = (float)((B5 + 8) >> 4)/10.0;
+
+    twi_writechar(BMP085_ADRESS, 0xf4, 0x34 + (oss << 6));
+    // Wait at least 4.5ms
+    wait(0.005);
+    UPressure = twi_readlong(BMP085_ADRESS, 0xf6) >> (8 - oss);
+
+    B6 = B5 - 4000;
+    X1 = (B2 * (B6 * B6) >> 12) >> 11;
+    X2 = (AC2 * B6) >> 11;
+    X3 = X1 + X2;
+    B3 = ((AC1 * 4 + X3) << oss) >> 2;    
+
+    X1 = (AC3 * B6) >> 13;
+    X2 = (B1 * (B6 * B6) >> 12) >> 16;
+    X3 = ((X1 + X2) + 2) >> 2;
+    B4 = AC4 * (X3 + 32768) >> 15;
+    
+    B7 = (unsigned long)(UPressure - B3) * (50000 >> oss);
+    
+    if (B7 < 0x80000000) 
+        {
+        P = (2 * B7) / B4;
+        }
+    else 
+        {
+        P = 2* (B7 / B4);
+        }
+    X1 = (P >> 8) * (P >> 8);
+    X1 = (X1 * 3038) >> 16;
+    X2 = (-7357 * P) >> 16;    
+    P = P + ((X1 + X2 + 3791) >> 4);
+    Pressure = (float)P / 100.0;
+    }
+
+
+// Hoehe u.M. berechnen  (Druck in hPa)
+// -----------------------------------------------
+float BMP085::CalcAltitude(float Press)
+    {
+    float A = Press/1013.25;
+    float B = 1/5.25588;
+    float C = pow(A,B);
+    
+    C = 1 - C;
+    C = C / 22.5577e-6;
+    return C;
+    }
+    
+        
+// Drucksensor initialisieren  
+// -----------------------------------------------
+void BMP085::Init () 
+    {
+    AC1 = twi_readshort(BMP085_ADRESS, 0xaa);
+    AC2 = twi_readshort(BMP085_ADRESS, 0xac);
+    AC3 = twi_readshort(BMP085_ADRESS, 0xae);
+    AC4 = twi_readshort(BMP085_ADRESS, 0xb0);
+    AC5 = twi_readshort(BMP085_ADRESS, 0xb2);
+    AC6 = twi_readshort(BMP085_ADRESS, 0xb4);
+    B1 = twi_readshort(BMP085_ADRESS, 0xb6);
+    B2 = twi_readshort(BMP085_ADRESS, 0xb8);
+    MB = twi_readshort(BMP085_ADRESS, 0xba);
+    MC = twi_readshort(BMP085_ADRESS, 0xbc);
+    MD = twi_readshort(BMP085_ADRESS, 0xbe);
+    }
+    
+    
+// -----------------------------------------------
+unsigned short BMP085::twi_readshort (int id, int addr) {
+    unsigned short i;
+
+    i2c_.start();
+    i2c_.write(id);
+    i2c_.write(addr);
+
+    i2c_.start();
+    i2c_.write(id | 1);
+    i = i2c_.read(1) << 8;
+    i |= i2c_.read(0);
+    i2c_.stop();
+
+    return i;
+}
+
+
+// -----------------------------------------------
+unsigned long BMP085::twi_readlong (int id, int addr) {
+    unsigned long i;
+
+    i2c_.start();
+    i2c_.write(id);
+    i2c_.write(addr);
+
+    i2c_.start();
+    i2c_.write(id | 1);
+    i = i2c_.read(1) << 16;
+    i |= i2c_.read(1) << 8;
+    i |= i2c_.read(0);
+    i2c_.stop();
+
+    return i;
+}
+
+
+// -----------------------------------------------
+void BMP085::twi_writechar (int id, int addr, int dat) {
+
+    i2c_.start();
+    i2c_.write(id);
+    i2c_.write(addr);
+    i2c_.write(dat);
+    i2c_.stop();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BMP085/BMP085.h	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,34 @@
+ 
+#ifndef BMP085_I2C_H
+#define BMP085_I2C_H
+
+#include "mbed.h"
+
+class BMP085
+    {
+    private:
+        I2C i2c_;
+        
+    public:
+        BMP085(PinName sda, PinName scl);
+    
+        void    Update();
+        float   Temperature;
+        float   Pressure;
+        float   CalcAltitude(float Press);
+        short   oss;
+    
+    protected:
+        void Init();
+        unsigned short twi_readshort (int, int);
+        unsigned long twi_readlong (int, int);
+        void twi_writechar (int, int, int);
+    
+    
+    private:
+    
+        short AC1, AC2, AC3, B1, B2, MB, MC, MD;
+        unsigned short AC4, AC5, AC6;
+    };
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HMC5883/HMC5883.cpp	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,166 @@
+#include "mbed.h"
+#include "HMC5883.h"
+
+#define I2CADR_W(ADR)           (ADR<<1&0xFE)
+#define I2CADR_R(ADR)           (ADR<<1|0x01)
+
+//Initialisieren
+HMC5883::HMC5883(PinName sda, PinName scl, Timer & GlobalTime_) :  i2c_(sda, scl),  GlobalTime(GlobalTime_)
+    {
+    Init();
+    }
+
+void HMC5883::Init()
+{
+    //Nullsetzen
+    MeasurementError= 0;
+    AutoCalibration= 0;
+    #pragma unroll
+    for(int i= 0; i < 3; i++)
+    {
+        RawMin[i]= -400;
+        RawMax[i]= 400;
+        Offset[i]= 0;
+        Scale[i]= 1.0;
+        RawMag[i]= 0;
+        Mag[i]= 0;
+    }
+    
+       
+    //HMC5883 initialisieren
+    char tx[4];
+    
+    //1 Sample pro Messung
+    //75Hz Output Rate
+    //Range: +- 1.3 Gauss
+    //Continuous-Measurement Mode
+    tx[0]= 0x00;
+    tx[1]= 0x78;    //Configuration Register A
+    tx[2]= 0x20;    //Configuration Register B
+    tx[3]= 0x00;    //Mode Register
+    i2c_.write(I2CADR_W(HMC5883_ADRESS), tx, 4);
+    
+    
+    Update();
+}
+
+//Rohdaten lesen
+void HMC5883::ReadRawData()
+{
+    //Drehrate aller Axen abholen    
+    char tx[1];
+    char rx[6];
+     
+    tx[0]= 0x03;
+    i2c_.write(I2CADR_W(HMC5883_ADRESS), tx, 1);
+    i2c_.read (I2CADR_R(HMC5883_ADRESS), rx, 6); 
+    
+    //Aus den einzelnen Bytes den 16-Bit-Wert zusammenbauen
+    short r[3];
+    r[0]= rx[0]<<8|rx[1];
+    r[1]= rx[4]<<8|rx[5];
+    r[2]= rx[2]<<8|rx[3];
+    
+    //Grober Messfehler?
+    if(r[0] == -4096
+    || r[1] == -4096
+    || r[2] == -4096)
+        MeasurementError= 1;
+    else
+    {
+        MeasurementError= 0;
+        RawMag[0]= r[0];
+        RawMag[1]= r[1];
+        RawMag[2]= r[2];
+    }
+}
+
+
+//Update-Methode
+void HMC5883::Update()
+{
+    //Rohdaten lesen
+    ReadRawData();
+    
+    if(AutoCalibration)
+    {
+        #pragma unroll
+        for(int i= 0; i < 3; i++)
+        {
+            //Neuer Min Max Wert?
+            RawMin[i]= RawMin[i] < RawMag[i] ? RawMin[i] : RawMag[i];
+            RawMax[i]= RawMax[i] > RawMag[i] ? RawMax[i] : RawMag[i];
+            
+            //Scale und Offset aus gesammelten Min Max Werten berechnen
+            //Die neue Untere und obere Grenze bilden -1 und +1
+            Scale[i]= 2.0 / (float)(RawMax[i]-RawMin[i]);
+            Offset[i]= 1.0 - (float)(RawMax[i]) * Scale[i];
+        }
+    }
+    
+    //Feldstaerke berechnen
+    Mag[0]= Scale[0] * (float)(RawMag[0]) + Offset[0];
+    Mag[1]= Scale[1] * (float)(RawMag[1]) + Offset[1];
+    Mag[2]= Scale[2] * (float)(RawMag[2]) + Offset[2];
+}
+
+
+//Kalibrieren
+void HMC5883::Calibrate(const short * pRawMin, const short * pRawMax)
+{
+    #pragma unroll
+    for(int i= 0; i < 3; i++)
+    {        
+        RawMin[i]= pRawMin[i];
+        RawMax[i]= pRawMax[i];
+    }
+    char AutoCalibrationBak= AutoCalibration;
+    AutoCalibration= 1;
+    Update();
+    AutoCalibration= AutoCalibrationBak;
+}
+
+void HMC5883::Calibrate(int s)
+{
+    char AutoCalibrationBak= AutoCalibration;
+    AutoCalibration= 1;
+    
+    //Ende der Kalibrierung in ms Millisekunden berechnen
+    int CalibEnd= GlobalTime.read_ms() + s*1000;
+    
+    while(GlobalTime.read_ms() < CalibEnd)
+    {
+        //Update erledigt alles
+        Update();
+    }
+    
+    AutoCalibration= AutoCalibrationBak;
+}
+
+// Winkel berechnen
+//---------------------------------------------------------------------------------------------------------------------------------------
+float HMC5883::getAngle(float x, float y)
+    {
+    #define Rad2Deg       57.295779513082320876798154814105
+    #define PI             3.1415926535897932384626433832795
+
+    float Heading;
+    float DecAngle; 
+    
+    DecAngle = 1.367 / Rad2Deg;         //Missweisung = Winkel zwischen geographischer und magnetischer Nordrichtung
+                                        //Bern ca. 1.367 Grad Ost
+                                        //http://www.swisstopo.admin.ch/internet/swisstopo/de/home/apps/calc/declination.html
+    Heading = atan2((float)y,(float)x);
+    
+    Heading += DecAngle;                //bei Ost-Deklination  += DecAngle, bei West-Deklination -= DecAngle
+ 
+    if(Heading < 0)  
+        Heading += 2*PI;                //korrigieren bei negativem Vorzeichen
+        
+    if(Heading > 2*PI)   
+        Heading -= 2*PI;                 //auf 2Pi begrenzen
+        
+    return  (Heading * 180/PI);         //Radianten in Grad konvertieren
+    }
+    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HMC5883/HMC5883.h	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,51 @@
+#ifndef HMC5883_H
+#define HMC5883_H
+
+//I2C Adresse
+#define HMC5883_ADRESS 0x1E
+
+
+class HMC5883
+{
+private:
+    I2C i2c_;
+    Timer & GlobalTime;
+        
+public:
+    //Calibration
+    char AutoCalibration;       //automatische Kalibrierung der Sensordaten
+    short RawMin[3], RawMax[3]; //gespeicherte Daten fuer die Auto-Kalibrierung
+    float Scale[3];             //jede Achse einzeln skalieren
+    float Offset[3];            //zum Schluss noch das Offset dazu
+    
+    //Feldstaerke auf allen drei Achsen
+    short RawMag[3];            //Rohdaten
+    float Mag[3];               //kalibrierte Rohdaten (nicht normalisiert)
+    
+    //Bei zu hoher Feldstaerke wird -4096 gelesen
+    //Wenn dies eine Achse betreffen sollte, ist diese Variable 1
+    short MeasurementError;
+    
+    float   getAngle(float x, float y);
+       
+    //Initialisieren
+    HMC5883(PinName sda, PinName scl, Timer & HMC5883_Time_);
+    void Init();
+
+private:    
+    //Rohdaten lesen
+    void ReadRawData();
+    
+public:
+    //Update-Methode
+    void Update();
+    
+    //Kalibrieren
+    //Fertige Daten benutzen. Scale[0] muss 1.0 sein!
+    void Calibrate(const short * pRawMin, const short * pRawMax);
+    
+    //Selbst auswerten, dauert s Sekunden lang
+    void Calibrate(int s);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Terminal-Emulation/terminal.cpp	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,19 @@
+
+#include "terminal.h"
+#include "mbed.h"
+
+terminal::terminal(PinName tx, PinName rx) : Serial(tx, rx) 
+    {
+    }
+
+
+void terminal::cls() 
+    {
+    printf("\x1B[2J");
+    }
+
+
+void terminal::locate(int Spalte, int Zeile) 
+    {
+    printf("\x1B[%d;%dH", Zeile + 1, Spalte + 1);
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Terminal-Emulation/terminal.h	Sat Oct 13 08:59:03 2012 +0000
@@ -0,0 +1,14 @@
+
+#include "mbed.h"
+
+#ifndef MBED_TERMINAL_H
+#define MBED_TERMINAL_H
+
+class terminal : public Serial 
+    {
+    public:
+        terminal(PinName tx, PinName rx);
+        void cls();
+        void locate(int column, int row);
+    };
+#endif
--- a/main.cpp	Fri Oct 05 14:05:10 2012 +0000
+++ b/main.cpp	Sat Oct 13 08:59:03 2012 +0000
@@ -1,84 +1,103 @@
 #include "mbed.h" // Standar Library
 #include "LCD.h" // Display
 #include "LED.h" // LEDs
-#include "L3G4200D.h" // Gyro
-#include "ADXL345.h" // Acc
+#include "L3G4200D.h" // Gyro (Gyroscope)
+#include "ADXL345.h" // Acc (Accelerometer)
+#include "HMC5883.h" // Comp (Compass)
+#include "BMP085.h" // Alt (Altitude sensor)
 #include "Servo.h" // Motor
-
-// initialisation
-LED LEDs;
-TextLCD LCD(p5, p6, p7, p8, p9, p10, p11, TextLCD::LCD16x2); // RS, RW, E, D0, D1, D2, D3, Typ
-L3G4200D Gyro(p28, p27);
-ADXL345 Acc(p28, p27);
-//Servo Motor(p12);
+#include "terminal.h"
 
 Timer GlobalTimer;
 
+// initialisation of hardware
+LED         LEDs;
+TextLCD     LCD(p5, p6, p7, p8, p9, p10, p11, TextLCD::LCD16x2); // RS, RW, E, D0, D1, D2, D3, Typ
+L3G4200D    Gyro(p28, p27);
+ADXL345     Acc(p28, p27);
+HMC5883     Comp(p28, p27, GlobalTimer);
+BMP085      Alt(p28, p27);
+//Servo     Motor(p12);
+
+struct terminal       pc(USBTX, USBRX);        //Achtung: Treiber auf PC fuer Serial-mbed installieren.
+                                            //hier herunterladen https://mbed.org/media/downloads/drivers/mbedWinSerial_16466.exe
+
 #define PI             3.1415926535897932384626433832795
 #define Rad2Deg        57.295779513082320876798154814105
 
 int main() {
     // LCD/LED init
-    LCD.cls(); // Display löschen
-    LCD.printf("FlyBed v0.2");
+    LCD.cls(); // Display l�schen
+    LCD.printf("Flybed v0.2");
     LEDs.roll(2);
-    LEDs = 15;
+    
+    pc.baud(57600); 
     
-    float Gyro_data[3];
-    int Acc_data[3];
-    //int Gyro_angle[3] = {0,0,0};
-    unsigned long dt = 0;
-    unsigned long time_loop = 0;
+    // variables for loop
+    float           Gyro_data[3];
+    int             Acc_data[3];
+    unsigned long   dt = 0;
+    unsigned long   time_loop = 0;
+    float angle[3] = {0,0,0};
+    float Acc_angle[2] = {0,0};
+    float Comp_angle = 0;
+    float tempangle = 0;
     
     //Motor.initialize();
+    //Kompass kalibrieren  --> Problem fremde Magnetfelder!
+    //Comp.AutoCalibration = 1;
+    short MagRawMin[3]= {-400, -400, -400};     //Gespeicherte Werte verwenden
+    short MagRawMax[3]= {400, 400, 400};
+    Comp.Calibrate(MagRawMin, MagRawMax);
+    //Comp.Calibrate(20);
     
-    float angle[3] = {0,0,0};
-    float Acc_angle[2] = {0,0};
+    //Oversampling des Barometers setzen
+    Alt.oss = 0;
     
     GlobalTimer.start();
     while(1) {
+        // read data from sensors
         Gyro.read(Gyro_data);
         Acc.read(Acc_data);
+        Comp.Update();
+        Alt.Update();
 
-        // Acc data angle
-        //float Acc_abs = sqrt(pow((float)Acc_data[0],2) + pow((float)Acc_data[1],2) + pow((float)Acc_data[2],2));
-        //float Acc_angle = Rad2Deg * acos((float)Acc_data[2]/Acc_abs);
+        // calculate the angles for roll and pitch from accelerometer
         Acc_angle[0] = Rad2Deg * atan2((float)Acc_data[1], (float)Acc_data[2]);
-        Acc_angle[1] = -Rad2Deg * atan2((float)Acc_data[0], (float)Acc_data[2]);
+        Acc_angle[1] = 3 -Rad2Deg * atan2((float)Acc_data[0], (float)Acc_data[2]); // TODO Offset accelerometer einstellen
+        
+        //calculate angle for yaw from compass
+        Comp_angle = Comp.getAngle(Comp.Mag[0], Comp.Mag[1]);
         
-        //dt berechnen
-        dt = GlobalTimer.read_us() - time_loop;
-        time_loop = GlobalTimer.read_us();
+        // meassure dt
+        dt = GlobalTimer.read_us() - time_loop; // Zeit in us seit letzter loop
+        time_loop = GlobalTimer.read_us(); // setze aktuelle zeit f�r n�chste messung
         
+        // calculate angles for roll, pitch an yaw
         angle[0] += (Acc_angle[0] - angle[0])/50 + Gyro_data[0] *dt/15000000.0;
         angle[1] += (Acc_angle[1] - angle[1])/50 + Gyro_data[1] *dt/15000000.0;
-        angle[2] += /*(Acc_angle[1] - angle[1])/50 +*/ Gyro_data[2] *dt/15000000.0;
-        //Gyro_angle[0] += (Gyro_data[0]) * 0.01;
-        LCD.locate(0,0);
-        //LCD.printf("%2.1f  %2.1f %2.1f", Gyro_data[0],Gyro_data[1],Gyro_data[2]);
-        //LCD.printf("%d %d |%2.1f   ",Acc_data[1],Acc_data[2] ,Acc_angle); //roll(x) pitch(y) yaw(z)
-        LCD.printf("     |%2.1f   ",Acc_data[2]/20.0);
+        angle[2] += (Comp_angle - angle[2])/50 - Gyro_data[2] *dt/15000000.0;
+        tempangle -= Gyro_data[2] *dt/15000000.0;
         
-        LCD.locate(1,0);
-        //LCD.printf("%d %d %d %2.1f  ", Acc_data[0],Acc_data[1],Acc_data[2]);
-        //LCD.printf("%2.1f %2.1f %2.2f %2.1f", Acc_angle,Acc_angle,dt/10000.0, angle);
-        LCD.printf("%2.1f %2.1f %2.1f   ", angle[0], angle[1], angle[2]);
+        int i =345;
+        // LCD output
+        pc.locate(10,5); // first line
+        pc.printf("Y:%2.1f %2.1fm   ", angle[2], Alt.CalcAltitude(Alt.Pressure));
+        //LCD.printf("%2.1f %2.1f %2.1f   ", Comp.RawMag[0], Comp.RawMag[1], Comp.RawMag[2]);
+        pc.locate(10,8); // second line
+        pc.printf("R:%2.1f P:%2.1f  ", angle[0], angle[1]);
+        //LCD.printf("R:%2.1f P:%2.1f  ", Comp_angle, tempangle);
+        //LCD.printf("%2.1f %2.1f %2.1f   ", Comp.Mag[0], Comp.Mag[1], Comp.Mag[2]);
         
-        //Motor = 1000 + abs(Acc_data[1]); // Motorwert anpassen
+        //Motor = 1000 + abs(Acc_data[1]); // set new motor speed
         
         //LED hin und her
         int ledset = 0;
-        if (Acc_angle < 0)
-            ledset += 1;
-        else
-            ledset += 8; 
-        if (angle < 0)
-            ledset += 2;
-        else
-            ledset += 4;
-        //wait(0.1);
+        if (angle[0] < 0) ledset += 1; else ledset += 8; 
+        if (angle[1] < 0) ledset += 2; else ledset += 4;
         LEDs = ledset;
         
         //LEDs.rollnext();
+        //wait(0.1);
     }
 }