kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Day by Day

(EN google-translate)

(PL google-translate)

Verzeichnis der im Verlauf des Semesters behandelten Themen

siehe auch day_by_day vom Sommersemester 2019: 45_Mikro17/30_day_by_day

Donnerstag 18.03.2021

  • Einführung in das Thema Mikrocontrollertechnik
  • Erste Schritte mit dem Board Arduino Micro und der Entwicklungsumgebung Arduino ISE

Links zu den passenden Inhalten auf kramann.info:

40_Mikrocontroller/01_Einfuehrung/01_Entwicklungsgeschichtliches
40_Mikrocontroller/01_Einfuehrung/02_Maschinensprache
40_Mikrocontroller/01_Einfuehrung/03_Assemblerbeispiel
45_Mikro17/02_ArduinoMicro
45_Mikro17/02_ArduinoMicro/01_Lauflicht
45_Mikro17/02_ArduinoMicro/02_Taster
atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf

"Saal"-übung 1: Ampel


void setup() 
{
  pinMode(8, OUTPUT); 
  pinMode(10, OUTPUT); 
  pinMode(12, OUTPUT); 
  digitalWrite(8, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(12, HIGH);
}

void loop() 
{
}

Code 0-1: ampel1: erst einmal alle LEDs leuchten lassen.

void setup() 
{
  pinMode(8, OUTPUT); 
  pinMode(10, OUTPUT); 
  pinMode(12, OUTPUT); 
  digitalWrite(8, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(12, HIGH);
}

void loop() 
{
    //  Phase1: rot
    digitalWrite(8, HIGH); //rot AN
    digitalWrite(10, LOW); //gelb AUS
    digitalWrite(12, LOW); //grün AUS 
    delay (2000);   
    //  Phase2: rot + gelb
    digitalWrite(8, HIGH); //rot AN
    digitalWrite(10, HIGH); //gelb AN
    digitalWrite(12, LOW); //grün AUS 
    delay (2000);   
    //  Phase3: grün
    digitalWrite(8,LOW); //rot AUS
    digitalWrite(10, LOW); //gelb AUS
    digitalWrite(12, HIGH); //grün AN 
    delay (2000);   
    //  Phase4: gelb
    digitalWrite(8,LOW); //rot AUS
    digitalWrite(10, HIGH); //gelb An
    digitalWrite(12, LOW); //grün Aus  
    delay (2000);  
}

Code 0-2: ampel2: jetzt Zeitsteuerung hinzufügen.


"Saal"-übung 2: Fußgängerampel


void setup() 
{
  pinMode(8, OUTPUT); 
  pinMode(10, OUTPUT); 
  pinMode(12, INPUT); 
  //digitalWrite(12, HIGH); //aktivieren des Pullupwiderstandes!
  digitalWrite(12, INPUT_PULLUP);
}

void loop() 
{
    //  Phase1: rot
    digitalWrite(8, HIGH); //rot AN
    digitalWrite(10, LOW); //grün AUS
    if(digitalRead(12) == LOW)
    {
        digitalWrite(8, LOW); //rot AUS
        digitalWrite(10, HIGH); //grün AN
        delay(5000);
    }
}

Code 0-3: neu hier: Reagieren auf Tastendruck.

Donnerstag 25.03.2021

  • Entleihen der Boxen mit Ihrer Hardware an der THB

Wenn Sie die Hardware haben, sollten Sie die Arduino IDE (Entwicklungsumgebung) installieren und ein erstes Beispielprojekt erstellen und sehen, ob es sich auf den Arduino-Micro übertragen läßt.

  • Um Sie hierbei zu unterstützen, habe ich Videos vorbereitet, in denen diese Vorgänge angeschaut werden können.
  • Die Umsetzung erfolgte auf einem 64-Bit PC unter Windows 10.
  • Ich selber arbeite (auch in der Vorlesung) unter Linux/Xubuntu 20.04.
  • Die Videos sind unter argem Zeitdruck entstanden, also nicht sehr ansprechend, sollten aber ihren Zweck erfüllen.
  • Die Links, die in den Videos vorkommen, sowie der Quelltext eines Beispielprojektes finden Sie nachfolgend auch:
https://www.arduino.cc/en/software -- Hier können Sie die Arduino IDE (Entwicklungsumgebung) für Ihr Rechner-System herunterladen.
void setup() 
{
    Serial.begin(9600);
}
int x=0;
void loop() 
{
    Serial.write(x+65);
    Serial.write("\n");
    delay(1000);
    x++;
    x%=20;
}

Code 0-4: Dies ist der Test-Quelltext, der im Video erstellt und verwendet wurde. Er schickt Zeichen an den PC, die im Serial Monitor der IDE angeschaut werden können.


Es folgen die Videos (als "nicht gelistet" auf youtube hochgealden und mit OBS-Studio erstellt):


Teil1 Um was geht es in den Videos?: https://youtu.be/hirAbmhThvA

Teil2 Download und Installation der Arduino IDE: https://youtu.be/BxzIk0RVjCc

Teil3 Erstellen, kompilieren und übertragen eines ersten Programms: https://youtu.be/s9a2o1YyruY

Donnerstag 08.04.2021

Teil 1 (praktischer Teil):

  • Gemeinsam Wiederholung und Testen der seriellen Datenübertragung.
  • Gemeinsam Wiederholung und Testen von "Fußgängerampel".
  • Hinweis: Digitale IOs sind in 8-Bit-Ports organisiert.

Wer findet den Port, von dem alle 8-Bit beim Arduino-Micro herausgeführt sind?


96_Arduino -- siehe Bild 0-2: Pinzuordnung zwischen Chip und Board.

Übung 1: Bauen Sie auf Ihrem Board die Schaltung hier unter Verwendung einzelner LEDs auf:


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

Übung 2: Programmieren Sie ein Lauflicht mit Hilfe der Programmiersprachelemente, die Sie bisher kennen.


Hinweise zu Übung 2:

  • Beim Lauflicht soll eine LED nach der anderen aufleuchten und der Vorgang dann wieder von vorne beginnen.
  • Verwenden sie die Funktion delay(...), um Pausen zwischen den Leuchtzuständen hinzubekommen.
void setup() 
{
    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() 
{
    digitalWrite(17,HIGH);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,HIGH);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,HIGH);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,HIGH);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,HIGH);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,HIGH);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,HIGH);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,HIGH);
    delay(200);

    
}

