kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




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

(EN google-translate)

(PL google-translate)

  • Angaben zu Veranstaltungszeiten und Räumen dieser Lehrveranstaltung finden Sie auf der Startseite kramann.info dieses Portals.
  • Hier bei "day by day" werden chronologisch im Verlauf des Semesters die behandelten Inhalte vermerkt.
  • Meistens werden Links innerhalb von kramann.info angegeben, wo der jeweils behandelte Stoff dargestellt wird.
  • Zur Orientierung finden Sie auf kramann.info auch noch das "day by day" der gleichen Lehrveranstaltung vom vorangehenden Jahr.
  • Die Prüfung in diesem Fach ist eine Klausur in elektronischer Form (E-Test)
  • Die folgenden vorab angegebenen Links stellen Quellen dar, aus denen die hier behandelten Inhalte genommen werden:

kramann.info -- Angaben zu Veranstaltungszeiten und Räumen dieser Lehrveranstaltung
96_Arduino
40_Mikrocontroller
45_Mikro17
95_ETEST -- Hinweise zu der elektronischen Prüfungsform "E-Test"

#1 Fr 24.03.2023

Die Lehrveranstaltung Grundlagen der Mikrocontrollertechnik richtet sich an Studierende der Ingenieurwissenschaften im vierten Semester. Es geht um die Programmierung von Mikrocontrollern, insbesondere um die Programmierung der internen Peripherie eines Mikrocontrollers. Unter dem Begriff "interne Peripherie" werden hier digitale Ein- und Ausgänge, Bussysteme, Timer, Analog- zu Digitalwandler und PWM-Geber zusammengefaßt. Im Wesentlichen soll die Konfiguration dessen, was in der Vorlesung mit interner Peripherie erlernt werden, sowie die Entwicklung von Software, die dann Gebrauch von dieser internen Peripherie macht. Die Programmierung erfolgt in C und C++. Für den leichten Einstieg werden zu Beginn die Arduino-IDE und die dort verfügbaren Befehle verwendet. Im weiteren Verlauf der Lehrveranstaltung soll aber auch die direkte Konfiguration von Registern erlernt werden und in diesem Zusammenhang der Gebrauch von Datenblättern zu Mikrocontrollern. In der Lehrveranstaltung und den zugehörigen Übungen kommt der Arduino Micro zum Einsatz. Jedoch wird in allgemeineren Betrachtungen auch auf andere Mikrocontroller verwiesen.


Einführend sollen die nachfolgenden Fragen behandelt werden:

Teil 1: Klären einiger grundlegender Fragen zu Mikrocontrollern
Motivation / Diskussion ... Grey Walter's tortoises 1949
  1. Was ist ein Mikrocontroller?
  2. Wozu werden Mikrocontroller verwendet?
  3. Was ist ein engebettetes System?
  4. Wer stellt Mikrocontroller her?
  5. Wie unterscheidet sich ein Mikrocontroller von einem PC?
  6. Wie zeichnet sich ein Mikrocontroller-Programm aus?
  7. Was ist ein Cross-Compiler?
  8. Was ist Maschinen nahes Programmieren?
  9. Worin liegt der Sinn darin, höhere Programmiersprachen wie C oder C++ zu verwenden?
1.1 Mikrocontroller auf Wikipedia
1.2 Foto Dual In Line DIL Bauweise eines ATmega32 von Atmel.
  • 2. Durch die Verwendung von Mikrocontrollern werden vormals rein mechanische und elektromechanische Geräte, beispielsweise solche aus dem Haushaltsbereich wie Herd, Kühlschrank usw., zu sehr viel preiswerter herzustellenden anteilig mehr elektronischen Geräten.
3. Eingebettete Systeme auf Wikipedia.
"... Im Februar 2021 erklärtedie Bundesregierung, nach ihrer Kenntnis seien Lieferengpässe bei Halbleitern und Halbleiter-Chips für die Automobilindustrie primär entstanden, weil es weltweit nur eine sehr begrenzte Zahl von Herstellern gebe, welche die von der Automobilindustriebenötigten Halbleiter-Chips fertigten. Zudem könnten deren Produktionskapazitäten kurzfristig nicht ausgeweitet werden. Zu Prognosen und Umsatzanalysenbei Halbleitern äußerte die Bundesregierung, laut Prognosen werde sich der weltweite Umsatz an Halbleiterchips im Jahr 2024 auf 532 Mrd. US Dollar erhöhen (rund 22% höher als in 2019). Asien (inkl. China) werde mit 62% der größte Chipabnehmer sein, gefolgt von Amerika mit rund 21% und Europa rund 9%. Aus Sicht der Halbleiterfertigung (ohne die sog. Foundries -Fertigungsdienstleister) sei die USA mit 50% dominierend am Umsatz beteiligt, gefolgt von Südkorea (18%), Japan (10%), Europäische Union (9%), Taiwan (6%) und China (5%). Ein anderes Bild ergebe sich, wenn die Foundries in der Analyse berücksichtigt würden. Mit rund 76% kontrollierten China, Taiwan, Südkorea und Japan den Halbleiterumsatzmarkt. Die USA falle mit 11% zurück, gefolgt von der Europäischen Union mit 8% und Singapur mit 5%.... "

