Einstieg Mikrocontroller mit dem Arduino         

4 Programmschleifen   


Elektronik-Labor   Projekte   AVR 





1.4.22: Einstieg Mikrocontroller mit Arduino
29.4.22: Einstieg Arduino 1, Portausgaben
6.5.22: Einstieg Arduino 2, Digitale Eingänge
13.5.22: Einstieg Arduino 3, Analoge Messungen
20.5.22: Einstieg Arduino 4, Programmschleifen
28.5.22: Einstieg Arduino 5, Übungen
10.6.22: Einstieg Arduino 6, Sesoren
21.6.22: Einstieg Arduino 7, Ampelsteuerung
24.6.22: Einstieg Arduino 8, Zufall und Spiele

Der Reaktionstester
 


Baue dir einen Reaktionstester mit sechs LEDs. Sie leuchten nacheinander von links nach rechts im Abstand von 0,1 s auf. Sobald die erste LED leuchtet, musst du auf den Knopf drücken. Wenn du länger brauchst, leuchten mehr LEDs und zeigen deine Reaktionszeit. 0,2 s sind ein guter Wert. Wer nicht gut geschlafen hat, braucht länger.

Das Programm verwendet einige Dinge, die man immer wieder gebrauchen kann. Dazu gehört die Zählschleife. Man verwendet dazu eine Variable, die vielleicht schon vorhanden ist. Sie könnte i heißen. Die Schleife sieht dann so aus:

  for (i=0; i<10; i++){
  …
  }

In diesem Fall wird i bis 9 hochgezählt. Weil die Zählung mit 0 beginnt, wird alles zwischen den geschweiften Klammern genau zehnmal durchgeführt. Im Reaktionstest wird die Variable n verwendet. Weil sie hier zum ersten Mal verwendet wird, muss der Typ (int) mit angegeben werden. Die Schleife wird sechsmal durchlaufen. Aber sie kann mit break (Unterbrechung) vorzeitig beendet werden, wenn du auf den Knopf drückst.

Das Programm verwendet eine Binärzahl int d=0b1111111100000000 mit insgesamt 16 Bit. Damit sollen die LEDs am PORTD geschaltet werden. Weil der Port D aber nur 8 Bit hat, gelten die rechten acht Nullen, sodass alle LEDs aus wären. Aber in der Schleife gibt es einen Schiebebefehl d>>=1 (Andere Schreibweise d=d>>1), der alle Bits um eine Stelle nach rechts verschiebt (shift right) und damit die LEDs einschaltet. Dieselbe Wirkung hätte übrigens eine Division durch 2. Probiere es aus: d=d/2 oder  d/=2.

//Reaktionstest 6 LEDs
void setup(){
  DDRD=252;
  pinMode(10, INPUT_PULLUP);
}
void loop() {
  int d=0b1111111100000000;
  for (int n=0; n<6; n++){
    d>>=1;
    PORTD=d;
    delay(100);
    if (digitalRead(10)==0) break;
  }
  delay (1000);
  PORTD=0; 
  delay (1500); 
}

Aufgabe: Ändere das Programm so, dass insgesamt acht LEDs verwendet werden, die doppelt so schnell durchlaufen.


Automatisches Morseprogramm



Das Morseprogramm verwendet ebenfalls das Verschieben von Bits und eine Zählschleife. Neu ist die Tonerzeugung mit analogWrite (9,100). Der Zweck von analogWrite ist eigentlich ein anderer, nämlich die Steuerung der Helligkeit einer LED. Aber dabei wird der Port 1000 Mal pro Sekunde ein- und ausgeschaltet. Beim Anschluss eines Lautsprechers entsteht deshalb ein Ton von 1000 Hz. Die übergebene Zahl 100 bedeutet, dass der Port im 100/255 der Gesamtzeit eingeschaltet ist, aber das ist für den Ton nicht entscheidend.

