kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




LV 16.06.2024 Grundlagen der Mikrocontrollertechnik 4EIT 4EMO 4MT im Sommersemester 2023

(EN google-translate)

(PL google-translate)


NEU Sommersemester 2024, LV #7, 16.06.2024 OOP / Bussysteme


Themen

  1. OOP
  2. Bussysteme
  3. Serielle Schnittstelle

1. OOP

  • Wiederverwendbarkeit
  • Kapselung
Einführung zu OOP mit dem Mikrocontroller: 40_Mikrocontroller/08_OOP
Im Unterricht entwickelte aufeinander aufbauende Beispiele
OOPneu001.zip -- direkt blinkende LED im Hauptprogramm mittels Arduino Befehlen.
OOPneu002.zip -- direkt blinkende LED im Hauptprogramm mittels Register Befehlen.
OOPneu003.zip -- Funktionen.
OOPneu004.zip -- Klasse LED (OOP).
OOPneu005.zip -- Klasse LED (OOP) mit Konstruktor.

2. Bussysteme

40_Mikrocontroller/06_UART/01_Bussysteme

3. Serielle Schnittstelle

40_Mikrocontroller/06_UART/02_UART
40_Mikrocontroller/06_UART/03_RS232
void setup() 
{
    Serial.begin(9600);
}

void loop() 
{
    Serial.write("Hallo\n");
    delay(500);
}

Code 0-1: erstes Beispiel zur seriellen Schnittstelle

ÜBUNG: Möglichkeit Objekt LED so zu konfigurieren, dass eine von 4 LEDs verwendet wird.
class LED 
{

private:
  int pin;
  int PDX;

public:
  LED(int num) 
  {

    pin = num;

    if (num == 1) {
      PDX = PD7;
    }
    if (num == 2) {
      PDX = PD4;
    }
    if (num == 3) {
      PDX = PD0;
    }
    if (num == 4) {
      PDX = PD1;
    }

    DDRD |= (1 << PDX);
  };

  void on() 
  {
    PORTD |= (1 << PDX);
  }

  void off() 
  {
    PORTD &= ~(1 << PDX);
  }
};

LED john(1);   // (Pin 6)
LED steven(2); // (Pin 4)
LED ryan(3);  // (Pin 3)
LED chris(4);  // (Pin 2)

void setup() 
{

}

void loop() 
{
  
  john.on();
  delay(200);
  steven.on();
  delay(200);
  ryan.on();
  delay(200);
  chris.on();
  delay(200);

  john.off();
  steven.off();
  ryan.off();
  chris.off();
  delay(200);
}

Code 0-2: Studentische Lösung.


ENDE NEU Sommersemester 2024, LV #7, 16.06.2024 OOP / Bussysteme


Thema: Objektorientierte Programmierung (OOP) in C++ auf Mikrocontrollern

Als Beispiele sollen die beiden Komponenten Servo und 4fach 7-Segment-Anzeige in eine Klasse überführt werden.

Vorteile der OOP und gleichzeitig Paradigmen:

  • Wiederverwendbarkeit
  • Kapselung
Einführung zu OOP mit dem Mikrocontroller: 40_Mikrocontroller/08_OOP
Im Unterricht entstandene Programme als Grundlage für die Übung:
OOPled001.zip
OOPled002.zip
OOPled003.zip
OOPled004.zip
OOPled005.zip
OOPled006konstruktor.zip
OOPled007servo.zip

#6 Fr 05.05.2023

Themen

  1. Timer als Zähler verwenden
  2. Entwicklung der Software für das Anzeigedisplay
  3. Übung: Uhr programmieren