zu 4. Quelle: Bundestag Einzelanfrage: Einzelfragen zurHalbleiterproduktion, Seite 10. Web 21.03.2023: https://www.bundestag.de/resource/blob/900450/25b9b36d92928f932b242efc8578386a/WD-5-004-22-pdf-data.pdf

Hersteller in Europa: NXP, ST, Infineon. Japan: Renesas. USA: Microchip.

5.1 Alan Turings Grundkonzept
5.2 Die unterschiedliche Architektur beim PC und beim Mikrocontroller
6. Wesentliches Merkmal eines Mikrocontroller-Programms: Die Loop-Funktion und die Manipulation von Registern.
  • 7. Mikrocontroller besitzen in der Regel keine Benutzerschnittstelle wie ein PC. Das Entwickeln und Kompilieren der Programme für einen Mikrocontroller erfolgt darum auf einem PC mit einer Entwicklungsumgebung und einem Cross-Compiler.
8. Exkurs: Programmentwurf in Assembler.

9. Höhere Programmiersprachen kommen zum Einsatz um die Wartbarkeit und Transparenz von Programmen zu verbessern.

Teil 2: Vorbereitungen auf die erste Übung
  1. Booten und Verwenden der (Linux-) Xubuntu-Distribution auf den PC-Pool-Rechnern
  2. Verwendung der Arduino-IDE
  3. Konfiguration der Arduino-IDE für einen über USB verbundenen Arduino Micro - Mikrocontroller
  4. Schreiben eines Programms
  5. Kompilieren eines Programms
  6. Übertragen eines Programms auf den Mikrocontroller
  7. Ansteuern digitaler Ein- und Ausgänge über Arduino-Befehle
  8. Vorbesprechung der ersten Übungsaufgaben
96_Arduino -- Überblick zum Arduino Micro
96_Arduino/01_Lauflicht -- Hardware (Steckboard mit Fritzing dargestellt) und Software (C / Arduino-Befehle) für ein "Lauflicht".
Im Verlauf des Unterrichts entstandene Quelltexte:
void setup() 
{
    pinMode(1,OUTPUT);
}

void loop() 
{
    digitalWrite(1,HIGH);
    delay(1000);
    digitalWrite(1,LOW);
    delay(1000);
}

Code 0-1: Blinkende LED

void setup() 
{
    pinMode(1,OUTPUT);
    pinMode(2,INPUT);
    digitalWrite(2,HIGH); //Pullup aktivieren, intern auf +5V setzen
}
int x;
void loop() 
{
    x = digitalRead(2);
    if(x<=0)
    {
        digitalWrite(1,HIGH);
        delay(1000);
        digitalWrite(1,LOW);
        delay(1000);
    }    
}

Code 0-2: Blinkende LED mit Taster

void setup() 
{
    pinMode(1,OUTPUT);
    pinMode(2,INPUT);
    digitalWrite(2,HIGH); //Pullup aktivieren, intern auf +5V setzen
}
int x;
void loop() 
{
    x = digitalRead(2);
    if(x<=0)
    {
        //digitalWrite(1,HIGH);
        //PORTD = 8;
        PORTD = 0b00001000;
        delay(1000);
        //digitalWrite(1,LOW);
        PORTD = 0;
        delay(1000);
    }    
}

Code 0-3: Blinkende LED unter Verwendung von Register-Befehlen