Code 0-5: Musterlösung zum Lauflicht.

Teil 2 (theoretischer Teil):

  • Konfigurieren und Schalten eines ganzen Ports über Registerbefehle.
  • Erster Test: Alle LEDs einschalten.
  • Wie kann man das Lauflicht mittels Registerbefehlen umsetzen?

Konventionen bei der Namensgebung für Funktionen, Klassen, Objekten und Variablen

https://de.wikipedia.org/wiki/Binnenmajuskel (studentischer Hinweis)

Lösungsvarianten

lauflicht1 -- konservative Variante
void setup() 
{
    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() 
{
    digitalWrite(17,HIGH);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,HIGH);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,HIGH);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,HIGH);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,HIGH);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,HIGH);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,HIGH);
    digitalWrite(11,LOW);
    delay(200);

    digitalWrite(17,LOW);
    digitalWrite(15,LOW);
    digitalWrite(16,LOW);
    digitalWrite(14,LOW);
    digitalWrite(8,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,HIGH);
    delay(200);

    
}

Code 0-6: lauflicht1

lauflicht2 -- Array verwenden
void setup() 
{
    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
}

int arr[] = {17,15,16,14,8,9,10,11};
int index = 0;
void loop() 
{
    //alle aus:
    for(int i=0;i<8;i++)
    {
        digitalWrite(arr[i],LOW);
    }

    //aktueller an:
    digitalWrite(arr[index],HIGH);
  
    delay(200);

    index++;

    if(index==8)
       index=0;

    //index%=8;  // Alternative zu if(..)..   
}

Code 0-7: lauflicht2

lauflicht3 -- Funktionen verwenden
void setup() 
{
    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
}

int arr[] = {17,15,16,14,8,9,10,11};
int index = 0;

void alleAus()
{
    //alle aus:
    for(int i=0;i<8;i++)
    {
        digitalWrite(arr[i],LOW);
    }   
}

void einschalten(int nummer)
{
    digitalWrite(arr[nummer],HIGH);
}

void loop() 
{
    alleAus();
    //aktueller an:
    einschalten(index);
    
    delay(200);

    index++;

    if(index==8)
       index=0;

    //index%=8;  // Alternative zu if(..)..   
}

Code 0-8: lauflicht3

lauflicht4 -- Register
void setup() 
{
    DDRB = 0b11111111;
}
int x = 1;
void loop() 
{
    // 1 dezimal => 0b00000001
    // 2 dezimal => 0b00000010
    // 4 dezimal => 0b00000100
    PORTB = x;
    x*=2; // erzeugt 1 2 4 8 16 32 64 128, dann wieder 1 2 ...
    if(x>=256)
        x = 1;
    delay(200);
}

Code 0-9: lauflicht4

lauflicht5 -- Bitshift-Operation
void setup() 
{
    DDRB = 0b11111111; // PB3 deaktiviert !!!!
}
unsigned char x = 0;
void loop() 
{
    // 1 dezimal => 0b00000001
    // 2 dezimal => 0b00000010
    // 4 dezimal => 0b00000100
    PORTB = (1<<x); // schiebe die 1 x-mal nach links.
    x++;
    x%=8; //Restdivision, liefert ganzzahligen Rest.
          // identisch mit x=x%8;, liefert 0 1 2 3 4 5 6 7 0 1 2...
    delay(200);
}

