www.mikrocontroller.net

Forum: Projekte & Code Einfacher Interpreter für Komandozeilen/Befehlszeilen


Autor: G. B. (garyb)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
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.

Autor: GaryB (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PS:
Backspaces als Zeichencode im String, werden auch als solche behandelt.
Trennzeichen für Operanden und Commando sind ebenfalls über defines 
einstellbar.

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 
:-)

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
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)

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Trim-Funktion könnte man recht einfach aus ltrim und rtrim 
zusammenstecken.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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;

Autor: G. B. (garyb)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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()*/

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: G. B. (garyb)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: shell (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: thisamplifierisloud (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ thisamplifierisloud : danke ;)

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: frist (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Rene B. (themason) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 !

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: frist (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: frist (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: garyb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Rene B. (themason) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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 :-)

Autor: G. B. (garyb)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Hallo - wie gewünscht das Update mit kleiner Verbesserung bei 
AVR_UART.h.

Gruss,
Gary

Autor: Sebastian M. (sebastian_m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
habs mir mal angeschaut, es tut was es soll

vielen Dank!!

Autor: Stefan L. (tarabas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: G. B. (garyb)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SEHR GUT!

Autor: Tom Milker (tom_micro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In dem Beispielprojekt  wäre doch ein Beispiel  für funktionierende 
Kommandos - Held, cm. .

Autor: Tom Milker (tom_micro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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;

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: garyb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

hab es mir angesehen.. das ist kein Fehler...
als Trennzeichen ist in cmd_interface.h ist definiert
#define dCMD_INTERFACE_OPERAND_SPLIT_SIGN  dASCII_COMMA  

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...
#define dCMD_INTERFACE_OPERAND_SPLIT_SIGN  dASCII_SPACE
#define dCMD_INTERFACE_COMMAND_SPLIT_SIGN  dASCII_SPACE

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

Autor: garyb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PS: Was bissi tricky war...

ich habe in der Funktion
char *strtrok_single_char(char *c_ptr_search, char c_search){
  static char *c_static_ptr;
  char *c_ptr_to_return = NULL;

  /* check if new pointer has been forwarded */
  if(NULL!=c_ptr_search) c_static_ptr=c_ptr_search;

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

Autor: Tom Milker (tom_micro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Schaltplanleser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[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]

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alles korrekt ; ) was du sagst

Autor: Arne S. (arnes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Gary B.

habe mir den Code angeschaut und eine Frage. Mit
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
uint8_t cmd_interface_add_cmd(char (*cmd_string), uint8_t (*cmd_func_ptr)(int8_t argc, char *argval[]) ){
  (...)
  cmd_interface_table[ui_static_cmd_counter].cmd=cmd_string;        /* add command-string to table */
  (...)
}/* 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?

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.