Programmieren mit Hilfe von ChatGPT (https://chat.openai.com/chat 21.03.2023):

Schreibe ein Arduino-Programm für den Arduino Micro, bei dem eine LED auf dem digitalen Ausgang D12 mit einem Hertz zum Blinken gebracht wird.

void setup() {
  pinMode(12, OUTPUT); // setze digitalen Ausgang D12 als Ausgang
}

void loop() {
  digitalWrite(12, HIGH); // schalte LED an
  delay(500); // warte 500 Millisekunden
  digitalWrite(12, LOW); // schalte LED aus
  delay(500); // warte weitere 500 Millisekunden
}

Code 0-4: Antwort von ChatGPT

"Das Programm setzt den digitalen Ausgang D12 als Ausgangspin und verwendet die digitalWrite()-Funktion, um die LED ein- und auszuschalten. Das Programm verwendet auch die delay()-Funktion, um eine Pause zwischen den Zustandsänderungen der LED einzufügen. In diesem Fall warten wir 500 Millisekunden (0,5 Sekunden) zwischen dem Einschalten und Ausschalten der LED, um eine Frequenz von einem Hertz zu erreichen. "

...und Kommentar von ChatGPT



ÜBUNG: Bauen Sie eine Arduino-Schlatung für Blinken auf und flashen Sie nachfolgende Programme.


Im Unterricht entstandene Programme
void setup() 
{
    pinMode(12,OUTPUT);
}

void loop() 
{
    digitalWrite(12,1);
    delay(500);
    digitalWrite(12,0);
    delay(500);
}

Code 0-5: Blinken auf digitalen Pin 12 mittels Arduino-Befehlen.

void setup() 
{
    //pinMode(12,OUTPUT);
    DDRD = 0b01000000;
}

void loop() 
{
    //digitalWrite(12,1);
    PORTD = 0b01000000;
    delay(500);
    //digitalWrite(12,0);
    PORTD = 0b00000000;
    delay(500);
}

Code 0-6: Blinken auf digitalen Pin 12 mittels Registerbefehlen.

#2 Fr 31.03.2023

Themen
  1. Wiederholung der praktischen Aspekte der Programmentwicklung für den Arduino Micro
  2. Quiz: Wiederholung wichtiger Merkmale eines Mikrocontrollers
  3. Vertiefung der bisherigen Programmierkenntnisse anhand der Analyse einfacher Beispiele
  4. Systematische Einführung von Registerbefehlen im Zusammenhang mit digitalen Ein- und Ausgängen
1. Wiederholung der praktischen Aspekte der Programmentwicklung für den Arduino Micro
  1. Booten und Verwenden der (Linux-) Xubuntu-Distribution auf den PC-Pool-Rechnern
  2. Verwendung der Arduino-IDE
  3. Konfiguration der Arduino-IDE für einen über USB verbundenen Arduino Micro - Mikrocontroller
  4. Schreiben eines Programms
  5. Kompilieren eines Programms
  6. Übertragen eines Programms auf den Mikrocontroller
  7. Ansteuern digitaler Ein- und Ausgänge über Arduino-Befehle
2. Quiz: Wiederholung wichtiger Merkmale eines Mikrocontrollers
  1. Wozu werden Mikrocontroller verwendet?
  2. Was ist ein engebettetes System?
  3. Wie unterscheidet sich ein Mikrocontroller von einem PC?
  4. Wie zeichnet sich ein Mikrocontroller-Programm aus?
  5. Was ist ein Cross-Compiler?
  6. Was ist Maschinen nahes Programmieren?
  7. Worin liegt der Sinn darin, höhere Programmiersprachen wie C oder C++ zu verwenden?
  8. Was wird in dieser Lehrveranstaltung unter dem Begriff interne Peripherie verstanden?
  9. Welche Programmier-technische Bedeutung haben Register beim Mikrocontroller?
  10. Wie groß ist ein Register?
  11. Haben Sie Bitmasken und Shiftoperatoren in der Informatikvorlesung behandelt? Was ist Ihnen erinnerlich?
40_Mikrocontroller/01_Einfuehrung/02_Maschinensprache
40_Mikrocontroller/01_Einfuehrung/03_Assemblerbeispiel
3. Vertiefung der bisherigen Programmierkenntnisse anhand der Analyse einfacher Beispiele
void setup() 
{
    pinMode(1,OUTPUT);
}

void loop() 
{
    digitalWrite(1,HIGH);
    delay(1000);
    digitalWrite(1,LOW);
    delay(1000);
}

Code 0-7: Blinkende LED

void setup() 
{
    pinMode(1,OUTPUT);
    pinMode(2,INPUT);
    digitalWrite(2,HIGH); //Pullup aktivieren, intern auf +5V setzen
}
int x;
void loop() 
{
    x = digitalRead(2);
    if(x<=0)
    {
        digitalWrite(1,HIGH);
        delay(1000);
        digitalWrite(1,LOW);
        delay(1000);
    }    
}

Code 0-8: Blinkende LED mit Taster

void setup() 
{
    pinMode(1,OUTPUT);
    pinMode(2,INPUT);
    digitalWrite(2,HIGH); //Pullup aktivieren, intern auf +5V setzen
}
int x;
void loop() 
{
    x = digitalRead(2);
    if(x<=0)
    {
        //digitalWrite(1,HIGH);
        //PORTD = 8;
        PORTD = 0b00001000;
        delay(1000);
        //digitalWrite(1,LOW);
        PORTD = 0;
        delay(1000);
    }    
}

Code 0-9: Blinkende LED unter Verwendung von Register-Befehlen

4. Systematische Einführung von Registerbefehlen im Zusammenhang mit digitalen Ein- und Ausgängen
  • Zugreifbar von Außen ist bei dem ArduinoMicro lediglich der Port B.
  • Jedem digitalen Ein- und Ausgang X sind drei Port-Register zugeordnet: PORTX, PINX, DDRX.
  • Beim Port B sind das somit: PORTB, PINB und DDRB.
  • DDRB legt die Richtung jedes der 8 Bits fest, also ob es als Ein- oder Ausgang verwendet wird.
  • PINB dient zur Abfrage, ob am Eingang etwas anliegt.
  • PORTB wird dazu verwendet, Ausgangszustände zu setzen und Pullup-Widerstände zu aktivieren.

Wie der Port B beim ArduinoMicro abgerufen werden kann, ist hier dargestellt:

96_Arduino/22_Universal/02_LED_Leiste -- siehe Bild 0-1: Anschlußplan der LED-Leiste.

Das ganze IO-Registerthema wird für den ATmega32 bzw. ATmega32u4 ausführlich hier behandelt:

  • Darstellung der IO-Register im Datenblatt
  • Elektrische Eigenschaften der digitalen Ein- und Ausgänge
  • Pullup-Widerstände
  • Zuständige Konfigurationsregister
  • Arbeiten mit Bitmasken und Bitshift-Operatoren
40_Mikrocontroller/03_DigitalIO/01_Elektrische_Eigenschaften
40_Mikrocontroller/03_DigitalIO/02_Pullup_Widerstaende
40_Mikrocontroller/03_DigitalIO
40_Mikrocontroller/03_DigitalIO/03_Bitmasken_Eingang
Im Verlauf des Unterrichts veränderte Varianten von "Blinkende LED mit Taster"
void setup() 
{
    pinMode(12,OUTPUT);
    pinMode(11,INPUT);
    digitalWrite(11,HIGH); //Pullup aktivieren, intern auf +5V setzen
}
int x;
void loop() 
{
    x = digitalRead(11);
    if(x<=0)
    {
        digitalWrite(12,HIGH);
        delay(300);
        digitalWrite(12,LOW);
        delay(300);
    }    
}

Code 0-10: Version 1: Verwendung von Arduino-Befehlen

void setup() 
{
    //pinMode(10,OUTPUT); //PB6 auf 1 in DDRB
   //      MSB      LSB
    DDRB = 0b01000000;
    //DDRB = 64; wäre das Gleiche!
    //pinMode(11,INPUT);  //PB7 auf 0 in DDRB
    //DDRB = 0b00000000;
    
    //digitalWrite(11,HIGH); //Pullup aktivieren, intern auf +5V setzen
    PORTB = 0b10000000;  //PB7 bei PORTB setzen == Pullup auf PB7
}
int x;
void loop() 
{
    //x = digitalRead(11);
    x = PINB; //nur PB7 interessant wäre 128
    if(x<128)
    {
        //digitalWrite(10,HIGH);
        PORTB = 0b11000000; // PB6=1, PB7 BLEIBT 1 wg. Pullup
        delay(300);
        //digitalWrite(10,LOW);
        PORTB = 0b10000000; // PB6=0, PB7 BLEIBT 1 wg. Pullup
        delay(300);
    }    
}

Code 0-11: Version 2: Verwendung von Register-Befehlen

void setup() 
{
    //pinMode(10,OUTPUT); //PB6 auf 1 in DDRB
   //      MSB      LSB
    //DDRB = 0b01000000;
    DDRB = DDRB | 0b01000000;  //gezielt PB6 auf 1 setzen => PB6 ist Ausgang
    //DDRB = 64; wäre das Gleiche!
    //pinMode(11,INPUT);  //PB7 auf 0 in DDRB
    //DDRB = 0b00000000;
    DDRB = DDRB & 0b01111111;  //gezielt PB7 auf 0 setzen => PB7 ist Eingang
    
    //digitalWrite(11,HIGH); //Pullup aktivieren, intern auf +5V setzen
    //PORTB = 0b10000000;  //PB7 bei PORTB setzen == Pullup auf PB7
    PORTB = PORTB | 0b10000000;  //PB7 bei PORTB setzen == Pullup auf PB7
}
int x;
void loop() 
{
    //x = digitalRead(11);
    x = PINB; //nur PB7 interessant wäre 128
    if(x<128)
    {
        //digitalWrite(10,HIGH);
        //PORTB = 0b11000000; // PB6=1, PB7 BLEIBT 1 wg. Pullup
        //Mit Bitmaske Bit PB6 setzen, ohne andere Bits zu beeinflussen:
        PORTB = PORTB | 0b01000000; //zu setzendes Bit in Maske 1 setzen
        delay(300);
        //digitalWrite(10,LOW);
        //PORTB = 0b10000000; // PB6=0, PB7 BLEIBT 1 wg. Pullup
        PORTB = PORTB & 0b10111111; //zu löschendes Bit in Maske 0 setzen
        delay(300);
    }    
}

Code 0-12: Version 3: Verwendung von Register-Befehlen mit Bitmasken

void setup() 
{
    //pinMode(10,OUTPUT); //PB6 auf 1 in DDRB
   //      MSB      LSB
    //DDRB = 0b01000000;
//    DDRB = DDRB | 0b01000000;  //gezielt PB6 auf 1 setzen => PB6 ist Ausgang
    DDRB = DDRB | (1<<PB6);  //.. ist auch 64! gezielt PB6 auf 1 setzen => PB6 ist Ausgang
    //DDRB = 64; wäre das Gleiche!
    //pinMode(11,INPUT);  //PB7 auf 0 in DDRB
    //DDRB = 0b00000000;
//    DDRB = DDRB & 0b01111111;  //gezielt PB7 auf 0 setzen => PB7 ist Eingang
    DDRB = DDRB & (~(1<<PB7));  //gezielt PB7 auf 0 setzen => PB7 ist Eingang
    
    //digitalWrite(11,HIGH); //Pullup aktivieren, intern auf +5V setzen
    //PORTB = 0b10000000;  //PB7 bei PORTB setzen == Pullup auf PB7
    PORTB = PORTB | 0b10000000;  //PB7 bei PORTB setzen == Pullup auf PB7
}
int x;
void loop() 
{
    //x = digitalRead(11);
    x = PINB; //nur PB7 interessant wäre 128
    if(x<128)
    {
        //digitalWrite(10,HIGH);
        //PORTB = 0b11000000; // PB6=1, PB7 BLEIBT 1 wg. Pullup
        //Mit Bitmaske Bit PB6 setzen, ohne andere Bits zu beeinflussen:
        PORTB = PORTB | 0b01000000; //zu setzendes Bit in Maske 1 setzen
        delay(300);
        //digitalWrite(10,LOW);
        //PORTB = 0b10000000; // PB6=0, PB7 BLEIBT 1 wg. Pullup
        PORTB = PORTB & 0b10111111; //zu löschendes Bit in Maske 0 setzen
        delay(300);
    }    
}

Code 0-13: Version 4: Verwendung von Register-Befehlen mit Bitmasken und Bitshiftoperationen

Übungen

a) Bauen Sie die nachfolgende Lauflichtschaltung auf:

