1.2 Aufgaben zum Einstieg

SOS ausgeben mit BoardLED D13. … – – – …

Auf der BoardLED 13 soll ein SOS-Blinken ausgegeben werden.
Hier das Zeitschema zur Ausgabe. Mit einer Variable int dit soll die Dauer des kurzen Signals einstellbar sein.
Zum Testen usw. https://morsedecoder.com
Erstellen Sie ein Programm, ein dit soll die Länge 200ms haben. Hier ein paar Anregungen:

  • Lineares Programm (Ausgaben einfach runter programmieren)?
  • Mit Unterprogrammen dit(), dah()
  • Zählschleifen verwenden
Lösungsvorschlag (klick mich wenn du nicht weiter kommst)
// https://de.wikipedia.org/wiki/Morsecode#Zeitschema_und_Veranschaulichung
#define BOARD_LED D13
char empfangByte = 0; // für empfangenes Byte
int dit = 200; // Basislänge in ms

void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(BOARD_LED, OUTPUT);  // Pin als Ausgang
    Serial.begin(115200); // Serielle Schnittstelle starten und Baudrate festlegen
}

void led_dit(){ // Dit ausgeben
  digitalWrite(BOARD_LED,HIGH);
  delay(dit);
  digitalWrite(BOARD_LED,LOW);
  delay(dit);   // ein dit Symbolabstand
  Serial.print(".");
}
void led_dah(){ // Dah ausgeben
  digitalWrite(BOARD_LED,HIGH);
  delay(3*dit);
  digitalWrite(BOARD_LED,LOW);
  delay(dit);   // ein dit Symbolabstand
  Serial.print("-");
}
void buchstabenAbstand(){ // Buchstabenstand ausgeben
  delay(2*dit);     // 3-1
  Serial.print(" ");
}
void wortAbstand(){ // Wortabstand ausgeben
  delay(6*dit);     // 7-1
  Serial.print(" ");
}
void morseSOS(){
  int i;
  for(i=0;i<3;i++){ // S
    led_dit();
  }
  buchstabenAbstand();
  for(i=0;i<3;i++){ // O
    led_dah();
  }
  buchstabenAbstand();
  for(i=0;i<3;i++){ // S
    led_dit();
  }
}
void loop(){;
    morseSOS();
    delay(1000);
}
// Vorgabe
#define BOARD_LED D13
int dit = 200; // Basislänge in ms

void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(BOARD_LED, OUTPUT);  // Pin als Ausgang
    Serial.begin(115200); // Serielle Schnittstelle starten und Baudrate festlegen
}
void morseSOS(){
  
}
void loop(){
    morseSOS();
    delay(1000);
}

String ausgeben … — …

  • Die SOS-Ausgabe soll nun mit Taste an PA1 starten und dabei ein Unterprogramm ausgebenString(s);
  • Der Morsecode soll in einem String s =“… — …“; gespeichert sein.
Lösungsvorschlag
#define BOARD_LED D13
char empfangByte = 0; // für empfangenes Byte
int dit = 200; // Basislänge in ms
String s="... --- ..."; // auszugebender Code

void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(BOARD_LED, OUTPUT);  // Pin als Ausgang
    pinMode(PA1,INPUT_PULLDOWN); // PA1 als Eingang
    Serial.begin(115200); // Serielle Schnittstelle starten und Baudrate festlegen
}

void led_dit(){ // Dit ausgeben
  digitalWrite(BOARD_LED,HIGH);
  delay(dit);
  digitalWrite(BOARD_LED,LOW);
  delay(dit);   // ein dit Symbolabstand
  Serial.print(".");
}
void led_dah(){ // Dah ausgeben
  digitalWrite(BOARD_LED,HIGH);
  delay(3*dit);
  digitalWrite(BOARD_LED,LOW);
  delay(dit);   // ein dit Symbolabstand
  Serial.print("-");
}
void buchstabenAbstand(){ // Buchstabenstand ausgeben
  delay(2*dit);     // 3-1
  Serial.print(" ");
}
void wortAbstand(){ // Wortabstand ausgeben
  delay(6*dit);     // 7-1
  Serial.print("  ");
}
void morseSOS(){
  int i;
  for(i=0;i<3;i++){ // S
    led_dit();
  }
  buchstabenAbstand();
  for(i=0;i<3;i++){ // O
    led_dah();
  }
  buchstabenAbstand();
  for(i=0;i<3;i++){ // S
    led_dit();
  }
}
void ausgebenString(String s){
  int i;
  for (i=0;i<s.length();i++){
    switch(s[i]){
      case '.':
        led_dit();
        break;
      case '-':
        led_dah();
        break;
      case ' ':
        buchstabenAbstand();    
    }
  }
}
void loop(){;
    //morseSOS();
    if(digitalRead(PA1)){ // wenn Taste A1 dedrückt
      ausgebenString(s);
      wortAbstand();
      ausgebenString("-- --- .-. ... .");
      Serial.println();
      delay(1000);
    }
}