Code 0-10: lauflicht5

Donnerstag 15.04.2021

Themen

  • Digitale Ein- und Ausgänge
  • Fortsetzung Bitshiftoperationen
  • Herausfiltern einzelner Bits
  • Behandlung von Ein- und Ausgängen über Registeroperationen.
45_Mikro17/03_DigitalIO
45_Mikro17/03_DigitalIO/01_Elektrische_Eigenschaften
45_Mikro17/03_DigitalIO/02_Pullup_Widerstaende
45_Mikro17/03_DigitalIO/03_Bitmasken_Eingang
45_Mikro17/03_DigitalIO/04_Bitmasken_Ausgang
Übung

Steuerbares Lauflicht, quasi Kreativaufgabe.


Für alle nachfolgenden Programmieraufgaben sind Register und Bitshiftoperationen einzusetzen.


  • Legen Sie vier digitale Eingänge fest, über die Ihr Lauflicht konfiguriert werden können soll.
  • Wieviele Konfigurationen sind dann möglich?
  • Aktivieren Sie für die Eingänge die Pullupwiderstände
  • Verbinden Sie die DIP-Schalter-Leiste mit diesen Eingängen
  • Überlegen Sie sich, wie Sie zwischen zwei Varianten der Lauflichtführung hin und her schalten könnten
  • Erweitern Sie die Einstellmöglichkeiten auf die maximale Anzahl.
  • Überlegen Sie ein möglichst einfaches und einfach zu konfigurierendes Konzept (für fiktive Kundenaufträge, die schnell erledigt werden können müssen)
DDRB = 0b11111111;
PORTB = 0b00000001;

//PORTD komplett als Eingang konfigurieren:
DDRD = 0b00000000;
//Alle Pullupwiderstände aktivieren für PORTD:
PORTD = 0b11111111;
}
void loop()
{
//Eingangsbyte mit Bitmaske verknüpfen und
//je nach Ergebnis Lauflicht laufen lassen:
if( (PIND & 0b00000001)==0 )
{
    PORTB = (PORTB>>7) | (PORTB<<1);
}
else 
{
    PORTB = (PORTB>>1) | (PORTB<<7);
}
delay(500);
}

Code 0-11: Beispiel für ein Lauflicht mit Richtungssteuerung

Donnerstag 22.04.2021

Themen

  • Zusammentragen der Ergebnisse bei der Lauflichtprogrammierung
  • Gemeinsam ein konfigurierbares Lauflicht entwickeln
  • Einführung der UART-Schnittstelle

Links zum Thema

45_Mikro17/07_UART (mit Unterkapiteln)
https://www.arduino.cc/reference/en/language/functions/communication/serial/
https://www.arduino.cc/reference/de/language/functions/communication/serial/begin/
45_Mikro17/05_OOP/02_OOP_ATmega32/04_Datentypen
Übung

Die Lauflichtsteuerung soll nun nicht mehr über die Schalter erfolgen, sondern durch Tippen von Ziffern auf der PC-Tastatur.


https://content.arduino.cc/assets/Pinout-Micro_latest.png -- besseres Pin-Layout

//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-12: Lauflicht, bei dem über die serielle Schnittstelle unter drei Mustern gewählt werden kann.

Donnerstag 29.04.2021

Themen

  • Mehrfach-7-Segmentanzeige: Datenblatt
  • Planung Uhr-Projekt
  • Was sind Timer?
  • Blick ins Datenblatt
  • CTC-Mode
  • Scheduler
96_Arduino/15_Scheduler
Übung: Längeres Projekt, an dessen Ende eine Digitaluhr steht

Teilübungen

  • Anschlüsse zwischen Mehrfach-7-Segment-Anzeige und Arduino festlegen
  • Funktion entwerfen, die eine einfache Handhabung ermöglicht
  • Ziffern 0..9 zyklisch im 1-Sekunden-Takt durchgehen und seriell anzeigen und auf Digit 1
  • Uhr, noch nicht Quarz genau implementieren
Übersicht zu LTC-2723WC

Bild 0-1: Übersicht zu LTC-2723WC

Datenblatt zu LTC-2723WC: lite-on_lites06369-1-1737209.pdf
Datenblatt zu Atmega32u4: Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf

int zahl = 0;

unsigned char pattern[] = 
{
 //  ABCDEFGP 
   0b11111100, // 0
   0b01100000, // 1
   0b11011010, // 2
   0b11110010, // 3
   0b01100110, // 4
   0b10110110, // 5
   0b10111110, // 6
   0b11100000, // 7
   0b11111110, // 8
   0b11110110, // 9
   0b11000000, // dp
};

