www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Terminal-Befehle aus mehreren Zeichen im Controller erkennen, wie am besten?


Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Tag an alle!

Ich möchte über Terminal und RS232 aus mehreren Zeichen bestehende 
Befehle an einen µC schicken und im µC die Befehle mit der Software 
erkennen und ausführen. Ich habe mir überlegt, wie die Software zum 
Erkennen der Befehle aufgebaut werden soll und zeige nachfolgend meine 
Überlegungen und möchte euch um eure Meinungen (ob gut oder schlecht) 
oder evtl. Vorschläge (wie besser) bitten.

Alle vorkommenden Befehle werden in dieser Form abgelegt:
typedef char CmdString[5];
CmdString CmdList[] = {"befehl1\n",
                 "befehl2\n",
                 "befehl3\n"};

Die vom Uart empfangenen Zeichen werden in einem Puffer solange 
abgelegt, bis ein Return-Zeichen kommt. Bei Return wird der 
Puffer-Inhalt Zeichen für Zeichen mit dem ersten Befehl CmdList[1] 
verglichen. Wenn keine Übereinstimmung mit dem 1. Befehl besteht, 
erfolgt der Vergleich mit dem 2. Befehl und so weiter. Stimmt der 
Puffer-Inhalt mit einem der Befehle aus CmdList überein, wird eine 
Varible mit einem Wert aus einer enum-Liste gesetzt und dann wird in 
einer switch-case-Struktur je nach Befehl der zugehörige Code 
ausgeführt. Ist der Puffer-Inhalt mit keinem der abgelegten Befehle 
übereinstimmend, wird eine Meldung ausgegeben.

Ist es sinnvoll, das so wie beschrieben zu machen. Oder habt ihr bessere 
Konzepte vorzuschlagen? Vielleicht gibt es so etwas ähnliches irgend wo 
schon fertig, das wäre natürlich ideal.

Ich danke für jeden Beitrag im Voraus!!!

