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.
PS:
Backspaces als Zeichencode im String, werden auch als solche behandelt.
Trennzeichen für Operanden und Commando sind ebenfalls über defines
einstellbar.
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
voidl_trim(char*string,charsign){
2
registeruint8_ta_cnt=0;
3
/* Search for the end of the string */
4
while(string[a_cnt]!=0)a_cnt++;
5
/* search at the end for char which is not the same like the one in char sign */
6
while(string[--a_cnt]==sign);
7
string[++a_cnt]=0;
8
}/* void l_trim(char *string, char sign) */
kann man in einer Pointer-Version zb so formulieren
1
voidl_trim(char*string,charsign){
2
/* Search for the end of the string */
3
while(*string++)
4
;
5
/* search at the end for char which is not the same like the one in char sign */
6
while(*(--string)==sign)
7
;
8
string++;
9
*string='\0';
10
}/* void l_trim(char *string, char sign) */
-> 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)
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 strtok
1
int8_tsplit_command_line(){
2
int8_top_cnt=0;/* operand counter which shall not exceed cmd_interface_max_no_operands*/
3
char*c_str=NULL;
4
charc_separator[2];
5
6
/* whipe previous results */
7
for(op_cnt=0;op_cnt<_CMD_INTERFACE_MAX_NO_OPERANDS;op_cnt++)cmd_line_split_parts[op_cnt][0]=0;/* Set EOS */
8
9
/* check for command */
10
op_cnt=0;/* index for command */
11
c_separator[0]=_CMD_INTERFACE_COMMAND_SPLIT_SIGN;/* character used to split commands from operands */
12
c_separator[1]=0;/* ÊOS */
13
trim(c_ptr_cmd_line,_ASCII_SPACE);
14
c_str=strtok(c_ptr_cmd_line,c_separator);
15
16
/* check for operands and store command.. if one has been found */
17
c_separator[0]=_CMD_INTERFACE_OPERAND_SPLIT_SIGN;/* character used to split operands */
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)
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
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.
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.
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
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
../AVR_CMD_INTERFACE.c:128: warning: 'USART_RXC_vect' appears to be a misspelled signal handler
2
../AVR_CMD_INTERFACE.c: In function 'avr_init_usart':
3
../AVR_CMD_INTERFACE.c:155: error: 'UCSRB' undeclared (first use in this function)
4
../AVR_CMD_INTERFACE.c:155: error: (Each undeclared identifier is reported only once
5
../AVR_CMD_INTERFACE.c:155: error: for each function it appears in.)
6
../AVR_CMD_INTERFACE.c:156: error: 'UCSRC' undeclared (first use in this function)
7
../AVR_CMD_INTERFACE.c:158: error: 'UBRRH' undeclared (first use in this function)
8
../AVR_CMD_INTERFACE.c:158: error: 'UBRRL' undeclared (first use in this function)
9
make: *** [AVR_CMD_INTERFACE.o] Error 1
10
Build failed with 6 errors and 1 warnings...
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.
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.
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.
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
int8_t execute_cmd_interface(char *string){
2
/* local variables */
3
register uint8_t ui8_cnt =0;
4
register char act_char =1;
5
register int8_t cmd_ret_val =0;
6
register uint8_t cmd_count =0;
7
int8_t ret_val =0;
8
/* start reading string */
9
act_char=string[ui8_cnt++]; /* Read next sign */
> 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
act_char=*string; /* Zuweisung erstes Zeichen über Indirektion * */
2
ui8_cnt=1;
> So wird auch viel hin-und herkopiert -k.A. ;)
Nicht das ich wüsste ;) es wird mit Pointern gearbeitet.
Gruss,
Gary
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
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
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
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
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 :-)
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...
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
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;
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
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...
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
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
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
[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]
Hallo Gary B.
habe mir den Code angeschaut und eine Frage. Mit
1
cmd_interface_add_cmd("help",&command_0);
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
cmd_interface_table[ui_static_cmd_counter].cmd=cmd_string;/* add command-string to table */
4
(...)
5
}/* cmd_interface_add_cmd */
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