void show(int ziffer, int digit)
{
    digitalWrite(0,HIGH);
    digitalWrite(1,HIGH);
    digitalWrite(2,HIGH);
    digitalWrite(3,HIGH);
    digitalWrite(4,HIGH);
    digitalWrite(digit,LOW);
    PORTB = pattern[ziffer];
}

void setup() 
{
    pinMode(0,OUTPUT);
    pinMode(1,OUTPUT);
    pinMode(2,OUTPUT);
    pinMode(3,OUTPUT);
    pinMode(4,OUTPUT);
    digitalWrite(0,HIGH);
    digitalWrite(1,HIGH);
    digitalWrite(2,HIGH);
    digitalWrite(3,HIGH);
    digitalWrite(4,HIGH);
    DDRB=255;
    PORTB=0;
    
    Serial.begin(9600);
}

void loop() 
{
    zahl++;
    zahl%=10;
    Serial.write(zahl+48);
    show(zahl,4);
    delay(1000);
}

Code 0-13: Quelltext 1. Teilübung: zyklisch 0..9 zählen


int zahl = 0;
int MAX = 60*24;
int DIGIT[]={0,0,0,0,0};
unsigned char pattern[] = 
{
 //  ABCDEFGP 
   0b11111100, // 0
   0b01100000, // 1
   0b11011010, // 2
   0b11110010, // 3
   0b01100110, // 4
   0b10110110, // 5
   0b10111110, // 6
   0b11100000, // 7
   0b11111110, // 8
   0b11110110, // 9
   0b11000000, // dp
};

void show(int ziffer, int digit)
{
    digitalWrite(0,HIGH);
    digitalWrite(1,HIGH);
    digitalWrite(2,HIGH);
    digitalWrite(3,HIGH);
    digitalWrite(4,HIGH);
    digitalWrite(digit,LOW);
    PORTB = pattern[ziffer];
}

void setup() 
{
    pinMode(0,OUTPUT);
    pinMode(1,OUTPUT);
    pinMode(2,OUTPUT);
    pinMode(3,OUTPUT);
    pinMode(4,OUTPUT);
    digitalWrite(0,HIGH);
    digitalWrite(1,HIGH);
    digitalWrite(2,HIGH);
    digitalWrite(3,HIGH);
    digitalWrite(4,HIGH);
    DDRB=255;
    PORTB=0;
    
    Serial.begin(9600);
}

int auffrischen = 0;

void loop() 
{
  if(auffrischen>=200)
  {
    DIGIT[0] = 10; 
    DIGIT[1] = (zahl/60)/10; 
    DIGIT[2] = (zahl/60)%10;
    DIGIT[3] = (zahl%60)/10;
    DIGIT[4] = (zahl%60)%10; 
    zahl++;
    zahl%=MAX;
    Serial.write(zahl+48);
    auffrischen=0;
  }  
  show(DIGIT[auffrischen%5],auffrischen%5);
  delay(5);
  auffrischen++;
}

Code 0-14: Quelltext 2.: Uhr, die jede Sekunde eine Minute hochzählt

Donnerstag 06.05.2021

Themen

  • Übung: Verwendung eines Timers, um die Uhr quarzgenau umzusetzen.
  • Feedback-Runde
  • Saalübung zur Modulo-Operation und deren Anwendungsmöglichkeiten (Umrechnung von Zahlensysteme insbesondere!)
  • Besprechung der Übung, dazu:
  • Was sind Timer?
  • Blick ins Datenblatt
  • CTC-Mode
  • Scheduler
96_Arduino/15_Scheduler
Datenblatt passend zu Mikrocontroller auf Arduino Micro: Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf
Übung
  • Analysieren Sie die Musterlösung zu der digitalen Uhr von letzter Woche und diskutieren und klären untereinander Verständnisprobleme
  • Schauen Sie sich die verschiedenen Timer-Anwendungen im Kapitel Arduino/15_Scheduler an (s.o.)
  • Analysieren Sie insbesondere das Programm unterhalb von "Aufblitzen mit 2Hz == 120BPM"
  • Nehmen Sie das Datenblatt zu Hilfe, um die Registerkonfigurationen zu verstehen.
  • Schauen Sie dazu in Kapitel 14.8.2 ab Seite 122 unten: " Clear Timer on Compare Match (CTC) Mode"
  • .. und 14.10.1: "Timer/Counter1 Control Register A - TCCR1A", ab Seite 131
  • .. und 14.10.3: "Timer/Counter1 Control Register B - TCCR1B", ab Seite 133
  • .. im Vergleich mit "CTC-Mode für Timer1 " in 15_Scheduler
  • Testen Sie das Programm unterhalb von "Aufblitzen mit 2Hz == 120BPM" in Arduino/15_Scheduler
  • Diskutieren Sie nun wie Sie das Programm unterhalb von "Aufblitzen mit 2Hz == 120BPM" anpassen und für das Digitaluhr-Projekt nutzbar machen können, um die Uhr quarzgenau realisieren zu können.
  • Setzen Sie Ihr Konzept für die Digitaluhr um. Takten Sie die Uhr testweise sekundenweise statt minutenweise, um das Ergebnis leichter überprüfen zu können, aber halten Sie auch eine Variante mit minutenweiser Taktung vor.
