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…
Abstimmanzeige (mit Sturm-Board)
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
}
PA10 | PA6 | PA1 | LC2 (PC2) | LC1 (PC1) | LC0 (PC0) |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 0 | 1 |
0 | 1 | 0 | 0 | 0 | 1 |
0 | 1 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 1 | 1 |
1 | 1 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 1 | 1 |
Lösungsvorschlag
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;
}
}
Zeiten mit Oszilloskop messen
Wie schnell kann ausgegeben werden? Die LED an D13 (PA5) so schnell wie möglich blinken lassen.
Das Signal mit einem Digital-Oszilloskop messen. (Bin hier im Paradies, hab ein Analog Discovery Studio)



In der Schule haben wir Hameg und Siglent Oszis.
Und neuerdings auch Analog Discovery Studios 😍.
Mit digitalWrite() Pin-Zustand ändern
#define Board_LED D13 //PA5
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(Board_LED, OUTPUT); // Pin als Ausgang
}
void loop(){
digitalWrite(Board_LED,HIGH);
digitalWrite(Board_LED,LOW);
}


Ein richtiger Oszi-Tastkopf ergibt sauberere Signale als bei Verwendung einfacher Strippen, wenn die Frequenz höher wird.
Durch die digitale Messwerterfassung lassen sich Werte für die Dauer des positiven-, negativen Pegels und der Frequenz des Signals sofort anzeigen. Zwischen High und Low vergehen 1,4µs, zwischen Low und High 2,2µs warum?
Die loop() braucht für irgendwelche Arbeit etwas Zeit, neuer Versuch mit while(1):
#define Board_LED D13
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(Board_LED, OUTPUT); // Pin als Ausgang
}
void loop(){
while(1){
digitalWrite(Board_LED,HIGH);
digitalWrite(Board_LED,LOW);
}
}

Yeah, wir werden schneller! Aber da geht noch was: Direkt auf das PortA zugreifen und das Bit 5 im ODR massieren:
Mit GPIOA->ODR die Hardware direkter ansprechen
Die Arduino-Funktion digitalWrite(..) ermöglicht einzelne Port-Pins einfach zu verändern. Das kostet allerdings Zeit durch interne Umrechnungen von Arduino-Bezeichnung des Pins zu passender Adresse und Bits des Ziel-Controllers. Wie Sie aus TG11 Assembler schon wissen, wird im ODR (Output Data Register) des Ports die Ausgabe-Bitwerte für die Port-Pins gespeichert. Wenn PA5 als Ausgang 1 sein soll, muss im ODR an Bit5 eine 1 stehen. Wenn ich nur PA5 auf 1 setzen will sollte ich die anderen Bits in Ruhe lassen. Mit GPIOA->ODR |= (1<<5) lade ich das ODR von PortA, mit |= (1<<5) setze ich an Bit5 eine 1 und schreibe den Wert ins ODR zurück. (1<<5) ist eine 1 5 mal nach links schieben also
(1<<5) ergibt 0b100000. Zum Rücksetzen von PA5 lade ich ODR und sorge mit einem bitweisen und mit einer 0 an Position 5 dafür dass PA0 wieder 0 wird.
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); // PA5 (D13) als Ausgang
}
void loop(){
while(1){
GPIOA->ODR |= (1<<5); // PA5 <-1
GPIOA->ODR &= ~(1<<5); // PA5 <-0
}
}

Wow, über sechs mal schneller, die Flanken werden flacher, das Messen kommt an Grenzen?
Geht da noch was? Noch schneller “Blinken”?
Statt den Port erst ein zu lesen und dann wieder neu zu setzen mit |= und &= gibt es noch ein BSRR-Register (Bit-Set-Reset-Register). Dabei werden die Bits im Ausgaberegister mit einem Maschinenbefehl direkt verändert:
Mit GPIOA->BSRR noch schneller werden
Statt ODR zu laden, zu verändern und wieder zu speichern gibt es ein Spezialregister BSRR (Bit Set Reset Register). Wenn dort in Position 0..15 eine 1 reingeschrieben wird ist im ODR an dieser Position eine 1 gesetzt, die Ausgabe an dem PortPin wird 1. Um ein Bit in der Position n zurück zu setzen, Reset, muss an Stelle 16+n eine 1 geschrieben werden. Unten das Beispiel für PA5 (D13):
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); // PA5 (D13) als Ausgang
}
void loop(){
while(1){
GPIOA->BSRR = (1<<5); // PA5 <-1 mit BSRR können Bits gesetzt werden
GPIOA->BSRR = (1<<16+5); // PA5 <-0 die oberen 16Bit sind fürs Rücksetzen
}
}