Bonus: String ausgeben „SOS“

  • Das Unterprogramm ausgebenString(String s) soll nun ganze Sätze verarbeiten können.
  • Die Eingabe kann über den Seriellen Monitor erfolgen.

Noch keine Lösung…

Ampelsteuerung (mit Sturm-Board, Multifunction-Shield)

Für die Ampeln werden auf dem Multifunction-Shield die LEDs LC0..LC7 an den Portpins PC0..PC7 verwendet.
Zuerst soll eine Straßenampel für Autos mit LC0,LC2 und LC4 implementiert werden. Später werden zusätzlich LC6 und LC7 für die Fußgänger verwendet. Tipp: Einschalten Rot mit digitalWrite(PC0,HIGH);
❗️Quellcode muss anständig formatiert und mit sinnvollen Kommentaren versehen sein!

Es gibt 4 Auto-Ampelphasen: Grün, Gelb, Rot, Rot-Gelb. Nach Rot-Gelb wird wieder auf Grün gesprungen.

Erstellen Sie den Code für setup(), um die verwendeten Port-Pins als Ausgänge zu definieren.
Erstellen Sie den Code für loop(), um mit digitalWrite(..), delay(1000) die Ampelphasen im Sekundentakt zu wechseln.

Lösungsvorschlag
void setup() {
  pinMode(PC0,OUTPUT); // Rot
  pinMode(PC2,OUTPUT); // Gelb
  pinMode(PC4,OUTPUT); // Grün
}

void loop() {
  digitalWrite(PC4,HIGH); // Grün an
  delay(1000);
  digitalWrite(PC4,LOW);  // Grün aus
  digitalWrite(PC2,HIGH); // Gelb an
  delay(1000);
  digitalWrite(PC2,LOW);  // Gelb aus
  digitalWrite(PC0,HIGH); // Rot an
  delay(1000);
  digitalWrite(PC2,HIGH); // Gelb an
  delay(1000);
  digitalWrite(PC0,LOW);  // Rot aus
  digitalWrite(PC2,LOW);  // Gelb aus
}
LEDs Multifunction-Shield
LEDs Multifunction-Shield

Fußgängerampel

Nun bauen wir eine Fußgängerampel, die Pins PC6 und PC7 sind dafür vorgesehen. Die Fußgänger bekommen nur während der Auto-Rot-Phase Grün, sonst sehen sie Rot. Mit digitalWrite(..) würde der Code lang und unübersichtlich werden, daher setzen wir die Ausgänge mit GPIOC->ODR = Bitmuster in einem Rutsch. Für das Bitmuster ergänzen Sie diese Tabelle:

Ampel-PhasePC7 Fuß-GNPC6 Fuß-RTPC5PC4 Auto-GNPC3PC2 Auto-GEPC1PC0 Auto-RTGPIOC->ODR=
0 Grün010100000b0101 0000
1 Gelb1000
2 Rot1000
3 Rot-Gelb000
Ampelphasen
Lösung
Ampel-PhasePC7 Fuß-GNPC6 Fuß-RTPC5PC4 Auto-GNPC3PC2 Auto-GEPC1PC0 Auto-RTGPIOC->ODR=
0 Grün010100000b0101 0000
1 Gelb010001000b0100 0100
2 Rot100000010b1000 0001
3 Rot-Gelb010001010b0100 0101
Ampelphasen

Die 4 Ampelphasen werden in einem Feld unsigned char ampelphase[4]= {0b0101 000,…}; gespeichert, vervollständigen Sie die Initialisierung. Für die grüne Ampelphase kann nun schlicht geschrieben werden: GPIOC->ODR= ampelphase[0];

Steuerung mit Taster an PA1

Ein Taster an PA1 ist für die Fußgänger. Er ist mit 3,3V verbunden, der PullDown-Widerstand an PA1 soll eingeschaltet werden.
Die Ampel zeigt normalerweise für die Autofahrer Grün (Phase 0). PA1 startet folgenden Ablauf:

  • 6 Sec warten (sinnlos weiter rumstehen)
  • Phase 1 ausgeben
  • 2 Sec warten
  • Phase 2 ausgeben
  • 6 Sec warten
  • Phase 3 ausgeben
  • 3 Sec warten
  • zurück zu Phase 0