Übung: Was ist eine ISR == Interrupt Service Routine?

Beispiel siehe: "Zusatz-LED bei Digital-Pin 12 durch Interrupt-Routine gesteuert " in Arduino/15_Scheduler.

  • Analysieren Sie dieses Beispiel.
  • Suchen Sie die dazu passenden Angaben im Datenblatt heraus.
  • Überlegen Sie sich, wie dieses Konzept auf die Digitaluhr übertragbar wäre.
  • Übertragen Sie das Konzept auf die Digitaluhr.
  • Diskutieren Sie: Gibt es Unterschiede in der Genauigkeit zwischen der ersten Lösung und dieser hier mit ISR, um die Digitaluhr quarzgenau zu bekommen?

Donnerstag 20.04.2021

Themen

  • Timer als "Timer" (Quarz genauer Zähler)
  • ISR Interrupt-Service-Routine
  • Uhr mit ISR
  • PWM mit Arduino-Funktionen zum Dimmen einer LED.
  • PWM mittels Register-Konfigurationen zum Dimmen einer LED.
  • PWM mit Arduino-Funktionen zum Ansteuern eines Modellbau-Servos.
  • PWM mittels Register-Konfigurationen zum Ansteuern eines Modellbau-Servos.

Timer als "Timer" (Quarz genauer Zähler)

Beispiel: Quarz genaue Ansteuerung einer LED. Sie soll mit 2Hz "aufblitzen"

Datenblatt zu ATmega32u4.
Register TCCR1A (S.131 Datenblatt)

Bild 0-2: Register TCCR1A (S.131 Datenblatt)

Übersichtstabelle (S.133 Datenblatt) zu den einzelnen Modes von Timer 1 und 3. Mode 12: CTC-Mode

Bild 0-3: Übersichtstabelle (S.133 Datenblatt) zu den einzelnen Modes von Timer 1 und 3. Mode 12: CTC-Mode "Clear Time on Compare Match".

Übersichtstabelle (S.134 Datenblatt) zu den möglichen Vorteilungen des Quarztaktes.

Bild 0-4: Übersichtstabelle (S.134 Datenblatt) zu den möglichen Vorteilungen des Quarztaktes.

Betrifft (S.137)

Bild 0-5: Betrifft (S.137) "TIMSK1"

void setup() 
{

  TCCR1B |= (1<<WGM13); //Mode 12
  TCCR1B |= (1<<WGM12);  //Setzen eines Bits
  TCCR1A &= ~(1<<WGM11); //Löschen eines Bits
  TCCR1A &= ~(1<<WGM10);
    
  //CS=100, 16000000 / 256 = 62500Hz 
  TCCR1B |= (1<<CS12);
  TCCR1B &= ~(1<<CS11);
  TCCR1B &= ~(1<<CS10);
    
  ICR1=31250;  //2Hz, benötigt eine halbe Sekunde bis dort hoch zu zählen
  
  TIMSK1 |= (1<<ICIE1); //Mode 12, dass ICR1 als Zählobergrenze genutzt wird.

  pinMode(2,OUTPUT);  
}

void loop() 
{     
  //Zeitsteuerung durch Auslesen des Zählerstandes:
  if(TCNT1<2000) //"Aufblitzen"
     digitalWrite(2,1);    
  else //sonst aus.
     digitalWrite(2,0);    
}

Code 0-15: Blitz-LED.

ArduinoMicro Pinlayout

Bild 0-6: ArduinoMicro Pinlayout

Testaufbau zur

Bild 0-7: Testaufbau zur "Blitz-LED".

Video zum Projekt.

ISR Interrupt-Service-Routine

Umsetzung des vorangehenden Beispiels mit einer Interrupt-Service-Routine (ISR).

  • 1) Zunächst nur Blinken mit 1Hz, aber mittels ISR als vollständiger Code:
#include<avr/interrupt.h>

volatile bool ZUSTAND=false;

//Interrupt-Service-Routine
ISR(TIMER1_COMPA_vect)
{
    //Zunächst nur Blinken von 1Hz umgesetzt!
    if(ZUSTAND)
        digitalWrite(2,ZUSTAND);
    else        
        digitalWrite(2,ZUSTAND);
        
    ZUSTAND=!ZUSTAND;        
}

