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


von noips (Gast)


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:
1
typedef char CmdString[5];
2
CmdString CmdList[] = {"befehl1\n",
3
                 "befehl2\n",
4
                 "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!!!

von kodac (Gast)


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).

von noips (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


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.

von Karl H. (kbuchegg)


Lesenswert?

noips schrieb:
>
1
> typedef char CmdString[5];
2
> CmdString CmdList[] = {"befehl1\n",
3
>                  "befehl2\n",
4
>                  "befehl3\n"};
5
>

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.
1
const char* CmdList = { "befehl1",
2
                        "befehl2",
3
                      };


> 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

von noips (Gast)


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).

von Karl H. (kbuchegg)


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_funktioniert_String-Verarbeitung_in_C.3F
http://www.mikrocontroller.net/articles/FAQ#Men.C3.BCs_mit_Funktionszeigern

von Udo R. S. (Gast)


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.

von noips (Gast)


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.

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

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?

von Klaus W. (mfgkw)


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.)

von Karl H. (kbuchegg)


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,
1
  char buffer[] = "gpa";
2
3
  if( strcmp( buffer, "gpa" ) == 0 )
4
     // gpa wurde erkannt
5
6
  else if( strcmp( buffer, "gpad" ) == 0 )
7
    // 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?

von noips (Gast)


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?

von Karl H. (kbuchegg)


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?

von noips (Gast)


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.

von noips (Gast)


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?

von kodac (Gast)


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'.

von Karl H. (kbuchegg)


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.

von noips (Gast)


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!

von Klaus W. (mfgkw)


Lesenswert?

und: die Funktion strcmp erkennt "gpa" und "gpad" als unterschiedliche 
Strings.

von noips (Gast)


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?