Der Taster PA1 wird so initialisiert und verwendet:

// setup
pinMode(PA1,INPUT_PULLDOWN); // Taster schaltet VCC
// abfragen
wert = digitalRead(PA1); // 1 wenn gedrückt

🖥 Ergänzen Sie den Code für setup(), das Feld und erstellen Sie den neuen Code für loop().

Lösungsvorschlag
void setup() {
  pinMode(PC0,OUTPUT); // Rot
  pinMode(PC2,OUTPUT); // Gelb
  pinMode(PC4,OUTPUT); // Grün
  pinMode(PC6,OUTPUT); // FRot
  pinMode(PC7,OUTPUT); // FGrün
  pinMode(PA1,INPUT_PULLDOWN);
}
const unsigned char ampelphase[4]={0b01010000,0b01000100,0b10000001,0b01000101};
void loop() {
  GPIOC->ODR = ampelphase[0]; // Grün
  while(!digitalRead(PA1));
  delay(6000);
  GPIOC->ODR = ampelphase[1]; // Gelb
  delay(2000);
  GPIOC->ODR = ampelphase[2]; // Rot
  delay(6000);
  GPIOC->ODR = ampelphase[3]; // RotGelb
  delay(3000);
}

Abstimmanzeige (mit Sturm-Board, Multifunction-Shield)

IO
IO