Irre, beinnahe drei mal schneller, das Oszi kommt noch gut hinterher bei 6,4 MHz!
Wir sind bei 30ns Befehlszeit zwischen an und aus, der Rück-Sprung in der While-Schleife braucht halt etwas…
Fazit: 6,4 MHz vs. 278 kHz: 23 mal schnellere Signale wenn hardwarenah programmiert wird.
Diskutieren Sie die Vor- und Nachteile bei der Programmentwicklung hardwarenah zu arbeiten versus die Arduino-Methoden zu verwenden.
Ausführungszeiten von Unterprogrammen und später Interrupt Service Routinen (ISR) mit Oszi messen können
Wie genau ist delay(500)?
#define ZEIT 500
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); // PA5 (D13) als Ausgang
}
void loop(){
while(1){
GPIOA->BSRR = (1<<5); // PA5 <-1
delay(ZEIT);
GPIOA->BSRR = (1<<16+5); // PA5 <-0
delay(ZEIT);
}
}


Okay, recht genau. Zum Spass wollen wir uns mal ein eigenes Delay basteln:
#define ZEIT 1000
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); // PA5 (D13) als Ausgang
}
//int a=0; // bei globaler Definition scheint kein volatile nötig
void myDelay(int n){ // mein Delay
volatile int a=0; // volatile bedeutet veränderlich
for(int i=0;i<n;i++){
a=!a; // in der Schleife muss es was zu tun geben
}
}
void loop(){
while(1){
GPIOA->BSRR = (1<<5); // PA5 <-1
myDelay(ZEIT);
GPIOA->BSRR = (1<<16+5); // PA5 <-0
myDelay(ZEIT);
}
}

War das eine Geburt, der Compiler optimiert sinnlose Befehlssequenzen weg, z.B. einfach Hochzählen ohne dabei etwas zu verändern. Die Variable a zu invertieren sollte eine Aufgabe sein. Allerdings muss sie dazu entweder global, d.h. ausserhalb von myDelay() definiert werden oder mit volatile als veränderlich gekennzeichnet sein. Probieren Sie es aus!
Bonus: Verbessern Sie myDelay() so, dass die Zeit stimmt.
Lösungsvorschlag
#define ZEIT 1000
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); PA5 (D13) als Ausgang
}
void myDelay(int n){
volatile int a=0;
for(int i=0;i<n*2905;i++){
a=!a;
}
}
void loop(){
while(1){
GPIOA->BSRR = (1<<5); // PA5 <-1
myDelay(ZEIT);
GPIOA->BSRR = (1<<16+5); // PA5 <-0
myDelay(ZEIT);
}
}
Brummen und Prellen messen
Das Oszi ist noch warm, noch ein paar Messungen damit machen. Den Tastkopf mit A1 verbinden, dort ist die Taste an PA1 angeschlossen. Dieses Programm ausführen und mit dem Finger die Leitung A1 berühren.
void setup(){ // Einmalige Ausführung => Initialisierungen...
pinMode(PA5, OUTPUT); PA5 (D13) als Ausgang
pinMode(PA1,INPUT);
}
void loop(){
digitalWrite(Board_LED,digitalRead(PA1));
}

Wir wirken wie eine Antenne für die 50Hz Netzspannung, also mit dem “Finger-Test” lässt sich rausfinden ob ein Eingang “hochohmig” also kein Pullup- oder Pulldown-Widerstand eingeschaltet ist. Jetzt INPUT_PULLDOWN einstellen und mal schauen ob der Taster an PA1 prellt:

Musste das Oszi auf Single-Shot umstellen und ein wenig rumprobieren bis mir diese Aufnahme gelungen ist.
150µs prellt es hier.

Richtig übel kann das Prellen beim Loslassen der Taste werden, wie lange ist es im Bild oben?
Zum Abschluss noch den blauen User-Button an PC13 auf dem Nucleo-Board analysieren: Aha mit PullUp-Widerstand verbunden, ist High, Fingertest erzeugt kein Brummen. Scheint sogar elektrisch entprellt zu sein.
Idee für Hardcore-Bonusaufgabe: Software, die Prellen aufzeichnet, und auf dem seriellen Plotter ausgibt..
Abstimmanzeige 2.0
Können Sie die Ausgabe der LED jetzt eleganter lösen?
Lösungsvorschlag
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;
}
}
Raffinierter Lösungsvorschlag
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;
while(ein >0){ // solange einsen
if(ein&1) aus=(aus<<1)+1; // zähle die 1
ein >>= 1; // schiebe 1 nach rechts
}
GPIOC->ODR = aus;
}
Lüftersteuerung mit LED-Leuchtbandanzeige

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.
Stufe | LC3 (PC3) | LC2 (PC2) | LC1 (PC1) | LC0 (PC0) |
---|---|---|---|---|
0 | – | – | – | – |
1 | – | – | – | O |
2 | – | – | O | O |
3 | – | O | O | O |
4 | O | O | O | O |
Um sich den Code (8 Zeilen mit pinMode(..) ) zum Festlegen von PC0..PC7 als Ausgang für die LEDs zu sparen werden die Ausgänge über das Mode-Register gesetzt:
GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
Ob und wie ein Port-Pin als Ausgang verwendet wird steht im MODER drin. Kann ja auch ohne Arduino-Zeug festgelegt werden.. Leider muss wenigstens ein pinMode(PC0, OUTPUT) im Quellcode rein sonst ging es nicht.. Wieso 0x5555 und nicht 0xFF könnte noch gefragt werden. Ja, so ein Pin kann Eingang oder Ausgang mit entweder OpenDrain oder PushPull sein. Also den Pin nach unten ziehen OpenDrain (OpenCollector) oder nix machen oder nach unten ziehen Pull -> 0 oder nach oben Push -> 1 liefern. Somit ergibt sich pro PortPin mehr als zwei Möglichkeiten Eingang, OpenDrain, PushPull. Folglich braucht es 2 Bit im ModeR pro Pin.
Dieser Test-Quellcode ist schon gegeben:
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
GPIOC->ODR = 0xff; // PC0..PC7 einschalten
delay(500);
GPIOC->ODR = 0; // PC0..PC7 ausschalten
pinMode(PA1,INPUT_PULLDOWN); // Ein-Aus-Schalten
pinMode(PA6,INPUT_PULLDOWN); // minusLuefter
pinMode(PA10,INPUT_PULLDOWN); // plusLuefter
}
void plusLuefter(){
Serial.println("plusLuefter");
if(GPIOC->ODR<0b1000){ // wenn noch nicht Stufe 4
GPIOC->ODR = (GPIOC->ODR<<1)+1; // schiebe nach links und setze PC0
}
}
void loop(){
if(digitalRead(PA10)){ // wenn Taste PA10 gedrückt
plusLuefter();
delay(2); // Entprellen
while(digitalRead(PA10)); // warten bis Taste losgelassen
delay(2); // Entprellen
}
}
Entwickeln Sie 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(115200); // Serielle Schnittstelle zum debuggen
pinMode(PC0, OUTPUT); // ohne diese Zeile klappts nicht
GPIOC->MODER = 0x5555; // PC0..PC7 als Ausgang
GPIOC->ODR = 0xff; // PC0..PC7 einschalten
delay(500);
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 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(){
if(digitalRead(PA1)){ // 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");
}
delay(2); // Entprellen
while(digitalRead(PA1)); // warten bis Taste losgelassen
delay(2); // Entprellen
}
else if(an){
if(digitalRead(PA10)){ // wenn Taste PA10 gedrückt
plusLuefter();
delay(2); // Entprellen
while(digitalRead(PA10)); // warten bis Taste losgelassen
delay(2); // Entprellen
}
if(digitalRead(PA6)){ // wenn Taste PA6 gedrückt
minusLuefter();
delay(2); // Entprellen
while(digitalRead(PA6)); // warten bis Taste losgelassen
delay(2); // Entprellen
}
}
}
Bessere Tastenabfrage mit buttonCheck()
Das Abfragen und Entprellen mehrerer Tasten müsste doch eleganter als oben 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.
Hier der Beispielcode:
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
pinMode(PC8, OUTPUT); // Pin als Ausgang
}
#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
buttonCheck(); // alle Tasten abfragen
GPIOC->BSRR = (1<<16+8); // PC8 <-0
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");
}
}
}
Messen Sie die 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.

Wieder so ein übles Nachprellen 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();
}
}
}
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);
}