Das Programm besitzt eine lange Liste, die den Aufbau der Morsezeichen enthält. Dazu gibt es ein Datenfeld (ein Array) mit bis zu 256 Daten von d[0] bis d[255]. An der Stelle 15 wird das O definiert. Der Computer verwendet für ein großes O die Zahl (den ASCII Code) 79. Aber die Zahl 15 selbst beschreibt das Morsezeichen für O: ---, also drei Striche. Die Dezimalzahl 15 lautet als Binärzahl 00001111. Die drei rechten Einsen stehen für die drei Striche. Die erste 1 von links markiert nur den Anfang des Morsezeichens, weil es ja kurze und lange Zeichen gibt. Wenn der Aufbau klar ist, kannst du selbst Morsezeichen zusammenbauen. Punkte werden mit Nullen kodiert. Ein S besteht aus drei Punkten. Der Code lautet also 00001000.

Um die Zeichen hörbar zu machen, muss das Programm genau abzählen, wo ein Zeichen beginnt, und wann alle acht Bits gelesen sind. Dazu werden auch while-Schleifen (while = solange, während) eingesetzt. Solange dies und jenes gilt, tue das und das.  Und der Code m wird immer wieder um eine Stelle nach links geschoben, diesmal mit einer Multiplikation mit 2. Zuerst wird mit while(m<128) beobachtet, wann die Start-Eins erscheint. Wenn sie nämlich links angekommen ist, ist der Start des eigentlichen Zeichens erkannt. Ab dann wird mit  if (m&128) geschaut, ob ein Strich oder ein Punkt erzeugt werden soll.

Alles zu kompliziert und zu viel auf einmal? Das ist normal. Ich habe einen halben Tag gebraucht, bis es richtig funktioniert hat. Es reicht, wenn man am Anfang nur etwas versteht. Wahrscheinlich hast du erkannt, dass  char text[] = "HALLO NANO   " festlegt, welcher Text gemorst werden soll. Also setzte mal deinen Namen oder einen anderen eigenen Text ein. Und char dot = 100 bestimmt die Geschwindigkeit. Damit kannst du die Funkamateure im Kurs auf die Probe stellen, welche Geschwindigkeit sie noch fehlerfrei hören können.
 

//Morse-Ausgabe  

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

