Hallo Leute ich habe mir eine keine Bibliothek zusammen geschustert, mit der ich VT100-Terminals ansteuern kann. Die Bibliothek liefert im Grunde nur Strings, die die Steuersignale enthalten. Dadurch sollte die Bibliothek ohne Probleme unter AVRs, ARMs und PICs gleichermaßen laufen (eventuell andere Namen der eingebundenen Standarbibliotheken). Auf welche Art man die Strings an das Terminal-Programm sendet bleibt einem selbst überlassen, dadurch soll die Bibliothek möglichst flexibel sein. Ich habe selbst eine Weile nach etwas entsprechendem gesucht aber leider nichts in die Richtung gefunden (vll bin ich auch nur zu doof für Google). Daher stelle ich sie auch hier zur Verfügung, damit sie jeder nutzen kann, wie er möchte. Aktuell verwende ich sie um Messdaten per UART an den PC zu senden und dort darzustellen. Als Terminal-Programm kann ich jedem Tera Term nur wärmstens empfehlen. Aktuell gibt es bei der Bibliothek noch zwei Baustellen: Zum einen wird an ein paar Stellen sprintf von mir verwendet. Diese Funktion frisst viel Speicher und ist langsam und soll deswegen ausgetauscht werden. Bis jetzt hatte ich auf meinem kleinen ATmega2560 zwar noch keine Speicherprobleme aber bei kleineren AVRs ist das ein großes Minus. Des weiteren sind keine Befehle implementiert, die eine Rückgabe des Terminal-Programms zur Folge haben. Da weiß ich noch nicht ob und wie ich das lösen werde, weil ich dann ja wiederum die Rückgabe des Terminal-Programms verarbeiten muss, was ohne Zugriff auf die Hardware schwierig ist. Eventuell habt ihr da eine Idee wie ich die beiden Probleme lösen könnte. Ansonsten viel Spaß damit!! :D Gruß Kai L.
:
Bearbeitet durch User
Kai L. schrieb: > ich habe mir eine keine Bibliothek zusammen geschustert, mit der ich > VT100-Terminals ansteuern kann. Schau Dir mal MCURSES an. Das ist eine abgespeckte Version der (n)curses-Bibliothek, welche speziell für die Benutzung auf Mikrocontrollern angepasst wurde. Sie "versteckt" nicht nur alle Escape-Sequenzen vor dem Programmierer, sondern liefert auch sämtliche Funktionalitäten mit, um ein VT100 (VT200/VT320/VT400) nicht nur anzusteuern, sondern versteht auch alle gängigen Funktions-/Cursor- und Navigationstasten. Diese werden direkt in numerische Tasten-Codes umgesetzt. MCURSES kann Attribute (Farbe, etc) setzen, Scrolling-Regions verwenden, Einzelzeichen- und String-Ausgabe und noch einiges mehr. Ein kleines Tetris-Spiel (Menüführungen etc.) lässt sich damit in Nullkommanix programmieren. Damit ist Deine "Bibliothek" mit gerade 3 simplen Funktionen, (welche auch noch das speicherhungrige sprintf() verwendet) komplett hinfällig. MCURSES läuft auf Unix, Linux, AVRs (ATmega, ATTiny) und anderen µCs wie STM32. Selbst auf Z80 (SDCC) ist die Bibliothek portiert. Die Portierung auf andere Prozessoren beschränkt sich im wesentlichen auf eine Implementierung von putchar() und getchar(). Der Rest ist portabler Code. Sorry, Frank
:
Bearbeitet durch Moderator
Anbei ist eine neue Version, die sprintf durch utoa und ein paar weitere Zeilen zur Formatierung ersetzt. Ohne sprintf sinkt der Speicherverbrauch in meinem kleinen Testprogramm um satte 1130 Byte. Die Geschwindigkeit sollte entsprechend auch gestiegen sein. Auch war vorher fälschlicherweise die stdlib.h statt der stdio.h eingebunden, was jetzt jedoch genau passend ist. Viel Spaß! :)
Hallo Frank, vielen Dank für den Tipp :) Bis jetzt war ich nur auf ncourses gestoßen, die abgespeckte Version davon kannte ich noch nicht. Meine Mini-Bibliothek kann da von der Funktionsvielfalt natürlich nicht mithalten, wobei sie geschätzt gerade einmal 400 Bytes groß ist (das ganze Programm samt Schleifen, etc. umfasst 558 Bytes) und zumindest für mich alle benötigten rudimentären Funktionen bereitstellt. Einen Nachteil der mcourses Bibliothekt sehe ich jedoch darin, dass sie um alle Funktionen zu realisieren auch die Hardwareseitig den UART kontrolliert, was ich nicht möchte. In einem Fall benutze ich nämlich meine kleine Bibliothek um auf dem ATmega2560 Daten raus zu liefern über den UART2. Die oben beschriebene Bibliothek kann aber maximal 2 UARTs verwalten. Daher müsste ich sie, wenn ich sie verwenden sollte umschreiben. Mir war es wichtig eine strikte Trennung zwischen den Befehlen und der Hardware-Ausgabe bereit zu stellen. Das physikalische Layer, auf dem die Daten übertragen werden wird dadurch irrelevant und ich kann ohne großen Aufwand einfach die Funktionen einer UART-Bibliothek verwenden um die Befehle an den PC zu senden. An der Bibliothek ändern muss ich dabei nichts. Ich hoffe ich habe irgendwie verständlich gemacht was mein Hintergedanke hierbei war ;) Die mcourses Bibliothek ist ganz klar von der Funktionalität her größer und besser, allerdings verfolge ich mit meiner Mini-Bibliothek einen anderen Ansatz. Gruß Kai
:
Bearbeitet durch User
Kai L. schrieb: > Einen Nachteil der mcourses Bibliothekt sehe ich jedoch darin, dass sie > um alle Funktionen zu realisieren auch die Hardwareseitig den UART > kontrolliert, was ich nicht möchte. Das stimmt so nicht. Durch Anpassung von nur 2 Funktionen void mcurses_phyio_putc (uint8_t ch) und uint8_t mcurses_phyio_getc (void) auf das gewünschte Ausgabe-Device, kannst Du kommunizieren, worüber Du willst. Das kann UART1, UART3, UARTX, SPI, I2C, ETHERNET oder sonstwas sein. > In einem Fall benutze ich nämlich > meine kleine Bibliothek um auf dem ATmega2560 Daten raus zu liefern über > den UART2. Die oben beschriebene Bibliothek kann aber maximal 2 UARTs > verwalten. Daher müsste ich sie, wenn ich sie verwenden sollte > umschreiben. Das meinst Du nur auf den ersten Blick. MCURSES hat strikt die logischen und die physikalischen Funktionen getrennt. > Ich hoffe ich habe irgendwie verständlich gemacht was mein Hintergedanke > hierbei war ;) Die mcourses Bibliothek ist ganz klar von der > Funktionalität her größer und besser, allerdings verfolge ich mit meiner > Mini-Bibliothek einen anderen Ansatz. Sie ist auf einem AVR gerade mal 1,9 KB groß - bei vielfachem Funktionsumfang. Deine 3 Funktionen verschlingen schon 500 Byte. Allein die Fallunterscheidung, ob x bzw. y eine 1-, 2- oder 3-stellige Zahl ist, ist viel zu kompliziert. statt 9 mal: buffer[5] = 'H'; ... buffer[6] = 'H'; ... buffer[7] = 'H'; zu schreiben, macht man das mit einer einzigen Zuweisung am Schluss der Funktion: buffer[pos] = 'H'; Du machst für so etwas einfaches eine Fallunterscheidung von 3x3 = 9 Fällen und brauchst alleine, um den Cursor zu setzen, 68 Zeilen Code. Hier die äquivalente Funktion in MCURSES: static void mymove (uint8_t y, uint8_t x) { mcurses_puts_P (SEQ_CSI); mcurses_puti (y + 1); mcurses_putc (';'); mcurses_puti (x + 1); mcurses_putc ('H'); } Die mcurses_puts/puti/putc-Funktionen sind alle vom Ausgabedevice komplett unabhängig. Sag mal: Wie lange programmierst Du schon?
:
Bearbeitet durch Moderator
Hier Deine 3 Funktionen in einer anderen Form, die nur 80 Bytes statt 322 auf einem ATmega88 an Code verbraten - bei gleicher Funktionalität:
1 | static uint8_t |
2 | myutoa10 (uint8_t i, char * buffer) |
3 | {
|
4 | uint8_t len; |
5 | |
6 | if (i >= 100) |
7 | {
|
8 | uint8_t ii = i / 100; |
9 | *buffer++ = ii + '0'; |
10 | i -= 100 * ii; |
11 | ii = i / 10; |
12 | *buffer++ = ii + '0'; |
13 | i -= 10 * ii; |
14 | len = 3; |
15 | }
|
16 | else if (i >= 10) |
17 | {
|
18 | uint8_t ii = i / 10; |
19 | *buffer++ = ii + '0'; |
20 | i -= 10 * ii; |
21 | len = 2; |
22 | }
|
23 | else
|
24 | {
|
25 | len = 1; |
26 | }
|
27 | |
28 | *buffer++ = i + '0'; |
29 | return rtc; |
30 | }
|
31 | |
32 | void VT_CURSOR_UP_N(char* buffer, uint8_t n) |
33 | {
|
34 | uint8_t pos; |
35 | |
36 | //buffer benoetigt eine Mindestlaenge von 7 Zeichen
|
37 | buffer[0] = '\e'; |
38 | buffer[1] = '['; |
39 | pos = 2; |
40 | pos += myutoa10(n, &buffer[pos]); |
41 | buffer[pos++] = 'A'; |
42 | buffer[pos] = 0; |
43 | }
|
44 | |
45 | |
46 | void VT_CURSOR_DOWN_N(char* buffer, uint8_t n) |
47 | {
|
48 | uint8_t pos; |
49 | |
50 | //buffer benoetigt eine Mindestlaenge von 7 Zeichen
|
51 | buffer[0] = '\e'; |
52 | buffer[1] = '['; |
53 | pos = 2; |
54 | pos += myutoa10(n, &buffer[pos]); |
55 | buffer[pos++] = 'A'; |
56 | buffer[pos] = 0; |
57 | }
|
58 | |
59 | void VT_CURSOR_SET(char* buffer, uint8_t x, uint8_t y) |
60 | {
|
61 | uint8_t pos; |
62 | |
63 | //buffer benoetigt eine Mindestlaenge von 11 Zeichen
|
64 | buffer[0] = '\e'; |
65 | buffer[1] = '['; |
66 | pos = 2; |
67 | pos += myutoa10(x, &buffer[pos]); |
68 | buffer[pos++] = ';'; |
69 | pos += myutoa10(y, &buffer[pos]); |
70 | buffer[pos++] = 'H'; |
71 | buffer[pos] = 0; |
72 | }
|
Damit sinkt der erzeugte Code auf unter ein Viertel. Aber nicht nur dieser ist um ein Vielfaches kürzer, sondern auch der C-Quelltext - und dabei immer noch gut lesbar. Die Funktion utoa() wurde dabei durch myutoa10() ersetzt. Diese ist nicht nur kürzer, sondern liefert auch direkt die Länge zurück. Code ungetestet, sehe aber auf Anhieb keinen Fehler. Übersetzt wurde beide Versionen mit
1 | int main () |
2 | {
|
3 | char buffer[12]; |
4 | |
5 | VT_CURSOR_SET(buffer, 10, 10); |
6 | VT_CURSOR_UP_N(buffer, 1); |
7 | VT_CURSOR_UP_N(buffer, 1); |
8 | }
|
Alt: Program: 322 bytes (3.9% Full) Neu: Program: 80 bytes (1.0% Full) Das Konzept, erst einen Buffer zu füllen, um ihn dann in einer Schleife auszugeben, halte ich für doppelt gemoppelt. Warum erst den Buffer füllen? Direkt raus auf das Device! Dann braucht man auch keine String-Längen zu berechnen, nur um zu verhindern, dass man sich den Buffer kaputtschießt.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Das meinst Du nur auf den ersten Blick. MCURSES hat strikt die > logischen und die physikalischen Funktionen getrennt. Unter einer strikten Trennung verstehe ich, dass die verschiedenen Layer auch in verschiedenen Bibliotheken untergebracht sind und ein entsprechendes Header-File mit den passenden Wrappern ausreicht, um das Programm über UART, I2C oder was auch immer kommunizieren zu lassen. Frank M. schrieb: > Sie ist auf einem AVR gerade mal 1,9 KB groß - bei vielfachem > Funktionsumfang. Deine 3 Funktionen verschlingen schon 500 Byte. An diesem Kommentar kann ich erkennen, dass du noch nicht verstanden hast, was meine Intention bei dem Programm war: Ich möchte lediglich für zum Beispiel Messungen in Tera Term eine Darstellung haben, bei der ich meine Messwerte sehe schön und übersichtlich darstellen kann. Dafür sind die wenigen zur Verfügung gestellten Befehle mehr als ausreichend. Und es wird zum Beispiel auch keine Abarbeitung von empfangenen Zeichen benötigt, die Ressourcen frisst. Möchte ich aufwendigere Aufgaben erledigen und Tetris in meinem Terminal-Programm spielen, greife ich natürlich auf die MCOURSES Bibliothek zurück, weil mein Funktionsumfang dafür nicht ansatzweise ausreicht aber das soll er ja auch nicht. Alle 3 Funktionen zusammen belegen aktuell 366 Bytes. Die utoa-Funktion macht davon 206 Bytes aus. In einer späteren Version kann ich die eventuell gegen eine kleinere, effektivere Funktion austauschen. Da ist noch Optimierungspotential vorhanden. Irgendwo habe ich mal gelesen, dass es tolle und extrem schnelle Algorithmen zur Umwandlung von binär zu BCD gibt aber da hatte ich bis jetzt keine Zeit mich rein zu lesen. Frank M. schrieb: > Allein die Fallunterscheidung, ob x bzw. y eine 1-, 2- oder 3-stellige > Zahl ist, ist viel zu kompliziert. > > statt 9 mal: > > buffer[5] = 'H'; > ... > buffer[6] = 'H'; > ... > buffer[7] = 'H'; > > zu schreiben, macht man das mit einer einzigen Zuweisung am Schluss der > Funktion: > > buffer[pos] = 'H'; > > Du machst für so etwas einfaches eine Fallunterscheidung von 3x3 = 9 > Fällen und brauchst alleine, um den Cursor zu setzen, 68 Zeilen Code. Ich arbeite an dieser Stelle mit einer Fallunterscheidung, weil diese für einen Mikrocontroller extrem simpel abzuarbeiten ist und es mir erspart weitere temporäre Variablen anzulegen und mit diesen zu rechnen. Das wäre für den Nutzer lesbarer aber für den Mikrocontroller mit mehr Aufwand verbunden. Frank M. schrieb: > Hier die äquivalente Funktion in MCURSES: > > static void > mymove (uint8_t y, uint8_t x) > { > mcurses_puts_P (SEQ_CSI); > mcurses_puti (y + 1); > mcurses_putc (';'); > mcurses_puti (x + 1); > mcurses_putc ('H'); > } Auf Grund meines Ansatzes durch die Bibliothek nur Strings auszugeben, die wiederum von Nutzer versendet werden können, ist es mir leider nicht möglich eine Funktion so aufzubauen. Sonst hätte ich es auch so getan. Bis auf die dadurch notwendigen Verzweigungen sind unsere beiden Funktionen identisch. Frank M. schrieb: > Sag mal: Wie lange programmierst Du schon? Ich programmiere seit 3 Jahren Mikrocontroller in C und habe demenstprechend noch viel zu lernen. Mir ist klar, das nicht alles was ich produziere perfekt ist, allerdings kann davon ausgegangen werden, dass ich mir bei den meisten Dingen auch etwas dabei gedacht habe wieso ich es so und nicht anders löse. Ich habe meine Bibliothek hier gepostet in der Hoffnung Leuten zu helfen, die durch googlen auch nichts entsprechendes gefunden haben, und um Feedback zu meiner Arbeit zu bekommen, was ich an welcher Stelle hätte besser machen können. Statt auf konstruktive Kritik treffe ich aber auf einen alten Platzhirsch, der seine eigene Bibliothek als Maß der Dinge ansieht und keine neuen Ideen und Ansätze zulässt. Ich habe mit meiner Arbeit keinen Anspruch darauf auch nur ansatzweise so gut wie etablierte Lösungen zu sein, die schon seit Jahren gepflegt werden. Sie ist aus einer Notwendigkeit heraus und dem Antrieb etwas über die Steuerung von Terminals zu lernen entstanden. Wenn ich aber hier keine konstruktive Kritik bekomme um meinen eigenen Code zu verbessern und etwas dabei zu lernen werde ich meine Ideen in Zukunft in einer anderen Bibliothek zur Verfügung stellen.
Frank M. schrieb: > Hier Deine 3 Funktionen in einer anderen Form, die nur 80 Bytes statt > 322 auf einem ATmega88 an Code verbraten - bei gleicher Funktionalität: Vielen Dank. So etwas verstehe ich schon eher unter konstruktiver Kritik. :) Bei deiner myutoa-Funktion verwendest du Divison um die einzelnen Stellen umzuwandeln. Gibt es eventuell Möglichkeiten (gerne auch zu Lasten der Lesbarkeit) um die Funktion ohne Division zu realisieren? Soweit ich weiß mögen AVRs ja keine Divison (sehr viel langsamer im Vergleich zu +, -, *). Da myutoa auch im Gegensatz zu utoa die Anzahl der Stellen der Dezimalzahl zurück gibt, brauche ich nicht durch if-Abfragen die Länge die Positionen der restlichen Zeichen im Array festlegen.
:
Bearbeitet durch User
Re: Bibliothek für VT100-Terminals schrieb: > Statt auf konstruktive Kritik treffe ich aber auf einen alten > Platzhirsch, der seine eigene Bibliothek als Maß der Dinge ansieht und > keine neuen Ideen und Ansätze zulässt. Tut mir leid, dass Du das so siehst. Aber einen Source mit drei Mini-Funktionen, welche ursprünglich einfach nur sprintf() aufriefen, eine "Bibliothek" zu nennen, erschien mir doch ein wenig ... merkwürdig - zumal die Header-Datei komplett weggelassen werden kann. Die drei Funktionen lassen sich nämlich komplett ohne zu übersetzen. Kein ernsthafter µC-Programmier verwendet für diesen schlichten Fall das Monstrum sprintf() - schon gar nicht für die einfache Ausgabe einer maximal dreistelligen Dezimalzahl. Deine Version mit utoa() war zwar schon etwas sportlicher, allerdings sieht Code, der 9-mal hingeschrieben wird - nur mit abweichenden Konstanten - nicht gerade wie ein Programm aus. Jede Programmiersprache kennt Variablen. Die sollte man auch nutzen ;-) > Ich habe mit meiner Arbeit keinen > Anspruch darauf auch nur ansatzweise so gut wie etablierte Lösungen zu > sein, die schon seit Jahren gepflegt werden. Sie ist aus einer > Notwendigkeit heraus und dem Antrieb etwas über die Steuerung von > Terminals zu lernen entstanden. Als ich den Source das erste Mal sah, fragte ich mich nach dem Nutzen: "Was will ich mit einer 'Bibliothek', die gerade mal den Cursor positionieren kann?" Tut mir leid, ich kann mir da nur schwerlich eine ernsthafte Anwendung vostellen. Es kann ja sein, dass ein schlichtes Positionieren in Deinem konkreten Fall ja genügen mag. Aber normalerweise erhebt eine Bibliothek den Anspruch einer gewissen allgemeineren Lösung, aus der man schöpfen kann. Dafür klingt für mich Deine Bezeichnung "Bibliothek" ziemlich hochgestochen. > Wenn ich aber hier keine konstruktive Kritik bekomme um meinen eigenen > Code zu verbessern und etwas dabei zu lernen werde ich meine Ideen in > Zukunft in einer anderen Bibliothek zur Verfügung stellen. Du musst lernen, mit Kritik umzugehen. Auch nicht-konstruktive Kritik kann durchaus ein Ansporn sein, es besser zu machen. In diesem konkreten Fall hielt ich es für den besseren Weg.
Kai L. schrieb: > Bei deiner myutoa-Funktion verwendest du Divison um die einzelnen > Stellen umzuwandeln. Gibt es eventuell Möglichkeiten (gerne auch zu > Lasten der Lesbarkeit) um die Funktion ohne Division zu realisieren? Nicht, dass ich wüsste. Um die einzelnen Stellen einer Dezimalzahl zu isolieren, musst Du teilen. utoa() arbeitet da nicht anders, teilt aber abweichend dazu durch die angegebene Basis. Leider ist für einen µC das Teilen durch 10 nicht so einfach wie das Teilen durch eine Zweierpotenz. > Da myutoa auch im Gegensatz zu utoa die Anzahl der Stellen der > Dezimalzahl zurück gibt, brauche ich nicht durch if-Abfragen die Länge > die Positionen der restlichen Zeichen im Array festlegen. Eben. Das war der Sinn der Sache. Ausserdem ist das "Ausrechnen" von maximal 3 Stellen in einer speziellen Version tatsächlich kürzer als die der allgemeinen Funktion utoa().
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.