void setup() 
{

//  TCCR1B |= (1<<WGM13); //Mode 12
  TCCR1B &= ~(1<<WGM13); //Mode 4
  TCCR1B |= (1<<WGM12);
  TCCR1A &= ~(1<<WGM11);
  TCCR1A &= ~(1<<WGM10);
    
  //CS=100, 16000000 / 256 = 62500Hz 
  TCCR1B |= (1<<CS12);
  TCCR1B &= ~(1<<CS11);
  TCCR1B &= ~(1<<CS10);
    
  OCR1A=31250;  //0,5Hz == 4 Schläge!
  
//  TIMSK1 |= (1<<ICIE1); //Mode 12
  TIMSK1 |= (1<<OCIE1A); //Mode 4
  sei(); //Aktivieren von Interrupts!

  pinMode(2,OUTPUT);  
  
}

void loop() 
{
    //Frei für beliebigen anderen Code!       
}

Code 0-16: Blinken mit 1Hz mit ISR.

Aufbau wie zuvor.

Übung
  • Diskutieren Sie in Gruppen, wie der Code mit ISR oben so umgeschrieben werden kann, dass auch hier die LED so wie im vorhergehenden Beispiel aufblitzt.
  • Setzen Sie Ihre Idee um.

Uhr mit ISR

#include<avr/interrupt.h>

volatile bool ZUSTAND=false; //volatile bool = flüchtig-wechselhaft

//Interrupt-Service-Routine
ISR(TIMER1_COMPA_vect)
{
//Zunächst nur Blinken von 1Hz umgesetzt!
if(ZUSTAND){
digitalWrite(2,ZUSTAND);
OCR1A=1250;
}
else {
digitalWrite(2,ZUSTAND);
OCR1A=62250;
}
ZUSTAND=!ZUSTAND;
}

void setup()
{

// TCCR1B |= (1<<WGM13); //Mode 12
TCCR1B &= ~(1<<WGM13); //Mode 4
TCCR1B |= (1<<WGM12);
TCCR1A &= ~(1<<WGM11);
TCCR1A &= ~(1<<WGM10);

//CS=100, 16000000 / 256 = 62500Hz
TCCR1B |= (1<<CS12);
TCCR1B &= ~(1<<CS11);
TCCR1B &= ~(1<<CS10);

OCR1A=31250; //0,5Hz == 4 Schläge!

// TIMSK1 |= (1<<ICIE1); //Mode 12
TIMSK1 |= (1<<OCIE1A); //Mode 4
sei(); //Aktivieren von Interrupts!

pinMode(2,OUTPUT);

}

void loop()
{
//Frei für beliebigen anderen Code!

}

//---- 2. Lösung ----

#include<avr/interrupt.h>

volatile int ZUSTAND=0 ;

//Interrupt-Service-Routine
ISR(TIMER1_COMPA_vect)
{
if(ZUSTAND==0)
digitalWrite(2,1); //LED
else//sonst aus.
digitalWrite(2,0);


ZUSTAND++;
if(ZUSTAND>9)
{
ZUSTAND = 0 ;
}
// ZUSTAND%=10 ;
}


void setup()
{

// TCCR1B |= (1<<WGM13); //Mode 12
TCCR1B &= ~(1<<WGM13); //Mode 4
TCCR1B |= (1<<WGM12);
TCCR1A &= ~(1<<WGM11);
TCCR1A &= ~(1<<WGM10);

//CS=100, 16000000 / 256 = 62500Hz
TCCR1B |= (1<<CS12);
TCCR1B &= ~(1<<CS11);
TCCR1B &= ~(1<<CS10);

OCR1A=3125; // 0,05s

// TIMSK1 |= (1<<ICIE1); //Mode 12
TIMSK1 |= (1<<OCIE1A); //Mode 4
sei(); //Aktivieren von Interrupts!

pinMode(2,OUTPUT);

}

void loop()
{
//Frei für beliebigen anderen
//Zeitsteuerung durch Auslesen des Zählerstandes:

}


Code 0-17: Studentische Lösungen

Übung

Kombinieren Sie nun das zuvor Erarbeitete und das Uhr-Projekt, um die Uhr mittels ISR Quarz-genau zu machen.

PWM mit Arduino-Funktionen zum Dimmen einer LED.

//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-18: Dimmen einer LED auf Digital-Pin9.

Video zum Projekt.

PWM mittels Register-Konfigurationen zum Dimmen einer LED.

  • Der Digitalpin 9 hat zusätzlich die Funktion, dass er als einer von zwei PWM-Ausgängen des Timers 1 dienen kann.
  • Diese alterative Funktion ist mit "OC1A" in der Pinbelegung des ATmega32u4 eingetragen.
  • Statt über Arduino-eigene Funktionen soll nun mittels Register-Konfiguration das Dimmen der LED auf dem digitalen Pin9 erfolgen.