void loop() {
  char text[] = "HALLO NANO   ";
  //char text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ      ";
  char dot = 100;     //Punktlänge 100 ms für 60 Buchstaben pro Minute
  pinMode(0, OUTPUT);
  int d[255];
  d[1] = 32;   
  d[2] = 69;     //"E",      . , 00000010, ASCII 69
  d[3] = 84;     //"T",      - , 00000011, ASCII 84
  d[4] = 73;     //"I",     .. , 00000100, ASCII 73
  d[5] = 65;     //"A",     .- , 00000101, ASCII 65
  d[6] = 78;     //"N",     -. , 00000110, ASCII 78
  d[7] = 77;     //"M",     -- , 00000111, ASCII 77
  d[8] = 83;     //"S",    ... , 00001000, ASCII 83
  d[9] = 85;     //"U",    ..- , 00001001, ASCII 85
  d[10] = 82;     //"R",    .-. , 00001010, ASCII 82
  d[11] = 87;     //"W",    .-- , 00001011, ASCII 87
  d[12] = 68;     //"d",    -.. , 00001100, ASCII 68
  d[13] = 75;     //"K",    -.- , 00001101, ASCII 75
  d[14] = 71;     //"G",    --. , 00001110, ASCII 71
  d[15] = 79;     //"O",    --- , 00001111, ASCII 79
  d[16] = 72;     //"H",   .... , 00010000, ASCII 72
  d[17] = 86;     //"V",   ...- , 00010001, ASCII 86
  d[18] = 70;     //"F",   ..-. , 00010010, ASCII 70
  d[19] = 32;
  d[20] = 76;     //"L",   .-.. , 00010100, ASCII 76
  d[21] = 32;
  d[22] = 80;     //"P",   .--. , 00010110, ASCII 80
  d[23] = 74;     //"J",   .--- , 00010111, ASCII 74
  d[24] = 66;     //"B",   -... , 00011000, ASCII 66
  d[25] = 88;     //"X",   -..- , 00011001, ASCII 88
  d[26] = 67;     //"C",   -.-. , 00011010, ASCII 67
  d[27] = 89;     //"Y",   -.-- , 00011011, ASCII 89
  d[28] = 90;     //"Z",   --.. , 00011100, ASCII 90
  d[29] = 81;     //"Q",   --.- , 00011101, ASCII 81
  d[30] = 32;
  d[31] = 32;
  d[32] = 53;     //"5",  ..... , 00100000, ASCII 53
  d[33] = 52;     //"4",  ....- , 00100001, ASCII 52
  d[34] = 32;
  d[35] = 51;     //"3",  ...-- , 00100011, ASCII 51
  d[36] = 32;
  d[37] = 32;
  d[38] = 32;
  d[39] = 50;     //"2",  ..--- , 00100111, ASCII 50
  d[40] = 32;
  d[41] = 32;
  d[42] = 32;
  d[43] = 32;
  d[44] = 32;
  d[45] = 32;
  d[46] = 32;
  d[47] = 49;     //"1",  .---- , 00101111, ASCII 49
  d[48] = 54;     //"6",  -.... , 00110000, ASCII 54
  d[49] = 32;
  d[50] = 32;
  d[51] = 32;
  d[52] = 32;
  d[53] = 32;
  d[54] = 32;
  d[55] = 32;
  d[56] = 55;     //"7",  --... , 00111000, ASCII 55
  d[57] = 32;
  d[58] = 32;
  d[59] = 32;
  d[60] = 56;     //"8",  ---.. , 00111100, ASCII 56
  d[61] = 32;
  d[62] = 57;     //"9",  ----. , 00111110, ASCII 57
  d[63] = 48;     //"0",  ----- , 00111111, ASCII 48 

  for (int n=0; n< strlen(text); n++){
    char c= text[n];    //Einzelne Zeichen abtrennen
    Serial.print(c);
    unsigned int m=0;
    for (int j=1; j<91; j++){
      if (d[j]==(unsigned int)c){
        m=j;           //Morsecode finden
        j=91;
      }
    }
    //Serial.println(m);
    int start=0;
    int i=1;
    if(m==1) delay (3*dot);  //Leerzeichen-Pause
    while(m<128){       //Startbit finden
      i++;
      m=m*2;
    }
    while(i<8){
      m=m*2;
      i++;
      if (m&128){       //Strich
        analogWrite (9,100);
        delay (3*dot);
        analogWrite (9,0);
        delay(dot);
      }
      else{             //Punkt
        analogWrite (9,100);
        delay (dot);
        analogWrite (9,0);
        delay(dot);
      }
    }
    delay (3*dot);      //Pause zwischen Zeichen
  }
}
 

Morsetaste und Dekoder



Diesmal sollst du selbst morsen. Und das Programm soll dir dabei zuhören und die Morsezeichen verstehen. Am Bildschirm erscheint dann, was du gemorst hast. Der Tastschalter wird als Morsetaste an D2 und GND angeschlossen. In Reihe zum Lautsprecher ist ein Widerstand eingebaut. Deshalb kannst du alle Arten von Lautsprechern und Kopfhörern verwenden. 

Dieses Programm verwendet dieselbe Tabelle wie das vorige Morseprogramm. Aber die Dekodierung der Morsezeichen funktioniert anders herum. Diesmal muss das Programm genau überwachen, wie lange jemand auf die Taste drückt und wie lange die Pausen dazwischen sind. Ist das ein Punkt oder ein Strich? Gehört dies noch zum selben Zeichen, oder beginnt hier ein neues? Oder ist die Pause noch länger, sodass ein Leerzeichen den Abstand zu einem neuen Wort markieren soll? 

Strichlängen und Pausen werden hier in Einheiten von 50 ms gemessen. Das bedeutet, dass ein Ton 50, 100, 150 oder z.B. 300 ms lang sein kann. Für einen Telegrafie-Meister sind die 50 ms viel zu lang, aber für den ersten Einstieg gerade richtig. Bei 40 BpM (Buchstaben pro Minute) dauert ein Punkt 150 ms und ein Strich 450 ms. Die Zeit t liegt mit 5 Einheiten mal 50 ms = 250 ms gerade dazwischen. Kürzer als 250 ms wird also als Punkt gewertet und länger als 250 ms als Strich. Man muss deshalb nicht genau mit 40 BpM morsen, sonders es darf auch etwas schneller oder etwas langsamer sein.