Die Anzahl der gedrückten Tasten PA1, PA6 und PA10 soll auf den LED LC0 (PC0), LC1 (PC1 und LC2 (PC2) als Leuchtband ausgegeben werden. Siehe Funktionstabelle.

Code-Vorgabe

void setup() {
  pinMode(PA1,INPUT_PULLDOWN); // PA1 als Eingang mit PullDown Widerstand
  pinMode(PA6,INPUT_PULLDOWN);
  pinMode(PA10,INPUT_PULLDOWN);
  pinMode(PC0,OUTPUT);
  pinMode(PC1,OUTPUT);
  pinMode(PC2,OUTPUT);
}

void loop() {
  int anzahl =0;
  if(digitalRead(PA1)) anzahl++;
  digitalWrite(PC0,HIGH); // LED LC0 einschalten
}
PA10PA6PA1LC2 (PC2)LC1 (PC1)LC0 (PC0)
000000
001001
010001
011011
100001
101011
110011
111111
Funktionstabelle nach Bitposition geordnet
Lösungsvorschlag mit switch und digitalWrite()
void setup() {
  pinMode(PA1,INPUT_PULLDOWN); // PA1 als Eingang mit PullDown Widerstand
  pinMode(PA6,INPUT_PULLDOWN);
  pinMode(PA10,INPUT_PULLDOWN);
  pinMode(PC0,OUTPUT);
  pinMode(PC1,OUTPUT);
  pinMode(PC2,OUTPUT);
}

void loop() {
  int anzahl =0;
  if(digitalRead(PA1)) anzahl++;
  if(digitalRead(PA6)) anzahl++;
  if(digitalRead(PA10)) anzahl++;
  switch (anzahl){
    case 0:
      digitalWrite(PC0,LOW);
      digitalWrite(PC1,LOW);
      digitalWrite(PC2,LOW);
      break;
    case 1:
      digitalWrite(PC0,HIGH);
      digitalWrite(PC1,LOW);
      digitalWrite(PC2,LOW);
      break;
    case 2:
      digitalWrite(PC0,HIGH);
      digitalWrite(PC1,HIGH);
      digitalWrite(PC2,LOW);
      break;
    case 3:
      digitalWrite(PC0,HIGH);
      digitalWrite(PC1,HIGH);
      digitalWrite(PC2,HIGH);
      break;      
  }
}

Eleganter mit GPIOC->ODR

Statt die Ausgabe-Pins einzeln mit digitalWrite(..) zu schalten, könnten doch alle gleich mit GPIOC->ODR= auf einmal eingestellt werden.
🖥 Erstellen Sie eine Lösung.

Lösungsvorschlag mit switch und GPIOC->ODR=
void setup() {
  pinMode(PA1,INPUT_PULLDOWN);
  pinMode(PA6,INPUT_PULLDOWN);
  pinMode(PA10,INPUT_PULLDOWN);
  pinMode(PC0,OUTPUT);
  pinMode(PC1,OUTPUT);
  pinMode(PC2,OUTPUT);
}

void loop() {
  int anzahl =0;
  if(digitalRead(PA1)) anzahl++;
  if(digitalRead(PA6)) anzahl++;
  if(digitalRead(PA10)) anzahl++;
  switch (anzahl){
    case 0: GPIOC->ODR =0; break;
    case 1: GPIOC->ODR =1; break;
    case 2: GPIOC->ODR =3; break;
    case 3: GPIOC->ODR =7; break;
  }
}

Mit Array led[]={..}

Die Ausgaben können auch mit einem Array umgerechnet werden und ausgegeben werden Codeschnipsel:

const unsigned char led[]={0b000,...}; // konstantes Array mit Ausgaben
GPIOC->ODR=led[anzahl];                // ausgeben Muster aus Array

🖥 Erstellen Sie eine Lösung.

Lösungsvorschlag mit Array
void setup() {
  pinMode(PA1,INPUT_PULLDOWN); // PA1 als Eingang mit PullDown Widerstand
  pinMode(PA6,INPUT_PULLDOWN);
  pinMode(PA10,INPUT_PULLDOWN);
  pinMode(PC0,OUTPUT);
  pinMode(PC1,OUTPUT);
  pinMode(PC2,OUTPUT);
}
const unsigned char led[]={0b000,0b001,0b011,0b111}; // konstantes Array mit Ausgaben
void loop() {
  int anzahl =0;
  if(digitalRead(PA1)) anzahl++;
  if(digitalRead(PA6)) anzahl++;
  if(digitalRead(PA10)) anzahl++;
  GPIOC->ODR=led[anzahl];             // ausgeben Muster aus Array
}

Bonus: Mit Bitschubserei zur Lösung

Zunächst: 1.1b 🚧🏋️ Bitschubserei lesen

Raffinierter Lösungsvorschlag mit Bitschubserei: Wie funktioniert das?
void setup() {
  pinMode(PA1,INPUT_PULLDOWN);
  pinMode(PA6,INPUT_PULLDOWN);
  pinMode(PA10,INPUT_PULLDOWN);
  pinMode(PC0,OUTPUT);
  pinMode(PC1,OUTPUT);
  pinMode(PC2,OUTPUT);
}

void loop() {
  int aus =0;
  int ein = GPIOA->IDR & 0b10001000010; // die Eingabe maskieren
  while(ein >0){              // solange Einsen da sind
    if(ein&1) aus=(aus<<1)+1; // zähle die 1 und baue Leuchtband auf
    ein >>= 1;                // schiebe 1 nach rechts
  }
  GPIOC->ODR = aus;
}

Lüftersteuerung mit LED-Leuchtbandanzeige

IO
IO

Ein Lüfter soll mit den Tasten PA1 (Ein/Aus) PA6 (Lüfter langsamer) und PA10 (Lüfter schneller) gesteuert werden. Es gibt 5 Lüfterstufen 0..4. Zunächst wird nur die LED-Anzeige (Leuchtband von LED LC0..LC3 an PC0..PC3) der Lüfterstufen entwickelt.
Die letzte Lüfterstufe soll beim Ausschalten gemerkt werden. Bei ausgeschaltetem Lüfter leuchtet keine LED und die Taster PA6 und PA10 sind ohne Funktion.

StufeLC3 (PC3)LC2 (PC2)LC1 (PC1)LC0 (PC0)
0
1O
2OO
3OOO
4OOOO
Anzeige der Lüfterstufen


Dieser Test-Quellcode ist schon gegeben:

void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(9600);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // PC0 als Ausgang
    pinMode(PC1, OUTPUT);
    pinMode(PC2, OUTPUT);
    pinMode(PC3, OUTPUT);
    GPIOC->ODR = 0xf;      // alle LED an zum Test
    delay(500);            // warte 0,5s
    GPIOC->ODR = 0;        // PC0..PC7 ausschalten
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
}
int stufe=0; // merken der Lüfterstufe
int an=0;    // ist der Lüfter an 
void ausgebenStufe(){
  if(an){
    GPIOC->ODR = stufe;
  }
  else GPIOC->ODR = 0;
}
void plusLuefter(){       // Lüfterstufe erhöhen und ausgeben
  Serial.printf("plusLuefter: %d\n",stufe);
  if(stufe<0b1000){  // wenn noch nicht Stufe 4
    stufe = (stufe<<1)+1; // schiebe nach links und setze Bit 0
  }
  ausgebenStufe();
}
void loop(){
  if(digitalRead(PA1)){   // wenn Taste PA1 gedrückt
    stufe = 0;            // Lüfterstufe = 0
    ausgebenStufe();
    Serial.println("zurückgesetzt");
    while(digitalRead(PA1)); // warten bis Taste losgelassen
  }
  if(digitalRead(PA10)){ // wenn Taste PA10 gedrückt
    an=1;
    plusLuefter();
    //delay(2); // Entprellen
    while(digitalRead(PA10)); // warten bis Taste losgelassen
    //delay(2); // Entprellen
  }
}