Umsetzung als Übung:

Übung
  • Analysieren und Testen Sie zunächst die Servoansteuerung weiter unten (Quelltext "Modellbau-Servos auf OC1A und OC1B ...").
  • Nehmen Sie dazu das Datenblatt zur Hand. Das ist ein bisschen anstrengend, aber der einzige Weg, wie Sie lernen können Ihre "eigenen" Register-Konfigurationen einmal durchführen zu können, sei es in Bezug auf Timer, aber auch für die verschiedenen Bussysteme.
  • Nun entfernen Sie den Servo wieder und vereinfachen den Quelltext so, dass die LED zyklische auf- und abgedimmt wird mittels des PWM-Signals.

Hilfestellung zum Verständnis der Servoansteuerung, siehe:

Notwendiges Zeitsignal zur Ansteuerung eines Servos.

Bild 0-8: Notwendiges Zeitsignal zur Ansteuerung eines Servos.

40_Mikrocontroller/04_PWM/08_LoesungUE3

PWM mit Arduino-Funktionen zum Ansteuern eines Modellbau-Servos.

Übung
  • Suchen Sie selber auf der Arduino-Seite (arduino.com) das Beispiel Sweep heraus
  • ...analysieren es,
  • ...passen es an den Arduino Micro an
  • ...und testen es.

PWM mittels Register-Konfigurationen zum Ansteuern eines Modellbau-Servos.

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

Dateneingang (orange oder gelb) des Servos ist gemeinsam mit der LED an Digital-Pin9 angeschlossen. Stromversogung des Servos: braun=Ground(Masse), rot=+5Volt.

Bild 0-9: Dateneingang (orange oder gelb) des Servos ist gemeinsam mit der LED an Digital-Pin9 angeschlossen. Stromversogung des Servos: braun=Ground(Masse), rot=+5Volt.

Video zum Projekt.

Donnerstag 03.06.2021


WICHTIG: Gruppenaufteilung für Probe-E-Test festlegen!


Themen

  • Quiz zu Bitshift-Modulo-Digital I/O
  • Gelegenheit, inhaltliche Fragen und Wünsche etwas zu wiederholen zu äußern
  • Objektorientierte Programmierung für Mikrocontroller
  • Fortsetzung des PWM-Themas von letzter Woche (s.o.).
  • Exkurs Objektorientierte Programmierung

zu OOP:

  • Welchen Nutzen hat OOP im Zusammenhang mit Mikrocontrollerprogrammierung?
  • Wie können Klassen sinnvollerweise aufgebaut werden, die die Verwendung bestimmter interner Peripherien erleichtern sollen?
45_Mikro17/05_OOP -- Allgemeines zu OOP und Mikrocontrollern
96_Arduino/24_OOP -- Prinzipielles Prinzip zur Verwendung von OOP in einem Arduino-Projekt.
Übung
  • Analysieren Sie erneut (s.o.): "PWM mittels Register-Konfigurationen zum Ansteuern eines Modellbau-Servos."
  • Kapseln Sie die Funktionalität der Servoansteuerung in eine Klasse.
  • Diskutieren Sie dazu zunächst: Welche Methoden sollte diese Klasse haben (ev. gemeinsam diskutieren)
  • Setzen Sie die OOP-Variante um und testen sie.

OOP für einzelne Ziffer

class Ziffern
{
    private:
        unsigned char pattern[11];// = 
    public:
        void start()
        {
            pattern[0]=0b11111100; // 0
            pattern[1]=0b01100000; // 1
            pattern[2]=0b11011010; // 2
            pattern[3]=0b11110010; // 3
            pattern[4]=0b01100110; // 4
            pattern[5]=0b10110110; // 5
            pattern[6]=0b10111110; // 6
            pattern[7]=0b11100000; // 7
            pattern[8]=0b11111110; // 8
            pattern[9]=0b11110110; // 9
            pattern[10]=0b11000000; // dp

            pinMode(0,OUTPUT);
            pinMode(1,OUTPUT);
            pinMode(2,OUTPUT);
            pinMode(3,OUTPUT);
            pinMode(4,OUTPUT);
            digitalWrite(0,HIGH);
            digitalWrite(1,HIGH);
            digitalWrite(2,HIGH);
            digitalWrite(3,HIGH);
            digitalWrite(4,HIGH);
            DDRB=255;
            PORTB=0;        
        }

        void show(int ziffer, int digit)
        {
            digitalWrite(0,HIGH);
            digitalWrite(1,HIGH);
            digitalWrite(2,HIGH);
            digitalWrite(3,HIGH);
            digitalWrite(4,HIGH);
            digitalWrite(digit,LOW);
            PORTB = pattern[ziffer];
        }
        
};

Code 0-20: Klasse Ziffern in File Ziffern.h