1. Timer als Zähler verwenden

  • Timer SIND Zähler, die Quarz genau zyklisch in einem Register herauf und/oder herunter zählen.
  • Die einfachste Konfiguration ist diejenige, bei der die Timer einfach nur zählen, ohne PWM-Signale u.ä. zu generieren.
  • Das Zählregister kann unmittelbar im C-Code abgerufen werden.
  • Das Zählregister kann 8, 10, oder 16 relevante Stellen haben, je nach Genauigkeit des Zählers.
  • Die Zählgeschwindigkeit erfolgt durch Vorteilung der Quarzfrequenz.
  • Die Vorteilung wird über bestimmte Konfigurationsbits in einem Konfigurationsregister eingestellt.
  • Zähler können bei entsprechender Konfiguration Software-Interrupts bei Erreichen der Zählobergrenze.
  • Software-Interrupts: Aufruf einer kleinen Funktion exakt zum Zeitpunkt des Erreichens des Zählerstandes.
  • ISR == Interrupt Service Routine

Beispielprogramm, bei denen ein Timer als Zähler arbeitet:

Analyse von Code 0-1: "Walfischgesänge" bei 96_Arduino/22_Universal/08_Lautsprecher

Analysieren Sie insbesondere Mode 4 im Datenblatt.

2. Entwicklung der Software für das Anzeigedisplay


Methode: Herunterbrechen einer komplexen Aufgabe in aufeinander aufbauenden Teilaufgaben, die alle für sich funktionieren.


Exkurs: Agile Projektmanagementmethode Scrum:

67_Echtzeitsysteme/13_Fuzzy/05_Softwareentwicklung/01_AgileSoftwareentwicklung

Aufeinander aufbauende Teillösungen:

  1. anzeige001: Schaltungsentwurf für die Verbindung zwischen Arduino und Anzeige.
  2. anzeige002: Aufbau der Schaltung.
  3. anzeige003: Eine LED auf einem Digit zum Leuchten bringen. Damit ist zu klären, ob es eine gemeinsame Anode oder eine gemeinsame Katode gibt.
  4. anzeige004: Pattern für die Ziffern 0 bis 9 und Minuszeichen entwickeln und auf einem Digit testen.
  5. anzeige005: Pattern auf beliebigem Digit durchlaufenn lassen.
  6. anzeige006: Konzept für das Festlegen und die gleichzeitige Anzeige von vier Zeichen.
  7. anzeige007: Erweiterung auf eine Funktion, die eine Zahl im Bereich +/-999 anzeigen kann.
  8. anzeige008: Objektorientierte Umsetzung des Gesamtkonzeptes.
  9. anzeige009: Kombination mit Servo-Programm.

Katoden (-):
A - PB0
B - PB1
C - PB2
D - PB3
E - PB4
F - PB6
G - PB7

PB5 : SERVO

Anoden (+):
Ditit1 - DIO 2
Ditit2 - DIO 3
Ditit3 - DIO 4

Code 0-3: Ditit4 - DIO 5


Jede Teillösung wird als eigenes Projekt gestestet und bewahrt.


anzeige003.zip
anzeige004.zip
anzeige005.zip

Nach erfolgreichem Test einer Teillösung, wird das Arduino-Projekt als Kopie unter dem Namen des Nachfolgeprojektes gespeichert und dort die weiter gehende Funktionalität ergänzt.

3. Übung: Uhr programmieren

  1. Entwerfen Sie ein Design, wie die Uhr arbeiten soll und wie sie gestaltet sein soll.
  2. Analysieren Sie, durch Abarbeiten welcher aufeinander aufbauenden Teilaufgaben das Konzept umgesetzt werden kann.
  3. Leiten Sie aus der vorangehenden Analyse einen Arbeitsplan ab.
  4. Setzen Sie Ihren Entwurf um und dokumentieren Sie die Schritte.
  5. Präsentieren Sie Ihre Lösung im Plenum.

#7 Fr 12.05.2023

Themen

  1. Ergänzungen zu Timern
  2. Einführung zu Bussystemen

Ergänzungen zu Timern

Analyse weiterer Modi neben ISR, um einen festen Takt zu bekommen. 1. Methode: Zähler auslesen, 2. Methode: Direkt Rechtecksignal erzeugen.