🖥 Testen Sie das Programm, funktioniert die Erhöhung der Lüfterstufen richtig?
🖥 Entfernen Sie die Kommentierung der delay(2) Anweisungen und testen Sie erneut [->Brummen und Prellen].
🖥 Entwickeln Sie nun das vollständige Programm.
Bonus: Eine Status-LED ob der Lüfter an ist wäre wünschenswert. LED5 an PC5 soll dafür genutzt werden.

Lösungsvorschlag
void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(9600);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // PC0 als Ausgang
    pinMode(PC1, OUTPUT);
    pinMode(PC2, OUTPUT);
    pinMode(PC3, OUTPUT);
    pinMode(PC5, OUTPUT);
    GPIOC->ODR = 0xff;      // alle LED an zum Test
    delay(500);            // warte 0,5s
    GPIOC->ODR = 0;        // PC0..PC7 ausschalten
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
}
int stufe=0; // merken der Lüfterstufe
bool an=0;    // ist der Lüfter an 
void ausgebenStufe(){
  if(an){
    GPIOC->ODR = stufe | 1<<5;
  }
  else GPIOC->ODR = 0;
}
void plusLuefter(){       // Lüfterstufe erhöhen und ausgeben
  Serial.printf("plusLuefter: %d\n",stufe);
  if(stufe<0b1000){  // wenn noch nicht Stufe 4
    stufe = (stufe<<1)+1; // schiebe nach links und setze Bit 0
  }
  ausgebenStufe();
}
void minusLuefter(){
  Serial.printf("minusLuefter: %d\n",stufe);
  if(stufe>0){ // wenn noch nicht Stufe 0
    stufe = stufe>>1;
  }
  ausgebenStufe();
}
void loop(){
  if(digitalRead(PA1)){   // wenn Taste PA1 gedrückt
    an = !an;
    ausgebenStufe();
    Serial.printf("Lüfteran: %d\n",an);
    delay(2); // Entprellen
    while(digitalRead(PA1)); // warten bis Taste losgelassen
    delay(2); // Entprellen
  }
  if(an){
    if(digitalRead(PA10)){ // wenn Taste PA10 gedrückt
      plusLuefter();
      delay(2); // Entprellen
      while(digitalRead(PA10)); // warten bis Taste losgelassen
      delay(2); // Entprellen
    }
    else if(digitalRead(PA6)){
      minusLuefter();
      delay(2); // Entprellen
      while(digitalRead(PA6)); // warten bis Taste losgelassen
      delay(2); // Entprellen
   }
  }
}

Entprellte Tastenabfrage mit buttonCheck()

Das Abfragen und Entprellen mehrerer Tasten müsste doch eleganter funktionieren. Durch das Warten bis eine Taste losgelassen wird ist das Hauptprogramm blockiert. Meine Lösung dafür ist ein Unterprogramm buttonCheck() das fragt die Tasten auf einmal ab und schreibt Änderungen (Flankendedektion) in zwei globale Variablen buttonEnter -bei Drücken von Tasten und buttonExit -bei Loslassen von Tasten. Das Unterprogramm benötigt dafür letztlich maximal die Zeit fürs Entprellen, das Hauptprogramm wird nicht lange blockiert. Im Hauptprogramm müssen nur noch die Bits der Tasten von buttonEnter bzw. buttonExit überprüft werden.
Zum Messen der Ausführungszeit von buttonCheck() wird PC8 als Ausgang verwendet.
Hier der Beispielcode:

void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(115200);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // PC0 als Ausgang
    pinMode(PC1, OUTPUT);
    pinMode(PC2, OUTPUT);
    pinMode(PC3, OUTPUT);
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
    pinMode(PC8, OUTPUT);         // Pin als Ausgang zum Messen der Ausführungszeit von buttonCheck()
}

#define BUTTONREADER GPIOA->IDR & 0b10001000010 // nur die relevanten Tastenbits
int buttonOld = 0;              // alter Tasten-Zustand 
int buttonEnter,buttonExit;     // gedrueckte und losgelassene Tasten 

