Grundlagen der Mikrocontrollertechnik 4EIT 4EMO 4MT im Sommersemester 2023
(EN google-translate)
(PL google-translate)
|
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.2 Foto Dual In Line DIL Bauweise eines ATmega32 von Atmel.
|
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.2 Die unterschiedliche Architektur beim PC und beim Mikrocontroller
6. Wesentliches Merkmal eines Mikrocontroller-Programms: Die Loop-Funktion und die Manipulation von Registern.
|
9. Höhere Programmiersprachen kommen zum Einsatz um die Wartbarkeit und Transparenz von Programmen zu verbessern.
Teil 2: Vorbereitungen auf die erste Übung
|
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
...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
NEU ergänzt im Sommersemester 2024, LV #3, 04.04.2024 (hybrid):
// 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:
96_Arduino
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:
ENDE NEU ergänzt im Sommersemester 2024, LV #3, 04.04.2024 (hybrid).
Themen
|
1. Wiederholung der praktischen Aspekte der Programmentwicklung für den Arduino Micro
|
2. Quiz: Wiederholung wichtiger Merkmale eines Mikrocontrollers
|
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-10: 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-11: 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-12: Blinkende LED unter Verwendung von Register-Befehlen
4. Systematische Einführung von Registerbefehlen im Zusammenhang mit digitalen Ein- und Ausgängen
|
|
Wie der Port B beim ArduinoMicro abgerufen werden kann, ist hier dargestellt:
Das ganze IO-Registerthema wird für den ATmega32 bzw. ATmega32u4 ausführlich hier behandelt:
|
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-13: 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-14: 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-15: 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-16: Version 4: Verwendung von Register-Befehlen mit Bitmasken und Bitshiftoperationen
Übungen
a) Bauen Sie die nachfolgende Lauflichtschaltung auf:
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-17: 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
NEU ergänzt im Sommersemester 2024, LV #4, 11.04.2024, BEGINN 11:30Uhr:
Hauptthema: Digitale Eingänge
|
96_Arduino/22_Universal/02_LED_Leiste
40_Mikrocontroller/01_Einfuehrung/05_Praxis/02_Byteoperationen
40_Mikrocontroller/03_DigitalIO/02_Pullup_Widerstaende
40_Mikrocontroller/03_DigitalIO/03_Bitmasken_Eingang
Bild 0-1: Auszug aus dem Datenblatt eines Drehkodierers: Kodierung.
Bild 0-2: Auszug aus dem Datenblatt eines Drehkodierers: Pinbelegung von oben durch das Gehäuse hindurch gesehen(?).
Bild 0-3: Einbau des Drehkodierers: 1,2,4,8 an PINF.
Bild 0-4: Sieben Segmentanzeige Kingbright SA52-11GWA (gemeinsame Anode!)
|
|
Bild 0-5: Verschaltung der Komponenten der Kodieranzeige. Die vier LEDs bei Digital Out 4,5,6,7 dienen zur direkten Kontrollausgabe des Binärcodes.
int x = 0; void setup() { DDRB = 0b11111111; } void loop() { delay(200); if(x<8) PORTB= 0b00000001<<x; else PORTB= 0b01000000>>(x-8); x++; x%=8+6; }
Code 0-18: Musterlösung "Zylonenlauflicht".
ENDE NEU ergänzt im Sommersemester 2024, LV #4, 11.04.2024:
Themen
|
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-19: Lauflicht mit Auswahl.
2. Quiz
|
3. Robo-Sumo
https://www.youtube.com/watch?v=lUpUQf16qzQ83_AV/05_SUMO/10_Umsetzung -- Zustandsübergangs-Modell
4. PWM Prinzip
40_Mikrocontroller/04_PWM5. 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-20: Dimmen einer LED mittels "analogWrite"
https://docs.arduino.cc/learn/electronics/servo-motors
Übung 1
|
Übung 2
|
#4 Fr 21.04.2023
NEU ergänzt im Sommersemester 2024, LV #5, 18.04.2024:
Themen heute:
|
Bild 0-6: Aufbau der Drehkodierer-Anzeige auf einer Laborplatine.
Musterlösung für den Drehkodierer mit Anzeige
uint8_t ziffer[] = { 0b00111111,//0 0b00000110,//1 0b01011011,//2 0b01001111,//3 0b01100110,//4 0b01101101,//5 0b01111101,//6 0b00000111,//7 0b01111111,//8 0b01101111,//9 0b01110111,//A 0b01111100,//B 0b00111001,//C 0b01011110,//D 0b01111001,//E 0b01110001//F }; void setup() { DDRB = 0b01111111; PORTB = 0b01111111; DDRF &= 0b00001111; PORTF |= 0b11110000; pinMode(4,OUTPUT); pinMode(5,OUTPUT); pinMode(6,OUTPUT); pinMode(7,OUTPUT); } void loop() { DDRB = 0b01111111; PORTB = ~ziffer[(PINF>>4)]; digitalWrite(4,((PINF & 0b00010000)>>4)); digitalWrite(5,((PINF & 0b00100000)>>5)); digitalWrite(6,((PINF & 0b01000000)>>6)); digitalWrite(7,((PINF & 0b10000000)>>7)); }
Code 0-21: Drehkodierer mit Anzeige
Studentische Lösung:
uint8_t displayValue[16] = { 0b00111111, // 0 0b00000110, // 1 0b01011011, // 2 0b01001111, // 3 0b01100110, // 4 0b01101101, // 5 0b01111101, // 6 0b00000111, // 7 0b01111111, // 8 0b01101111, // 9 0b01110111, // A 0b01111100, // B 0b00111001, // C 0b01011110, // D 0b01111001, // E 0b01110001, // F }; void setup() { //explicitly tells the register B, all pins are outputs DDRB = 0xFF; // DDRF &= tells the register F, the leftmost 4 ports are inputs while not // altering the other bits DDRF &= ~((1 << PF4) | (1 << PF5) | (1 << PF6) | (1 << PF7)); // PORTF |= sets the leftmost 4 bits to activate internal pull-up PORTF |= (1 << PF4) | (1 << PF5) | (1 << PF6) | (1 << PF7); } void loop() { int switchValue = (PINF & ((1 << PF4) | (1 << PF5) | (1 << PF6) | (1 << PF7))) >> PF4; PORTB = ~(displayValue[switchValue]); }
Code 0-22: Drehkodierer mit Anzeige
ENDE NEU ergänzt im Sommersemester 2024, LV #5, 18.04.2024:
Themen
|
1. Prinzip der Generierung von PWM-Signalen bei Mikrocontrollern
40_Mikrocontroller/04_PWM/03_Generierung2. 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-23: Modellbau-Servos auf OC1A und OC1B mittels Timer 1 mit hoher Genauigkeit ansteuern.
Übungen
|
Bild 0-7: Pinbelegung bei der 4-fach-7-Segmentanzeige HDSP-B08G.
Bild 0-8: Lage der Pins bei HDSP-B08G.
Bild 0-9: 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:
|
Im Unterricht entstandene Programme als Grundlage für die Übung:
OOPled001.zipOOPled002.zip
OOPled003.zip
OOPled004.zip
OOPled005.zip
OOPled006konstruktor.zip
OOPled007servo.zip
#6 Fr 05.05.2023
Themen
|
1. Timer als Zähler verwenden
|
Beispielprogramm, bei denen ein Timer als Zähler arbeitet:
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:
Aufeinander aufbauende Teillösungen:
|
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-24: 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
|
#7 Fr 12.05.2023
Themen
|
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.
|
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-25: 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:
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-26: 1Hz über fest konfiguriertes Toggeln auf Pin9 und 10, kein Code in loop() nötig!
Behandlung von Bussystemen
Themen:
|
1. Überblick zu Bussystemen
40_Mikrocontroller/06_UART/01_Bussysteme2. Einführung zu UART (serielle Schnittstelle) mit RS232-Protokoll
40_Mikrocontroller/06_UART/02_UART40_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-27: Verbindung zweier Arduino über Kreuz.
Warum ist die GND-Verbindung notwendig?
Bild 0-10: Aufbau mit zwei Arduino-Micro. Vom ersten wird der serielle Monitor am PC geöffnet.
Bild 0-11: Zwei Arduino Micro werden nun von der Arduino IDE erkannt.
Bild 0-12: Aufbau der Testschaltung.
Bild 0-13: 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-28: 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-29: 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
Bild 0-14: 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-30: Sertest2reg
5. Objektorientierte Kapselung der seriellen Schnittstelle
(Saal-) ÜBUNG
|
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-31: Verbindung MPU6050 mit Arduino
Bild 0-15: 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.
Bild 0-16: Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).
Bild 0-17: 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-32: Testprogramm, um alle Sensordaten, die der MPU6050 liefert im Serial Monitor ansehen zu können.
Aufgaben
|
(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
|
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-33: Verbindung MPU6050 mit Arduino
Bild 0-18: 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.
Bild 0-19: Schaltplan zur Verbindung des MPU6050 mit dem Arduino Micro (LED-Teil wurde hier weggelassen).
Bild 0-20: 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-34: Testprogramm, um alle Sensordaten, die der MPU6050 liefert im Serial Monitor ansehen zu können.
Übung 1 -- Verkippwarnung
Entwickeln Sie folgendes System:
|
#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-35: 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-36: Verbindung zweier Arduino über Kreuz.
Warum ist die GND-Verbindung notwendig?
Bild 0-21: Aufbau mit zwei Arduino-Micro. Vom ersten wird der serielle Monitor am PC geöffnet.
Bild 0-22: Zwei Arduino Micro werden nun von der Arduino IDE erkannt.
Bild 0-23: Aufbau der Testschaltung.
Bild 0-24: 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-37: 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-38: 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
Bild 0-25: 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-39: 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
|
|
|
Bild 0-26: Formel, Datenblatt zu atmega32u4, Seite 127.
|
Mehr zu objektorientierter Programmierung
Wichtige Paradigmen der Objektorientierten Programmierung:
|
Das UML-Klassendiagramm zur visuellen Darstellung von Klassen:
2. Kapselung des MPU6050-Moduls als Objekt
|
3. Serielle Kommunikation zwischen PC und Arduino mittels Java/Processing
4. Übungen
|
#11 Fr 16.06.2023 Probe E-Test
#12 Fr 23.06.2023
Themen
|
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:
|
Bild 0-27: Anschlüsse am Plotter von links nach rechts: Penlifter, X-Achse analoger Eingang, Y-Achse analoger Eingang.
Bild 0-28: 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-40: Testprogramm: Ein Leerzeichen über die Serielle Schnittstelle gesendet startet das Zeichnen eines Kreises. Stopp bei Erreichen von 360Grad (2PI).
ÜBUNG
|
#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-41: 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-42: 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-43: Timer1.h (extra Tab) -- OOP-Variante bei Verwendung von Timer 1
Kür: Smiley malen, Beispiel
plotter005_kuer.zipBild 0-29: Smiley-Plot.
#13 Fr 30.06.2023 E-Test
Bitte beachten: Gruppe A: 9-10:30, Gruppe B: 10:30-12Uhr im Mechatroniklabor.