96_Arduino/22_Universal/02_LED_Leiste

b) Schreiben Sie unter Verwendung von Arduino-Befehlen ein Programm, das alle acht LEDs zum Leuchten bringt und testen das.

c) Ersetzen Sie das Programm von b) durch eines das Registerkonfigurationen benutzt, um die LEDs zum Leuchten zu bringen.

d) Schreiben Sie für die Schaltung ein Lauflichtprogramm, das Arduino-Befehle benutzt.

e) Ersetzen Sie das Programm von d) durch eines, bei dem Registerbefehle verwendet werden. Ausnahme: delay(...). Hinweis: Hier kann man mit Hilfe von Bitshift-Operationen eine sehr effiziente Lösung finden.

void setup() {
DDRB = 0b11111111;
}

void loop() {

    for(int i=0;i<8;i++){
      delay(200);
      PORTB= 0b00000001<<i;
    }

}

Code 0-14: Studentische Lösung.

f) Zusatzaufgabe: Denken Sie sich Varianten zum einfachen Lauflicht aus und entwickeln Sie ein System, bei dem man durch Taster zwischen den verschiedenen Lauflichtvarianten umschalten kann.

#3 Fr 14.04.2023

Themen

  1. Lauflicht mit Auswahl
  2. Quiz
  3. Robo-Sumo
  4. PWM Prinzip
  5. PWM mit Arduino-Befehlen
  6. Preview: PWM über Register-Konfigurationen
  7. Übungen