void buttonCheck(){             // Tastaturabfrage mit Flankendedektion  
  int buttonTest,tmp; 
  buttonEnter = 0, buttonExit = 0; 
  buttonTest = BUTTONREADER;       // Einlesen
  if (buttonOld != buttonTest){    // hat sich was getan 
    delay(5);                      // 5ms Prellen abwarten 
    tmp = BUTTONREADER;            // noch mal Einlesen
    if (tmp == buttonTest){        // ist es stabil? 
      buttonEnter = (~buttonOld) & buttonTest; // steigende Flanke !alt und neu 
      buttonExit = buttonOld & (~buttonTest);  // fallende Flanke alt und !neu 
      buttonOld = buttonTest; 
    } 
  } 
} 
int an=0;    //  Lüfter aus
void loop(){
  GPIOC->BSRR = (1<<8);    // PC8 <-1 zum Messen
  buttonCheck();  // alle Tasten abfragen
  GPIOC->BSRR = (1<<16+8); // PC8 <-0 zum Messen
  if(buttonEnter&(1<<1)){ // wenn Taste PA1 gedrückt an/aus
    if(an){ // wenn Lüfter an
      an=0;
      GPIOC->ODR=0;
      Serial.println("Luefter aus");
    }
    else{
      an=1;
      GPIOC->ODR= 1<<5;  // Betriebs-LED an
      Serial.println("Luefter an");
    }
  }
}

Hier eine Messung der Ausführungsdauer von buttonCheck() an PC8 ohne Tastendruck. Die Zeit bei einem Tasten-Ereignis zu messen ist schwieriger, da im Single-Shot Modus des Oszis auf ein Tastenereignis getriggert werden muss.

Entprellen mit buttonCheck
Entprellen mit buttonCheck

Wieder so ein übles Nachprellen (Offnen der Taste) einer Taste (C1 gelb), buttonCheck() hält brav inne und wartet das Prellen ab (C2 blau). Scharfen Beobachtern fällt auf, dass die Wartezeit kleiner als das programmierte 5ms Delay ist. Wie funktioniert eigentlich das Arduino-Delay? Eine simple Warteschleife ist es jedenfalls nicht, sonst wäre die Zeit eher länger…
Hat was mit Timern zu tun und das lernen Sie bald kennen..
Bauen Sie buttonCheck() in ihre Lösung ein.

Lösungsvorschlag
void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(115200);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
}

#define BUTTONREADER GPIOA->IDR & 0b10001000010 // nur die relevanten Tastenbits
int buttonOld = 0;              // alter Tasten-Zustand 
int buttonEnter,buttonExit;     // gedrueckte und losgelassene Tasten 

void buttonCheck(){             // Tastaturabfrage mit Flankendedektion  
  int buttonTest,tmp; 
  buttonEnter = 0, buttonExit = 0; 
  buttonTest = BUTTONREADER;       // Einlesen
  if (buttonOld != buttonTest){    // hat sich was getan 
    delay(5);                      // Prellen abwarten 
    tmp = BUTTONREADER;            // noch mal Einlesen
    if (tmp == buttonTest){        // ist es stabil? 
      buttonEnter = (~buttonOld) & buttonTest; // steigende Flanke !alt und neu 
      buttonExit = buttonOld & (~buttonTest);  // fallende Flanke alt und !neu 
      buttonOld = buttonTest; 
    } 
  } 
} 
int stufe=0; // merken der Lüfterstufe
int an=0;    // ist der Lüfter an 
void plusLuefter(){
  Serial.println("plusLuefter");
  if(stufe<0b1000){ // wenn noch nicht Stufe 4
    stufe = (stufe<<1)+1;
  }
  GPIOC->ODR = stufe | an<<5;
}
void minusLuefter(){
  Serial.println("minusLuefter");
  if(stufe>0){ // wenn noch nicht Stufe 0
    stufe = stufe>>1;
  }
  GPIOC->ODR = stufe | an<<5;
}
void loop(){
  buttonCheck();  // alle Tasten abfragen
  if(buttonEnter&(1<<1)){ // wenn Taste PA1 gedrückt an/aus
    if(an){ // wenn Lüfter an
      an=0;
      GPIOC->ODR=0;
      Serial.println("Luefter aus");
    }
    else{
      an=1;
      GPIOC->ODR=stufe | an<<5;
      Serial.println("Luefter an");
    }
  }
  else if(an){
    if(buttonEnter&(1<<10)){ // wenn Taste PA10 gedrückt
      plusLuefter();
    }
    if(buttonEnter&(1<<6)){ // wenn Taste PA6 gedrückt
      minusLuefter();
    }
  }
}