Wer noch nie etwas über Morsezeichen erfahren hat, kann es hiermit lernen. Probiere es einfach aus und lerne ein paar Zeichen. Es ist wichtig, sich einfach den Klang und den Rhythmus zu merken, aber keine Worte. Wenn man ein Zeichen sprechen will, kann man sagen: da da dit dit dit (für --… = „7“)Auf Kurzwelle hört man diese beiden Abkürzungen besonders häufig, und deshalb lerne sie am besten zuerst: 

CQ  (Allgemeiner Anruf, kommt wohl von „I seek you“, ich suche dich)
73   (Viele Grüße, vielleicht weil es so schön klingt?)



//Morse-Eingabe
 
void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
}
 

void loop() {
  int dot=150;     //Punktlänge 150 ms für 40 Buchstaben pro Minute
  int t=dot/30;
  pinMode(0, OUTPUT);
  int d[255];
  d[1] = 32;   
  d[2] = 69;     //"E",      . , 00000010, ASCII 69
  d[3] = 84;     //"T",      - , 00000011, ASCII 84
  d[4] = 73;     //"I",     .. , 00000100, ASCII 73
  d[5] = 65;     //"A",     .- , 00000101, ASCII 65
  d[6] = 78;     //"N",     -. , 00000110, ASCII 78
  d[7] = 77;     //"M",     -- , 00000111, ASCII 77
  d[8] = 83;     //"S",    ... , 00001000, ASCII 83
  d[9] = 85;     //"U",    ..- , 00001001, ASCII 85
  d[10] = 82;     //"R",    .-. , 00001010, ASCII 82
  d[11] = 87;     //"W",    .-- , 00001011, ASCII 87
  d[12] = 68;     //"d",    -.. , 00001100, ASCII 68
  d[13] = 75;     //"K",    -.- , 00001101, ASCII 75
  d[14] = 71;     //"G",    --. , 00001110, ASCII 71
  d[15] = 79;     //"O",    --- , 00001111, ASCII 79
  d[16] = 72;     //"H",   .... , 00010000, ASCII 72
  d[17] = 86;     //"V",   ...- , 00010001, ASCII 86
  d[18] = 70;     //"F",   ..-. , 00010010, ASCII 70
  d[19] = 32;
  d[20] = 76;     //"L",   .-.. , 00010100, ASCII 76
  d[21] = 32;
  d[22] = 80;     //"P",   .--. , 00010110, ASCII 80
  d[23] = 74;     //"J",   .--- , 00010111, ASCII 74
  d[24] = 66;     //"B",   -... , 00011000, ASCII 66
  d[25] = 88;     //"X",   -..- , 00011001, ASCII 88
  d[26] = 67;     //"C",   -.-. , 00011010, ASCII 67
  d[27] = 89;     //"Y",   -.-- , 00011011, ASCII 89
  d[28] = 90;     //"Z",   --.. , 00011100, ASCII 90
  d[29] = 81;     //"Q",   --.- , 00011101, ASCII 81
  d[30] = 32;
  d[31] = 32;
  d[32] = 53;     //"5",  ..... , 00100000, ASCII 53
  d[33] = 52;     //"4",  ....- , 00100001, ASCII 52
  d[34] = 32;
  d[35] = 51;     //"3",  ...-- , 00100011, ASCII 51
  d[36] = 32;
  d[37] = 32;
  d[38] = 32;
  d[39] = 50;     //"2",  ..--- , 00100111, ASCII 50
  d[40] = 32;
  d[41] = 32;
  d[42] = 32;
  d[43] = 32;
  d[44] = 32;
  d[45] = 32;
  d[46] = 32;
  d[47] = 49;     //"1",  .---- , 00101111, ASCII 49
  d[48] = 54;     //"6",  -.... , 00110000, ASCII 54
  d[49] = 32;
  d[50] = 32;
  d[51] = 32;
  d[52] = 32;
  d[53] = 32;
  d[54] = 32;
  d[55] = 32;
  d[56] = 55;     //"7",  --... , 00111000, ASCII 55
  d[57] = 32;
  d[58] = 32;
  d[59] = 32;
  d[60] = 56;     //"8",  ---.. , 00111100, ASCII 56
  d[61] = 32;
  d[62] = 57;     //"9",  ----. , 00111110, ASCII 57
  d[63] = 48;     //"0",  ----- , 00111111, ASCII 48  

  while(1){
    int m=1;
    while (digitalRead(2)==1);  //Start abwarten
    int ende =0;
    while (ende ==0){           //bis zur Zeichenpause
      m<<=1;
      int t1=0;
      while (digitalRead(2)==0){
        analogWrite (9,100);
        delay (50);
        analogWrite (9,0);
        t1++;                   //Tonlänge messen
      }
      if (t1>t) m+=1;           //lang genug? Strich!
      int t2=0;
      while (digitalRead(2)==1){
        delay (50);
        t2++;                   //Pause messen
        if (t2>t) {
          ende=1;               //lang genug? Zeichen-Ende!
          break;              
        }
      }
    }
    Serial.print((char) d[m]);  //Zeichen drucken
    int t3=0;
    while (digitalRead(2)==1){
      delay (20);
      t3++;                     //Pause messen
      if (t3>(3*t)){            //lang genug? Wort-Ende
        Serial.print (" ");
        break;
      }
    }
  }
}
 

 