Methode 1: 1 Hz Blinken über CTC-Mode des Zählers 1. Zählstand wird ständig geprüft.
  • Mode: 12, CTC-Mode, Datenblatt zu Atmega32u4, S.133
  • TOP einstellbar mit ICR1
void setup() 
{
       ICR1 = 62500-1; // Siehe Datenblatt Seite 123
       TCNT1=0; //Zählregister von Timer 1
       // WGM13...WGM10 = 1100 => Mode 12 == CTC, mit ICR1 als TOP, siehe Datenblatt zu Atmega32u4, Seite 133.
       // CS12..CS10 = 100 => Vorteilung 256 => Zählen läuft mit 62500Hz, Seite 134.
       // COM1A1,COM1A0 = 00, COM1B1,COM1B0 = 00 => "normal Port-Operation" (Keine Signale herausschicken), Seite 132.
       TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10);
       TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10); 

       // PB7 == DIO 11, vergl. kramann.info LED-Leiste
       DDRB |= (1<<PB7);
       PORTB &= ~(1<<PB7); //LED aus
}

// 1Hz Blinken
void loop() 
{
    if(TCNT1<31250) //Bei der unteren Hälfte des Zählvorgangs aus.
        PORTB &= ~(1<<PB7); //LED aus
    else    
        PORTB |= (1<<PB7); //LED an
}

Code 0-4: 1 Hz Blinken über CTC-Mode des Zählers 1. Zählstand wird ständig geprüft.

Methode 2: 1 Hz Blinken über CTC-Mode mit Ausgangssignal.

siehe auch:

96_Arduino
void setup() 
{
       ICR1 = 62500/2-1; // Siehe Datenblatt Seite 123
       TCNT1=0; //Zählregister von Timer 1
       // WGM13...WGM10 = 1100 => Mode 12 == CTC, mit ICR1 als TOP, siehe Datenblatt zu Atmega32u4, Seite 133.
       // CS12..CS10 = 100 => Vorteilung 256 => Zählen läuft mit 62500Hz, Seite 134.
       // COM1A1,COM1A0 = 01, COM1B1,COM1B0 = 01 => "Toggle OCnA/OCnB/OCnC on compare match", Seite 131.
       // Frequenz, siehe: S.123.
       TCCR1A = (0<<COM1A1) | (1<<COM1A0) | (0<<COM1B1) | (1<<COM1B0) | (0<<COM1C1) | (1<<COM1C0) | (0<<WGM11) | (0<<WGM10);
       TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10); 

       // OCR1A ist auf Digital Pin 9 == PB5, vergl. kramann.info, Arduino.
       // OCR1B ist auf Digital Pin 10 == PB6, vergl. kramann.info, Arduino.
       DDRB |= (1<<PB5) | (1<<PB6);
       //pinMode(9,OUTPUT);  
       //pinMode(10,OUTPUT);  
}

void loop() 
{
}

Code 0-5: 1Hz über fest konfiguriertes Toggeln auf Pin9 und 10, kein Code in loop() nötig!

Behandlung von Bussystemen

Themen:

  1. Überblick zu Bussystemen
  2. Einführung zu UART (serielle Schnittstelle) mit RS232-Protokoll
  3. Verwendung der seriellen Schnittstelle mittels der Arduino-Library
  4. Verwendung der seriellen Schnittstelle mittels Registerkonfiguration
  5. Objektorientierte Kapselung der seriellen Schnittstelle
  6. Auslesen eines MPU6050 über die I2C-Schnittstelle mittels Library

1. Überblick zu Bussystemen

40_Mikrocontroller/06_UART/01_Bussysteme

2. Einführung zu UART (serielle Schnittstelle) mit RS232-Protokoll

40_Mikrocontroller/06_UART/02_UART
40_Mikrocontroller/06_UART/03_RS232

3. Verwendung der seriellen Schnittstelle mittels der Arduino-Library