1. Lauflicht mit Auswahl

//Rotieren realisieren
unsigned int arr[] = {0b1111111100000000,
                      0b1000000010000000,
                      0b1010101010101010};
unsigned int auswahl = 0;
void setup() 
{
    DDRB = 0b11111111;
    Serial.begin(9600);
}
void loop() 
{
    if(Serial.available())
    {
         auswahl = arr[Serial.read()%3];
    }
    auswahl = ((auswahl << 1) | (auswahl >> 15)) ;     
    PORTB = auswahl;
    delay(200);
}

Code 0-15: Lauflicht mit Auswahl.

2. Quiz

  • Wie groß ist ein Register?
  • Welche Register-Typen gibt es im Zusammenhang mit digitalen Ein- und Ausgängen?
  • Was ist dezimal 1<<3
  • Was ist dezimal 1>>3
  • Was ist dezimal 3<<1
  • Was ist dezimal 0<<1
  • Was ist dezimal 1<<0
  • Was ist dezimal 1<
  • Was ist dezimal (1<
  • Was ist dezimal 0b00001111 & 0b11110000
  • Was ist dezimal 0b00001111 | 0b11110000
  • Was ist dezimal 0b00001111 | ~0b11110000

3. Robo-Sumo

https://www.youtube.com/watch?v=lUpUQf16qzQ
83_AV/05_SUMO/10_Umsetzung -- Zustandsübergangs-Modell

4. PWM Prinzip

40_Mikrocontroller/04_PWM

5. PWM mit Arduino-Befehlen

//Dimmen einer LED mittels "analogWrite"
void setup() 
{
  
}
int x=0;
int y=0;
void loop() 
{
    if(x<256)
       y=x;
    else
       y=511-x;   
    analogWrite(9,y);
    x++;
    x%=512;
    delay(5);
}

Code 0-16: Dimmen einer LED mittels "analogWrite"

https://www.arduino.cc/reference/en/libraries/servo/
https://docs.arduino.cc/learn/electronics/servo-motors
Übung 1
  • Schreiben Sie ein Lauflicht mit 8 möglichen Zuständen aller 8 LEDs.
  • Die 8 Zustände sollen in 8 Byte-Variablen in einem Array abgelegt sein.
  • In einem der 8 Schritte 0..7 wird das Muster aus dem Array am korrespondierenden Platz 0..7 ausgewählt und angezeigt.
Übung 2
  • Über die Tasten 1,2,3,4,5 sollen bei einem Servo die Winkel -90 Grad, -45 Grad, 0 Grad +45 Grad und +90Grad einstellbar sein.
  • Außerdem soll jeweils eine von sieben LEDs in einer Reihe passend dazu aufleuchten. Die mittlere LED grün, die anderen rot.

#4 Fr 21.04.2023

Themen
  1. Prinzip der Generierung von PWM-Signalen bei Mikrocontrollern
  2. Gemeinsame Analyse einer Servo-Ansteuerung via PWM über Register-Konfigurationen
  3. Übungen

1. Prinzip der Generierung von PWM-Signalen bei Mikrocontrollern

40_Mikrocontroller/04_PWM/03_Generierung

2. Gemeinsame Analyse einer Servo-Ansteuerung via PWM über Register-Konfigurationen

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-17: Modellbau-Servos auf OC1A und OC1B mittels Timer 1 mit hoher Genauigkeit ansteuern.

Übungen
  1. Testen Sie die Register-basierte Ansteuerung eines Servos "Modellbau-Servos auf OC1A und OC1B mittels Timer 1", s.o.
  2. Ergänzen Sie eine Funktion, der man einen Winkel als float-Wert übergibt (+/-90Grad) und den Servo in die gewünschte Position bringt.
  3. Nehmen Sie alle notwendigen Änderungen vor, wenn nun der Servo mit 50Hz angesteuert wird. -- Bringt das irgendwelche Vorteile?
  4. Recherchieren Sie, wie man den gewünschten Winkel über den seriellen Monitor an den Arduino schicken könnte und setzen das unter Verwendung der implementierten Funktion um.
  5. Zusatzaufgabe: Der aktuelle Winkel (+/-90Grad) soll mit Hilfe einer 4-fach-7-Segmentanzeige des Typs HDSP-B08G angezeigt werden.
Datenblatt zu HDSP-B08G: https://www.farnell.com/datasheets/2095874.pdf
Pinbelegung bei der 4-fach-7-Segmentanzeige HDSP-B08G.

Bild 0-1: Pinbelegung bei der 4-fach-7-Segmentanzeige HDSP-B08G.

Lage der Pins bei HDSP-B08G.

Bild 0-2: Lage der Pins bei HDSP-B08G.

Zuordnung der LEDs zu den Pins.

Bild 0-3: Zuordnung der LEDs zu den Pins.

#5 Fr 28.04.2023


Ab jetzt immer Beginn ab 09:00Uhr


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-18: 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-19: 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-20: 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-21: 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-4: 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-5: Zwei Arduino Micro werden nun von der Arduino IDE erkannt.

Aufbau der Testschaltung.

Bild 0-6: Aufbau der Testschaltung.

Kommunikation auf dem seriellen Monitor.

Bild 0-7: 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-22: 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-23: 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-8: 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-24: 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-25: Verbindung MPU6050 mit Arduino

Schematischer Versuchsaufbau zum Testen des MPU6050.

Bild 0-9: 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-10: Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).