Download: Arduino4.zip 

Zurück: ArduinoKurs3.htm


Erweiterungen

Der Reaktionstester wurde mit vereinten Kräften an zwei Stellen erweitert. Zum einen wurde eine Ausgabe eingebaut, damit man die die gemessenen Reaktionszeiten im seriellen Monitor sehen kann. Und zum anderen wurde eine Zufallszeit zwischen den einzelnen Spielen eingebaut, damit man sich nicht an den gleichmäßigen Takt gewöhnen kann.

//Reaktionstest 6 LEDs
void setup(){
  Serial.begin(9600);
  DDRD=252;
  pinMode(10, INPUT_PULLUP);
}
void loop() {
  int n;
  int d=0b1111111100000000;
  for (n=0; n<6; n++){
    d>>=1;
    //d=d>>1;
    //d=d/2;
    //d/=2;
    PORTD=d;
    delay(100);
    if (digitalRead(10)==0) break;
  }
  Serial.print((n+1)*100);
  Serial.println(" ms");
  delay (1000);
  PORTD=0;  
  delay (random(500, 1500));         
}





Bei den Morseprogrammen kam der Gedanke auf, dass ein Arduino morsen sollte und ein zweiter die Zeichen empfangen sollte. Deshalb wurde hier in das Ausgabeprogramm eine Erweiterung eingebaut, mit der der Port D12 als Sendeleitung dient. Sie muss an D2 des Empfängers angeschlossen werden. Ein Test hat gezeigt, dass es funktioniert. Zwar sendet der Sender mit 60 BpM und dem Empfänger ist auf 40 BpM eingestellt, aber der Empfänger ist tolerant genug gegen Abweichungen der Geschwindigkeit, sodass die Zeichen fehlerfrei gelesen werden.

void loop() {
  char text[] = "HALLO NANO   ";
  //char text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ      ";
  char dot = 100;     //Punktlänge 100 ms für 60 Buchstaben pro Minute
  pinMode(12, OUTPUT);
  digitalWrite (12,1);
...
...
      if (m&128){       //Strich
        analogWrite (9,100);
        digitalWrite (12,0);
        delay (3*dot);
        analogWrite (9,0);
        digitalWrite (12,1);
        delay(dot); 
      }
      else{             //Punkt
        analogWrite (9,100);
        digitalWrite (12,0);
        delay (dot);
        analogWrite (9,0);
        digitalWrite (12,1);
        delay(dot); 
      }

Anwendungen und Erweiterungen hierzu im Kurs 5.


Elektronik-Labor   Projekte   AVR