von noips (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


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.

von noips (Gast)


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?

von Rogie (Gast)


Lesenswert?

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

von noips (Gast)


Lesenswert?

OK, verstanden.

von Rogie (Gast)


Lesenswert?

Kannst auch mal hier nachschauen:

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

von noips (Gast)


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!!!
1
#include "msp430x54x.h"
2
#include "SPI_master.h"
3
#include "stdint.h"
4
#include "string.h"
5
6
#define STRINGSIZE 15           // max. size of the input variable
7
8
9
typedef void (*FctPtr)(void);
10
11
struct sCmd {
12
  char cpCmd [10];
13
  FctPtr Function;
14
};
15
16
17
18
volatile char cEnterFlag;
19
volatile char cStopMarker;
20
char caCmdBuffer[10];
21
char* cpCmdBuffer = caCmdBuffer;
22
23
void vSend(void);
24
void vGet(void);
25
void vDo(void);
26
27
struct sCmd sCmdList[] = {
28
  {"Send", vSend},
29
  {"Get" , vGet},
30
  {"Do"  , vDo}
31
};
32
33
34
void vSendText(char Text[STRINGSIZE])
35
{  int i;
36
37
   i=0;
38
   while (Text[i]!='\0')         // transmit string via RS232
39
   {  UCA1TXBUF=Text[i++];
40
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
41
   }
42
}
43
44
void vSend() {
45
  vSendText("Send\n");
46
}
47
48
void vGet() {
49
  vSendText("Get\n");
50
}
51
52
void vDo() {
53
  vSentText("Do\n");
54
}
55
56
57
/****************************************************************************************
58
* void main(void):                                                                      *
59
****************************************************************************************/
60
void main(void)
61
{
62
63
  while(1){
64
65
    if(cEnterFlag == 1)
66
    {
67
      struct sCmd* spItem;
68
      while(1) {
69
      qsort (sCmdList, 3, sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
70
      spItem = (struct sCmd*)bsearch (cpCmdBuffer, sCmdList, 3, sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
71
      if (spItem != NULL)
72
      spItem->Function();
73
      else
74
     vSendText("Ungueltige Eingabe!\n");
75
      }
76
      
77
     }
78
  }
79
80
}//main
81
82
83
84
85
#pragma vector=USCI_A1_VECTOR
86
__interrupt void USCI_A1_ISR(void)
87
{
88
89
90
  switch(__even_in_range(UCA1IV,4))
91
  {
92
    case 0: break;
93
    case 2:                                 // UCRXIFG
94
    
95
      if(UCA1RXBUF != 13)          // if not Enter key
96
      {
97
        if(cpCmdBuffer < (caCmdBuffer + 10))
98
          *cpCmdBuffer++ = UCA1RXBUF;
99
        // sonst Meldung anzeigen!!
100
      }
101
      else
102
      {
103
        cEnterFlag = 1;
104
      }
105
      break;
106
    case 4:                                 // UCTXIFG
107
      break;
108
    default: break;
109
  }
110
}

von Karl H. (kbuchegg)


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.

von Klaus W. (mfgkw)


Lesenswert?

@ noips:
Bist du aus Franken?

von noips (Gast)


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.

von noips (Gast)


Lesenswert?

@Klaus Wachtler

nein, warum?

von noips (Gast)


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!

von Klaus W. (mfgkw)


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.

von Klaus W. (mfgkw)


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 :-)

von noips (Gast)


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.

von noips (Gast)


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.
1
void vSetDigOutput(void){
2
3
  int iOut;
4
  char temp[10];
5
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
6
  while (cEnterFlag == 0) {}
7
  cEnterFlag = 0;
8
  *cpCmdBuffer = 0;
9
  sscanf(caCmdBuffer, "%x", &iOut);
10
  sprintf(temp, "%i", iOut);
11
  vSendText(temp);
12
}

von Karl H. (kbuchegg)


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:
1
void vSetDigOutput(void){
2
3
  int iOut;
4
  char temp[10];
5
6
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
7
  while (cEnterFlag == 0) {}
8
  cEnterFlag = 0;
9
  *cpCmdBuffer = 0;
10
11
  vSendText( "Habe gelesen: #%s# ", caCmdBuffer );
12
13
  sscanf(caCmdBuffer, "%x", &iOut);
14
  sprintf(temp, "%i", iOut);
15
  vSendText(temp);
16
}

und dann sieht man erst mal weiter.

von noips (Gast)


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
1
  int iOut;
2
  char temp[10];
3
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
4
  while (cEnterFlag == 0) {}
5
  cEnterFlag = 0;
6
  *cpCmdBuffer = 0;
7
  vSendText(caCmdBuffer);           // caCmdBuffer wird falsch übergeben
8
  sscanf(caCmdBuffer, "%x", &iOut);
9
  sprintf(temp, "%i", iOut);
10
  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
1
  int iOut;
2
  char temp[10];
3
  char* ptr;
4
  ptr = caCmdBuffer;
5
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
6
  while (cEnterFlag == 0) {}
7
  cEnterFlag = 0;
8
  *cpCmdBuffer = 0;
9
  vSendText(ptr);
10
  sscanf(ptr, "%x", &iOut);
11
  sprintf(temp, "%i", iOut);
12
  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?

von Karl H. (kbuchegg)


Lesenswert?

noips schrieb:

> einem Grund wurde bei der Übergabe von caCmdBuffer als Parameter nicht
> die richtige Adresse übergeben. Bei diesem Code
>
1
>   int iOut;
2
>   char temp[10];
3
>   vSendText("Ausgangswerte eingeben (hexadezimal):\r");
4
>   while (cEnterFlag == 0) {}
5
>   cEnterFlag = 0;
6
>   *cpCmdBuffer = 0;
7
>   vSendText(caCmdBuffer);           // caCmdBuffer wird falsch übergeben
8
>   sscanf(caCmdBuffer, "%x", &iOut);
9
>   sprintf(temp, "%i", iOut);
10
>   vSendText(temp);
11
>
> 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?
1
Ausgangswerte eingeben (hexadezimal):
2
123                                            <- Hier gibts du ein
3
Ausgangswerte eingeben (hexadezimal):
4
0                                              <- Das kommt dann vom sprintf

> Warum, ist mir ein Rätsel!

Da geht irgendwas grauslich schief.

Kompletten Code herzeigen

von noips (Gast)


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:
1
#include "msp430x54x.h"         // Standard Definition Control Register
2
#include "SPI_master.h"
3
#include "stdint.h"
4
#include "string.h"
5
#include "stdio.h"
6
7
#define STRINGSIZE 15           // max. size of the input variable
8
#define DATA_SIZE  32
9
10
/****************************************************************************************
11
* global variables:                                                                     *
12
****************************************************************************************/
13
14
15
16
typedef void (*FctPtr)(void);
17
18
struct sCmd {
19
  char cpCmd [10];
20
  FctPtr Function;
21
};
22
23
24
25
volatile char cEnterFlag;
26
volatile char cStopMarker;
27
volatile char cDummy;
28
char caCmdBuffer[10];
29
char* cpCmdBuffer = caCmdBuffer;
30
31
void vSendProfibus(void);
32
void vSendSpi(void);
33
void vResEeprom(void);
34
void vConfigData(void);
35
void vGetError(void);
36
void vGetEeprom(void);
37
void vGetDigInput(void);
38
void vSetDigOutput(void);
39
void vGetAnaInput(void);
40
void vSetAnaOutput(void);
41
void vUserDiag(void);
42
void vGetStatus(void);
43
44
struct sCmd sCmdList[] = {
45
  {"send_pb"  , vSendProfibus},
46
  {"send_spi" , vSendSpi},
47
  {"res_epr"  , vResEeprom},
48
  {"config"   , vConfigData},
49
  {"get_err"  , vGetError},
50
  {"get_epr"  , vGetEeprom},
51
  {"get_din"  , vGetDigInput},
52
  {"set_dout" , vSetDigOutput},
53
  {"get_ain"  , vGetAnaInput},
54
  {"set_aout" , vSetAnaOutput},
55
  {"user_diag", vUserDiag},
56
  {"get_stat" , vGetStatus}
57
};
58
59
60
/****************************************************************************************
61
* UART Settings:                                                                         *
62
****************************************************************************************/
63
// terminal connection
64
static void vInitUart1(void)
65
{
66
  P5SEL |= 0xC0;                            // P5.6,7 = USCI_A1 TXD/RXD
67
  UCA1CTL1 |= UCSWRST;                      // **Put state machine in reset**
68
  UCA1CTL1 |= UCSSEL_2;                     // SMCLK
69
  UCA1BR0 = 8;                              // 1MHz 115200 (see User's Guide)
70
  UCA1BR1 = 0;                              // 1MHz 115200
71
  UCA1MCTL |= 0xB1;            // Modulation UCBRSx=, UCBRFx=11, Oversampling Mode
72
  UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
73
  UCA1IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
74
}
75
76
/****************************************************************************************
77
* Clock Settings:                                                                       *
78
*   System-Clock ->  8 Mhz                                                              *
79
****************************************************************************************/
80
static void vInitClock(void)
81
{
82
  P7SEL |= 0x03;                // XIN, XOUT aktivieren
83
  UCSCTL3 |= SELREF_2;            // FLL Ref = REFO
84
  UCSCTL6 &= ~XT1OFF;              // Set XT1 On
85
  UCSCTL6 |= XTS;                // Select HF-mode
86
87
  do
88
  {
89
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
90
    SFRIFG1 &= ~OFIFG;            // Clear fault flags
91
  }
92
  while ((SFRIFG1 & OFIFG) != 0);        // XT1-Fault flag still set?
93
  UCSCTL4 &= ~0x44;              // XT1CLK is MCLK source and SCLK source
94
}
95
96
/****************************************************************************************
97
* IO Settings                                                                           *
98
****************************************************************************************/
99
static void vInitIO(void)
100
{
101
  P3DIR |= 0x30;              // P3.4-5 output (HOLD and DE signal)
102
  
103
//  P2DIR |= 0x00;
104
//  P2REN |= 0x04;                            // Enable P2.2 internal resistance
105
//  P2OUT |= 0x04;                            // Set P2.2 as pull-Up resistance
106
//  P2IE |= 0x04;                             // P2.2 interrupt enabled
107
//  P2IES |= 0x04;                            // P2.2 Hi/Lo edge
108
//  P2IFG &= ~0x04;                           // P2.2 IFG cleared
109
  
110
  P1DIR |= 0x0B;              // Trigger, LED, Slave Select
111
  P3SEL |= 0x0F;                            // P3.0-3 option select
112
  P5SEL |= 0x30;              // P5.4-5 option select
113
  P3SEL |= 0x80;
114
  P1OUT |= 0x08;              // Slave select off
115
}
116
117
118
119
/****************************************************************************************
120
* void Init(void): Initialisation of the controller ports                               *
121
****************************************************************************************/
122
void Init(void)
123
{
124
  vInitClock();                     // config internal clock generator
125
  vInitIO();                        // config IO's
126
  vInitUart1();                     // config USART1 as UART
127
  
128
129
  WDTCTL = WDTPW + WDTHOLD;         // Stop watchdog timer
130
  SFRIE1 |= WDTIE;
131
  _EINT();                          // Enable interrupts
132
133
}
134
135
/****************************************************************************************
136
* void vSendText(char Text[20]): send a text via RS232                                  *
137
*                                 last characters are always "\0"                       *
138
****************************************************************************************/
139
void vSendText(char Text[DATA_SIZE])
140
{  int i;
141
142
   i=0;
143
   while (Text[i]!='\0')         // transmit string via RS232
144
   {  UCA1TXBUF=Text[i++];
145
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
146
   }
147
}
148
149
void vSendProfibus() {
150
  vSendText("Send\r");
151
}
152
153
void vSendSpi(void) { vSendText("SendSpi\r");
154
}
155
void vResEeprom(void){vSendText("ResEeprom\r");
156
}
157
void vConfigData(void){vSendText("config\r");
158
}
159
void vGetError(void){vSendText("GetError\r");
160
}
161
void vGetEeprom(void){vSendText("GetEeprom\r");
162
}
163
void vGetDigInput(void){vSendText("GetDIN\r");
164
}
165
void vSetDigOutput(void){
166
167
  int iOut;
168
  char temp[10];
169
  vSendText("Ausgangswerte eingeben (hexadezimal):\r");
170
  while (cEnterFlag == 0) {}
171
  cEnterFlag = 0;
172
  *cpCmdBuffer = 0;
173
  vSendText(caCmdBuffer);
174
  vSendText("\r");
175
  sscanf(caCmdBuffer, "%x", &iOut);
176
  sprintf(temp, "%i", iOut);
177
  vSendText(temp);
178
}
179
void vGetAnaInput(void){
180
}
181
void vSetAnaOutput(void){
182
}
183
void vUserDiag(void){
184
}
185
void vGetStatus(void){
186
}
187
188
189
190
/****************************************************************************************
191
* void main(void):                                                                      *
192
****************************************************************************************/
193
void main(void)
194
{
195
  int iOut;
196
  caCmdBuffer[0] = 'a';
197
  caCmdBuffer[1] = 'b';
198
  Init();
199
  qsort(sCmdList, (sizeof(sCmdList)/sizeof(struct sCmd)), sizeof(struct sCmd), (int(*)(const void*,const void*))strcmp);
200
  vSendText(caCmdBuffer);
201
  sscanf(caCmdBuffer, "%x", &iOut);
202
  while(1){
203
204
    if(cEnterFlag == 1)
205
    {
206
      struct sCmd* spItem;
207
      cEnterFlag = 0;
208
      *cpCmdBuffer = 0;
209
      cpCmdBuffer = caCmdBuffer;
210
      spItem = (struct sCmd*)bsearch (cpCmdBuffer, sCmdList, (sizeof(sCmdList)/sizeof(struct sCmd)), sizeof(struct sCmd), (int(*)(const void*,const void*)) strcmp);
211
      if (spItem != NULL)
212
      spItem->Function();
213
      else
214
     vSendText("Ungueltige Eingabe!\r");
215
    
216
      
217
     }
218
  }
219
220
}//main
221
222
223
224
#pragma vector=USCI_A1_VECTOR
225
__interrupt void USCI_A1_ISR(void)
226
{
227
228
229
  switch(__even_in_range(UCA1IV,4))
230
  {
231
    case 0: break;
232
    case 2:                                 // UCRXIFG
233
    
234
      if(UCA1RXBUF == 13)          // if not Enter key
235
      {
236
        cEnterFlag = 1;
237
      }
238
      else
239
      {
240
        if (UCA1RXBUF != 10) {
241
          if(cpCmdBuffer < (caCmdBuffer + 10))
242
            *(cpCmdBuffer++) = UCA1RXBUF;
243
            // sonst Meldung anzeigen!!
244
        }
245
      }
246
      break;
247
    case 4:                                 // UCTXIFG
248
      break;
249
    default: break;
250
  }
251
}

von Karl H. (kbuchegg)


Lesenswert?

Dreh das hier ...
1
void vSendText(char Text[DATA_SIZE])
2
{  int i;
3
4
   i=0;
5
   while (Text[i]!='\0')         // transmit string via RS232
6
   {  UCA1TXBUF=Text[i++];
7
      while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
8
   }
9
}

... um

Du willst zuerst warten ob die USART bereit ist und erst dann das 
Zeichen los werden.
1
//
2
// transmit string via RS232
3
//
4
void vSendText( const char* Text )
5
{
6
  while ( *Text != '\0' )
7
  {
8
    // USCI_A1 TX buffer ready?
9
    while (!(UCA1IFG&UCTXIFG))
10
      ;
11
12
    UCA1TXBUF = *Text++;
13
  }
14
}

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

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

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.

von noips (Gast)


Lesenswert?

Ok! Danke für die Hinweise!

von Remote O. (remote1)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.