Darstellung des Aufbaus auf dem Steckboard.

Bild 0-11: 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-26: 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.

#8 Fr 26.05.2023 -- Tag der Offenen Tür.


Unsere Lehrveranstaltung fällt aus, u.a. auch weil das Mechatroniklabor dann belegt sein wird.


#9 Fr 02.06.2023

Themen

  • Bussysteme: Auslesen eines MPU6050 über die I2C-Schnittstelle mittels Library
  • Emulation einer seriellen Schnittstelle.

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-27: Verbindung MPU6050 mit Arduino

Schematischer Versuchsaufbau zum Testen des MPU6050.

Bild 0-12: 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-13: Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).

Darstellung des Aufbaus auf dem Steckboard.

Bild 0-14: 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-28: 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.
Übung 1 -- Verkippwarnung

Entwickeln Sie folgendes System:

  • Solange der MPU6050 waagerecht liegt, soll eine grüne LED leuchten.
  • Ist die Platine des MPU6050 mehr als 10 Grad geneigt, soll statt der grünen eine rote LED leuchten.
#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;
double VecB[] = {AcX,AcY,AcZ};
double VecA[] = {0.0,0.0,-1.0};
double phi = 0.0;
double c= 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);
  pinMode(10,1);
}


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)
  VecB[0] = AcX;
  VecB[1] = AcY;
  VecB[2] = AcZ;
  VecB[0] = VecB[0]*VecB[0];
  VecB[1] = VecB[1]*VecB[1];
  VecB[2] = VecB[2]*VecB[2];
  c= VecB[0]+VecB[1]+VecB[2];
 
  phi =((sqrt(VecB[2]))/(sqrt(c)));
  Serial.print(" vorphi = "); Serial.print(phi);
  phi = acos(phi);
  phi = 360*phi/(2*3.1415);

  if(phi>3)
  digitalWrite(10,1);
  else
  digitalWrite(10,0);
  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(" | phi = "); Serial.print(phi); 
  Serial.print(" | c = "); Serial.println(sqrt(c));

  delay(200);
}

Code 0-29: Studentische Lösung.

Thema 2: Emulation einer seriellen Verbindung

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-30: 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-15: 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-16: Zwei Arduino Micro werden nun von der Arduino IDE erkannt.

Aufbau der Testschaltung.

Bild 0-17: Aufbau der Testschaltung.

