kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




LV #8, 23.05.2024 Grundlagen der Mikrocontrollertechnik im Sommersemester 2024

(EN google-translate)

(PL google-translate)

Themen

  1. Servos mit OOP
  2. TWI / I2C Schnittstelle und Anschluss eines dreiachsigen Beschleunigungssensors MPU6050
  3. Objektorientierte Kapselung des Beschleunigungssensors
  4. Übung

Servos mit OOP

//Mode 8
//Phasen- und Frequenz-korrekt
//WGM1   3 2 1 0
//       1 0 0 0
//ICR1=..... TOP
//fpwm = fclk/(2*N*TOP)
//Vorteilung
//N=8
//80Hz = 16000000/(2*8*TOP)
//TOP = 16000000/(2*8*80Hz)=12500
//dt==1000ms*(1/80Hz)/12500 == 0,001ms (1 Schritt == 0,001ms)
//=>
//1ms == 1000 Schritte
//1,5ms == 1500 Schritte
//2ms == 2000 Schritte

class MeinServo
{
    public:
       void start()
       {
         TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0)
                | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10);
         TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10); //Vort. 8, s.S. 135       
         ICR1=12500;       
         DDRB |= (1<<PB5); //OCR1A
         DDRB |= (1<<PB6); //OCR1B
         OCR1A = 1500; //PWM-Breite auf Mitte setzen.  
         OCR1B = 1500; //PWM-Breite auf Mitte setzen.         
       }

       int getPWMminus90()
       {
           return 500;
       }
       int getPWMplus90()
       {
           return 2500;
       }
       int getPWMmitte()
       {
           return 1500;
       }

       void setPWMA(int value)
       {
           OCR1A = value;
       }
       void setPWMB(int value)
       {
           OCR1B = value;
       }

       /*
             Übergebener Winkel in Grad für Servo an Digital Pin 9
             Bereich: -90.0 bis +90.0
        */
       void setzeWinkelA(double winkel)
       {
           if(winkel<-90.0)
              winkel=-90.0;
           if(winkel>90.0)
              winkel=90.0;

           double pwm = 500.0 + 2000.0*(winkel+90.0)/180.0 +0.5;
           setPWMA((int)pwm);     
       }
       void setzeWinkelB(double winkel)
       {
           if(winkel<-90.0)
              winkel=-90.0;
           if(winkel>90.0)
              winkel=90.0;

           double pwm = 500.0 + 2000.0*(winkel+90.0)/180.0 +0.5;
           setPWMB((int)pwm);     
       }
};

Code 0-1: Klasse MeinServo.

#include "MeinServo.h"

MeinServo servo;

void setup() 
{
    servo.start();
}
void loop() 
{
       //servo.setPWMA(servo.getPWMminus90());
       //servo.setPWMB(servo.getPWMminus90());
       servo.setzeWinkelA(-90.0);
       servo.setzeWinkelB(-90.0);
       delay(3000);    
       servo.setPWMA(servo.getPWMmitte());
       servo.setPWMB(servo.getPWMmitte());
       delay(3000);    
       servo.setPWMA(servo.getPWMplus90());
       servo.setPWMB(servo.getPWMplus90());
       delay(3000);    
}

Code 0-2: Hauptprogramm.

Ausgangspunkt ist das bereits besprochene Register basierte Programm:

Datenblatt zum ATmega32u4 -- https://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf
#define WMIN 1000
#define WMITTE 1500
#define WMAX 2000
#define SCHRITTE 1000
//Mode 8
//Phasen- und Frequenz-korrekt
//WGM1   3 2 1 0
//       1 0 0 0
//ICR1=..... TOP
//fpwm = fclk/(2*N*TOP)
//Vorteilung
//N=8
//80Hz = 16000000/(2*8*TOP)
//TOP = 16000000/(2*8*80Hz)=12500
//dt==1000ms*(1/80Hz)/12500 == 0,001ms (1 Schritt == 0,001ms)
//=>
//1ms == 1000 Schritte
//1,5ms == 1500 Schritte
//2ms == 2000 Schritte
void setup() 
{
       TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0)
              | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10);
       TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10); //Vort. 8, s.S. 135       
       ICR1=12500;       
       DDRB |= (1<<PB5); //OCR1A
       DDRB |= (1<<PB6); //OCR1B
       OCR1A = WMITTE; //PWM-Breite auf Mitte setzen.  
       OCR1B = WMITTE; //PWM-Breite auf Mitte setzen.  
}
void loop() 
{
       OCR1A = WMAX; //PWM-Breite auf Null setzen.  
       OCR1B = WMAX; //PWM-Breite auf Null setzen.         
       delay(3000);    
       OCR1A = WMITTE; //PWM-Breite auf Null setzen.  
       OCR1B = WMITTE; //PWM-Breite auf Null setzen.         
       delay(3000);    
       OCR1A = WMIN; //PWM-Breite auf Null setzen.  
       OCR1B = WMIN; //PWM-Breite auf Null setzen.         
       delay(3000);    
}