#include "Ziffern.h"

Ziffern ziffern;

int zahl = 0;
void setup() 
{
    ziffern.start();
    
    Serial.begin(9600);
}

void loop() 
{
    zahl++;
    zahl%=10;
    Serial.write(zahl+48);
    ziffern.show(zahl,4);
    delay(1000);
}

Code 0-21: Hauptprogramm dazu

... Auffrischen von vier Ziffern:

class Ziffern
{
    private:
        int digitindex;
        int z[4];
        unsigned char pattern[11];// = 
    public:
        void start()
        {
            digitindex=0;
            pattern[0]=0b11111100; // 0
            pattern[1]=0b01100000; // 1
            pattern[2]=0b11011010; // 2
            pattern[3]=0b11110010; // 3
            pattern[4]=0b01100110; // 4
            pattern[5]=0b10110110; // 5
            pattern[6]=0b10111110; // 6
            pattern[7]=0b11100000; // 7
            pattern[8]=0b11111110; // 8
            pattern[9]=0b11110110; // 9
            pattern[10]=0b11000000; // dp

            z[0] = pattern[0];
            z[1] = pattern[1];
            z[2] = pattern[2];
            z[3] = pattern[3];

            pinMode(0,OUTPUT);
            pinMode(1,OUTPUT);
            pinMode(2,OUTPUT);
            pinMode(3,OUTPUT);
            pinMode(4,OUTPUT);
            digitalWrite(0,HIGH);
            digitalWrite(1,HIGH);
            digitalWrite(2,HIGH);
            digitalWrite(3,HIGH);
            digitalWrite(4,HIGH);
            DDRB=255;
            PORTB=0;        
        }

        void show(int ziffer, int digit)
        {
            z[digit] = pattern[ziffer];
          
            /*
            digitalWrite(0,HIGH);
            digitalWrite(1,HIGH);
            digitalWrite(2,HIGH);
            digitalWrite(3,HIGH);
            digitalWrite(4,HIGH);
            digitalWrite(digit,LOW);
            PORTB = pattern[ziffer];
            */
        }

        void auffrischen()
        {
            digitalWrite(0,HIGH);
            digitalWrite(1,HIGH);
            digitalWrite(2,HIGH);
            digitalWrite(3,HIGH);
            digitalWrite(4,HIGH);
            if(digitindex<2)
                digitalWrite(digitindex,LOW);
            else  
                digitalWrite(digitindex+1,LOW);
            PORTB = pattern[z[digitindex]];
            
            digitindex++;
            digitindex%=4;
        }
};

Code 0-22: Klasse Ziffern

#include "Ziffern.h"

Ziffern ziffern;

int zahl = 0;
int ZZZ=0;
void setup() 
{

    ziffern.start();
    
}

void loop() 
{
  if(ZZZ>=100)
  {
    ZZZ=0; 
    zahl++;
    zahl%=10000;
    ziffern.show(zahl%10,3);
    ziffern.show((zahl/10)%10,2);
    ziffern.show((zahl/100)%10,1);
    ziffern.show((zahl/1000)%10,0);
  }  
  delay(1);
  ziffern.auffrischen();
  ZZZ++;
}

Code 0-23: Hauptprogramm

Studentische Musterlösung der Übung
#define WMIN1 1000
#define WMITTE1 1500
#define WMAX1 2000
#define SCHRITTE1 1000

#include "SERVO.h"

SERVO servo;
int winkel=-900;
void setup()
{

servo.start();
}

void loop()
{
servo.setWinkel((float)winkel*0.1);
delay(10);
winkel++;
if(winkel>900)
winkel=-900;


}

//#include "Arduino.h"
class SERVO

{
private:
int WMIN=1000; //Konstanten sollen möglichst mit Großbuchstaben geschrieben werden
int WMITTE=1500;
int WMAX=2000;
int SCHRITTE=1000; //die Variablen können auch im Hauptprogramm außerhalb der Klasse bleiben



public:

void start() //Methoden sollen mit kleinen Buchstaben beginnen um sie von Klassen zu unterscheiden
{
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 set (int wert)
{

OCR1A = wert; //PWM-Breite auf Null setzen.
OCR1B = wert; //PWM-Breite auf Null setzen.
}
/*
* wert= + - 45°
*/
void setWinkel (float wert)
{
wert=wert+45.0; //Bereich auf 0 bis 90 verschieben.
wert=wert/90.0; //Bereich auf 0 bis 1 stauchen.
wert=wert*1000.0; //Bereich dehnen auf 0 bis 1000.
wert=wert+1000.0; //Bereich shiften nach 1000 bis 2000

OCR1A = (int)wert; //float wird in int umgewandelt
OCR1B = (int)wert;
}

};

Code 0-24: Studentische Musterlösung Servo-Klasse.