Kommunikation auf dem seriellen Monitor.

Bild 0-18: 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-31: 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-32: 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-19: 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-33: Sertest2reg

ÜBUNG 2

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 eine Interrupt Service Routine, um ein zeitlich korrektes Verhalten zu realisieren.

#10 Fr 09.06.2023

Themen

  1. Wiederholungen in Form eines Quiz
  2. Kapselung des MPU6050-Moduls als Objekt
  3. Serielle Kommunikation zwischen PC und Arduino mittels Java/Processing
  4. Übungen

1. Wiederholungen in Form eines Quiz

  • Wie funktioniert der Shiftoperator?
  • Programmieren eines UND-Gatters mittels Registerkonfiguration. Eingänge PB0,PB1, Ausgang PB2.
  • Was bedeuten die Parameter in der folgenden Formel?
  • Geben Sie eine sinnvolle Servo-Konfiguration gemäß nachfolgender Formel an.
Formel, Datenblatt zu atmega32u4, Seite 127.

Bild 0-20: Formel, Datenblatt zu atmega32u4, Seite 127.

  • Wie kann eine C++-Klasse im Zusammenhang mit der Programmierung des Arduino Micro aufgebaut werden?

Mehr zu objektorientierter Programmierung


Wichtige Paradigmen der Objektorientierten Programmierung:

  1. Abstraktion
  2. Kapselung
  3. Wiederverwendbarkeit
Siehe beispielsweise auch: https://www.programmierenlernenhq.de/grundlagen-der-objektorientierten-programmierung-in-java/

Das UML-Klassendiagramm zur visuellen Darstellung von Klassen:

30_Informatik3/02_UML/02_UML_Klassendiagramm

2. Kapselung des MPU6050-Moduls als Objekt

  • Eine Klasse MPU6050 wird entwickelt, die als einzige public-Methode getWinkel() besitzt.
MPU6050_OOP_001.zip -- Projekt mit der im Unterricht entwickelten Klasse.

3. Serielle Kommunikation zwischen PC und Arduino mittels Java/Processing

4. Übungen

  1. Integrieren Sie die Klasse MPU6050 in Ihr eigenes Programm von letzter Woche und testen es.
  2. Kapseln Sie in Ihrem Programm auch die Ansteuerung der LEDs als Klasse mit den Methoden schlageAlarm() stoppeAlarm().
  3. Der Kippwinkel soll in einem Java/Processing-Programm angezeigt werden. -- Setzen Sie zuerst eine einfache Lösung um, aber wenden Sie im Anschluss die Prinzipien der Objektorientierten Programmierung auf Ihre Software an.

#11 Fr 16.06.2023 Probe E-Test

#12 Fr 23.06.2023

Themen

  1. Fragen
  2. Plotter
  3. Zustandsmaschine (State Machine)
  4. Übung
LAST CHALLENGE

Heute soll in einer größeren Übung ein X-Y-Plotter über den Arduino-Micro angesteuert werden.

Nachfolgendes Beispiel zeigt, wie das gehen kann:

  • Das Heben und Senken des Stiftes erfolgt über den TTL-Eingang von "PENLIFT" und erfolgt Arduino-seitig über den digitalen Ausgang 12.
  • Die X-Achse wird über ein PWM-Signal beim Ausgang 9, die Y-Achse über Ausgang 10 des Arduino gesteuert.
  • Bei einer Einstellung von 0.5 Volt pro Cewntimeter für beide Achsen, ergibt sich eine bemalbare Fläche von 10cm X 10cm.
  • Bei Verwendung der Arduino-Befehle analogWrite(..), kann eine Auflösung von 256 Schritten erzielt werden.
https://youtu.be/BMDe0T7cWEk -- Video zum Kreis zeichnen.
Anschlüsse am Plotter von links nach rechts: Penlifter, X-Achse analoger Eingang, Y-Achse analoger Eingang.

Bild 0-21: Anschlüsse am Plotter von links nach rechts: Penlifter, X-Achse analoger Eingang, Y-Achse analoger Eingang.

Anschlüsse beim Arduino. Blau: Ground. Links von unten nach oben rot: Ausgang 12 für den Penlifter, darüber PWM-Ausgänge 10 und 9.

Bild 0-22: Anschlüsse beim Arduino. Blau: Ground. Links von unten nach oben rot: Ausgang 12 für den Penlifter, darüber PWM-Ausgänge 10 und 9.

#include<math.h>

//Stift testen
float x,y,phi;
bool maleKreis=false;
void setup() 
{
    pinMode(12,OUTPUT);
    Serial.begin(9600);
}


void loop() 
{
    if(Serial.available())
    {
        char c = Serial.read();
        if(c==' ')
        {
            maleKreis=true;
        }
    }

    if(maleKreis==true)
    {
        phi+=0.1;
        x = (sin(phi)+1.0)*125.0;
        y = (cos(phi)+1.0)*125.0;
        analogWrite(9,x);
        analogWrite(10,y);
        delay(100);
        digitalWrite(12,HIGH);

        if(phi>=PI*2.0f)
        {
            maleKreis=false;
        }
    }
    else
    {
        digitalWrite(12,LOW);
        phi=0.0f;
    }
}

