Datum:
Angehängte Dateien: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.
Datum:
PS: Backspaces als Zeichencode im String, werden auch als solche behandelt. Trennzeichen für Operanden und Commando sind ebenfalls über defines einstellbar.
Datum:
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
:-)
Datum:
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
Datum:
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
void l_trim(char *string, char sign) { register uint8_t a_cnt=0; /* Search for the end of the string */ while(string[a_cnt]!=0) a_cnt++; /* search at the end for char which is not the same like the one in char sign */ while (string[--a_cnt]==sign); string[++a_cnt]=0; }/* void l_trim(char *string, char sign) */ |
kann man in einer Pointer-Version zb so formulieren
void l_trim(char *string, char sign) { /* Search for the end of the string */ while( *string++ ) ; /* search at the end for char which is not the same like the one in char sign */ while( *(--string) == sign ) ; string++; *string = '\0'; }/* 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)
Datum:
Die Trim-Funktion könnte man recht einfach aus ltrim und rtrim zusammenstecken.
Datum:
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.
Datum:
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;
Datum:
Angehängte Dateien: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
int8_t split_command_line(){
int8_t op_cnt =0; /* operand counter which shall not exceed cmd_interface_max_no_operands*/
char *c_str=NULL;
char c_separator[2];
/* whipe previous results */
for (op_cnt=0; op_cnt < _CMD_INTERFACE_MAX_NO_OPERANDS; op_cnt++) cmd_line_split_parts[op_cnt][0]=0; /* Set EOS */
/* check for command */
op_cnt=0; /* index for command */
c_separator[0]=_CMD_INTERFACE_COMMAND_SPLIT_SIGN; /* character used to split commands from operands */
c_separator[1]=0; /* ÊOS */
trim(c_ptr_cmd_line,_ASCII_SPACE);
c_str= strtok(c_ptr_cmd_line, c_separator);
/* check for operands and store command.. if one has been found */
c_separator[0]=_CMD_INTERFACE_OPERAND_SPLIT_SIGN; /* character used to split operands */
while ( (NULL!=c_str) && (_CMD_INTERFACE_MAX_NO_OPERANDS>op_cnt) ){
trim(c_str,_ASCII_SPACE);
if (_CMD_INTERFACE_MAX_OPERAND_LENGTH>strlen(c_str)){
strcpy(&cmd_line_split_parts[op_cnt++][0],c_str);
}else return -1; /* error - too many char per operand/command*/
c_str= strtok(NULL, c_separator); /* strtok uses the last used string and position if NULL is forwarded */
}/* while */
/* check whether further operands would be available */
if (NULL!=c_str) {
op_cnt = -2; /* error - too many operands */
}
return op_cnt;
}/*inline int8_t execute_command()*/
|
Datum:
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.
Datum:
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)
char* cmd_line_split_parts[ _CMD_INTERFACE_MAX_NO_OPERANDS ]; int8_t split_command_line() { int8_t op_cnt; char* c_str=NULL; char c_separator[2] = { CMD_INTERFACE_COMMAND_SPLIT_SIGN, '\0' }; for( op_cnt=0; op_cnt < _CMD_INTERFACE_MAX_NO_OPERANDS; op_cnt++ ) cmd_line_split_parts[op_cnt] = NULL; op_cnt = 0; c_str= strtok( c_ptr_cmd_line, c_separator ); while( c_str && op_cnt < _CMD_INTERFACE_MAX_NO_OPERANDS ) { trim( c_str, _ASCII_SPACE ); cmd_line_split_parts[op_cnt] = c_str; c_str = strtok( NULL, c_separator ); } if( c_str ) op_cnt = -2; /* error - too many operands */ return op_cnt; } |
Noch was
if (_CMD_INTERFACE_MAX_OPERAND_LENGTH>strlen(c_str)){
strcpy(&cmd_line_split_parts[op_cnt++][0],c_str);
|
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.
Datum:
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
Datum:
PS: das
if (_CMD_INTERFACE_MAX_OPERAND_LENGTH>strlen(c_str)){
strcpy(&cmd_line_split_parts[op_cnt++][0],c_str);
|
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.
Datum:
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.
Datum:
Btw:
_CMD_INTERFACE_MAX_OPERAND_LENGTH |
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.
Datum:
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
Datum:
Angehängte Dateien: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
Datum:
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.
Datum:
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.
Datum:
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
Datum:
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.
../AVR_CMD_INTERFACE.c:128: warning: 'USART_RXC_vect' appears to be a misspelled signal handler ../AVR_CMD_INTERFACE.c: In function 'avr_init_usart': ../AVR_CMD_INTERFACE.c:155: error: 'UCSRB' undeclared (first use in this function) ../AVR_CMD_INTERFACE.c:155: error: (Each undeclared identifier is reported only once ../AVR_CMD_INTERFACE.c:155: error: for each function it appears in.) ../AVR_CMD_INTERFACE.c:156: error: 'UCSRC' undeclared (first use in this function) ../AVR_CMD_INTERFACE.c:158: error: 'UBRRH' undeclared (first use in this function) ../AVR_CMD_INTERFACE.c:158: error: 'UBRRL' undeclared (first use in this function) make: *** [AVR_CMD_INTERFACE.o] Error 1 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.
Datum:
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.
Datum:
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.
/* Internal Defines
****************************************************************************/
#ifndef F_CPU
#error " _AVR_USART_H: F_CPU has not been defined -> please check your project-settings"
#endif
#ifndef _XTAL
#define _XTAL F_CPU+'UL'
/* Taktfrequenz des Prozessors in Hz
>> Vorsicht mus unsigned long sein sonst kommt es zu fehlerhaften Baudraten Berechnung */
#endif
...
...
/** @details Makro calculating required content of the USART Baud Rate Register (UBRR) in "SINGLE SPEED MODE"
*/
#define _AVR_USART_UBRR_CALC(_BAUD) (((_XTAL)/((_BAUD)*16UL))-1)
|
void avr_usart_init_9600_8_1_N_Asyn( void ) {
/* Set UART-Control-Registers
\todo this code is a little bit smaller without those macros
*/
_AVR_USART_ENABLE_TRANSMIT();
_AVR_USART_ENABLE_RECEIVE();
_AVR_USART_DISABLE_RECV_IRQ();
_AVR_USART_DISABLE_TRANS_IRQ();
_AVR_USART_MODE_SYNCHRONOUS();
_AVR_USART_SET_PARITY_NONE();
_AVR_USART_SET_DATABITS_TO_8();
_AVR_USART_SET_STOPPBITS_1();
_AVR_USART_SAMPLE_AT_POS_EDGE();
/* Set Baudrate */
UBRRH = (uint8_t)(_AVR_USART_UBRR_CALC(9600)>>8); /*9600UL 19200UL 57600UL 115200UL*/
UBRRL = (uint8_t) _AVR_USART_UBRR_CALC(9600);
} /* void init_usart( void ) */
|
hier Deine eigne Routine, in der UBBR mit einem festen Wert beschrieben wird. Welchen Hintergrund hatte diese Entscheidung?
void avr_init_usart( void ) {
/* Set UART-Control-Registers - 9600,8,1,none */
UCSRB = ( 1 << TXEN )|( 1 << RXEN )|( 1 << RXCIE )|( 0 << TXCIE )|(0<<UCSZ2);
UCSRC = ( 1<<UMSEL )|( 1<<UCSZ1 )|( 1<<UCSZ0 );
/* Set Baudrate */
UBRRH = 0x00; UBRRL = 0x67;
} /* void init_usart( void ) */
|
Danke und viele Grüße Axelr.
Datum:
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?
Datum:
> 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
Datum:
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 im Beitrag #1719684: > 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:
int8_t execute_cmd_interface(char *string){
/* local variables */
register uint8_t ui8_cnt =0;
register char act_char =1;
register int8_t cmd_ret_val =0;
register uint8_t cmd_count =0;
int8_t ret_val =0;
/* start reading string */
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.
act_char=*string; /* Zuweisung erstes Zeichen über Indirektion * */ ui8_cnt=1; |
> So wird auch viel hin-und herkopiert -k.A. ;)
Nicht das ich wüsste ;) es wird mit Pointern gearbeitet.
Gruss,
Gary
Datum:
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.
Datum:
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
Datum:
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
Datum:
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 !
Datum:
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
Datum:
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
Datum:
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
Datum:
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
Datum:
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
Datum:
@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 :-)
Datum:
Angehängte Dateien:Hallo - wie gewünscht das Update mit kleiner Verbesserung bei AVR_UART.h. Gruss, Gary
Datum:
habs mir mal angeschaut, es tut was es soll vielen Dank!!
