Grundlagen der Mikrocontrollertechnik im Sommersemester 2025
(EN google-translate)
(PL google-translate)
|




#1 Do 27.03.2025
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

|


|

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.



|

9. Höhere Programmiersprachen kommen zum Einsatz um die Wartbarkeit und Transparenz von Programmen zu verbessern.
Teil 2: Vorbereitungen auf die erste Übung
|


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
...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 Do 03.04.2025
Blinken: Umsetzung mit Arduino-Befehlen und alternativ mit Register-Befehlen
// Definiere den Pin für die LED const int ledPin = 11; void setup() { // Setze den Pin für die LED als Ausgang //pinMode(ledPin, OUTPUT); DDRB = 0b10000000; //PB7 als Ausgang konfigurieren. //DDRB = 128; } void loop() { // Schalte die LED ein //digitalWrite(ledPin, HIGH); PORTB = 0b10000000; // 5 Volt auf Digitalpin 11 'rausschicken, bzw. auf PB7 // Warte für 500 Millisekunden (Halbe Periode) delay(500); // Schalte die LED aus //digitalWrite(ledPin, LOW); PORTB = 0b00000000; // Warte für weitere 500 Millisekunden (andere Hälfte der Periode) delay(500); }
Code 0-7: Blinken auf PB7 mit Registerbefehlen.
Siehe auch:


