Hallo,
anbei ein kleiner aber feiner C-Code für einen einfachen Interpreter von
Komandozeilen. Vielleicht kann jemand so etwas ja brauchen.
Komandozeilen folgen dabei folgender Syntax...
Befehl Operand1, Operand2, ... Operand5<CR>
Kurz zur Funktion/den Eigenschaften:
Dem Interpreter werden über einen String die Kommandozeile oder deren
Bruchstücke übergeben. Bruchstücke? Nun ja bei mir war das Problem, dass
ich über die serielle Kopplung nicht immer alles auf einmal bekomme.
Die Bruchstücke werden bis zum Erkennen eines Zeilenendes, anhand des
Zeilenende-Zeichens, in einem internen Puffer zusammengesetzt. Das
Zeichen für Zeilenende ist einstellbar in cmd_interface.h über den
define _CMD_INTERFACE_EOL. Wird ein Zeilenende erkannt wird die Zeile an
die "Analyse-Abteilung" übergeben.
Erkennt die "Analyse-Abteilung" einen Befehl, so ruft der Code die dem
Befehl zugeordnete Funktion auf. Diese musste vorher nebst dem
Befehl-String über die Funktion
cmd_interface_add_cmd("help", &command_0);
in den variablen Befehlsvorat eingetragen werden.
Der Rest ist hoffentlich selbsterklärend... der Code ist in einem
kleinen Beispiel für den AVR-Mega32 verpackt, aber grundsätlich sollte
es dem Code egal sein auf welcher Plattform er zum laufen kommt.
Wenns einem hilft ist schön... wenns einer besser kann... dann darf er
mich auch gerne belehren.
Viele Grüße, Gary
PS: Keine Haftung, Keine Garantie... das ist open-source.
Gast
#1518684
PS: Backspaces als Zeichencode im String, werden auch als solche behandelt. Trennzeichen für Operanden und Commando sind ebenfalls über defines einstellbar.
Gast
#1527841
Grade mal überflogen.. was mir aufgefallen ist:
>register uint8_t ui_cnt=0;
"register" war vor gefühlten 100 Jahren mal wichtig..inzwischen machen
die Compiler das nach Gutdünken..und legen eh alles machbare in Register
:-)
naja... Compiler-Magie ;O) das ist mal ne Ecke in die ich mich nur
begebe wenn ich wirklich muss... Bestaune immer die Werke von so manchen
"Freaks" die hier PreCompiler-Kunst vom Feinsten vom Stapel lassen.
Aber danke für den Hinweis... :)
Anmerkung: ich hab da aus Versehen noch eine "Testversion" des Beispiels
reingehangen, bei der die Befehle über direkt in ein Array gelegt
werden.
Der Schönheit wegen sollte man aber die obige Funktion
cmd_interface_add_cmd("help", &command_0);
nehmen.
Frohe Weihnachten
Gruss, Gary
Ich hoffe du bist mir nicht böse, wenn ich ein wenig 'meckere': Vermeide in String-Funktionen die Array-Sicht eines Strings, wie der Teufel das Weihwasser. In den meisten Fällen benötigt man die Array Sicht nicht und durch die Indizierungsvariable handelt man sich eine Beschränkung auf irgendeine Stringlänge ein, die man nicht haben will oder muss. Sieh einen String so an: Du hast einen Pointer auf den Anfang des Strings. Ist das Zeichen an der Stelle des Pointers ein '\0' Zeichen, dann ist dort der String zu Ende. In allen anderen Fällen kannst du den Pointer inkrementieren und kommst so zum nächsten Zeichen des Strings. Mit diesem Kochrezept kannst du die meisten String-Funktionen einfacher formulieren und das bei gleichzeitig mehr Universialität, weil die Indizierungsvariable gar nicht mehr auftaucht. Deine Funktion
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
kann man in einer Pointer-Version zb so formulieren
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
-> Kein a_cnt mehr und damit auch keine Beschränkung auf eine maximale Stringlänge von 255 (Ach ja. Das ist mir auch schon vor einiger Zeit aufgefallen. Ein einzelnes Zeichen ist im Englischen ein character. Ein sign ist ein Hinweisschild (zb beim Autofahren) bzw. in der Programmierung ist es das Vorzeichen einer Zahl. Aber keinesfalls bezeichnet das englische 'sign' in der Programmierung ein Zeichen aus einem String) Hmm. Du möchtest vielleicht auch noch darüber nachdenken, wo in einem String 'links' und 'rechts' ist und wie hier der Zusammenhang zwischen 'Beschneiden am Anfang' bzw. 'Beschneiden am Ende' lautet. r_trimm/l_trimm macht das genau Gegenteil von dem was man erwarten würde. Und dann solltest du dir noch den Vorrat an Standardfunktionen ansehen. Deine string_copy Funktion macht IMHO dasselbe wie strncpy. Auch bin ich mir nicht sicher, ob man die String Splitterei nicht mittels strtok eleganter und vor allen Dingen ohne notwendigen Zwischenspeicher cmd_line_split_parts lösen könnte. (Soll heißen: Ich bin mir ziemlich sicher, dass das mit strtok eleganter geht, bin aber jetzt zu faul, das entsprechend auszuformulieren ohne dir die Freude an der Sache zu nehmen)
Gast
#1528342
Die Trim-Funktion könnte man recht einfach aus ltrim und rtrim zusammenstecken.
Hey warum soll ich sauer sein - ich meine dafür sind solche Foren da. Man programmiert im stillen Kämmerlein, aber wenn man sich nich der Kritik stellt... wird man nicht besser. @Karl-Heinz Das ist ein gutes Beispiel... diese Funktionen haben sich "ergeben" ich hab die gebraucht, aber auf die Feinheit mit der Indizierungsvariable nicht geachtet. Super "Kritik" zum Rest... naja das mit dem links und rechts schreibe ich mal dem Rotwein zu den ich beim Programieren getrunken habe ;O) Das mit dem Englischen... wie du an den restlichen Kommentaren siehst bin ich mit eigentlich schon bewusst was ein character ist, aber irgendwie muss man das Kind nennen.... dennoch vielen Dank. strtok - kannte ich bisher noch nicht.. werd ich mir mal ansehen. @Sven Ist klar, aber ich hab es so gemacht, weil ich die Aufrufzeit sparen wollte. Platz ist noch kein Problem. Ob es was bringt hab ich noch nicht gemessen. Ich verbessere das Ganze mal und stell ne neue Version rein.
G. B. schrieb: > @Sven > Ist klar, aber ich hab es so gemacht, weil ich die Aufrufzeit sparen > wollte. Du bist hier ganz dicht am UI. Das heist an dieser Stelle bestimmt die Übertragungszeit des Strings die Laufzeit. Die beiden Aufrufe fallen absolut nicht ins Gewicht. Und selbst wenn der String irgendwo anders herkommt: Alleine die Suchzeit des Schlüsselwortes in deiner Commando-Tabelle ist höher als das bischen Funktions-Aufruf. Nichts gegen Optimieren. Aber Optimieren dort, wo es sinnvoll ist. Optimierungen, die darauf abzielen, im Maschinencode ein paar wenige Befehle einzusparen, können zwar in manchen Situationen der Lebensretter sein, sind es aber meistens nicht. Optimierungen auf algorithmischer Ebene bringen da deutlich mehr. Zb. würde eine alphabetische Sortierung der Schlüsselworte dir den Einsatz eines binären Suchens erlauben, wodurch du bei genügender Anzahl an Schlüsselworten die Suchzeit drastisch senken könntest. Anstelle O(n) hättest du dann O(log2(n)). (qsort und bsearch aus der Standlibrary sind deine Freunde :-) Und immer daran denken: Premature optimization is the root of all evil Ach da ist noch was und ich gebe zu: das ist auch ein wenig Geschmackssache. return ist kein Funktionsaufruf! return(ret_val); Lass die Klammern weg. Sie bringen keine Klarheit und sind nur optischer Ballast. Wenn beim return ein komplizierter arithmetischer Ausdruck steht, können Klammern, wie bei jedem komplizierteren arithmetischen Ausdruck, für Klarheit beim Verstehen der Berechnung sorgen, aber diesen Fall hast du ja nicht. return ret_val;
Hallo Karl-Heinz,
so die Feiertage habe ich genutzt, um deine Anregungen einzuarbeiten und
das Beispielprogramm aufzupeppen.
Die Anregungen mit Ergebniss ....
a) Vermeidung von Index-Zugriffen auf String bei Trim-Funktionen.
-> eingebaut und bringt sogar einen wesentliche Verbesserung der
Laufzeit.
neue Version mit Pointer alte Version mit Index
trim 19,13 µs 30,88 µs
l_trim 17,75 µs 19,13 µs
r_trim 14,94 µs 31,25 µs
Testbedingung findest du als Kommentar am Ende der Datei
string_addon.c
Mit dieser Messung hat sich aber auch gezeigt das eine aus
l_trim und r_trim zusammen gesetzte Funktion langsamer sein muss.
Aber korrekter Weise nicht wegen der Aufrufzeiten, sondern wegen
der separaten Bearbeitung ohne "Synergien".
Aber deine Hinweise in Richtung Algorithmus ist sicher richtig,
nur wenn die Basis nicht stimmt... macht auch ein Algorithmus
nicht alles aus.
b) Verwendung von strtok bzw. Standard-Funktionen für die
String-Zerlegung
-> Ich habe die Funktion split_command_line() umgeschrieben und das
Ergebniss war wirklich "schöner", aber leider auch wesentlich
langsamer [57 µs] als die Variante zu Fuss. Beides wurde schon mit
der neuen Trim-Funktion gemessen.
strtok version 218,75 µs
alte version 160,94 µs
Neue Funktion (mit define herausgenommen) und Ergenissen sind
wieder am Ende der Datei command_interface.c zu finden.
Den Code mit strtok hab ich der Einfachheit wegen unten angefügt.
Anhang - neue Versionen der split_command_line mit strtok1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
PS: Deine Anregungen zum Suchen über Sortierung werde ich mir auch noch einmal ansehen und schauen was es bringt. Ich hoffe nun umgekehrt das meine Antworten nicht zu "peniebel" (schreibt man das so ;O) erscheinen, aber ich war wirklich froh über deine Anregungen. Ich versuch nur ein Gefühl für die Laufzeiten zu bekommen, da ich derzeit mit der Ansteuerung von Motoren spiele (Stepper/ BLDC). Leider habe ich da noch wenig Gefühl für und deswegen meine Messungen.
G. B. schrieb: > b) Verwendung von strtok bzw. Standard-Funktionen für die > String-Zerlegung > -> Ich habe die Funktion split_command_line() umgeschrieben und das > Ergebniss war wirklich "schöner", aber leider auch wesentlich > langsamer [57 µs] als die Variante zu Fuss. Beides wurde schon mit > der neuen Trim-Funktion gemessen. > strtok version 218,75 µs > alte version 160,94 µs Du hast hier immer noch einen wesentlichen Fehler: Du brauchst die Teilstrings nicht umkopieren! Alles was du brauchst ist ein Array von char Pointern, das du dir von strtok füllen lässt. Im Prinzip so (ungetesteter Code)
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
Noch was
1 | |
2 | |
das ist suboptimal. Sowohl strlen als auch strcpy durchlaufen den String. Hier könntest du besser strncpy einsetzen. Aber wie gesagt: Wozu eigentlich Teilstrings bilden. Braucht kein Mensch.
Hallo Karl Heinz, ich hatte das schon verstanden, aber mein Gedanke war das ich der Anwender-Funktion diese Strings mit übergebe. Wenn ich das nicht durch umkopieren mache, dann müsste ich in die String-Ende-Kennungen für die einzelnen Sequenzen in die cmd_line direkt eintragen. Im Grunde solte das aber gehen, weil der String mit der Kommandozeile erhalten bleibt bis die Anwender-Funktion für ein Kommando abgearbeitet ist. Ich machs mal ... mal schauen ob es klappt. Im Übrigen hat mir das Prinzip des Codes mit strtok so gut gefallen... das ich weiter geschaut habe und meinen eigene strtok-funktion geschrieben habe... damit wurde der Code sogar doppelt so schnell wie die ursprünglich Version. Der Unterschied... weis ich nicht so genau... meine Vermutung war das die strtok-funktion aus dem Standard Speicher allokiert für das Ergebnis und das dies die Funktion langsam macht... deswegen hab ich das weg gelassen und 89µs anstatt 160µs erreicht. Danke .. und melde mich wenn ich was Neues habe. Gruss Gary
PS: das
1 | |
2 | |
diente alleine dem Zweck einen Indikator für die Fehlermeldung zu haben. strncpy gibt mir ja keine Rückmeldung und schliest de kopierten String auch nicht mit /0 ab.
G. B. schrieb: > ich hatte das schon verstanden, aber mein Gedanke war das ich der > Anwender-Funktion diese Strings mit übergebe. Kannst du ja. Für die Funktion ist es doch völlig unerheblich, ob sie 5 Pointer in 5 Variablen bekommt, oder einen Pointer auf ein Array von Pointern. Und wenn man möchte kann man den Funktionen immer noch die 5 Pointer aus dem Array einzeln übergeben. Schau dir doch einmal an, wie die Schnittstelle zu main() es macht, eine beliebige Anzahl an String-Argumenten an main() zu übergeben. genau das gleiche Prinzip int main( int argc, char* argv[] ) argv ist ein Array von Pointern, wobei jeder Pointer auf einen String zeigt. Wird ein Program test.exe im Verzeichnis C:\tmp mit den Argumenten "juhu.txt" "-o" "out.dat" gestartet, lautete die Commandline also C:\tmp> test -o out.dat dann sieht das argv, welches das Programm erhält, so aus argv +------+ | o-------------> "C:\tmp\test.exe" +------+ | o----------------------> "-o" +------+ | o----------> "out.dat" +------+ | NULL | +------+ Ob beim Programmnamen der Pfad dabei ist oder nicht, ist BS abhängig, aber darum geht es nicht. Es geht um den Mechanismus, wie man eine beliebige Anzahl an Argumenten geordnet an eine Funktion übergeben kann, ohne dass die Funktion geändert werden muss, wenn sich diese Anzahl ändert. > Wenn ich das nicht durch > umkopieren mache, dann müsste ich in die String-Ende-Kennungen für die > einzelnen Sequenzen in die cmd_line direkt eintragen. Genau das macht doch strtok sowieso. Ansonsten könntest du ja auch nicht mit strlen auf die Einzelteile losgehen. > Im Grunde solte das aber gehen, weil der String mit der Kommandozeile > erhalten bleibt bis die Anwender-Funktion für ein Kommando abgearbeitet > ist. Das ist ein Argument. Aber auch das kann man leicht umgehen, indem man mit 2 Buffern arbeitet, die wechselweise befüllt werden. Der jeweils gerade von der UART befüllte wird mittels strtok in Einzelteile zerlegt, während der andere die nächsten UART Zeichen aufnimmt. Ist die nächste Zeile komplett empfangen, tauschen die beiden Buffer die Plätze. > geschrieben habe... damit wurde der Code sogar doppelt so schnell wie > die ursprünglich Version. Der Unterschied... weis ich nicht so genau... > meine Vermutung war > das die strtok-funktion aus dem Standard Speicher allokiert für das > Ergebnis und das dies die Funktion langsam macht... Nein. strtok allokiert nichts. strtok manipuliert den Ursprungsstring. Es tauscht das Zeichen an der Trennstelle gegen ein '\0' Zeichen aus. Aus dem String char buffer[] = "Hallo world"; char* tmp = buffer; tmp +-----+ | o | +-|---+ | | v +---+---+---+---+---+---+---+---+---+---+---+---+ | H | a | l | l | o | | w | o | r | l | d | \0| +---+---+---+---+---+---+---+---+---+---+---+---+ und einem Trennstring mit einem Leerzeichen, macht strtok tmp +-----+ | o | +-|---+ | | v +---+---+---+---+---+---+---+---+---+---+---+---+ | H | a | l | l | o | \0| w | o | r | l | d | \0| +---+---+---+---+---+---+---+---+---+---+---+---+ ^ ^ | | | | wobei du nacheinander die beiden unteren Pointer zurückbekommst. Der komplette String wird also 'in-Place' zerlegt, und strtok gibt dir Pointer auf die Einzelteile. Die Einzelteile sind (durch das \0) bereits in einer Form, dass sie als gültige C-Strings benutzt werden können. Nach wie vor stehen aber die einzelnen Strings an der selben Stelle, an der sie auch vorher schon waren. Kein String wird umkopiert. Die Pointer werden in einem Array von Pointern gesammelt tmp +-----+ | o | +-|---+ | | v +---+---+---+---+---+---+---+---+---+---+---+---+ | H | a | l | l | o | \0| w | o | r | l | d | \0| +---+---+---+---+---+---+---+---+---+---+---+---+ ^ ^ | | | | +--------------+ | | | | | args | | +------+ | | | o------------+ | +------+ | | o---------------------+ +------+ und zusammen mit der Anzahl an die Funktion übergeben function( args, op_cnt ) Die Funktion selbst kann nicht mehr ohne weiteres feststellen, ob die Pointer jetzt Zeiger in eigentlich nur einen einzigen String sind, oder ob die Pointer auf jeweils einzeln allokierte Speicherbereiche zeigen. Für die Funktion sind das einfach nur 2 Pointer, die jeweils auf den Anfang eines C-Strings zeigen. Der Unterschied in der Zeit wird sich wohl eher so erklären, dass das originale strtok eine Sammlung von Auftrennzeichen behandeln kann (daher auch der String als 2.tes Argument) während deine Funktion nur ein einzelnes Trennzeichen benutzt.
Btw:
1 | |
Da Du gerade den Anschein machst, dass Du erst anfängst zu programmieren: Spar Dir führende Unterstriche! Das ist den Entwicklern des Compilers und der lib vorbehalten.
Hallo Karl-Heinz, also Anregung aufgegriffen und umgesetzt.... Der Code übergibt nun nur noch Pointer -> nochmal bischen Zeit gespart. Der Doppelpuffer ist bereits durch einen Ringpuffer gegeben, welchen ich beim Empfangen der Daten benutze. Der ist aber nicht Bestandteil des Teils des Kommando-Interfaces. Bin ehrlich gesagt verblüfft von deiner umfangreichen Antwort... vielen Dank das du dir soviel Mühe gibst, aber das Prinzip hatte ich schon verstanden bzw. gekannt. Hallo Patrick, wuups... warum erweckt das den Anschein? Ich habe vor ca. 10-15 Jahren das letzte mal ernsthaft in C programmiert. Mit Mikrocontrollern "spiele" ich erst sein einem Jahr als Hobby und da erst über Assembler. Mein C ist wirklich eingerostet, aber was ich mit Karl-Heinz diskutiert habe ware ja der Algorithmus und nicht C. Genau genommen kommt aus der "alten Zeit" auch dieser Umgang mit den Defines.. sprich ich hab schon damals den Underscore benutzt... Wo gibt es den solche "bestehenden" Festlegungen nachzulesen? Gruss, Gary
so und nun die neue Variante als Anhang.... macht es eurer Meinung nach Sinn diese noch etwas weiter zu dokumentieren mit Beispielen am Terminal - oder ist das zu trivial? Ich hab hier halt viele Fragen dazu gelesen aber keine Umsetzung gefunden. Aus diesem Grund dachte ich ... vielleicht ist es von Interesse für den einen oder anderen Forums-Besucher. Gruss, Gary
G. B. schrieb: > Genau genommen kommt aus der "alten Zeit" auch dieser Umgang mit den > Defines.. sprich ich hab schon damals den Underscore benutzt... Wo gibt > es den solche "bestehenden" Festlegungen nachzulesen? Genau genommen: Im Dokument, welches die Sprachdefinition beschreibt. Aber der ist sehr trocken geschrieben und beschriebt nur die normierte Sprache. Einiges aus dieser Sprachdefinition hat Auswirkungen, die man zwischen den Zeilen heruaslesen muss. So zb, dass es für Makros eine Konvention gibt, nach der Makros die nur aus Grossbuchstaben bestehen und mit einem _ anfangen, für die 'Implementierung' reserviert sind. Hä? Was will uns der Dichter sagen? Ganz einfach: In C gibt es keine Namensräume, mit denen man Dinge verbergen könnte. Wann immer du irgendein System-Header-File inkludierst, und das tut man praktisch in jedem Programm, so kommen damit eine Menge #define durch dieses Header File in deinen Source Code hinein. Was man also braucht, ist eine Regelung, damit es zu keinen Namenskonflikten zwischen den Makros aus dem System-Header-File und deinen eigenen #define kommt. Nichts ist ärgerlicher, als wenn du ein #define machst und der Compiler akzeptiert es nicht, weil es schon ein #define mit dem gleichen Namen in irgendeinem System-Header-File gibt. Danach sucht man nämlich lange. Daher diese Regelung: Alle in den System-Header-Files vorkommenden Makros sind immer noch diesem Schema aufgebaut: zuerst ein _ und dann alles andere in Grossbuchstaben (es gibt noch eine 2-te Konvention, aber die ist nicht ganz so gebräuchlich, daher lasse ich sie hier der Einfachheit halber weg). Benutzt du daher selbst niemals Makronamen, die nicht mit einem _ anfangen, dann bist du auf der sicheren Seite: Es kann per Definition keine Namenskollision mit Makros in System-Header-Files geben. Sieh den _ einfach als eine Art Präfix an, der deinen 'Namensraum' vom Namensraum der Implementierung trennt.
Gast
#1577373
Karl heinz Buchegger schrieb: > Aus diesem Grund dachte ich ... vielleicht ist es von Interesse für den > einen oder anderen Forums-Besucher. Für mich wäre das sehr von Interesse.
shell schrieb:
> Für mich wäre das sehr von Interesse.
Hallo shell, was fehlt dir in dem Beispielprojekt.
Wenn du mir sagst was du wissen möchtest... denn kann ich gerne darauf
eingehen.
Gruss,
Gary
Gast
#1719698
Hallo Gary, bin dabei, mir Deinen Code anzusehen und zu verstehen. Erstmal Danke für die Mühe, die sicher darin steckt. ich möchte in diesem Thread keine Fragen zu Funktion an sich stellen, nur auf eine Sache hinweisen, die mir eben beim compilieren auffiehl: Den Ringpuffer kann man per defines in alle Einzelheiten konfiguriert zerdröseln. Warum hast Du das bei der USART nicht auch gemacht? Ich habe einen Controller mit zwei USART und da geht der Code dann leider nicht.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Es muss wohl irgentwie zwischen USART0 und USART1 unetrschieden werden... Ich bin interessierter, unerfahrener C-Hobbyist und komme aus der Hardware-Ecke. Magst Du die Defines in der usart.h entsprechend anpassen/erweitern, damit man auch andere Controller verwenden kann? Hier ein Atmega128A - dankeschön. BTW: ich bin hier Beitrag "AT Kommandos / Befehlsliste verarbeiten" nochmals allegemein auf die Problematik Kommandozeile und Befelsauswertung eingegangen. Frohe Pfingsten und VIELEN DANK an Dich nochmal für die wirklich umfangreiche Vorlage und an Karl Heinz für die geleistete Unterstützung. Axelr.
Gast
#1719707
Ohne in den DeTAILs abzutauchen sehe ich einen SEHR ordentlich dokumentierten Code. Kann man lesen wie in einem Buch ! Muß man doch auch mal lobenswert erwähnen.
Gast
#1719784
Das der Quelltext gut kommentiert ist, gefällt mir auch gut. @Gary mir fiel im Zuge der Durcharbeitung noch etwas auf: im Ersten Ansatz hast Du "_AVR_USART_UBRR_CALC" als Baudratenkonstante errechnen lassen, verwendest aber zur eigentlichen Initialisierung der USART gar nicht den Code aus der USART.c, sondern einen eigenen Startup-Code, in dem die errechnete Konstante "_AVR_USART_UBRR_CALC" keine Rolle mehr spielt. Fiel mir auf, als ich auf 115200 umstellen wollte.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
hier Deine eigne Routine, in der UBBR mit einem festen Wert beschrieben wird. Welchen Hintergrund hatte diese Entscheidung?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
Danke und viele Grüße Axelr.
@ thisamplifierisloud : danke ;)
Hallo Axel > Den Ringpuffer kann man per defines in alle Einzelheiten konfiguriert > zerdröseln. > Warum hast Du das bei der USART nicht auch gemacht? > Ich habe einen Controller mit zwei USART und da geht der Code dann > leider nicht. Genau wegen sowas - ich hab das am Anfang probiert, aber wenn du das versuchst wirklich "plattform-neutral" zu halten ist das mehr Aufwand als ab und an ins Handbuch zu schauen. Bei meinen ersten Versuchen hat das ein Gewitter von defines gegeben, das nicht mehr lesbar war. > Magst Du die Defines in der usart.h entsprechend anpassen/erweitern, > damit man auch andere Controller verwenden kann? Hier ein Atmega128A - > dankeschön. Welchen der beiden USARTs willst du benutzen?
> im Ersten Ansatz hast Du "_AVR_USART_UBRR_CALC" als Baudratenkonstante > errechnen lassen, verwendest aber zur eigentlichen Initialisierung der > USART gar nicht den Code aus der USART.c, sondern einen eigenen > Startup-Code, in dem die errechnete Konstante "_AVR_USART_UBRR_CALC" > keine Rolle mehr spielt. Ja ich weis ;) wie so oft im Leben - es hat keinen .. ich hab hier gespielt und das zieht sich nun schon seit einiger Zeit via Copy und Paste mit. Sorry das dies nun in dem Beispiel gelandet ist. Wie ich oben schon geschrieben habe ursprünglich war das mal eine USART-Datei die auch via Defines mehr oder weniger einstellbar war, nach einer Aufräumaktion ist das übrig geblieben. Ich konnte mich nie aufraffen das Energie zu investieren. Gruss, Gary
Auf deine Punkte zu meinem Code aus dem anderen Beitrag antworte ich auch mal lieber hier um alles beieinander zu halten Axel Rühl schrieb: > Leider sind hier verschiedene Punkte für mich unverständlich, da hier > ganz viel Pointerrechnerei betrieben wurde und viele vorhandene > String-Funktionen selbst geschrieben wurde und ich bei denen leider mit > meinen bescheidenen C-Kenntnissen nicht wirklich durchsteige. String-Funktionen wurde nur eine neu geschrieben strtok - Grund war einfach weil meine Funktion etwas schneller ist. Alle anderen wie die TRIM Funktionen gibt es so nicht - zumindes nicht das ich es wüsste... lasse mich gerne eine Besseren belehren. > Ein ganz simples Beispiel aus dem Thread:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
> Speziell /* Read next sign */ > > um welches Vorzeichen handelt es sich? Ich dachte char ist deshalb > Vorzeichen behaftet, weil die Druckbaren Zeich im 7Bit Bereich liegen, > also bis 127 gehen. > Das ist genau die Stelle an der nicht mit Pointern gearbeitet wird ;) Hier wird nur das erste Zeichen aus dem Übergebenen String in die Variable act_char übergeben. Ich erlaube mir nun zu vermuten das du aber die Operation string[ui8_cnt++] nicht verstehst. Deswegen kurz erklärt .. und verzeih mir wenn ich falsch gegelegen haben sollte. string ist ein Pointer = Adresse auf den STring im Speicher C erlaubt es dazu einen Offset anzugeben in Form eines Index in eckigen Klammern. Der Compiler berechnet daraus die Adresse des indizierten Elementes.... Adresse aus dem Pointer + Index * Byte des Datentyps (hier char = 1 Byte) Deswegen ist diese Zeile auch ein bischen "hoch gegriffen" - wieder mal Copy und Paste - weil eigentlich hätte man es auch so - einfach - schreiben können.
1 | |
2 | |
> So wird auch viel hin-und herkopiert -k.A. ;)
Nicht das ich wüsste ;) es wird mit Pointern gearbeitet.
Gruss,
Gary
Gast
#1721544
Hallo Gary, ich lass dein Beispiel erstmal so in einem meiner Unterverzeichnisse stehen. Mir ist das schlicht zu kompliziert und viel zu viel - sry. Ich dachte, man kann einen String einfacher in Teilstrings zerlegen und dann über ein Array auf die einzelnen gewonnenen Teilstrings zugreifen und... Nocheinmal herzichen Dank für die geleistete Unterstützung! Viele Grüße Axelr.
Hallo Axel, klar kannst du den String einfach nur zerlegen, wenn du ihn den einmal vollständig hast. Letztlich macht mein Beispiel nichts anderes. Der Rest ist im Prinzip nur Verwaltung und Zusammenstellen des Strings bis eine gültige Endekennung (CR) gelesen wird. Schau dir einfach die Funktion split_command_line() bzw. strtrok_single_char im string_addon.c an. Die erlauben dir genau das. Also viel Erfolg. Gary
Gast
#1722582
Hallo Gerhard, ich werde mir Deinen Interpreter mal anschauen, ich glaube ich kann genau so was gerade brauchen. Vielen Dank fürs Veröffentlichen. Nur eine Frage: Gibt's eine aktualisierte Version? Viele Grüße Flo
Wird zwar nicht so gern gesehen, aber ich entführe den Thread mal eben um Werbung für meinen Kommandozeilen Interpreter zu machen. Zu finden unter hier : Beitrag "uParse - ein kompakter und vielseitiger Parser" Bei meinem Parser ist die Syntax frei wählbar (also nicht beschränkt af Befehl Op1, Op2, Op3, Op4, Op5), ebenso die Anzahl der maximal möglichen übergebenen Parameter ist einstellbar. Ansonsten : Gute Arbeit Gary !
frist schrieb: > Hallo Gerhard, > ich werde mir Deinen Interpreter mal anschauen, ich glaube ich kann > genau so was gerade brauchen. Vielen Dank fürs Veröffentlichen. Nur eine > Frage: Gibt's eine aktualisierte Version? > > Viele Grüße > Flo Hallo Flo - die hier eingestellte Version vom 01.01.2010 - ist auch noch die aktuelle. Rene Böllhoff schrieb: > Bei meinem Parser ist die Syntax frei wählbar (also nicht beschränkt af > Befehl Op1, Op2, Op3, Op4, Op5), ebenso die Anzahl der maximal möglichen > übergebenen Parameter ist einstellbar. Hallo Rene - bei meinem auch ;O) - Danke. Gruss, Gary
Gast
#1723132
Hallo G. B. > die hier eingestellte Version vom 01.01.2010 - ist auch noch > die aktuelle. OK. Danke. Ich hatte gehofft, Du hättest vielleicht die UART Initialisierung geändert, aber das ist eh nur eine Kleinigkeit. @Rene Ich werd' Deinen auch probieren. :-) Grüße Flo
Hallo Flo, komisch das mit der UART-Initialisierung scheint jetzt wohl mehr Leute zu stören, dabei ist der UART ja nur eine mögliche Quelle für Zeichen. Aber wie ich es schon geschrieben hatte... die ist "geworden", wenn du eine variablere Variante hast.. her damit.. oder noch besser bau Sie gleich ein. Möge das bessere Progi gewinnen ;O) Grüße, Gerhard
Gast
#1723199
Hallo > komisch das mit der UART-Initialisierung scheint jetzt wohl mehr > Leute zu stören, Ich hab Deine Interpreter schon vor einiger Zeit mal kurz ausprobiert und da hat mich die UART Initialisierung etwas Nerven gekostet. Ich hab nämlich den Makefile an meine Taktfrequenz angepasst und dann nur kurz in den Code geschaut und gedacht UBRR wird automatisch neu berechnet und richtig gesetzt - war aber nicht so, falsche Baudrate. Dann hab ich die Makros, die das vermeintlich machen angeschaut und konnte da keinen grundsätzlichen Fehler finden und erst später bin ich drauf gekommen, dass die gar nicht verwendet werden sondern UBRR hart codiert auf einen Wert gesetzt wird. Ich denke anderen wird's beim Lesen ähnlich gegangen sein. das hat ja alles nicht wirklich was mit dem Kern der Geschichte, dem Interpreter zu tun. Grüße Flo
Gast
#1723293
Hallo Flo, danke für das Feedback - das verstehe ich und nix ist ärgerlicher als ein Beispiel was nicht sofort funktioniert. Ich passe das an in den nächsten Tagen an. Gruss, Gary
@frist
Viel Spaß beim probieren. Falls es Probleme gibt, du weißt ja wo der
Thread zu finden ist :))
@gary
>und nix ist ärgerlicher als ein Beispiel was nicht sofort funktioniert.
Ist mir bei meinem Parser auch passiert. Der wollte keine 0 mehr
verstehen :-)
Hallo - wie gewünscht das Update mit kleiner Verbesserung bei AVR_UART.h. Gruss, Gary
habs mir mal angeschaut, es tut was es soll vielen Dank!!
Interessanter Code. In den Quellen ist keine Lizenz angegeben. Inwiefern und inwieweit darf man diesen Code wofür verwenden (unverändert/verändert)? Gilt als Lizenz (allgemein für Code im Forum hier), wenn im Code selbst nichts anderes angegeben, die ShareAlike-CC? Danke, Grüße aus Berlin, t.
Kaum zu glauben kaum vier Jahre später.... hab das Teil noch einmal in Visual Studio zum Einsatz gebracht und da war tatsächlich ein Copy&Paste-Fehler drin, welcher Aufrufe mit Operanden nicht mehr erlaubte... hier nun ein Projekt-Beispiel mit Visual Studio 2013... vielleicht braucht es ja einer...
Gast
#3946717
SEHR GUT!
Hallo Forum. Ich beschäftige mich im moment mit dem Programm "Einfacher Interpreter für Komandozeilen/Befehlszeilen". Erst mal vielen Dank für die Bereitstellung Ihrer tollen für Arbeit. Ich versuche es gerade auf einen XMega zu portieren. Das Programm ist bereits ausführbar und die UART ist auf STDIN und STDOUT umgelenkt. Ich habe es aber noch nicht geschaft ein gültiges Kommando zu übertragen. Ich habe folgende Eingaben versucht: "help !", "HELP 5" etc. Leider bekomme ich immer ein "dCMD_INTERFACE_ERR_NO_CMD" zurück. Kann mir bitte jemand ein Beispiel für ein gültiges Kommando geben. Danke. Vg tom_micro
Gast
#4173848
In dem Beispielprojekt wäre doch ein Beispiel für funktionierende Kommandos - Held, cm. .
Ein Problem habe ich gefunden: void trim(char *string[], char sign) in void trim(char *string, char sign) geändert. Jetzt gehen die Kommandos soweit, aber die Optionen werden nicht getrennt. Kommando: cmd_1 22 33 44 Antwort im Terminal: 0:22 33 44; ??? Wenn ich es richtig verstanden habe, müsste die Anwort doch so heißen: 0:22; 1:33; 2:44;
Gast
#4174103
Da stimmt was nicht in der Routine split_command_line hier wird bei strtrok_single_char ein Null übergeben - muss ich mir mal ansehen was ich da vertütelt hab - sieht nach Test code aus
Gast
#4175025
Hallo, hab es mir angesehen.. das ist kein Fehler... als Trennzeichen ist in cmd_interface.h ist definiert
1 | |
also muss deine Eingabe nicht .... cmd_1 22 33 44 sondern cmd_1 22,33,44 lauten.. wenn du das anders haben willst, dann must du das einfach umdefinieren...
1 | |
2 | |
War jetzt interessant da noch einmal so tief reinzuschauen... heute würde ich da einiges anders machen, aber es entwickelt sich halt alles weiter ;) Bei void trim(char *string[], char sign) hast du aber recht das ist ein Fehler.. an der Funktion ist es wieder korrekt. Viel Spass damit
Gast
#4175027
PS: Was bissi tricky war... ich habe in der Funktion
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
eine Optimierung eingebaut, welche bei der Übergabe eines Nullzeigers für c_ptr_search den letzten statisch gemerkten Zeiger übernimmt.... das sah so merkwürdig aus. Für die Aufgabe von strtrok_single_char gibt es heute fertig definierte Funktionen. Gruss
Hallo, mein Fehler habe ich auch entdeckt. Ich hatte nicht gleich gesehen, dass es zwei verschiede Trennzeichen in der Funktion gibt und hab das Leerzeichen als alleiniges Trennzeichen angesehen. So habe ich auch ein wenig tiefer gesucht ;-). Vielen Dank für Deine Hilfe. Was würdest du heute anders machen? Grüße
Gast
#4175070
Ich würde z.b auf eigene string-Funktionen verzichten und auf libs wie Boot setzen - kann dir aber noch nicht sagen ob die im Bereich Mikrocontroller ratsam sind
Gast
#4175223
[Korinthenkackermodus] Ja, einen Character (zu Deutsch etwa "Zeichen") mit "sign" im Englischen zu bezeichnen hat sich durch die Evolution des Codes nicht verändert. Noch so eine Verballhornung: act_char meint wohl actual, also "aktueller" Character. Das englische Wort "actual" ist aber in der Deutschen Bedeutung eher "tatsächlich". Wen man aktuell meint sollte man im Englischen "current" verwenden. Wenn schon act_char warum eigentlich dann nicht gleich act_sign? ;-) [/Korinthenkackermodus]
Gast
#4175283
Alles korrekt ; ) was du sagst
Gast
#4225489
Hallo Gary B. habe mir den Code angeschaut und eine Frage. Mit
1 | |
hängt man ein Kommando ein. Das funktioniert doch nur, wenn der Compiler "help" ins Flash (i.a. Festwertspeicher) schreibt und nicht (wie ein 'normalen' Parameter) auf den Stack schiebt? Denn
1 | |
2 | |
3 | |
4 | |
5 | |
weist einem char* ein char* (Pointer!) zu. Wenn die Add-Funktion beendet wird, existieren die Parameter ja offiziell nicht mehr auf dem Stack. Ist das eine Eigenheit des AVR-GCC, die Du da ausnutzt?
Hallo Arne, da hast du vollkommen recht.. beim AVR wird das nach meinem Wissen aber alles in den Flash/Programmspeicher gelegt - deswegen funktioniert das anscheinend. Beim Beispiel mit dem Visual Studio hatte ich das geändert. char command0[] = "help"; char command1[] = "cmd_1"; char command2[] = "cmd_2"; .... cmd_interface_add_cmd(command0, &command_0); Wie es halt oft so ist.. hat gleich funktioniert und danach hab ich nicht mehr darüber nachgedacht... Gruß, Gary
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.