Typischerweise wird ein externes Gerät seriell an die Hauptplatine angebunden. Dies soll hier prinzipiell demonstriert werden, indem zwei Arduino seriell über Kreuz miteinander verbunden werden:

             RXD____  ____TXD
Arduino 1           \/        Arduino 2
                    /\
             TXD----  ----TXD

             GND----------GND


Code 0-6: Verbindung zweier Arduino über Kreuz.


Warum ist die GND-Verbindung notwendig?


Aufbau mit zwei Arduino-Micro. Vom ersten wird der serielle Monitor am PC geöffnet.

Bild 0-1: Aufbau mit zwei Arduino-Micro. Vom ersten wird der serielle Monitor am PC geöffnet.

Zwei Arduino Micro werden nun von der Arduino IDE erkannt.

Bild 0-2: Zwei Arduino Micro werden nun von der Arduino IDE erkannt.

Aufbau der Testschaltung.

Bild 0-3: Aufbau der Testschaltung.

Kommunikation auf dem seriellen Monitor.

Bild 0-4: Kommunikation auf dem seriellen Monitor.

void setup() 
{
    Serial.begin(9600);
    Serial1.begin(9600);
}

void loop() 
{
    if(Serial.available())
    {
        char c = Serial.read();
        Serial1.write(c);
    }
    if(Serial1.available())
    {
        char antwort = Serial1.read();
        Serial.print("Antwort: ");
        Serial.write(antwort);
        Serial.println();
    }
}

Code 0-7: Sertest1 für Arduino ttyACM0, wo später der serielle Monitor geöffnet wird.

void setup() 
{
    Serial1.begin(9600);
}

void loop() 
{
    if(Serial1.available())
    {
        char c = Serial1.read();
        Serial1.write(c+1);
    }
}

Code 0-8: Sertest2 für Arduino ttyACM1

4. Verwendung der seriellen Schnittstelle mittels Registerkonfiguration

Sertest2 soll nun mittels Registerbefehlen funktionieren und als Sertest2reg gespeichert werden:


Laut Pinbelegung des Chips sind die herausgeführten seriellen Pins RXD1 und TXD1


Chip ATmega32u4.

Bild 0-5: Chip ATmega32u4.

Notwendige Registerkonfigurationen, vergl. Datenblatt ab Seite 111.

#include<avr/io.h>
#define TAKTFREQUENZ 16000000
#define BAUDRATE 9600

void setup() 
{
    //Serial1.begin(9600);
    
    //Merken des in UBRR zu speichernden Wertes.
    //Examples of Baud Rate Setting, Seite 206
    //Seite 191 Datenblatt
    //Seite 213 Datenblatt
//    unsigned int baudregister = (TAKTFREQUENZ/(16*BAUDRATE))-1; 
    unsigned int baudregister = (1000000/BAUDRATE)-1; 

    //setzen der Baudrate
    //UBRRH1 = (unsigned char) (baudregister>>8); //Setzen des HIGH-Bytes des Baudraten-Registers
    //UBRRL1 = (unsigned char)  baudregister;     //Setzen des LOW -Bytes des Baudraten-Registers
    UBRR1 = baudregister;
    //Einschalten des Senders und des Empfängers
    //Seite 190 Datenblatt
    UCSR1B = (1<<TXEN1) | (1<<RXEN1) | (0<<UCSZ12);  

    //Setzen des Nachrichtenformats: 8 Datenbits, 1 Stopbits
    //  USBS1 == 0 == 1 stop bit
    //  UCSZ12 UCSZ11 UCSZ10 == 0 1 1 == 8 Datenbit
    //Seite 212 Datenblatt
    UCSR1C = (1<<UCSZ11) |  (1<<UCSZ10) |  (0<<USBS1);
}

void loop() 
{
    while( !(UCSR1A & (1<<RXC1)) ) DDRB|=0;  //Warten bis der Uebertragungspuffer ein Zeichen empfangen hat
  
    int c = UDR1;
    //Serial1.write(c+1);
    while( !(UCSR1A & (1<<UDRE1)) ) DDRB|=0; //Warten bis der Uebertragungspuffer leer ist
    UDR1 = c+1;
}