Manipulation einzelner Bits bei Register-Befehlen durch Verwendung von Bitmasken
void setup() { //DDRB = 0b10000000; //PB7 als Ausgang konfigurieren. //NUR PB7 setzen, ohne die anderen Bits zu verändern!: DDRB = DDRB | 0b10000000; //Bitweises ODER-Verknüpfen eines Registers mit einer Bitmaske } void loop() { PORTB = PORTB | 0b10000000; // 5 Volt auf Digitalpin 11 'rausschicken, bzw. auf PB7 delay(500); PORTB = PORTB & 0b01111111; delay(500); }
Code 0-8: Nur höchstwertigstes Bit manipulieren (PB7), mit Hilfe von Bitmasken und Bitoperationen.
void setup() { //DDRB = 0b10000000; //PB7 als Ausgang konfigurieren. //NUR PB7 setzen, ohne die anderen Bits zu verändern!: //DDRB = DDRB | 0b10000000; //Bitweises ODER-Verknüpfen eines Registers mit einer Bitmaske //DDRB = DDRB | (1<<7); //Verschiebe die 1 sieben mal nach links: // 0b00000001 << 7 => 0b10000000 // //DDRB = DDRB | (1<<PB7); DDRB |= (1<<PB7); //bedeutet das Gleiche, wie Zeile darüber, Kurzschreibweise } void loop() { //PORTB = PORTB | 0b10000000; // 5 Volt auf Digitalpin 11 'rausschicken, bzw. auf PB7 PORTB |= (1<<PB7); delay(500); //PORTB &= (0<<PB7); // PORTB = PORTB & 0b00000000 ...ist nicht das, was wir wollen!! PORTB &= ~(1<<PB7); // PORTB = PORTB & ~(0b10000000), passt, weil: ~(0b10000000) == ~(0b01111111) // (1<<0) == 1 == 2^0 // (1<<1) == 2 == 2^1 // (1<<2) == 4 == 2^2 // (1<<3) == 8 == 2^3 // ... // (1<<7) == 128 == 2^7 delay(500); }
Code 0-9: Verwendung des Linksshift-Operators.
siehe auch:

Aufgabe 1:
|
Aufgabe 2:
|

|

Studentische Lösungen
//Lauflicht mit Bitshift-Befehlen: void setup() { //ganzer Port B als Ausgang: DDRB = 0b11111111; //DDRB = 255; } void loop() { PORTB |= (1<<PB0); delay(250); PORTB &= ~(1<<PB0); PORTB |= (1<<PB1); delay(250); PORTB &= ~(1<<PB1); PORTB |= (1<<PB2); delay(250); PORTB &= ~(1<<PB2); PORTB |= (1<<PB3); delay(250); PORTB &= ~(1<<PB3); PORTB |= (1<<PB4); delay(250); PORTB &= ~(1<<PB4); PORTB |= (1<<PB5); delay(250); PORTB &= ~(1<<PB5); PORTB |= (1<<PB6); delay(250); PORTB &= ~(1<<PB6); PORTB |= (1<<PB7); delay(250); PORTB &= ~(1<<PB7); delay(250); }
Code 0-10: Lösung 1
//Kür: void setup() { //ganzer Port B als Ausgang: DDRB = 0b11111111; //DDRB = 255; } void loop() { for ( int i=0;i<7;i++) { PORTB |= (1<<i); delay(100); PORTB &= ~(1<<i); } for ( int i=7;i>0;i--) { PORTB |= (1<<i); delay(100); PORTB &= ~(1<<i); } }
Code 0-11: Lösung 2
void setup() { // put your setup code here, to run once: DDRB = 0b11111111; //DDRB = 255; //PB0 <=> 17 //PB1 <=> 15 //PB2 <=> 16 //PB3 <=> 14 pinMode(17, OUTPUT); //PB0 pinMode(15, OUTPUT); //PB1 pinMode(16, OUTPUT); //PB2 pinMode(14, OUTPUT); //PB3 pinMode(8, OUTPUT); //PB4 pinMode(9, OUTPUT); //PB5 pinMode(10, OUTPUT); //PB6 pinMode(11, OUTPUT); //PB7 } void loop() { // put your main code here, to run repeatedly: digitalWrite(17, HIGH); delay(100); digitalWrite(17, LOW); digitalWrite(15, HIGH); delay(100); digitalWrite(15, LOW); digitalWrite(16, HIGH); delay(100); digitalWrite(16, LOW); digitalWrite(14, HIGH); delay(100); digitalWrite(14, LOW); digitalWrite(8, HIGH); delay(100); digitalWrite(8, LOW); digitalWrite(9, HIGH); delay(100); digitalWrite(9, LOW); digitalWrite(10, HIGH); delay(100); digitalWrite(10, LOW); digitalWrite(11, HIGH); delay(100); digitalWrite(11, LOW); delay(500); digitalWrite(17, HIGH); digitalWrite(15, HIGH); digitalWrite(16, HIGH); digitalWrite(14, HIGH); digitalWrite(8, HIGH); digitalWrite(9, HIGH); digitalWrite(10, HIGH); digitalWrite(11, HIGH); delay(500); digitalWrite(17, LOW); digitalWrite(15, LOW); digitalWrite(16, LOW); digitalWrite(14, LOW); digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, LOW); digitalWrite(11, LOW); delay(500); digitalWrite(11, HIGH); delay(100); digitalWrite(11, LOW); digitalWrite(10, HIGH); delay(100); digitalWrite(10, LOW); digitalWrite(9, HIGH); delay(100); digitalWrite(9, LOW); digitalWrite(8, HIGH); delay(100); digitalWrite(8, LOW); digitalWrite(14, HIGH); delay(100); digitalWrite(14, LOW); digitalWrite(16, HIGH); delay(100); digitalWrite(16, LOW); digitalWrite(15, HIGH); delay(100); digitalWrite(15, LOW); digitalWrite(17, HIGH); delay(100); digitalWrite(17, LOW); delay(500); digitalWrite(17, HIGH); digitalWrite(15, HIGH); digitalWrite(16, HIGH); digitalWrite(14, HIGH); digitalWrite(8, HIGH); digitalWrite(9, HIGH); digitalWrite(10, HIGH); digitalWrite(11, HIGH); delay(500); digitalWrite(17, LOW); digitalWrite(15, LOW); digitalWrite(16, LOW); digitalWrite(14, LOW); digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, LOW); digitalWrite(11, LOW); delay(500); }
Code 0-12: Lösung 3 -- Nur Arduino-Befehle
#3 Do 10.04.2025
Themen
Digitaler Eingang +
|
1. Schwarmvehikel im Master
|



Verwendete interne Peripherie:
|
2. Bussysteme und UART






void setup() { Serial.begin(9600); } int x=0; void loop() { Serial.print("Hallo "); Serial.println(x); delay(200); x++; }
Code 0-13: VON Arduino AN PC senden mittels Arduino-Befehlen.
void setup() { Serial.begin(9600); } void loop() { if(Serial.available()) { char c = Serial.read(); if(c=='1') { Serial.println("Es wurde die 1 gedrueckt."); } else if(c=='0') { Serial.println("Es wurde die 0 gedrueckt."); } } }
Code 0-14: VOM PC ein Zeichen empfangen.
import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port void setup() { size(200, 200); String portName = Serial.list()[0]; println("Verbindung mit "+portName); myPort = new Serial(this, portName, 9600); } void draw() { background(255); String s=""; while ( myPort.available() > 0) { // If data is available, char val = (char)myPort.read(); s+=""+val; } if(s.length()>0) println(s); } public void keyPressed() { if(key=='0') { myPort.write('0'); } else if(key=='1') { myPort.write('1'); } }
Code 0-15: Minimales Processing-Programm, um am PC mit dem Arduino zu kommunizieren, siehe auch processing.org.
3. Analoger Eingang
|




BITTE NIE VERPOLEN!!!!
|
4. Digitaler Eingang
|


5. Objektorientierte Programmierung

BEISPIELPROGRAMME
6. Übungen

Bild 0-1: Grundschaltung für alle Übungsteile.
BEISPIELPROGRAMME: Mit oberer Taste untere LED steuern:
void setup() { pinMode(11,OUTPUT); //digitalWrite(11,HIGH); pinMode(8,INPUT); digitalWrite(8,HIGH);//Pullup } void loop() { if(digitalRead(8)==0) digitalWrite(11,HIGH); else digitalWrite(11,LOW); }
Code 0-16: Arduino-Befehls-Variante.
void setup() { //pinMode(11,OUTPUT); DDRB = DDRB | 0b10000000; // PB7 auf 1 setzen //pinMode(8,INPUT); DDRB = DDRB & 0b11101111; // PB4 auf 0 setzen //digitalWrite(8,HIGH);//Pullup PORTB = PORTB | 0b00010000; // PB4 setzen, PULLUP } void loop() { if( (PINB & 0b00010000) == 0 ) PORTB = PORTB | 0b10000000; else PORTB = PORTB & 0b01111111; }
Code 0-17: Register-Befehls-Variante.
Aufgabe 1
|
Aufgabe 2
|
Aufgabe 3
|
|
#4 Do 24.04.2025
Projekt + Variation + Präsentation
|
Aufgabenstellung in Kurzfassung
|
* Übungen sind teilweise bereits in den Projekten verfügbar. Sie können diese gerne übernehmen, bzw. modifizieren.
Siehe zu Thema 1,2,3 auch:
















Siehe dazu auch:


#5 Do 07.05.2025
FORTSETZUNG Projekt + Variation + Präsentation (s.o.)
Themen
|
1. Präsentation und Übung zu Thema #13: Serielle Verbindung zweier Arduino Micro

ÜBUNG
Verändern Sie das Programm so, dass Sie beliebig viele Ziffern eingeben können und jede Ziffer wird als Zahl interpretiert und es soll 10 addiert werden. Das Ergebnis der Addition soll bei "Antwort" ausgegeben werden.
void setup() { Serial.begin(9600); Serial1.begin(9600); } void loop() { if (Serial.available()) { char c=Serial.read(); Serial1.write(c); Serial.print("Frage: "); Serial.println(c); } if (Serial1.available()) { char antwort = Serial1.read(); Serial.print("Antwort: "); Serial.println(antwort); } }
Code 0-18: Arduino 1 -- Ausgangsprogramm
void setup() { Serial1.begin(9600); } void loop() { if(Serial1.available()) { char c = Serial1.read(); Serial1.write(c+1); } }
Code 0-19: Arduino 2 -- Ausgangsprogramm

2. Fortsetzung der Projektarbeiten
#6 Do 15.05.2025
Themen
|
1. Einführung in die objektorientierte Programmierung bei Mikrocontrollern (entspricht Thema #6)




2. Präsentation und Übung zu Projekt #9: Ansteuerung eines LCD-Displays (Hardware geändert)
//YWROBOT //Compatible with the Arduino IDE 1.0 //Library version:1.1 #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display int x=1; int potPin = A0; int volt; void setup() { lcd.init(); // initialize the lcd // Print a message to the LCD. lcd.backlight(); lcd.setCursor(0,0); lcd.print("Herzlich Willkommen"); delay(3000); lcd.setCursor(2,1); lcd.print("Vielen Dank"); lcd.setCursor(1,2); lcd.print("fuer die Nutzung"); lcd.setCursor(1,3); lcd.print("unseres Produktes"); delay(2500); } void loop() { lcd.clear(); lcd.setCursor(0,1); lcd.print("Digital-Voltmeter"); volt = analogRead(potPin); lcd.setCursor(1,2); lcd.print("Spannung:"); lcd.setCursor(11,2); lcd.print(volt); lcd.setCursor(16,2); lcd.print("mV"); delay(200); }
Code 0-20: Studentisches Beispiel-Projekt Voltmeter.
Studentische ÜBUNG
ANSCHLUSS: Verbinden Sie die beschrifteten Anschlüsse +5Volt, GND, SDA, SCL passend mit dem Arduino Micro.
siehe auch:


|
//YWROBOT //Compatible with the Arduino IDE 1.0 //Library version:1.1 #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display void setup() { lcd.init(); // initialize the lcd // Print a message to the LCD. lcd.backlight(); lcd.setCursor(0,0); lcd.print("Herzlich Willkommen"); delay(3000); lcd.setCursor(2,1); lcd.print("Vielen Dank"); lcd.setCursor(1,2); lcd.print("fuer die Nutzung"); lcd.setCursor(1,3); lcd.print("unseres Produktes"); delay(2500); lcd.clear(); } void loop() { }
Code 0-21: ÜBUNG
3. Präsentation und Übung zu Projekt #5 Verwendung eines inkrementellen Drehgebers mit einem Arduino Micro


4. Fortsetzung der Projektarbeiten
#7 Do 22.05.2025
Themen
|
1. Erzeugung von PWM-Signalen mit Hilfe des Timers 1 und Ansteuerung eines Modellbau-Servos mit Hilfe von Registerbefehlen


|
$ f_OC1= \frac {f_clk}{2 \cdot N \cdot TOP} $
Formel 0-1: Frequenz einer Periode eines Phase- und Frequenz- korrekten PWM-Signals mit Timer 1 & N: Vorteilung & TOP: Eingestellte Zähl-Obergrenze.
ÜBUNG
|
#define WMIN 1000 #define WMITTE 1500 #define WMAX 2000 #define SCHRITTE 1000 //Mode 8 (vergleiche Datenblatt zum ATmega32u4, der im Arduino Micro verbaut ist) //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. 256, s.S. 125 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; OCR1B = WMAX; delay(3000); OCR1A = WMITTE; OCR1B = WMITTE; delay(3000); OCR1A = WMIN; OCR1B = WMIN; delay(3000); }
Code 0-22: Beispielprojekt.
Varianten an studentischen Lösungen
#define WMIN 1000 #define WMITTE 1500 #define WMAX 2000 #define SCHRITTE 1000 //Mode 8 (vergleiche Datenblatt zum ATmega32u4, der im Arduino Micro verbaut ist) //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. 256, s.S. 125 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() { for(int i=1000; i<2000; i++) { OCR1A = i; OCR1B = i; delay(2); } }
Code 0-23: Studentische Lösung 1.
#define WMIN 1000 #define WMITTE 1500 #define WMAX 2000 #define SCHRITTE 1000 int pos = 0; 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. 256, s.S. 125 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() { for(pos = WMIN; pos <= WMAX; pos += 5) { OCR1A = pos; delay(15); } for(pos = WMAX; pos >= WMIN; pos -= 5) { OCR1A = pos; delay(15); } }
Code 0-24: Studentische Lösung 2.
2. Fortsetzung Objektorientierte Programmierung am Beispiel eines Servos.
3. Präsentationen
Studentisches Projekt: Pulsweitenmodulation selber programmieren
Originalcode:
int zaehler = 0; int xPWM = 0; void setup() { // put your setup code here, to run once: pinMode(9,OUTPUT); pinMode(4, OUTPUT); pinMode(3, OUTPUT); pinMode(13, OUTPUT); //Board LED //Eine Richtung festlegen und PWM aus: digitalWrite(4,HIGH); digitalWrite(9,LOW); digitalWrite(3,LOW); //LED aus digitalWrite(13,LOW); } void loop() { digitalWrite(3,HIGH); //20 Ticks == 0,02 Sekunden entspricht 50Hz delay(xPWM); digitalWrite(3,LOW); delay(20-xPWM); if(zaehler==0) { digitalWrite(13,HIGH); xPWM++; xPWM%=20; } else if(zaehler==25) digitalWrite(13,LOW); zaehler++; zaehler%=50; //eine Sekunde == 50 PWM-Perioden }
Code 0-25: Originalcode.
Erklärung:
Der Code steuert einen Gleichstrommotor über eine L293-Motortreiberbrücke. Dabei wird eine feste Richtung eingestellt, und der Motor wird durch ein manuell erzeugtes PWM-Signal an Pin 3 mit variierender Pulsweite (xPWM) angesteuert.
|
Der Motor startet bei 0 % Tastverhältnis (steht) und wird jede Sekunde ein kleines Stück schneller, bis xPWM wieder bei 0 ist (wegen % 20). Dadurch ergibt sich ein langsames Hochdrehen mit Wiederholung.
Variante: Der Motor dreht immer schneller in eine Richtung, dann wird langsamer und dann dreht immer schneller in die andere Richtung und so weiter und so fort:
int zaehler = 0; int xPWM = 0; bool rampingUp = true; bool forward = true; void setup() { pinMode(9, OUTPUT); pinMode(4, OUTPUT); pinMode(3, OUTPUT); pinMode(13, OUTPUT); digitalWrite(4, HIGH); digitalWrite(9, LOW); digitalWrite(3, LOW); digitalWrite(13, LOW); } void loop() { digitalWrite(3, HIGH); delay(xPWM); digitalWrite(3, LOW); delay(20 - xPWM); if (zaehler == 0) { digitalWrite(13, HIGH); if (rampingUp) { xPWM++; if (xPWM >= 19) rampingUp = false; } else { xPWM--; if (xPWM <= 0) { rampingUp = true; forward = !forward; if (forward) { digitalWrite(4, HIGH); digitalWrite(9, LOW); } else { digitalWrite(4, LOW); digitalWrite(9, HIGH); } } } } zaehler++; zaehler %= 50; }
Code 0-26: Variante.
Variation: Schalten der LEDs in Parallelschaltung, sodass: Eine LED leuchtet auf, wenn der Motor in eine Richtung läuft, eine andere leuchtet auf, wenn er in eine andere Richtung läuft.
Übung:
|
int zaehler = 0; int xPWM = 0; bool rampingUp = true; bool forward = true; void setup() { pinMode(9, OUTPUT); pinMode(4, OUTPUT); pinMode(3, OUTPUT); pinMode(13, OUTPUT); digitalWrite(4, HIGH); digitalWrite(9, LOW); digitalWrite(3, LOW); digitalWrite(13, LOW); //TASTER EINRICHTEN pinMode(10,INPUT); digitalWrite(10,HIGH); //Pullup setzen } void loop() { if(digitalRead(10)==LOW) { forward = !forward; delay(200); } digitalWrite(3, HIGH); delay(xPWM); digitalWrite(3, LOW); delay(20 - xPWM); if (zaehler == 0) { digitalWrite(13, HIGH); if (rampingUp) { xPWM++; if (xPWM >= 19) rampingUp = false; } else { xPWM--; if (xPWM <= 0) { rampingUp = true; //forward = !forward; if (forward) { digitalWrite(4, HIGH); digitalWrite(9, LOW); } else { digitalWrite(4, LOW); digitalWrite(9, HIGH); } } } } zaehler++; zaehler %= 50; }
Code 0-27: Musterlösung.
ENDE
4. Fortsetzung der Projektarbeiten
#9 Do 05.06.2025
Themen
|
A Theoretischer Teil
|
1. Vorstellung von Thema #7 Tonerzeugung über Toggle-Ausgängen im CTC-Mode : 96_Arduino/28_CTC

gemeinsamer Blick ins Datenblatt zum ATmega32u4.
2. Ergänzungen zur Darstellung von Thema #7, insbesondere Entwurf zu einer objektorientierten Herangehensweise



Angaben zum zerstörungsfreien Anschluss eines Lautsprechers
Entwurf einer sinnvollen Struktur im Zusammenhang mit der Tonerzeugung im CTC-Mode.
3. Verwendung eines IMU vom Typ MPU6050 über das I2C (aus patentrechtlichen Gründen im Datenblatt TWI -- Two Wire Interface -- genannt) Bussystem genannt
|



B Praktischer Teil
|
Test zur Tonerzeugung mit 440Hz -- Ausgangspunkt für Ihr Projekt
void setup() { TCCR1B &= ~(1<<WGM13); //Mode 4 TCCR1B |= (1<<WGM12); TCCR1A &= ~(1<<WGM11); TCCR1A &= ~(1<<WGM10); //Vorteilung 1: 16000000Hz TCCR1B &= ~(1<<CS12); TCCR1B &= ~(1<<CS11); TCCR1B |= (1<<CS10); //f = fclk/(2*N*(OCR1A+1)) //OCR1A = (fclk/(f*2*N))-1 //OCR1A = 8000000/440 - 1 == 18181 OCR1A=18181; //0,5Hz == 4 Schläge! //Toggle auf OC1A: TCCR1A &= ~(1<<COM1A1); TCCR1A |= (1<<COM1A0); pinMode(9,OUTPUT); } void loop() { }
Code 0-28: Test zur Tonerzeugung mit 440Hz -- Ausgangspunkt für Ihr Projekt.
Musterlösung zur OOP Umsetzung (studentische Lösung)

#include<math.h> #include<Wire.h> #include "tongenerator.h" //MPU6050: const int MPU=0x68; // I2C address of the MPU-6050 int16_t AcX,AcY,AcZ; int x,y,z; tongenerator n; void setup() { //MPU6050 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); Serial.begin(9600); n.start(); } void loop() { // put your main code here, to run repeatedly: 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) x = (AcX/163); if(x>100) x=100; if(x<-100) x=-100; y = (AcY/163); if(y>100) y=100; if(y<-100) y=-100; z = (AcZ/163); if(z>100) z=100; if(z<-100) z=-100; n.dreh(z); Serial.print(x); Serial.print(" "); Serial.print(y); Serial.print(" "); Serial.println(z); delay(200); }
Code 0-29: mpu.ino
class tongenerator { public: const unsigned int LOOKUP_OCR1[128] = { 61155, 57723, 54483, 51425, 48539, 45814, 43243, 40816, 38525, 36363, 34322, 32395, 30577, 28861, 27241, 25712, 24269, 22907, 21621, 20407, 19262, 18181, 17160, 16197, 15288, 14430, 13620, 12855, 12134, 11453, 10810, 10203, 9630, 9090, 8580, 64792, 61155, 57723, 54483, 51425, 48539, 45814, 43243, 40816, 38525, 36363, 34322, 32395, 30577, 28861, 27241, 25712, 24269, 22907, 21621, 20407, 19262, 18181, 17160, 16197, 15288, 14430, 13620, 12855, 12134, 11453, 10810, 10203, 9630, 9090, 8580, 8098, 7644, 7214, 6809, 6427, 6066, 5726, 5404, 5101, 4815, 4544, 4289, 4049, 3821, 3607, 3404, 3213, 3033, 2862, 2702, 2550, 2407, 2272, 2144, 2024, 1910, 1803, 1702, 1606, 1516, 1431, 1350, 1275, 1203, 1135, 1072, 1011, 955, 901, 850, 803, 757, 715, 675, 637, 601, 567, 535, 505, 477, 450, 425, 401, 378, 357, 337, 318}; const unsigned int LOOKUP_N1[128] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; void start() { TCCR1B &= ~(1<<WGM13); //Mode 4 TCCR1B |= (1<<WGM12); TCCR1A &= ~(1<<WGM11); TCCR1A &= ~(1<<WGM10); //Vorteilung 1: 16000000Hz TCCR1B &= ~(1<<CS12); TCCR1B &= ~(1<<CS11); TCCR1B |= (1<<CS10); //f = fclk/(2*N*(OCR1A+1)) //OCR1A = (fclk/(f*2*N))-1 //OCR1A = 8000000/440 - 1 == 18181 //OCR1A=18181; //0,5Hz == 4 Schläge! //Toggle auf OC1A: TCCR1A &= ~(1<<COM1A1); TCCR1A |= (1<<COM1A0); pinMode(9,OUTPUT); } int x=0, y=0; void dreh(int midi) { if (midi<0) midi = midi*-1; TCCR1B &= 248; //unteren drei Bit löschen TCCR1B |= LOOKUP_N1[midi]; OCR1A=LOOKUP_OCR1[midi]; } };
Code 0-30: tongenerator.h
#10 Do 12.06.2025
Die heutige Lehrveranstaltung findet online statt. Der Link für die Videokonferenz wurde per E-Mail verschickt.
Themen
|
1. Organisatorisches
|
2. Quiz
|
3. Übungsaufgaben
Die Aufgaben werden vorbesprochen, dann erfolgt eine Arbeitsphase und zu einem vereinbarten Zeitpunkt kommen wir wieder zusammen und besprechen alles.
Aufgabe 1
Geben Sie als Dezimalzahl an, was die nachfolgenden Bitoperationen ergeben. Beispiel: 0b11110000 + 0b00001111 = 0b11111111 entspricht dezimal 255. Sie müßten dann 255 eintragen.
(0b10101010 | 0b00010101) ergibt als Dezimalzahl:
(0b00001001 & 0b10101011) ergibt als Dezimalzahl:
~(0b11001100 | 0b00110000) ergibt als Dezimalzahl:
Aufgabe 2

Bild 0-2: Arduino-Micro Pinlayout.
Vervollständigen Sie das nachfolgende Arduino-Micro-Programm so, dass am digitalen Ausgang Nr. 11 zyklisch für eine Sekunde ein High-Pegel (+5Volt) anliegt, gefolgt von einem Low-Pegel, ebenfalls für eine Sekunde. Das Programm beginnt mit einem High-Level. Verwenden Sie die Arduino-Bibliotheks-Befehle.
void setup() { //...vervollständigen } void loop() { //...vervollständigen }
Code 0-31: Grundaufbau des Mikrocontroller-Programms
|
Aufgabe 3
Betrachten Sie erneut die Angaben aus Aufgabe 2. Vervollständigen Sie das nachfolgende Programm so, dass es das gleiche macht, wie das Programm aus Aufgabe 2. Verwenden Sie aber jetzt Port-Befehle zur Konfiguration der Register des Mikrocontrollers anstatt Arduino-Befehle. Eine Ausnahme stellt die Realisierung der Pausen dar. Diese kann wie oben erfolgen.
HINWEIS: Die Bitzuordnung der verwendeten Pins zu PORTB ist im Bild oben mit vermerkt (PB 4 5 6 7). Die Aufgabe soll so gelöst werden, dass keine Registerbits geändert werden, die nicht benötigt werden. Der Zustand aller nicht benötigten Registerbits gilt als unbekannt.
void setup() { //...vervollständigen } void loop() { //...vervollständigen }
Code 0-32: Grundaufbau des Mikrocontroller-Programms
Aufgabe 4
Betrachten Sie nachfolgenden Schaltplan mit einem Arduino-Micro. Ergänzen Sie das zugehörige Mikrocontroller C-Programm so, dass die LED nur leuchtet, wenn gleichzeitig Taster 1 (T1) und Taster 2 (T2) gedrückt sind.
HINWEIS: Verwenden Sie nur Registerbefehle. Die Bitzuordnung der verwendeten Pins zu PORTB ist im Bild mit vermerkt (PB 4 5 6 7). Die Aufgabe soll so gelöst werden, dass keine Registerbits geändert werden, die nicht benötigt werden. Der Zustand aller nicht benötigten Registerbits gilt als unbekannt.

Bild 0-3: Arduinoschaltung.
void setup() { //...vervollständigen } void loop() { //...vervollständigen }
Code 0-33: Grundaufbau des Mikrocontroller-Programms
|
Aufgabe 5
Im so genannten Normal-Mode (siehe Tabellen unten) zählt der 16-Bit-Timer 1 mit einer Taktrate hoch, die der Taktfrequenz des Mikrocontrollers geteilt durch eine Vorteilung N entspricht.
Man kann den Zählerstand über das Register TCNT1 abrufen und ihn auch im eigenen Programm ändern.
Als Ersatz für die Zeile delay(1000), könnten dann im loop()-Bereich eines Programms die folgenden beiden Zeilen stehen:
while(TCNT1<250000) PORTB|=0; TCNT1=0;
Code 0-34: Zeilen in loop() beim selbst programmierten delay(1000).
Die Taktfrequenz des Arduino-Micro ist 16MHz.
Welche Vorteilung N muß für den Timer 1 eingestellt werden (Zahl angeben)? Hinweise: Siehe Tabellen weiter unten.
Wie müssen die Befehle lauten, die diese Vorteilung festsetzen, ohne andere Registerbits zu verändern? Hinweise: Siehe Tabellen weiter unten.

Bild 0-4: timer1.

Bild 0-5: a3.

Bild 0-6: a2.
Timer/Counter1 Control Register B - TCCR1B:

Bild 0-7: tccy.
Fortsetzung der Aufgabe "Kür":
|
4. Arbeitsphase
5. Besprechung der Übungsaufgaben
Nachtrag zu Aufgabe 5
|
unsigned char akku; void setup() { DDRB |= 0b10000000; //PB7 statt PB4 TCCR1A &= 0b11111100; TCCR1B &= 0b11100101; TCCR1B |= 0b00000101; //101 => 1024 als Vorteilung } void loop() { PORTB|=0b10000000; //while(TCNT1<250000) PORTC|=0; //Kann NIE erreicht werden!!! while(TCNT1<15625) PORTC|=0; //Kann NIE erreicht werden, Maximum; 64364!!! TCNT1=0; PORTB&=~0b10000000; //while(TCNT1<250000) PORTC|=0; while(TCNT1<15625) PORTC|=0; TCNT1=0; }
Code 0-35: Mögliche Lösung zu Aufgabe 5.
#11 Do 19.06.2025
Themen
|
#12 Do 26.06.2025
Thema: Intermezzo -- Objektorientierte Programmierung bei der Simulation von Schwarmrobotik
|






Der Termin heute bietet die Gelegenheit, die Relevanz eines bereits behandelten Themas (Objektorientierte Programmierung) anhand eines spannenden Beispiels (Schwarmrobotik) aufzuzeigen und praktische Ansätze dazu aufzuzeigen:
Einzelthemen
|
1. Schwarmintelligenz in der Natur
Am Beispiel des Verhaltens von Schwärmen von Staren, lässt sich das Konzept "Schwarmverhalten" sehr anschaulich erfassen.

Wissenschaftlich wird das Verhalten als "Flocking" bezeichnet und algorithmisch beschrieben:

Ein prominentes Beispiel für "Schwarmintelligenz" findet sich im Verhalten von Ameisen, wenn es ihnen als Gemeinschaft gelingt, "Straßen" zu bilden, die eine möglichst kurze Verbindung zwischen einem Futterplatz und dem bau bilden.
Dabei wurde herausgefunden, dass bei dem gemeinschaftlichen etablieren des besten Weges, das Ausschütten von Duftstoffen (Pheromone) eine wichtige Rolle spielt.
Ein Optimierungsalgorithmus, der sich an diesem Prinzip orientiert, wurde u.a. von Marco Dorigo entwickelt, siehe beispielsweise:


2. Schwarmrobotik
3. Anwendungsgebiete in der Technik


4. Simulation von Roboterschwärmen


Bild 0-8: Screenshot zu Processing-Projekt Schwarmvehikel007: Blockade des Antriebs eines der Vehikel wird mit einem roten B angezeigt.
5. Zelluläre Automaten -- Schwarmintelligenz programmieren
Siehe Wikipedia "Game of Life"

|
|
import java.util.Random; //Random zufall = new Random(System.currentTimeMillis()); Random z = new Random(0); int[][] m = new int[60][60]; public boolean istGeerntet(int i, int k, int[][] m) { if(m[i-1][k]==1 && m[i+1][k]==1 && m[i][k+1]==1 && m[i][k-1]==1) return true; else return false; } public boolean mussStehen(int i, int k, int[][] m) { if(i>0 && m[i-1][k]==2) return true; if(i<m.length-1 && m[i+1][k]==2) return true; if(k>0 && m[i][k-1]==2) return true; if(k<m.length-1 && m[i][k+1]==2) return true; return false; } public void schritt(int[][] m) { for(int ii=0;ii<m.length*m.length;ii++) { int i = z.nextInt(m.length); int k = z.nextInt(m.length); if(m[i][k]==2) { if(istGeerntet(i,k,m)) m[i][k]=0; } else if(m[i][k]==1) { if(!mussStehen(i, k, m)) { if(z.nextBoolean()) { if(z.nextBoolean()) { if(i+1<m.length-1 && m[i+1][k]==0) { m[i][k]=0; m[i+1][k]=1; } } else { if(i-1>=0 && m[i-1][k]==0) { m[i][k]=0; m[i-1][k]=1; } } } else { if(z.nextBoolean()) { if(k+1<m.length-1 && m[i][k+1]==0) { m[i][k]=0; m[i][k+1]=1; } } else { if(k-1>=0 && m[i][k-1]==0) { m[i][k]=0; m[i][k-1]=1; } } } } } } } public void setup() { for(int i=0;i<300;i++) m[z.nextInt(m.length)][z.nextInt(m.length)]=1; for(int i=0;i<100;i++) m[z.nextInt((m.length)/3)*3+1][z.nextInt((m.length)/3)*3+1]=2; size(600,600); frameRate(30); } public void draw() { schritt(m); for(int i=0;i<m.length;i++) { for(int k=0;k<m[i].length;k++) { float x = (float)k*width/(float)m.length; float y = (float)i*width/(float)m.length; if(m[i][k]==0) fill(0); else if(m[i][k]==1) fill(255,0,0); else fill(0,255,0); rect(x,y,width/(float)m.length,width/(float)m.length); } } }
Code 0-36: "Fresser" -- einfaches Beispiel für Schwarmverhalten als Stand-Alone-Simulation mit Processing.

Bild 0-9: Szene aus "Fresser".
5. Java / Processing -- im Unterricht erarbeitet
6. Klassen und Objekte mit Java / Processing -- im Unterricht erarbeitet
7. Objektorientierte Programmierung von Schwarmindividuen mit Verhaltensregeln -- im Unterricht erarbeitet
8. ÜBUNG -- im Unterricht erarbeitet
9. Fragenbeantwortung zur anstehenden Prüfung kommende Woche



|