Leistungssteuerung mit PWM durch analogWrite()

Achtung! Erst ruhig alles durchlesen, Links verfolgen und verstehen, dann anfangen. 🤠
Die LED an PC7 soll jetzt entsprechend der Lüfterleistungsstufe verschieden hell leuchten.
Dazu wird PulsWeitenModulation (PWM) verwendet. Lesen: Einfach [arduino] Umfangreich [wikipedia]
Entsprechend der Lüfterstufe wird ein Helligkeitswert durch analogWrite(…) ausgegeben:
stufe->hell: 0->0; 1->10; 2->40; 3->100; 4->255.
Verwenden Sie analogWrite(PC7,hell), wobei int hell einen Wert von 0..255 annehmen kann.
Notwendige Umbauarbeiten: Die Variable stufe beinhaltet nun nicht mehr das Ausgabemuster sondern die Werte 0..4. Ein Unterprogramm ausgebenStufe() lässt die entsprechenden LED PC0..PC3 leuchten und setzt den Wert von stufe in den Helligkeitswert hell um und gibt ihn mit analogWrite(..) aus.

Messen Sie den Signalverlauf an PC7 bei verschiedenen Helligkeitsstufen, das Signal kann mit dem Tastkopf hier abgegriffen werden [Portpins]. Mit welcher Frequenz wird das Signal ausgegeben?
Bonus: Die Auflösung könnte gesteigert werden mit analogWriteResolution().

Lösungsvorschlag
void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(115200);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
    //analogWriteResolution (16); //0-65535 PWM duty cycle
    //analogWriteFrequency(2000); // Set PMW period to 2000 Hz instead of 1000
    pinMode(PA5,OUTPUT);
}

#define BUTTONREADER GPIOA->IDR & 0b10001000010 // nur die relevanten Tastenbits
int buttonOld = 0;              // alter Tasten-Zustand 
int buttonEnter,buttonExit;     // gedrueckte und losgelassene Tasten 

void buttonCheck(){             // Tastaturabfrage mit Flankendedektion  
  int buttonTest,tmp; 
  buttonEnter = 0, buttonExit = 0; 
  buttonTest = BUTTONREADER;       // Einlesen
  if (buttonOld != buttonTest){    // hat sich was getan 
    delay(5);                      // Prellen abwarten 
    tmp = BUTTONREADER;            // noch mal Einlesen
    if (tmp == buttonTest){        // ist es stabil? 
      buttonEnter = (~buttonOld) & buttonTest; // steigende Flanke !alt und neu 
      buttonExit = buttonOld & (~buttonTest);  // fallende Flanke alt und !neu 
      buttonOld = buttonTest; 
    } 
  } 
} 
int stufe=0; // merken der Lüfterstufe
int an=0;    // ist der Lüfter an 
void plusLuefter(){
  Serial.println("plusLuefter");
  if(stufe<4){ // wenn noch nicht Stufe 4
    stufe++;
  }
  ausgebenStufe();
}
void minusLuefter(){
  Serial.println("minusLuefter");
  if(stufe>0){ // wenn noch nicht Stufe 0
    stufe--;
  }
  ausgebenStufe();
}
void ausgebenStufe(){
  GPIOC->ODR =0;
  for(int i=0;i<stufe;i++){
    GPIOC->ODR = (GPIOC->ODR<<1)+1;
  }
  GPIOC->ODR |= an<<5; // PC5 als Status LED
  int hell;
  switch (stufe){
    case 0: hell=0;break;
    case 1: hell=10;break;
    case 2: hell=40;break;
    case 3: hell=100;break;
    case 4: hell=255;break;
  }
  Serial.printf("Stufe: %d Helligkeit: %d\n",stufe,hell);
  analogWrite(PC7,hell);
}

void loop(){
  buttonCheck();  // alle Tasten abfragen
  if(buttonEnter&(1<<1)){ // wenn Taste PA1 gedrückt an/aus
    if(an){ // wenn Lüfter an
      an=0;
      GPIOC->ODR=0;
      Serial.println("Luefter aus");
    }
    else{
      an=1;
      ausgebenStufe();
      Serial.println("Luefter an");
    }
  }
  else if(an){
    if(buttonEnter&(1<<10)){ // wenn Taste PA10 gedrückt
      plusLuefter();
    }
    if(buttonEnter&(1<<6)){ // wenn Taste PA6 gedrückt
      minusLuefter();
    }
  }
}

ToDo: Motor mit Propeller über L293D anschließen.