Code 0-9: Sertest2reg

5. Objektorientierte Kapselung der seriellen Schnittstelle

(Saal-) ÜBUNG
  • Entwerfen Sie eine Klasse, die die oben verwendete Funktionalität der Register-Befehle birgt.
  • Orientieren Sie sich bei dem Entwurf geeigneter Methoden an der Arduino-Library.

6. Auslesen eines MPU6050 über die I2C-Schnittstelle mittels Library

Der MPU6050 vereinigt einen dreiachsigen Beschleunigungssensor und einen dreiachsiges Gyroskopsensor. Er findet Anwendung in mobilen Devices, steht aber auch für die Verwendung mit Arduino auf kleinen Zusatzplatinen zur Verfügung. Er kann über den I2C-Bus angesprochen und ausgelesen werden.

MPU6050 ---- Arduino

VCC     ---- 3.3V
GND     ---- GND
SCL     ---- SCL
SDA     ---- SDA

Code 0-10: Verbindung MPU6050 mit Arduino

Schematischer Versuchsaufbau zum Testen des MPU6050.

Bild 0-6: Schematischer Versuchsaufbau zum Testen des MPU6050.

Neben einem dreiachsigen Beschleunigungssensor und einem Gyriskop besitzt der MPU6050 auch einen Temperaturfühler, der sich so kalibrieren läßt, dass die Werde gleich in Grad Celsius zur Verfügung stehen.

Die Abfrage dieser Temperaturwerte ist nicht ganz einfach, da sie über ein Bussystem (SPI-Bus) erfolgt. Es gibt bestimmte Befehlsfolgen, die den Baustein konfigurieren und in setup() stehen und andere, die die Daten abfragen.

Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).

Bild 0-7: Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).

Darstellung des Aufbaus auf dem Steckboard.

Bild 0-8: Darstellung des Aufbaus auf dem Steckboard.

#include<math.h>
#include<Wire.h>
const int MPU=0x68;  // I2C address of the MPU-6050
int16_t AcX=0,AcY=0,AcZ=0,Tmp=0,GyX=0,GyY=0,GyZ=0;
double TEMPERATUR = 0.0;
void setup() 
{
  Serial.begin(9600);
  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);
  delay(500);
}

void loop() 
{
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,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)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

  TEMPERATUR = Tmp/340.00+36.53;
  
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(TEMPERATUR);  //equation for temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.print(GyZ);
  
  delay(500);
}

Code 0-11: Testprogramm, um alle Sensordaten, die der MPU6050 liefert im Serial Monitor ansehen zu können.

TemperaturAbfrage.zip -- Sketch mit obigem Testprogramm als .zip-File -- Sie können das .zip-File herunterladen und in Ihren Sketch-Ordner legen (Name Arduino). Nach einem Neustart der IDE erscheint auch dieser Sketch im Sketchbook.
Aufgaben
  • Bemühen Sie sich um ein grundsätzliches Verständnis des Testprogramms. Dieses wird im gemeinsamen Gespräch während der LV noch vertieft werden. Fragen Sie bitte proaktiv nach.
(Saal-) ÜBUNG

Emulieren Sie in folgender einfacher Weise eine serielle Schnittstelle mit Hilfe eines gewöhnlichen digitalen Ausgangs:

Die Hardwareanordnung entspricht der von oben bei Sertest1 und Sertest2. Die Aufeinanderfolge von Low und High-Pegeln soll auf dem gleichen digitalen Ausgang für den zweiten Mikrocontroller so erfolgen, dass dadurch seriell mit einem Start-Bit und acht Datenbits zyklisch der Buchstabe "A" an den ersten Mikrocontroller gesendet wird. Nutzen Sie den Timer 1 im CTC-Mode, um die Dauer der Low- und Highpegel zu steuern.