Autor: kodac (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schau dir dazu mal die Funktion strstr und strtok. Du musst auch immer 
davon ausgehen, dass man sich mal verschreibt, Groß/Kleinschreibung 
nicht beachtet oder ein return nicht kommt. Deswegen lieber nicht jedes 
Zeichen miteinander vergleichen (also nicht strcmp).

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uups...  Die Größe von CmdString[] soll natürlich passend sein. Im 
obigen Code wäre Größe 5 nicht ausreichend.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So kann man das schon machen, die Trennung aus Empfang von Zeichen und 
der Verarbeitung empfangener Zeichenketten ist schon mal ein sinnvoller 
Ansatz.

Du könntest allerdings Deine Datenstrukturen und die Decodierung 
vereinfachen; wenn Du einen Strukturtyp vereinbarst, der die zu 
erkennende Zeichenkette und einen Funktionspointer enthält, kannst Du 
gleich nach dem Vergleich der Zeichenketten die Funktion aufrufen, ohne 
den Umweg über einen enum und eine switch-case-Kette gehen zu müssen.

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
>
> typedef char CmdString[5];
> CmdString CmdList[] = {"befehl1\n",
>                  "befehl2\n",
>                  "befehl3\n"};
> 

Den typedef schmeisst du am besten gleich wieder raus. Vor allen Dingen 
deshalb, weil deine jetzigen Command-Strings schon länger als 4 Zeichen 
sind. Und den \n braucht es auch nicht wirklich in der Befehlsliste. 
Deine Empfangsroutine weiß wann eine Zeile zu Ende ist und gibt dir das 
Empfangene in Form einer Zeile. Den \n braucht da niemand wirklich im 
String. Wenn ihn die Empfangsroutine gleich rauswirft
  * sparst du Speicher
  * musst dich im weiteren auch nicht darum kümmern, ob der Sender
    ein \n alleine, oder ein \r alleine, oder \r\n oder \n\r
    gesendet hat.
const char* CmdList = { "befehl1",
                        "befehl2",
                      };


> erfolgt der Vergleich mit dem 2. Befehl und so weiter. Stimmt der
> Puffer-Inhalt mit einem der Befehle aus CmdList überein, wird eine
> Varible mit einem Wert aus einer enum-Liste gesetzt und dann wird in
> einer switch-case-Struktur je nach Befehl der zugehörige Code
> ausgeführt.

Den Umweg über einen enum braucht es nicht wirklich. Warum rufst du 
nicht gleich bei Erkennen des Strings, nach dem strcmp, die 
entsprechende Funktion auf?

> Ist es sinnvoll, das so wie beschrieben zu machen. Oder habt ihr bessere
> Konzepte vorzuschlagen?

Sicher: Funktionspointer

http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Schau dir dazu mal die Funktion strstr und strtok. Du musst auch immer
>davon ausgehen, dass man sich mal verschreibt, Groß/Kleinschreibung
>nicht beachtet oder ein return nicht kommt. Deswegen lieber nicht jedes
>Zeichen miteinander vergleichen (also nicht strcmp).


Ich bin ziemlich unerfahren im Programmieren und kenne die wenigsten 
fertigen Funktionen. strcmp ist mir auch unbekannt, ich würde alles "zu 
Fuß" programmieren. Darum bitte ich auch um Hinweise, welche fetige 
Funktionen man in diesem Fall verwenden könnte ( wenn möglich mit der 
Quellbibliothek).

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:

> Ich bin ziemlich unerfahren im Programmieren und kenne die wenigsten
> fertigen Funktionen. strcmp ist mir auch unbekannt,

Autsch!

Du brauchst ganz dringend ein C-Buch!
Du erfindest ja aller selber und weißt nicht, was dir die Sprache und 
ihre Standardlibrary für Möglichkeiten schon fertig zur Benutzung 
mitgibt.


http://www.mikrocontroller.net/articles/FAQ#Wie_fu...
http://www.mikrocontroller.net/articles/FAQ#Men.C3...

Autor: Udo R. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie wäre es mit einem guten Buch und erst mal ein oder zwei gemütliche 
Leseabende.
Merke: Wissen ist nicht durch Nichtwissen zu ersetzen.
Ausser vieleicht in der Finanzbranche und im Management. Da muss man nur 
einen Sündenbock finden, laut jammern und trotzdem Boni kassieren.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein totaler Anfänger bin ich nicht. Ich habe 2 Semester Informatik aus 
dem Studium und einiges aus Praxissemester und Übungen. Ein C-Buch habe 
ich eigentlich schon. Das sehe ich schon ein, dass ich wissen sollte 
welche Mittel mir die Standard-Bibl. bietet. Wie empfohlen, werde ich 
mir demnächst mal ein Bild machen, was es alles für Funktionen gibt. Es 
soll hier erst nur um Konzept gehen.

>Den typedef schmeisst du am besten gleich wieder raus. Vor allen Dingen
>deshalb, weil deine jetzigen Command-Strings schon länger als 4 Zeichen
>sind. Und den \n braucht es auch nicht wirklich in der Befehlsliste.
>Deine Empfangsroutine weiß wann eine Zeile zu Ende ist und gibt dir das
>Empfangene in Form einer Zeile. Den \n braucht da niemand wirklich im
>String. Wenn ihn die Empfangsroutine gleich rauswirft
>  * sparst du Speicher
>  * musst dich im weiteren auch nicht darum kümmern, ob der Sender
>    ein \n alleine, oder ein \r alleine, oder \r\n oder \n\r
>    gesendet hat.

>
>const char* CmdList = { "befehl1",
>                        "befehl2",
>                      };

Die Länge des definierten Typs würde ich dann schon richtig anpassen. 
Was mir nicht klar ist, wie halte ich bei der vorgeschlagenen Liste die 
Befehle auseinander. Sie können ja aus unterschiedlicher Anzahl an 
Zeichen bestehen. Den \n habe ich deshalb angehängt, damit ich weiß wann 
ein Befehl zu Ende ist. Es kann möglicherweise die Befehle "gpa" und 
"gpad" geben und ich daran ob ein \n am Ende ist, würde ich erkennen ob 
der Befehl aus der Liste weiter geht oder schon zu Ende ist. Der Sender 
sendet am Ende des Befehls immer nur CR (ASCII-Wert 13) und den \n würde 
ich dem gesendeten empfangenen Befehl vor dem Vergleich noch dranhängen.

Wie weiß die Empfangsroutine, wann eine Zeile zu Ende ist?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beim Empfangen kannst du natürlich das Ende an \n oder so
einem Zeichen (Leerzeichen etc.) prima erkennen.
Aber dann muß man es nicht mehr speichern und vergleichen.

Wenn es auf Dauer etliche Befehle werden, würde ich nebenbei
wie schon empfohlen je Befehl eine struct mit Funktionszeiger
machen, alle solchen structs in einem sortierten Feld halten
und mit bsearch nach dem richtigen fahnden.
Dadurch muß nicht jedesmal die ganze Liste anageklappert werden
und die Suche geht deutlich schneller.
(Gleich vorweg: bsearch() braucht nur wenige Byte Code.)

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> Ein totaler Anfänger bin ich nicht. Ich habe 2 Semester Informatik aus
> dem Studium und einiges aus Praxissemester und Übungen. Ein C-Buch habe
> ich eigentlich schon.

Dann solltest du darin auch mal schmökern.
Ich hab für vieles Verständnis, aber ich hab kein Verständnis dafür ein 
C-Buch zu besitzen und die Familie der str... Funktionen nicht zu 
kennen. Gleich nach dem Kapitel über Arrays gehts meistens in den 
Büchern mit Strings weiter und ab dann werden gerade die str... 
Funktionen bis zum Exzess eingesetzt.


>>const char* CmdList = { "befehl1",
>>                        "befehl2",
>>                      };[/c]
>
> Die Länge des definierten Typs würde ich dann schon richtig anpassen.

Lass das den Compiler machen. Der macht das zuverlässiger.
Und da du die Strings nicht verändern willst, reicht es völlig aus, wenn 
du dir ein Array von Pointern auf die (konstanten) Strings einrichtest.

> Was mir nicht klar ist, wie halte ich bei der vorgeschlagenen Liste die
> Befehle auseinander.

Jeder Pointer im Array zeigt auf einen String.

> Sie können ja aus unterschiedlicher Anzahl an
> Zeichen bestehen. Den \n habe ich deshalb angehängt, damit ich weiß wann
> ein Befehl zu Ende ist.

Jeder String in C hat IMMER ein \0 Zeichen am Ende! Daran wird das Ende 
eines Strings erkannt. Das ist C-Konvention und du solltest dich 
unbedingt daran halten und nicht dein eigenes Süppchen kochen. Eine 
Abfolge von Zeichen die mit einem \0 Zeichen aufhört, ist in C per 
Definition ein String.
Schreibst du also selber Zeichen in ein Array, so ist das solange kein 
String, solange du die Abfolge der Zeichen nicht mit einem \0 
abschliesst.

Aber das ist auch alles in dem Link über Stringverarbeitung enthalten, 
den ich dir weiter oben gegeben habe. Allerdings: Dort findest du nur 
das absolut Notwendigste zum Thema Stringverarbeitung.

> Es kann möglicherweise die Befehle "gpa" und
> "gpad" geben und ich daran ob ein \n am Ende ist,
  char buffer[] = "gpa";

  if( strcmp( buffer, "gpa" ) == 0 )
     // gpa wurde erkannt

  else if( strcmp( buffer, "gpad" ) == 0 )
    // gpad wurde erkannt

> würde ich erkennen ob
> der Befehl aus der Liste weiter geht oder schon zu Ende ist.

Pack dir einen C-Compiler auf deinen PC, nimm dein C-Buch und arbeite 
die ersten paar Kapitel durch!

> Der Sender
> sendet am Ende des Befehls immer nur CR (ASCII-Wert 13) und den \n würde
> ich dem gesendeten empfangenen Befehl vor dem Vergleich noch dranhängen.
>
> Wie weiß die Empfangsroutine, wann eine Zeile zu Ende ist?

Wenn das Zeichen \n empfangen wurde?

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Klaus Wachtler

>Beim Empfangen kannst du natürlich das Ende an \n oder so
>einem Zeichen (Leerzeichen etc.) prima erkennen.
>Aber dann muß man es nicht mehr speichern und vergleichen.

Durch \n will das Ende des Befehls nicht beim Emfangen erkennen sondern 
beim Vergeleichen. Beim Emfangen erkenne ich das Ende der Befehlseingabe 
am Carriage Return.
Warum muss man es dann nicht mehr speichern und vergleichen? Was meinst 
du damit?

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> @  Klaus Wachtler
>
>>Beim Empfangen kannst du natürlich das Ende an \n oder so
>>einem Zeichen (Leerzeichen etc.) prima erkennen.
>>Aber dann muß man es nicht mehr speichern und vergleichen.
>
> Durch \n will das Ende des Befehls nicht beim Emfangen erkennen sondern
> beim Vergeleichen.

N E I N

Lies es von meinen Lippen, äh Tastendrücken ab:
Ein String wird mit einem \0 beendet.
Immer? Immer!

> Beim Emfangen erkenne ich das Ende der Befehlseingabe
> am Carriage Return.

\n ist die C-Schreibweise für Carriage Return!

> Warum muss man es dann nicht mehr speichern und vergleichen?

Weil deine Zeile laut deiner Definition sowieso hinten dann immer einen 
\n hat. Deine Befehle in der Befehlsliste haben hinten auch einen \n. 
D.h. das letzte Zeichen stimmt in jedem Fall überein! Das muss man daher 
auch nicht testen. Was man aber nicht testen muss, muss man auch nicht 
speichern.


Bist du sicher, dass du Informatik studierst?

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Bist du sicher, dass du Informatik studierst?

Oh, Entschuldigung, ich habe mich weiter oben falsch ausgedrückt. Ich 
wollte sagen, dass ich in meinem Studium (Elektrotechnik) 2 Semester 
lang die Vorlesungen zu Informatik gehört und die Prüfungen bestanden 
habe.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>\n ist die C-Schreibweise für Carriage Return!

\n ist nach meinem Wissen die Schreibweise für ASCII-Wert 000 und 
Carriage Return hat den ASCII-Wert 013! Oder sehe ich da was falsch?

Autor: kodac (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
There are a few characters which can indicate a new line. The usual ones 
are these two:

    * '\n' or '0x0A' (10 in decimal) -> This character is called "Line 
Feed" (LF).
    * '\r' or '0x0D' (13 in decimal) -> This one is called "Carriage 
return" (CR).


Different Operating Systems handle newlines in a different way. Here is 
a short list of the most common ones:

    * DOS and Windows

      They expect a newline to be the combination of two characters, 
namely '\r\n' (or 13 followed by 10).

    * Unix (and hence Linux as well)

      Unix uses a single '\n' to indicate a new line.

    * Mac

      Macs use a single '\r'.

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
>>\n ist die C-Schreibweise für Carriage Return!
>
> \n ist nach meinem Wissen die Schreibweise für ASCII-Wert 000 und
> Carriage Return hat den ASCII-Wert 013! Oder sehe ich da was falsch?

Du siehst das falsch!

\0  ist die Schreibweise für das Zeichen mit dem ASCII Wert 0
\n  ist genau genommen LineFeed und
\r  ist genau genommen Carriage Return

Aber in den meisten Fällen kommt man damit durch, dass man bei der 
Ausgabe über die Standard Ausgabefunktionen nur \n ausgeben muss bzw. 
bei der Eingabe über die Standardfunktionen nur auf \n prüfen muss. Die 
Standardfunktionen setzen \n dann auf die Form um, der auf dem konkreten 
System üblich ist ( nur \n, nur \r, \r\n). Lediglich dann, wenn 
Ein/Ausgabe an den Standardfunktionen vorbeilaufen, muss man auf diese 
Feinheiten achten.

Schön langsam solltest auch du erkennen, dass deine Lücken so dermassen 
gross sind, dass du UNBEDINGT als Allererstes dein Buch zur Hand nehmen 
solltest.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Schön langsam solltest auch du erkennen, dass deine Lücken so dermassen
>gross sind, dass du UNBEDINGT als Allererstes dein Buch zur Hand nehmen
>solltest.

Hm.. Schön langsam erkenne ich es auch!

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
und: die Funktion strcmp erkennt "gpa" und "gpad" als unterschiedliche 
Strings.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Pack dir einen C-Compiler auf deinen PC, nimm dein C-Buch und arbeite
>die ersten paar Kapitel durch!

Gern! Könntest du mir einen C-Compiler empfehlen?

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist ja nicht so, dass ich das ganze ohne auszuprobieren mache. Ich 
programmiere in AVR-Studio. Aber nicht viel bis jetzt.

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> Es ist ja nicht so, dass ich das ganze ohne auszuprobieren mache. Ich
> programmiere in AVR-Studio.

AVR-Studio hilft dir da nicht viel, weil du die Programme nicht auf dem 
PC laufen lassen kannst. Dadurch wird erst einmal das Debuggen des 
Programms erschwert.

Grundlagen in C lassen sich am besten auf dem PC lernen. Da 
funktionieren erst einmal alle Programme aus deinem Buch ohne 
Änderungen. Die Ein/Ausgabe ist soweit eingerichtet, dass alles was du 
per printf ausgibst, auf der Konsole landet. Und das ist für die ersten 
Schritte ein mehr als nur dicker Plus-Punkt. Bei einem AVR muss man da 
selber Hand anlegen und schon definitiv wissen was man tut, ehe ein 
printf über die serielle Schnittstelle oder auf einem LCD ausgeben kann.

Um die Grundlagen C zu lernen kannstdu im Prinzip jeden Compiler nehmen. 
Die Unterschiede sind da eher in der IDE zu suchen. Aber die Fähigkeiten 
C Quelltext in ausführbare Programme korrekt umzusetzen, beherrschen die 
alle aus dem efef

Empfehlen kann ich dir keinen, weil ich seit Jahren auf die Microsoft 
Compiler angewiesen bin. Von MS gibts auch eine kostenlose Version. Auch 
den gcc gibts in unterschiedliche Pakete eingepackt als freien Compiler.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Grundlagen in C lassen sich am besten auf dem PC lernen.

Das habe ich mir auch gedacht. Allerdings muss ich die Grundlagen nicht 
neu lernen. Ich muss meine Lücken schließen und das Wissen erfrischen.

Gibt es einen Freeware C-Compiler, den man zum lernen am PC verwenden 
könnte?

Autor: Rogie (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> Gern! Könntest du mir einen C-Compiler empfehlen?
Visual Studio C++ Express z.B.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK, verstanden.

Autor: Rogie (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kannst auch mal hier nachschauen:

http://www.thefreecountry.com/compilers/cpp.shtml

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmals!

Ich habe mir jetzt die String-Funktionen aus der Standard Library 
angeschaut und mit dem Visual Studio C++ Exspress ausprobiert.

Dann habe ich ein Programm im Visual Studion geschrieben, in dem ich 
Befehle mit scanf einlese, mit den Befehlen aus einer Liste vergleiche 
und bei Erkennung den entspr. Befehl ausführe. Die Ausführung besteht 
dabei nur aus der Ausgabe vom Text in der Konsole mit printf. Die 
Auswertung und Ausführung mache ich wie von euch weiter oben empfohlen. 
Das ging im VS auch einwandfrei.

Nun habe ich dieses Programm für den µC angepasst. Statt printf benutze 
ich da die Funktion vSendText. Das Einlesen geht so:
Zeichen vom Terminal werden vom UART (per Interrupt) solange im 
caCmdBuffer gepuffert, bis Eingabetaste kommt. Nach Eingabetaste wird 
eine Flag-Variable gesetzt, worauf im Hauptprogramm die Auswertung des 
Puffers und Ausführung des Befehls erfolgt.

Beim Compilieren wird aber ein Linker Error gemeldet:

undefined first referenced
  symbol       in file
 --------- ----------------
 vSentText ./SPI_master.obj

Ich sitze schon längere Zeit und komme nicht drauf, was falsch ist! Drum 
bitte ich um eure Hilfe, falls jemand sich das anschauen möchte. Danke 
im Voraus für eure Hinweise!!!

#include "msp430x54x.h"
#include "SPI_master.h"
#include "stdint.h"
#include "string.h"

#define STRINGSIZE 15           // max. size of the input variable


typedef void (*FctPtr)(void);

struct sCmd {
  char cpCmd [10];
  FctPtr Function;
};



volatile char cEnterFlag;
volatile char cStopMarker;
char caCmdBuffer[10];
char* cpCmdBuffer = caCmdBuffer;

void vSend(void);
void vGet(void);
void vDo(void);

struct sCmd sCmdList[] = {
  {"Send", vSend},
  {"Get" , vGet},
  {"Do"  , vDo}
};


void vSendText(char Text[STRINGSIZE])
{  int i;

   i=0;
   while (Text[i]!='\0')         // transmit string via RS232
   {  UCA1TXBUF=Text[i++];
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
   }
}

void vSend() {
  vSendText("Send\n");
}

void vGet() {
  vSendText("Get\n");
}

void vDo() {
  vSentText("Do\n");
}


/****************************************************************************************
* void main(void):                                                                      *
****************************************************************************************/
void main(void)
{

  while(1){

    if(cEnterFlag == 1)
    {
      struct sCmd* spItem;
      while(1) {
      qsort (sCmdList, 3, sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
      spItem = (struct sCmd*)bsearch (cpCmdBuffer, sCmdList, 3, sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
      if (spItem != NULL)
      spItem->Function();
      else
     vSendText("Ungueltige Eingabe!\n");
      }
      
     }
  }

}//main




#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
{


  switch(__even_in_range(UCA1IV,4))
  {
    case 0: break;
    case 2:                                 // UCRXIFG
    
      if(UCA1RXBUF != 13)          // if not Enter key
      {
        if(cpCmdBuffer < (caCmdBuffer + 10))
          *cpCmdBuffer++ = UCA1RXBUF;
        // sonst Meldung anzeigen!!
      }
      else
      {
        cEnterFlag = 1;
      }
      break;
    case 4:                                 // UCTXIFG
      break;
    default: break;
  }
}



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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:

> Ich sitze schon längere Zeit und komme nicht drauf, was falsch ist!

Steht doch da

  symbol       in file
 --------- ----------------
 vSentText ./SPI_master.obj


Gesucht wird die Funktion SentText (mit einem harten t)
Deine Funktion heißt aber SendText (mit einem weichen d)

PS:
>Beim Compilieren wird aber ein Linker Error gemeldet:

Das ist ein Widerspruch in sich.
Compilieren und Linken sind 2 verschiedene Dinge

  Compilieren:  Übersetzen der Einzelteile in Maschinensprache
  Linken:       Zusammenfügen der übersetzten Einzelteil zum
                kompletten Programm


Wenn du daher beim Linken einen Fehler hast, dann ist das kein 
C-Syntaxfehler mehr (den hätte der Compiler gefunden) sondern du benutzt 
einen Namen, den es so nicht gibt. Oft ist das einfach nur ein 
Tippfehler, Zeichendreher etc.
-> Den Namen, den der Linker anmäkelt genau ansehen und auf derartige 
Flüchtigkeitsfehler absuchen.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ noips:
Bist du aus Franken?

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es fällt mir jetzt noch auf, dass ich vergessen habe, die innere 
while(1)-Schleife zu entfernen. Außerdem muss ich natürlich noch die 
Flag-Variable und den Buffer-Pointer rücksetzen. Aber das wirkt sich 
nicht auf den gemeldeten Linker-Fehler aus.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Klaus Wachtler

nein, warum?

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uuups! So peinlich! Ich vermute den Fehler in fehlenden Prototypen, in 
falscher Parameterübergabe und sonstiges, und der Steckt in einem 
einfachen Tipp-Fehler. Besten Dank für den Hinweis!!


Mit dem Compilieren und Linken wusste ich eigentlich schon, dass das 
nicht dasselbe ist. Nur habe ich keinen Begriff gefunden, mit dem man 
beides verbinden kann. Das was in der IDE "Build" heißt.

Vielen Dank!

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Weil die solchen zumindest sprachlich d und t nicht
auseinanderhalten, ebensowenig wie b und B.

Beim Sprechen ist das jeweils exakt gleich.
Beim Buchstabieren hört sich das dann so an:
"A wie Anton, weiches b wie Bertha, hartes b wie Baula,
d wie Dora, d wie Deodor,..."
(siehe Antwort von KHB:
> ...mit einem harten t...mit einem weichen d...
, das liest sich auch eher süddeutsch).
In Norddeutschland habe ich noch niemandem beim Buchstabieren
von hart und weich reden hören, da hört man es schon beim
Sprechen von d oder t, was gemeint ist.

Wäre jetzt interessant, wenn sich das auch aufs Schreiben
auswirkt: SentText <> SendText.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> Uuups! So peinlich! Ich vermute den Fehler in fehlenden Prototypen,...

Naja letztlich schon.
Wenn du beim Kompilieren die Warnungen einschaltest und auch
beachtest und beispielsweise vSendText deklarierst, aber
einmal versehentlich vSentText aufrufst, wird dir jeder
vernünftige Compiler dann eine Warnung ausgeben von wegen
irgendwas undeclared.

Verschreiben ist nicht peinlich, Warnungen ignorieren schon :-)

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Naja letztlich schon.
>Wenn du beim Kompilieren die Warnungen einschaltest und auch
>beachtest und beispielsweise vSendText deklarierst, aber
>einmal versehentlich vSentText aufrufst, wird dir jeder
>vernünftige Compiler dann eine Warnung ausgeben von wegen
>irgendwas undeclared.
>
>Verschreiben ist nicht peinlich, Warnungen ignorieren schon :-)

Hmm... hier hat der Compiler keine Warnung gebracht! Meistens schaue ich 
mir die Warnungen auch an. Spätestens wenn ich vor so einem Problem 
stehe wie dieses Mal. Und mit dem Aktivieren muss ich mir noch 
anschauen, was sich da Einstellen lässt.


Was das Verschreiben und meine Herkunft angeht: Ich wohne seit 1996 in 
Südbayern, Augsburger Gegend, bin ursprünglich aber Russlandsdeutscher. 
In der Schule habe ich auch schon änhliche Fehler gehabt. Hab z.B. statt 
Mord Mort geschrieben, obwohl ich wusste wie es richtig geschrieben 
wird.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe jetzt ein Problem in einem Unterprogramm (Auszug unten). Der Aufruf 
von sscanf bewirkt irgendwie gar nichts in diesem Unterprogramm. D. h. 
wenn ich im Debugger diese Zeile ausführe, ändert sich der Inhalt der 
Variable iOut nicht. Dagegen funktioniert bei dem gleichen Code im 
Hauptprogramm alles wie erwartet. Ich habe vermutet, dass es an den 
Optimazer-Einstellungen liegen könnte. Die Änderungen an den 
Einstellungen hat aber auch nichts gebracht. Weiß vielleicht jemand, 
woran dieses Problem liegt? Programmiert wird ein MSP430 mit CCE von 
Texas Instruments.
void vSetDigOutput(void){

  int iOut;
  char temp[10];
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
  while (cEnterFlag == 0) {}
  cEnterFlag = 0;
  *cpCmdBuffer = 0;
  sscanf(caCmdBuffer, "%x", &iOut);
  sprintf(temp, "%i", iOut);
  vSendText(temp);
}

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:
> Habe jetzt ein Problem in einem Unterprogramm (Auszug unten). Der Aufruf
> von sscanf bewirkt irgendwie gar nichts in diesem Unterprogramm. D. h.
> wenn ich im Debugger diese Zeile ausführe, ändert sich der Inhalt der
> Variable iOut nicht.

Lass dir doch mal caCmdBuffer ausgeben, damit du siehst, ob da überhaupt 
das drinnen steht, was deiner Meinung nach drinnen stehen sollte. Ich 
wette, dass da nichts für sscanf verwertbares enthalten ist.

Bei der Fehlersuche immer vorne anfangen. Als erstes werden die Eingaben 
überprüft, dann die Verarbeitung. Nichts als gegeben annehmen, alles in 
Frage stellen und sei es noch so trivial.

Also:

void vSetDigOutput(void){

  int iOut;
  char temp[10];

  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
  while (cEnterFlag == 0) {}
  cEnterFlag = 0;
  *cpCmdBuffer = 0;

  vSendText( "Habe gelesen: #%s# ", caCmdBuffer );

  sscanf(caCmdBuffer, "%x", &iOut);
  sprintf(temp, "%i", iOut);
  vSendText(temp);
}

und dann sieht man erst mal weiter.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Lass dir doch mal caCmdBuffer ausgeben, damit du siehst, ob da überhaupt
>das drinnen steht, was deiner Meinung nach drinnen stehen sollte. Ich
>wette, dass da nichts für sscanf verwertbares enthalten ist.

Danke für den Rat! Das hat mich ein Stück weiter gebracht. In 
caCmdBuffer stand auch tatsächlich das, was ich an sscanf weitergeben 
wollte. Ich sehe das im Watch-Fensters des Debuggers. Aber aus irgend 
einem Grund wurde bei der Übergabe von caCmdBuffer als Parameter nicht 
die richtige Adresse übergeben. Bei diesem Code
  int iOut;
  char temp[10];
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
  while (cEnterFlag == 0) {}
  cEnterFlag = 0;
  *cpCmdBuffer = 0;
  vSendText(caCmdBuffer);           // caCmdBuffer wird falsch übergeben
  sscanf(caCmdBuffer, "%x", &iOut);
  sprintf(temp, "%i", iOut);
  vSendText(temp);
bewirkte die Zeile vSendText(caCmdBuffer); die Ausgabe "Ausgangswerte 
eingeben (hexadezimal):" von weiter oben. Warum, ist mir ein Rätsel!

Dagegen bei diesem Code
  int iOut;
  char temp[10];
  char* ptr;
  ptr = caCmdBuffer;
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
  while (cEnterFlag == 0) {}
  cEnterFlag = 0;
  *cpCmdBuffer = 0;
  vSendText(ptr);
  sscanf(ptr, "%x", &iOut);
  sprintf(temp, "%i", iOut);
  vSendText(temp);
geht alles wie erwartet. Außerdem wenn man den ersten Code (in diesem 
Posting) im Hauptprogramm hatte ging alles auch wie erwartet. Das würde 
mich interessieren, was der Grund ist?

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

Bewertung
0 lesenswert
nicht lesenswert
noips schrieb:

> einem Grund wurde bei der Übergabe von caCmdBuffer als Parameter nicht
> die richtige Adresse übergeben. Bei diesem Code
>
>   int iOut;
>   char temp[10];
>   vSendText("Ausgangswerte eingeben (hexadezimal):\r");
>   while (cEnterFlag == 0) {}
>   cEnterFlag = 0;
>   *cpCmdBuffer = 0;
>   vSendText(caCmdBuffer);           // caCmdBuffer wird falsch übergeben
>   sscanf(caCmdBuffer, "%x", &iOut);
>   sprintf(temp, "%i", iOut);
>   vSendText(temp);
> 
> bewirkte die Zeile vSendText(caCmdBuffer); die Ausgabe "Ausgangswerte
> eingeben (hexadezimal):" von weiter oben.

D.h. du siehst den Text dann 2-mal auf deiner Ausgabe?
Ausgangswerte eingeben (hexadezimal):
123                                            <- Hier gibts du ein
Ausgangswerte eingeben (hexadezimal):
0                                              <- Das kommt dann vom sprintf

> Warum, ist mir ein Rätsel!

Da geht irgendwas grauslich schief.

Kompletten Code herzeigen

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt geht alles! Ich habe alle Break-Pointer entfernt und das Programm 
einfach ununterbrochen laufen lassen, und jetzt geht alles auch mit der 
1. Code-Variante. Wahrscheinlich gab es wegen dem Anhalten der 
Codeausführung durch Debugger Probleme in der Kommunikation mit Terminal 
per UART.
Wenn du den Code aber trotzdem anschauen willst:
#include "msp430x54x.h"         // Standard Definition Control Register
#include "SPI_master.h"
#include "stdint.h"
#include "string.h"
#include "stdio.h"

#define STRINGSIZE 15           // max. size of the input variable
#define DATA_SIZE  32

/****************************************************************************************
* global variables:                                                                     *
****************************************************************************************/



typedef void (*FctPtr)(void);

struct sCmd {
  char cpCmd [10];
  FctPtr Function;
};



volatile char cEnterFlag;
volatile char cStopMarker;
volatile char cDummy;
char caCmdBuffer[10];
char* cpCmdBuffer = caCmdBuffer;

void vSendProfibus(void);
void vSendSpi(void);
void vResEeprom(void);
void vConfigData(void);
void vGetError(void);
void vGetEeprom(void);
void vGetDigInput(void);
void vSetDigOutput(void);
void vGetAnaInput(void);
void vSetAnaOutput(void);
void vUserDiag(void);
void vGetStatus(void);

struct sCmd sCmdList[] = {
  {"send_pb"  , vSendProfibus},
  {"send_spi" , vSendSpi},
  {"res_epr"  , vResEeprom},
  {"config"   , vConfigData},
  {"get_err"  , vGetError},
  {"get_epr"  , vGetEeprom},
  {"get_din"  , vGetDigInput},
  {"set_dout" , vSetDigOutput},
  {"get_ain"  , vGetAnaInput},
  {"set_aout" , vSetAnaOutput},
  {"user_diag", vUserDiag},
  {"get_stat" , vGetStatus}
};


/****************************************************************************************
* UART Settings:                                                                         *
****************************************************************************************/
// terminal connection
static void vInitUart1(void)
{
  P5SEL |= 0xC0;                            // P5.6,7 = USCI_A1 TXD/RXD
  UCA1CTL1 |= UCSWRST;                      // **Put state machine in reset**
  UCA1CTL1 |= UCSSEL_2;                     // SMCLK
  UCA1BR0 = 8;                              // 1MHz 115200 (see User's Guide)
  UCA1BR1 = 0;                              // 1MHz 115200
  UCA1MCTL |= 0xB1;            // Modulation UCBRSx=, UCBRFx=11, Oversampling Mode
  UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
  UCA1IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
}

/****************************************************************************************
* Clock Settings:                                                                       *
*   System-Clock ->  8 Mhz                                                              *
****************************************************************************************/
static void vInitClock(void)
{
  P7SEL |= 0x03;                // XIN, XOUT aktivieren
  UCSCTL3 |= SELREF_2;            // FLL Ref = REFO
  UCSCTL6 &= ~XT1OFF;              // Set XT1 On
  UCSCTL6 |= XTS;                // Select HF-mode

  do
  {
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
    SFRIFG1 &= ~OFIFG;            // Clear fault flags
  }
  while ((SFRIFG1 & OFIFG) != 0);        // XT1-Fault flag still set?
  UCSCTL4 &= ~0x44;              // XT1CLK is MCLK source and SCLK source
}

/****************************************************************************************
* IO Settings                                                                           *
****************************************************************************************/
static void vInitIO(void)
{
  P3DIR |= 0x30;              // P3.4-5 output (HOLD and DE signal)
  
//  P2DIR |= 0x00;
//  P2REN |= 0x04;                            // Enable P2.2 internal resistance
//  P2OUT |= 0x04;                            // Set P2.2 as pull-Up resistance
//  P2IE |= 0x04;                             // P2.2 interrupt enabled
//  P2IES |= 0x04;                            // P2.2 Hi/Lo edge
//  P2IFG &= ~0x04;                           // P2.2 IFG cleared
  
  P1DIR |= 0x0B;              // Trigger, LED, Slave Select
  P3SEL |= 0x0F;                            // P3.0-3 option select
  P5SEL |= 0x30;              // P5.4-5 option select
  P3SEL |= 0x80;
  P1OUT |= 0x08;              // Slave select off
}



/****************************************************************************************
* void Init(void): Initialisation of the controller ports                               *
****************************************************************************************/
void Init(void)
{
  vInitClock();                     // config internal clock generator
  vInitIO();                        // config IO's
  vInitUart1();                     // config USART1 as UART
  

  WDTCTL = WDTPW + WDTHOLD;         // Stop watchdog timer
  SFRIE1 |= WDTIE;
  _EINT();                          // Enable interrupts

}

/****************************************************************************************
* void vSendText(char Text[20]): send a text via RS232                                  *
*                                 last characters are always "\0"                       *
****************************************************************************************/
void vSendText(char Text[DATA_SIZE])
{  int i;

   i=0;
   while (Text[i]!='\0')         // transmit string via RS232
   {  UCA1TXBUF=Text[i++];
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
   }
}

void vSendProfibus() {
  vSendText("Send\r");
}

void vSendSpi(void) { vSendText("SendSpi\r");
}
void vResEeprom(void){vSendText("ResEeprom\r");
}
void vConfigData(void){vSendText("config\r");
}
void vGetError(void){vSendText("GetError\r");
}
void vGetEeprom(void){vSendText("GetEeprom\r");
}
void vGetDigInput(void){vSendText("GetDIN\r");
}
void vSetDigOutput(void){

  int iOut;
  char temp[10];
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
  while (cEnterFlag == 0) {}
  cEnterFlag = 0;
  *cpCmdBuffer = 0;
  vSendText(caCmdBuffer);
  vSendText("\r");
  sscanf(caCmdBuffer, "%x", &iOut);
  sprintf(temp, "%i", iOut);
  vSendText(temp);
}
void vGetAnaInput(void){
}
void vSetAnaOutput(void){
}
void vUserDiag(void){
}
void vGetStatus(void){
}



/****************************************************************************************
* void main(void):                                                                      *
****************************************************************************************/
void main(void)
{
  int iOut;
  caCmdBuffer[0] = 'a';
  caCmdBuffer[1] = 'b';
  Init();
  qsort(sCmdList, (sizeof(sCmdList)/sizeof(struct sCmd)), sizeof(struct sCmd), (int(*)(const void*,const void*))strcmp);
  vSendText(caCmdBuffer);
  sscanf(caCmdBuffer, "%x", &iOut);
  while(1){

    if(cEnterFlag == 1)
    {
      struct sCmd* spItem;
      cEnterFlag = 0;
      *cpCmdBuffer = 0;
      cpCmdBuffer = caCmdBuffer;
      spItem = (struct sCmd*)bsearch (cpCmdBuffer, sCmdList, (sizeof(sCmdList)/sizeof(struct sCmd)), sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
      if (spItem != NULL)
      spItem->Function();
      else
     vSendText("Ungueltige Eingabe!\r");
    
      
     }
  }

}//main



#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
{


  switch(__even_in_range(UCA1IV,4))
  {
    case 0: break;
    case 2:                                 // UCRXIFG
    
      if(UCA1RXBUF == 13)          // if not Enter key
      {
        cEnterFlag = 1;
      }
      else
      {
        if (UCA1RXBUF != 10) {
          if(cpCmdBuffer < (caCmdBuffer + 10))
            *(cpCmdBuffer++) = UCA1RXBUF;
            // sonst Meldung anzeigen!!
        }
      }
      break;
    case 4:                                 // UCTXIFG
      break;
    default: break;
  }
}





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

Bewertung
0 lesenswert
nicht lesenswert
Dreh das hier ...
void vSendText(char Text[DATA_SIZE])
{  int i;

   i=0;
   while (Text[i]!='\0')         // transmit string via RS232
   {  UCA1TXBUF=Text[i++];
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
   }
}

... um

Du willst zuerst warten ob die USART bereit ist und erst dann das 
Zeichen los werden.
//
// transmit string via RS232
//
void vSendText( const char* Text )
{
  while ( *Text != '\0' )
  {
    // USCI_A1 TX buffer ready?
    while (!(UCA1IFG&UCTXIFG))
      ;

    UCA1TXBUF = *Text++;
  }
}

Auch solltest du an deinen Codeformatierungen arbeiten. Insbesondere 
konsistente Formatierung bei { } Blöcken und Einrückungen.

So etwas
void vResEeprom(void){vSendText("ResEeprom\r");
}

geht gar nicht. Jetzt ist dein Programm noch klein und überschaubar. 
Aber wenn es wächst, wirst du dir mit inkonsistenter Formatierung immer 
wieder selbst ein Bein stellen.

Autor: noips (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok! Danke für die Hinweise!

Autor: Remote One (remote1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aus Speicherplatzgründen wäre es auch besser, die konstanten 
Zeichenketten (die Befehle) mit progmem zu verlagern.
Also in der Form:

static const char entry1[] PROGMEM = "send_pb";

in der Struktur schreibst du dann

struct sCmd sCmdList[] = {
  {entry1  , vSendProfibus},
};

wobei diese eigentlich auch konstant ist und ausgelagert werden kann.

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.