Code 0-3: Modellbau-Servos auf OC1A und OC1B mittels Timer 1 mit hoher Genauigkeit ansteuern.

TWI / I2C Schnittstelle und Anschluss eines dreiachsigen Beschleunigungssensors MPU6050

72_COACH2/05_I2C -- Infos zu I2C

Anschluss des Sensors, siehe "6. Auslesen eines MPU6050 über die I2C-Schnittstelle mittels Library " in dem vorangehenden "day by day" Dokument.

Objektorientierte Kapselung des Beschleunigungssensors

Umsetzung in Vorlesung.

Übung

Als Vorstufe für eine elektronische Wasserwaage, soll eine rote LED aufleuchten, sobald die Laborplatine um einen größeren Winkel als 10 Grad aus der Waagerechten gebracht wird.

  1. Verschaffen Sie sich Klarheit über den Gravitationsvektor in [AcX,AcY,AcZ] über die Ausgaben auf die serielle Schnittstelle.
  2. Finden Sie auf dem Papier einen Weg, aus dem Gravitationsvektor die Abweichung der Laborplatine von der Waagerechten zu berechnen.
  3. Implementieren Sie diese Berechnung und lassen Sie sich das Ergebnis über die serielle Schnittstelle anzeigen.
  4. Setzen Sie schließlich die LED-Anzeige um und kontrollieren Sie beispielsweise mit dem Geodreieck, ob es funktioniert.
  5. Überlegen Sie, wie die Verwendung des MPU6050 als Klasse umsetzen können und tuen es.
#include <math.h>
#include <Wire.h>
#include "Level.h"

Level level;

void setup() {
  pinMode (4, OUTPUT);
  Serial.begin(9600);
  level.begin();
  delay(500);
}

void loop() {

  level.aquire();

  if (level.angle > 10.0){
    digitalWrite(4, HIGH);
  }
  else{
    digitalWrite(4, LOW);
  }

  Serial.print(level.angle);Serial.print("Grad
");

  delay(50);

}

Code 0-4: Hauptprogramm studentische Lösung.

class Level {

private:

  const int MPU = 0x68;  // I2C address of the MPU-6050

  double calculateAngleDEG(int AcX, int AcY, int AcZ) {
    // a = gemessener Vektor (x,y,z)
    // b = (0,0,1)
    //   a*b   = AcX*0 + AcY*0 + AcZ*1 = AcZ
    // || a || = sqrt(AcX^2 + AcY^2 + AcZ^2)
    // || b || = sqrt(0^2 + 0^2 + 1^2) = 1
    // || a || * || b || = 1*sqrt(AcX^2 + AcY^2 + AcZ^2)

    double rawValue = AcZ / (sqrt(AcX*AcX + AcY*AcY + AcZ*AcZ));

    double angleDEG = acos(rawValue) * (180 / M_PI);

    return angleDEG;
  }

public:

  int16_t AcX = 0, AcY = 0, AcZ = 0;
  double angle = 0.0;

  void begin() {

    Wire.begin();
    Wire.beginTransmission(MPU);
    Wire.write(0x6B);  // PWR_MGMT_1 register
    Wire.write(0);     // set to zero (wakes up the MPU-6050)
    Wire.endTransmission(true);
  }

  void aquire() {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);        // request a total of 14 registers
    AcX = Wire.read() << 8 | Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
    AcY = Wire.read() << 8 | Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    AcZ = Wire.read() << 8 | Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    angle = calculateAngleDEG(AcX, AcY, AcZ);
  }
};

Code 0-5: Klasse Level studentische Lösung.