Aufgaben: Analogwert von Poti an PA0 einlesen und ausgeben

Erstellen Sie ein Programm, dass den Wert von Poti mit analogRead(PA0) einliest und als Leuchtband auf LED0..6 ausgibt, LED7 soll entsprechend des Wertes mit analogWrite(..) verschieden hell leuchten. Die normale Auflösung von analogRead() ist 10 Bit, d.h. der Wertebereich ist 0..1023.
Bonus: Mit analogReadResolution(12) kann die Auflösung auf 12Bit hochgesetzt werden.

Lösungsvorschlag
void setup(){   // Einmalige Ausführung => Initialisierungen...
    Serial.begin(115200);  // Serielle Schnittstelle zum debuggen
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PA1,INPUT_PULLDOWN);  // Ein-Aus-Schalten
    pinMode(PA6,INPUT_PULLDOWN);  // minusLuefter
    pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
    //analogWriteResolution (16); //0-65535 PWM duty cycle
    //analogWriteFrequency(2000); // Set PMW period to 2000 Hz instead of 1000
    //analogReadResolution(12);
    pinMode(PA5,OUTPUT);
}

void ausgebenLeuchtband(int n){
  GPIOC->ODR =0;
  for(int i=0;i<n;i++){
    GPIOC->ODR = (GPIOC->ODR<<1)+1;
  }
}

void loop(){
  int adWert = analogRead(PA0);
  ausgebenLeuchtband(adWert/(1023/7));
  int hellLED7 = adWert>>2;
  analogWrite(PC7,hellLED7);
  Serial.printf("AD-: %4d Helligkeit: %3d\n",adWert,hellLED7);
  delay(300);
}

Erstellen Sie ein Programm, dass den Wert von Poti an PA0 einliest und zuerst einstellig und dann zweistellig auf der 7-Segmentanzeige ausgibt.
Tipp: Verwenden Sie ein Umwandungs-Array int bcd_7seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

Lösungsvorschlag einstellig
void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PC11,OUTPUT); // Einer
    pinMode(PC12,OUTPUT); // Zehner
}

int bcd_7seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; // Umrechnung

void ausgebenSiebenSegment(int n){
  GPIOC->ODR = bcd_7seg[n] | (1<<11); // Einer einschalten
}

void loop(){
  int adWert = analogRead(PA0);
  ausgebenSiebenSegment(adWert/(1023/9));
  delay(100);
}
Lösungsvorschlag zweistellig
void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PC11,OUTPUT); // Einer
    pinMode(PC12,OUTPUT); // Zehner
}

int bcd_7seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; // Umrechnung
int adWert=0;
void loop(){
  adWert = (analogRead(PA0)+adWert*9)/10; // Werte glätten
  int aus=adWert/10;
  //aus = aus>99?99:aus; // Wert begrenzen
  if(aus>99) aus=99;
  GPIOC->ODR = bcd_7seg[aus%10] | (1<<11); // Einer ausgeben
  delay(10);
  GPIOC->ODR = bcd_7seg[aus/10] | (1<<12); // Zehner ausgeben
  delay(10);
}

Bonus: Temperaturwert von NTC-Widerstand an PA4 einlesen

In der Boardbeschreibung wird ein NTC-Temperaturabhängiger Widerstand [Pollin] auf Seite 5 erwähnt...

void setup(){   // Einmalige Ausführung => Initialisierungen...
    pinMode(PC0, OUTPUT);  // ohne diese Zeile klappts nicht
    GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
    pinMode(PC11,OUTPUT); // Einer
    pinMode(PC12,OUTPUT); // Zehner
    analogReadResolution(12); // damit Temperatur genauer wird 12Bit
}

int bcd_7seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; // Umwandlung
float Wert = 0;
void loop(){
  float R2_25 = 1500; // Widerstandswert NTC bei 25°C
  float R2_theta;
  float R1 = 1500;    // Widerstandswert R1
  float dt;
  float t;
  float alpha = -0.045;
  Wert = (analogRead(PA4)+Wert*9)/10; // Messungen beruhigen, mitteln
  R2_theta = R1*Wert/(4095-Wert);
  dt = (R2_theta/R2_25-1)/alpha; // näherungsweise
  t = 25+dt;
  int aus = t; // in int umwandeln
  GPIOC->ODR = bcd_7seg[aus%10] | (1<<11); // Einer ausgeben
  delay(10);
  GPIOC->ODR = bcd_7seg[aus/10] | (1<<12); // Zehner ausgeben
  delay(10);
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert