Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
strings [2019/01/29 17:09]
huwi [Videozusammenfassung]
strings [2019/07/27 16:51] (aktuell)
huwi
Zeile 2: Zeile 2:
 Zeichenketten,​ auch Strings genannt, sind dem Wesen nach Felder (//​Arrays//​) vom  Typ //char// im Arbeitsspeicher (//SRAM//) des Controllers. Die String-Klasse ist aber eben nicht nur der eigentliche Speicher für die Zeichenketten,​ sondern repräsentiert auch eine Reihe von Operationen,​ die auf diese Speicher ausgeführt werden können. Dabei ist einer der wichtigsten Unterschiede zwischen einem //​Charakter-Array//​ und einer String-Klasse die dynamische Speicherverwaltung. Das bedeutet, dass beim einfachen //Array// die Größe des benötigten Speichers zum Zeitpunkt des Kompilierens fest steht und sich dann nicht mehr ändert. Bei der String-Klasse wird erst zur Laufzeit der dafür benötigte Speicherplatz angefordert und bei String-Operationen auch zur Laufzeit der Anwendung vergrößert oder verkleinert. Gleichzeitig gibt es String-Operationen,​ die eleganten Operatoren wie **+**, **+=** oder **==** zugeordnet sind und ein sehr modernes und komfortables Arbeiten erlauben. Leider gibt es das alles nicht zum Nulltarif. String-Klassen sollten nur benutzt werden, wenn ausreichend FLASH und SRAM zur Verfügung stehen. Der ATmega8 steckt das durchaus noch weg, aber so mancher Tiny eben nicht. Trotzdem kann auch auf kleineren Controllern noch objektorientiert gearbeitet werden, wenn man zum Beispiel auf den Komfort der String-Klasse verzichtet und wie im klassischen C mit Arrays arbeitet. Zeichenketten,​ auch Strings genannt, sind dem Wesen nach Felder (//​Arrays//​) vom  Typ //char// im Arbeitsspeicher (//SRAM//) des Controllers. Die String-Klasse ist aber eben nicht nur der eigentliche Speicher für die Zeichenketten,​ sondern repräsentiert auch eine Reihe von Operationen,​ die auf diese Speicher ausgeführt werden können. Dabei ist einer der wichtigsten Unterschiede zwischen einem //​Charakter-Array//​ und einer String-Klasse die dynamische Speicherverwaltung. Das bedeutet, dass beim einfachen //Array// die Größe des benötigten Speichers zum Zeitpunkt des Kompilierens fest steht und sich dann nicht mehr ändert. Bei der String-Klasse wird erst zur Laufzeit der dafür benötigte Speicherplatz angefordert und bei String-Operationen auch zur Laufzeit der Anwendung vergrößert oder verkleinert. Gleichzeitig gibt es String-Operationen,​ die eleganten Operatoren wie **+**, **+=** oder **==** zugeordnet sind und ein sehr modernes und komfortables Arbeiten erlauben. Leider gibt es das alles nicht zum Nulltarif. String-Klassen sollten nur benutzt werden, wenn ausreichend FLASH und SRAM zur Verfügung stehen. Der ATmega8 steckt das durchaus noch weg, aber so mancher Tiny eben nicht. Trotzdem kann auch auf kleineren Controllern noch objektorientiert gearbeitet werden, wenn man zum Beispiel auf den Komfort der String-Klasse verzichtet und wie im klassischen C mit Arrays arbeitet.
  
->>>>>>>>>>>>>>​>​{{:​string.jpg?​300|}}+>​{{:​string.jpg?​300|}}
  
 ====== Erste Schritte mit der String-Klasse ====== ====== Erste Schritte mit der String-Klasse ======
Zeile 8: Zeile 8:
 Strings können wie jede andere Variable als Attribut der Klasse Controller oder lokal angelegt werden. Als Klassenattribut stehen diese in allen Operationen der Klasse zur Verfügung, belegen dafür aber zur gesamten Laufzeit Arbeitsspeicher (SRAM). Als lokale Variablen wird nur für die Dauer der Gültigkeit des Strings SRAM belegt. ​ Strings können wie jede andere Variable als Attribut der Klasse Controller oder lokal angelegt werden. Als Klassenattribut stehen diese in allen Operationen der Klasse zur Verfügung, belegen dafür aber zur gesamten Laufzeit Arbeitsspeicher (SRAM). Als lokale Variablen wird nur für die Dauer der Gültigkeit des Strings SRAM belegt. ​
  
->>><​code cpp>+><​code cpp>
 class Application : public Controller class Application : public Controller
 { {
Zeile 29: Zeile 29:
 Als erste Übung mit der Klasse //String// soll eine Zeichenkette einfach per UART an den PC gesendet werden. Der String ist als Klassenattribut zu realisieren. Wie gehabt formulieren wir unsere Entwurfsgedanken erst mal als Kommentare. ​ Als erste Übung mit der Klasse //String// soll eine Zeichenkette einfach per UART an den PC gesendet werden. Der String ist als Klassenattribut zu realisieren. Wie gehabt formulieren wir unsere Entwurfsgedanken erst mal als Kommentare. ​
  
->>><​code cpp>+><​code cpp>
 //////////////////////////////////////////////////////////////////////​ //////////////////////////////////////////////////////////////////////​
 // ENTWURF Beispiel String1 // ENTWURF Beispiel String1
Zeile 54: Zeile 54:
 Bei der folgenden Realisierung kommt die Zuweisung des Inhaltes der Zeichenkette recht unspektakulär mit einer einfachen Wertzuweisung aus. Vergleichen Sie dies mit den Möglichkeiten von C eine  [[http://​www.google.de/#​hl=de&​sugexp=llsin&​gs_nf=1&​pq=c%20string%20funktionen&​cp=5&​gs_id=3l&​xhr=t&​q=strcpy&​pf=p&​sclient=psy-ab&​oq=strcp&​aq=0&​aqi=g3g-s1&​aql=&​gs_l=&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=802&​bih=909| Charakter-Array zur Laufzeit zu füllen]] oder kurz gesagt in C ist das zum Beispiel die Funktion **strcpy(text,​ "​Hallo"​);​** Diese ist zwar noch überschaubar,​ aber sexy ist es nicht, denn der Entwickler möchte den Inhalt an dieser Stelle zuweisen und muss sich mit einer Kopierfunktion auseinandersetzen. Den Versuch in C einem Charter-Array seinen Inhalt dynamisch per Wertzuweisung zu geben, wird mit den lakonischen Fehlermeldungen //invalid array assignment//​ vom Compiler abgewiesen. Geschweige denn dass sich C darum kümmert, wenn das Array zu klein für die Zeichenkette ist.  Bei der folgenden Realisierung kommt die Zuweisung des Inhaltes der Zeichenkette recht unspektakulär mit einer einfachen Wertzuweisung aus. Vergleichen Sie dies mit den Möglichkeiten von C eine  [[http://​www.google.de/#​hl=de&​sugexp=llsin&​gs_nf=1&​pq=c%20string%20funktionen&​cp=5&​gs_id=3l&​xhr=t&​q=strcpy&​pf=p&​sclient=psy-ab&​oq=strcp&​aq=0&​aqi=g3g-s1&​aql=&​gs_l=&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=802&​bih=909| Charakter-Array zur Laufzeit zu füllen]] oder kurz gesagt in C ist das zum Beispiel die Funktion **strcpy(text,​ "​Hallo"​);​** Diese ist zwar noch überschaubar,​ aber sexy ist es nicht, denn der Entwickler möchte den Inhalt an dieser Stelle zuweisen und muss sich mit einer Kopierfunktion auseinandersetzen. Den Versuch in C einem Charter-Array seinen Inhalt dynamisch per Wertzuweisung zu geben, wird mit den lakonischen Fehlermeldungen //invalid array assignment//​ vom Compiler abgewiesen. Geschweige denn dass sich C darum kümmert, wenn das Array zu klein für die Zeichenkette ist. 
  
->>><​code cpp>+><​code cpp>
 //////////////////////////////////////////////////////////////////////​ //////////////////////////////////////////////////////////////////////​
 // Beispiel String1 // Beispiel String1
Zeile 79: Zeile 79:
 Bilden, übertragen und testen sie das Programm. Beachten Sie die Einstellungen im myAVR Controlcenter.\\ Es sollte folgendes Ergebnis zu sehen sein. Bilden, übertragen und testen sie das Programm. Beachten Sie die Einstellungen im myAVR Controlcenter.\\ Es sollte folgendes Ergebnis zu sehen sein.
  
->>>>​>​{{:​string1.jpg?​600|}}+>​{{:​string1.jpg?​600|}}
  
 ====== Zeichenketten manipulieren ====== ====== Zeichenketten manipulieren ======
 Besonders bei der Interaktion mit dem Benutzer eines Systems ist es oft notwendig, textuelle Ausgaben zum Beispiel auf einem LC-Display aus verschiedenen Zeichenketten zusammenzusetzen bzw. die Strings in geeigneter Form anzupassen. Im Folgenden eine kleine Übersicht zu wichtigen Stringmanipulationen über Operatoren. Besonders bei der Interaktion mit dem Benutzer eines Systems ist es oft notwendig, textuelle Ausgaben zum Beispiel auf einem LC-Display aus verschiedenen Zeichenketten zusammenzusetzen bzw. die Strings in geeigneter Form anzupassen. Im Folgenden eine kleine Übersicht zu wichtigen Stringmanipulationen über Operatoren.
  
->>><​code cpp>+><​code cpp>
  /////////////////////////////////////////////////​  /////////////////////////////////////////////////​
  // Übersicht zu den Operatoren der Klasse String  // Übersicht zu den Operatoren der Klasse String
Zeile 108: Zeile 108:
 Das erste Beispiel für eine Stringmanipulation soll eine Erweiterung des vorangegangenen Beispiels sein. Es soll per Uart abwechselnd **"​Hallo myAVR"​** und **"​Hallo AVR C++"** ausgegeben werden. Dabei ist der jeweils aktuelle Ausgabestring aus den drei Teilstrings **"​Hallo"​**,​ **"​myAVR"​** und **"AVR C++"** zusammenzusetzen. Zum Einsatz kommt dabei der //​Plus-Operator//​. Das erste Beispiel für eine Stringmanipulation soll eine Erweiterung des vorangegangenen Beispiels sein. Es soll per Uart abwechselnd **"​Hallo myAVR"​** und **"​Hallo AVR C++"** ausgegeben werden. Dabei ist der jeweils aktuelle Ausgabestring aus den drei Teilstrings **"​Hallo"​**,​ **"​myAVR"​** und **"AVR C++"** zusammenzusetzen. Zum Einsatz kommt dabei der //​Plus-Operator//​.
  
->>><​code cpp>+><​code cpp>
 //////////////////////////////////////////////////////////////////////​ //////////////////////////////////////////////////////////////////////​
 // ENTWURF Beispiel Stringmanipulation 1 // ENTWURF Beispiel Stringmanipulation 1
Zeile 135: Zeile 135:
 Nachdem wir uns ein Konzept per Kommentar zurechtgelegt und das ganze noch mal einem prüfenden Blick unterworfen haben, kann die Realisierung beginnen. Es soll jede Ausgabe auf einer neuen Zeile beginnen. Dazu schließen wir den String mit dem Sonderzeichen **"​\n"​** ab. Im myAVR Controlcenter ist es möglich, die Einstellung für den Zeilenvorschub in der Textansicht anzupassen. In ASCII-Code ist der Zeilenvorschub (Line Feed LF) mit der Zahl 10 codiert oder in hexadezimaler Schreibweise 0x0A.  Nachdem wir uns ein Konzept per Kommentar zurechtgelegt und das ganze noch mal einem prüfenden Blick unterworfen haben, kann die Realisierung beginnen. Es soll jede Ausgabe auf einer neuen Zeile beginnen. Dazu schließen wir den String mit dem Sonderzeichen **"​\n"​** ab. Im myAVR Controlcenter ist es möglich, die Einstellung für den Zeilenvorschub in der Textansicht anzupassen. In ASCII-Code ist der Zeilenvorschub (Line Feed LF) mit der Zahl 10 codiert oder in hexadezimaler Schreibweise 0x0A. 
  
->>>>>​>​{{:​textausgabeanpassen.jpg?​600|}}+>​{{:​textausgabeanpassen.jpg?​600|}}
  
->>><​code cpp>+><​code cpp>
 //////////////////////////////////////////////////////////////////////​ //////////////////////////////////////////////////////////////////////​
 // Beispiel Stringmanipulation 1 // Beispiel Stringmanipulation 1
Zeile 192: Zeile 192:
 Für den ersten Test zur Formatierung von Strings inkrementieren wir einfach eine Zahl und schauen mal, was sich mit den verschiedenen Formaten anstellen lässt. Zuerst wieder das Konzept. Für den ersten Test zur Formatierung von Strings inkrementieren wir einfach eine Zahl und schauen mal, was sich mit den verschiedenen Formaten anstellen lässt. Zuerst wieder das Konzept.
  
->>><​code cpp>+><​code cpp>
 ////////////////////////////////////////////////////////////////​ ////////////////////////////////////////////////////////////////​
 // ENTWURF Beispiel String-Formatierung // ENTWURF Beispiel String-Formatierung
Zeile 218: Zeile 218:
 ein bischen etwas über die Funktion [[http://​www.google.de/#​hl=de&​sugexp=llsin&​gs_nf=1&​pq=printf&​cp=8&​gs_id=9&​xhr=t&​q=printf+C&​pf=p&​sclient=psy-ab&​oq=printf+C&​aq=0&​aqi=g4&​aql=&​gs_l=&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|printf]] nach. Wichtig ist, dass ein **%** immer anzeigt, dass an dieser Stelle in der angegebenen Zeichenkette eine Formatierung eines Parameters erfolgt. Die Anzahl der folgenden Parameter muss unbedingt mit der Anzahl der Formatierungszeichen übereinstimmen. Desweiteren denken sie daran, dass wir die Funktion nicht auf dem PC mit schier unendlichen Ressourcen fahren, sondern auf einem 8 Bit Mikrocontroller. Also müssen wir ein paar Einschränkungen gegenüber den Möglichkeiten der Standard C Funktion akzeptieren. ​ ein bischen etwas über die Funktion [[http://​www.google.de/#​hl=de&​sugexp=llsin&​gs_nf=1&​pq=printf&​cp=8&​gs_id=9&​xhr=t&​q=printf+C&​pf=p&​sclient=psy-ab&​oq=printf+C&​aq=0&​aqi=g4&​aql=&​gs_l=&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|printf]] nach. Wichtig ist, dass ein **%** immer anzeigt, dass an dieser Stelle in der angegebenen Zeichenkette eine Formatierung eines Parameters erfolgt. Die Anzahl der folgenden Parameter muss unbedingt mit der Anzahl der Formatierungszeichen übereinstimmen. Desweiteren denken sie daran, dass wir die Funktion nicht auf dem PC mit schier unendlichen Ressourcen fahren, sondern auf einem 8 Bit Mikrocontroller. Also müssen wir ein paar Einschränkungen gegenüber den Möglichkeiten der Standard C Funktion akzeptieren. ​
  
->>><​code cpp>+><​code cpp>
 ////////////////////////////////////////////////////////////////​ ////////////////////////////////////////////////////////////////​
 // Beispiel String-Formatierung // Beispiel String-Formatierung
Zeile 246: Zeile 246:
 Bilden, übersetzen und testen Sie das Programm. Beachten Sie die Einstellungen für den Zeilenvorschub im myAVR Controlcenter. Variieren Sie die Formatierung. Im Folgenden einige Anregungen: Bilden, übersetzen und testen Sie das Programm. Beachten Sie die Einstellungen für den Zeilenvorschub im myAVR Controlcenter. Variieren Sie die Formatierung. Im Folgenden einige Anregungen:
  
->>><​code cpp>+><​code cpp>
 text.format("​wert = %6u das ist als HEX %4X \n",​wert,​wert);​ text.format("​wert = %6u das ist als HEX %4X \n",​wert,​wert);​
 text.format("​wert = %06d das ist als HEX %04X \n",​wert,​wert);​ text.format("​wert = %06d das ist als HEX %04X \n",​wert,​wert);​
Zeile 257: Zeile 257:
 Viele Mikrocontrolleranwendungen erfassen analoge Messwerte oder/und realisieren mehr oder weniger ausgefeilte Steuer- und Reglungskonzepte,​ bei denen mit einmal Kommazahlen ins Spiel kommen. Der AVR verfügt über keine Einheit für Fließkommaarithmetik ([[http://​de.wikipedia.org/​wiki/​Gleitkommaeinheit|FPU,​ Floting Point Unit]]). Eine Berechnung von Kommazahlen muss dieser aufwendig in Softwareroutinen abbilden. Das frisst selbst in Assembler aber auch in C erheblich FLASH und Rechenzeit. Die Arithmetik mit ganzzahligen Werten erledigt ein AVR durchaus fix und speichereffizient. Für viele Anwendungsfälle genügt es, die gewünschte Berechnung einer Zahl mit Kommastellen in [[http://​www.google.de/#​hl=de&​sclient=psy-ab&​q=integer+arithmetik+&​oq=integer+arithmetik+&​aq=f&​aqi=g1&​aql=&​gs_l=serp.3..0.5l5l4l337l1l1l0l0l0l0l96l96l1l1l0.llsin.&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|Integerarithmetik]] abzubilden. Ein einfaches Konzept Kommazahlen mit hinreichender Genauigkeit zu ermitteln ist, dass man einfach die gewünschten Operationen um eins, zwei oder drei Potenzen erhöht. Also nicht mit Metern und zwei Nachkommastellen rechen, sonder einfach in Zentimeter ohne Komma oder statt in Volt mit drei Kommastellen in Millivolt. Die Integerarithmetik unterstützt diese Vorgehensweise sehr gut und bietet Operatoren an, um auch mit der jeweils zugehöhrigen Kommazahl einigermaßen ordentlich umgehen zu können. Dabei werden die Vorkommastellen und die Nachkommastellen jedoch getrennt voneinander gehandhabt. Bei einer ganzzahligen Division erhält man als Ergebnis leider nur die Vorkommastellen. Der verbleibende Rest hinter dem Komma scheint verloren. Mit dem [[http://​www.google.de/#​hl=de&​sclient=psy-ab&​q=modulo+C&​oq=modulo+C&​aq=f&​aqi=g4&​aql=&​gs_l=serp.12..0l4.0l0l9l4098l0l0l0l0l0l0l0l0ll0l0.llsin.&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|Modulo]] lässt sich dieser aber auch ermitteln, womit wir dann die Nachkommastellen in der Hand haben. Im Folgenden Beispiel soll die Ausgabe von Kommazahlen ohne die tatsächliche Anwendung von Gleikommaarithmetik demonstriert werden. Viele Mikrocontrolleranwendungen erfassen analoge Messwerte oder/und realisieren mehr oder weniger ausgefeilte Steuer- und Reglungskonzepte,​ bei denen mit einmal Kommazahlen ins Spiel kommen. Der AVR verfügt über keine Einheit für Fließkommaarithmetik ([[http://​de.wikipedia.org/​wiki/​Gleitkommaeinheit|FPU,​ Floting Point Unit]]). Eine Berechnung von Kommazahlen muss dieser aufwendig in Softwareroutinen abbilden. Das frisst selbst in Assembler aber auch in C erheblich FLASH und Rechenzeit. Die Arithmetik mit ganzzahligen Werten erledigt ein AVR durchaus fix und speichereffizient. Für viele Anwendungsfälle genügt es, die gewünschte Berechnung einer Zahl mit Kommastellen in [[http://​www.google.de/#​hl=de&​sclient=psy-ab&​q=integer+arithmetik+&​oq=integer+arithmetik+&​aq=f&​aqi=g1&​aql=&​gs_l=serp.3..0.5l5l4l337l1l1l0l0l0l0l96l96l1l1l0.llsin.&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|Integerarithmetik]] abzubilden. Ein einfaches Konzept Kommazahlen mit hinreichender Genauigkeit zu ermitteln ist, dass man einfach die gewünschten Operationen um eins, zwei oder drei Potenzen erhöht. Also nicht mit Metern und zwei Nachkommastellen rechen, sonder einfach in Zentimeter ohne Komma oder statt in Volt mit drei Kommastellen in Millivolt. Die Integerarithmetik unterstützt diese Vorgehensweise sehr gut und bietet Operatoren an, um auch mit der jeweils zugehöhrigen Kommazahl einigermaßen ordentlich umgehen zu können. Dabei werden die Vorkommastellen und die Nachkommastellen jedoch getrennt voneinander gehandhabt. Bei einer ganzzahligen Division erhält man als Ergebnis leider nur die Vorkommastellen. Der verbleibende Rest hinter dem Komma scheint verloren. Mit dem [[http://​www.google.de/#​hl=de&​sclient=psy-ab&​q=modulo+C&​oq=modulo+C&​aq=f&​aqi=g4&​aql=&​gs_l=serp.12..0l4.0l0l9l4098l0l0l0l0l0l0l0l0ll0l0.llsin.&​pbx=1&​bav=on.2,​or.r_gc.r_pw.r_qf.,​cf.osb&​fp=466f7f41d54d539d&​biw=1144&​bih=909|Modulo]] lässt sich dieser aber auch ermitteln, womit wir dann die Nachkommastellen in der Hand haben. Im Folgenden Beispiel soll die Ausgabe von Kommazahlen ohne die tatsächliche Anwendung von Gleikommaarithmetik demonstriert werden.
  
- +><​code cpp>
->>><​code cpp>+
 /////////////////////////////////////////////////////////////////////​ /////////////////////////////////////////////////////////////////////​
 // Konzept für das Beispiel Kommazahlen ohne float // Konzept für das Beispiel Kommazahlen ohne float
Zeile 285: Zeile 284:
 Damit wir die Übersicht behalten, soll die Ausgabe zeilenweise und tabellarisch erfolgen.  ​ Damit wir die Übersicht behalten, soll die Ausgabe zeilenweise und tabellarisch erfolgen.  ​
  
->>><​code cpp>+><​code cpp>
 /////////////////////////////////////////////////////////////////////​ /////////////////////////////////////////////////////////////////////​
 // Beispiel Kommazahlen ohne float // Beispiel Kommazahlen ohne float
Zeile 319: Zeile 318:
 Bilden, übertragen und testen sie das Programm. Beachten sie die Einstellungen für den Zeilenvorschub im myAVR Controlcenter. Bilden, übertragen und testen sie das Programm. Beachten sie die Einstellungen für den Zeilenvorschub im myAVR Controlcenter.
  
->>>>​>​{{:​stringformat.jpg?​600|}}+>​{{:​stringformat.jpg?​600|}}
  
 ====== Videozusammenfassung ====== ====== Videozusammenfassung ======
->>><​html><​iframe width="​700"​ height="​550"​ src="​https://​www.youtube.com/​embed/​lKbmpxNVD_c"​ frameborder="​0"​ allow="​accelerometer;​ autoplay; encrypted-media;​ gyroscope; picture-in-picture"​ allowfullscreen></​iframe></​html>​+><​html><​iframe width="​700"​ height="​550"​ src="​https://​www.youtube.com/​embed/​lKbmpxNVD_c"​ frameborder="​0"​ allow="​accelerometer;​ autoplay; encrypted-media;​ gyroscope; picture-in-picture"​ allowfullscreen></​iframe></​html>​
  
 ====== Nächstes Thema ====== ====== Nächstes Thema ======
 [[ADC|analoge Daten verarbeiten]] [[ADC|analoge Daten verarbeiten]]