Code 0-34: Testprogramm: Ein Leerzeichen über die Serielle Schnittstelle gesendet startet das Zeichnen eines Kreises. Stopp bei Erreichen von 360Grad (2PI).

ÜBUNG
  • Alle nutzen notgedrungen den gleichen Plotter und bringen Ihr Mikrocontrollerboard zum Testen dorthin und verbinden es.
  • Zuerst wird das gegebene Programm aufgespielt und getestet.
  • Dann soll ein Programm mit feinerer Auflösung auch zum Kreis zeichnen geschrieben werden. Hierzu soll der 16-Bit-Timer 1 mit Registerbefehlen passend konfiguriert werden. (Vegleiche Servo-Beispiele).
  • Dann kommt die Kür: Ein Programm soll geschrieben werden, dass ein Kunstwerk zeichnet. Die Gruppe, die das beeindruckenste Bild zustande bringt gewinnt :-)
#include<math.h>

//Stift testen
float x,y,phi;
int MODE=0;
void setup() 
{
    MODE=0;
    pinMode(12,OUTPUT);
    digitalWrite(12,LOW);
    Serial.begin(9600);
}


void loop() 
{
    if(Serial.available())
    {
        char c = Serial.read();
        if(c==' ')
        {
            MODE=1;
        }
    }

    if(MODE==1)
    {
        digitalWrite(12,LOW);
        delay(1000);
        phi=0.0;
        x = (sin(phi)+1.0)*125.0;
        y = (cos(phi)+1.0)*125.0;
        analogWrite(9,x);
        analogWrite(10,y);
        delay(3000);
        digitalWrite(12,HIGH);
        delay(500);
        MODE=2;
    }
    else if(MODE==2)
    {
        phi+=0.1;
        x = (sin(phi)+1.0)*125.0;
        y = (cos(phi)+1.0)*125.0;
        analogWrite(9,x);
        analogWrite(10,y);
        delay(100);
        digitalWrite(12,HIGH);

        if(phi>=PI*2.0f)
        {
            MODE=0;
            digitalWrite(12,LOW);
            delay(1000);
        }
    }
    else //MODE==0
    {
        digitalWrite(12,LOW);
        phi=0.0f;
    }
}

Code 0-35: Musterlösung zu "State Machine"

OOP-Variante bei Verwendung von Timer 1:
#include<math.h>
#include "Timer1.h"
#define FAKTOR 4000.0
//Stift testen
float x,y,phi;
int MODE=0;

Timer1 timer1;

void setup() 
{
    MODE=0;
    pinMode(12,OUTPUT);
    digitalWrite(12,LOW);
    delay(500);
    Serial.begin(9600);

    timer1.start();
}


void loop() 
{
    if(Serial.available())
    {
        char c = Serial.read();
        if(c==' ')
        {
            MODE=1;
        }
    }

    if(MODE==1)
    {
        digitalWrite(12,LOW);
        delay(1000);
        phi=0.0;
        x = 500.0+(sin(phi)+1.0)*FAKTOR;
        y = 500.0+(cos(phi)+1.0)*FAKTOR;
        analogWrite(9,x);
        analogWrite(10,y);
        delay(3000);
        digitalWrite(12,HIGH);
        delay(500);
        MODE=2;
    }
    else if(MODE==2)
    {
        digitalWrite(12,HIGH);
        x = 500.0+(sin(phi)+1.0)*FAKTOR;
        y = 500.0+(cos(phi)+1.0)*FAKTOR;
        phi+=0.001;
        analogWrite(9,x);
        analogWrite(10,y);
        delay(1);

        if(phi>PI*2.0f)
        {
            MODE=0;
            digitalWrite(12,LOW);
            delay(1000);
        }
    }
    else //MODE==0
    {
        digitalWrite(12,LOW);
        phi=0.0f;
    }
}

Code 0-36: Hauptprogramm -- OOP-Variante bei Verwendung von Timer 1

//für servo, hier nicht!
//#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
class Timer1
{
    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=10000;  //bei 10cm Kantenlänge der Bilder, wäre das eine Auflösung von 0.01mm theoretisch       
           DDRB |= (1<<PB5); //OCR1A
           DDRB |= (1<<PB6); //OCR1B
           OCR1A = 0; //PWM-Breite auf Mitte setzen.  
           OCR1B = 0; //PWM-Breite auf Mitte setzen.          
        }

        void setX(int x)
        {
          OCR1A = x;
        }
        void setY(int y)
        {
          OCR1A = y;
        }
};

Code 0-37: Timer1.h (extra Tab) -- OOP-Variante bei Verwendung von Timer 1

Kür: Smiley malen, Beispiel
plotter005_kuer.zip
Smiley-Plot.

Bild 0-23: Smiley-Plot.

Einen Smiley plotten -- https://youtu.be/nwHe-E4kfdM

#13 Fr 30.06.2023 E-Test


Bitte beachten: Gruppe A: 9-10:30, Gruppe B: 10:30-12Uhr im Mechatroniklabor.