Forum: Mikrocontroller und Digitale Elektronik printf in Verwendung mit eclipse+gcc für msp430


von Nico (Gast)


Lesenswert?

Hallo Leute,
ich habe folgendes Problem mit Eclipse und dem gcc-Compiler. Ich möchte 
gerne printf benutzen um Text und Zahlen über die serielle Schnittstelle 
meines MSP430 ausgeben.
Leider bekomme ich nicht eindeutige Fehlermeldungen:

msp430-gcc -mmcu=msp430x149 -otest  ./test.o
/cygdrive/c/Programme/mspgcc/bin/../lib/gcc-lib/msp430/3.2.3/../../../.. 
/msp430/lib/msp2/libc.a(printf.o):  In function `printf':
/f/prog/msp430/sf/packaging/build/msp430-libc/src/stdlib/printf.c:39: 
undefined reference to `putchar'
make: *** [test] Error 1

Da ich bis jetzt immer mit einem 8051 gearbeitet habe, ist mir die 
Eclipse Umgebung neu.
Kann es sein das es printf beim gcc nicht gibt? In der stdio.h taucht 
aber ein Eintrag auf, den ich aber nicht verstehe:

int __attribute__((format (printf, 2, 3))) uprintf(int (*func)(int c
),const char *fmt, ...);
int __attribute__((format (printf, 3, 4))) snprintf (char *buf, size_t 
size, const char *fmt, ...);
int __attribute__((format (printf, 2, 3))) sprintf (char *buf, const 
char *fmt, ...);
int __attribute__((format (printf, 1, 2))) printf(const char *string, 
...);
int vuprintf(int (*func)(int c), const char *fmt0, va_list ap);
int vsnprintf(char *dest, size_t maxlen, const char *string, va_list 
ap);
int vsprintf(char *dest, const char *string, va_list ap);
int vprintf(const char *string, va_list ap);


Über jede Hilfe bin ich sehr dankbar.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Fehlermeldung, auf die es ankommt, ist die hier:

In function `printf':
undefined reference to `putchar'


Das bedeutet, daß die von printf benötigte Funktion "putchar" nicht 
existiert.

Die musst Du zur Verfügung stellen.

printf etc. ist dem Compiler sehr wohl bekannt, nur müssen die Zeichen 
ja auch irgendwohin geschickt werden - und dafür ist die 
putchar-Funktion zuständig. Schreibe eine, die die übergebenen Zeichen 
auf die serielle Schnittstelle ausgibt (die musst Du natürlich vorher 
noch irgendwie initialisieren), und die Linkerfehlermeldung verschwindet 
... und printf funktioniert.

von Nico (Gast)


Lesenswert?

Hallo Rufus,
danke für die schnelle Antwort!
Ich dachte das putchar schon implementiert ist, da in der stdio.h schon 
folgendes steht:

int puts(const char *s);
int putchar(int c);

liege ich da falsch?
Danke.

von Josephine M. (Gast)


Lesenswert?

Ja, da liegst du falsch. DU mußt putchar definieren, damit GCC weiß, 
wohin er deinen Text schicken soll.

von Nico (Gast)


Lesenswert?

Hat vielleicht schon jemand solch eine putchar-Funktion geschrieben?
Die serielle Schnittstelle habe ich bereits initialisiert und die 
funktioniert auch supi.

Danke!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Na, wenn sie "supi" funktioniert, dann hast Du schon eine 
putchar-Funktion, auch wenn die vielleicht noch anders heißt.
Den Prototypen hast Du ja schon gefunden, benenne also Deine "gib ein 
Zeichen auf der seriellen Schnittstelle aus"-Funktion entsprechend um 
... und dann funktioniert auch printf "supi".

von Nico (Gast)


Lesenswert?

Das Problem ist nur, dass ich in meiner Funktion nur einzelne Zeichen 
sende!
Was mache ich wenn ich eine float-Zahl habe und diese übertragen will?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Na, Du rufst printf auf - das macht aus Deiner Zahl einen schön 
formatierten String und ruft für jedes einzelne Zeichen daraus Deine 
"gib ein Zeichen aus"-Funktion auf.
Wo ist das Problem?

von Nico (Gast)


Lesenswert?

Okay, ich schiebe in putchar einfach ein Zeichen in den TXBUF.
Ich bekomme aber nur Bruchstücke von meiner Zeichenkette, die ich per 
printf an die serielle Schnittstelle schicke!
Woran liegt das?

von Karl heinz B. (kbucheg)


Lesenswert?

> Okay, ich schiebe in putchar einfach ein Zeichen in den TXBUF

Wenn du das wirklich machst, dann überläufst du den TXBUF.
Deine Funktion sollte vielleicht warten, bis dir die Hardware
signalisiert, dass sie bereit ist ein neues Zeichen anzunehmen.

Deine Funktion funktionierte anscheinend doch nicht so supi
(Konnte mir das nicht verkneifen :-)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Daran, daß Du in putchar nicht nach dem "Zeichen in den TX-Buf-Schieben" 
darauf wartest, daß das Zeichen auch wirklich übertragen wurde.

Dazu wirst Du ein Statusbit der UART abfragen müssen. Das könnte TXIFG 
heißen, aber so firm bin ich beim msp430 nicht, daß ich das mit 
Sicherheit wüsste.

von Nico (Gast)


Lesenswert?

Okay, ich frage jetzt das Statusbit ab welches meldet das der TX-Puffer 
leer ist.

int putchar(int c)
{
  while(!(UTXIFG0));
  TXBUF0 = c;
  return 0;
}

Klappt aber immernoch nicht! Es kommen nur Bruchstücke der Zeichenkette 
im Hyperterm an.
Hab ich noch 'nen Denkfehler?

von Christian R. (supachris)


Lesenswert?

Wieso liest du nicht mal die C-Demos von TI? Da steht doch ganz klar 
drin, wie man die UART im Polling betreibt:
Müsste bei dir dann heißen:

int putchar(int c)
{
  while (!(IFG1 & UTXIFG0));                // USART0 TX buffer ready?
  TXBUF0 = (unsigned char)c;
  return 0;
}

Polling ist allerdings ziemlich schlechte Programmierweise, lieber einen 
Buffer anlegen, das erste Zeichen direkt schicken und alle weiteren per 
TX-Interrupt abholen lassen.

Du kommst aus der PC-Programmier-Ecke, oder? Bei µC linkt man 
normalerweise nicht einfach mal so nen riesen Brocken von lib wie die 
stdio dazu. Da ist ja gleich massenhaft an Flash und RAM weg....das kann 
man selber auch viel kürzer schreiben, wenn man nicht alle Funktionen 
braucht.

von Nico (Gast)


Lesenswert?

Hallo Christian!
Ich komme eher aus der Elektronik-Ecke und brauche das printf "nur" zum 
debuggen.
Leider funktioniert die Funktion so wie Du sie gepostet hast auch nicht!
Wie würde ich das denn mit Interrupts lösen?

von Christian R. (supachris)


Lesenswert?

Wenn das nicht klappt, muss es am printf() liegen. Diese Funktion 
schickt einfach jedes Zeichen, wenn der Buffer frei ist. Da geht nix 
verloren.

Mit INT? Naja, du musst erst alle Zeichen in einen Buffer einsortieren, 
dann das erste schicken, und bei jedem TX-Interrupt ein weiteres, bis 
alle ist.

Das ganze kann man natürlich auch mit einem Ringpuffer machen, aber das 
is bissl aufwendiger.

von Nico (Gast)


Lesenswert?

Das komische ist, wenn ich die selbst geschriebene 
putchar-Funktion(siehe 3 Threads weiter oben) allein teste, also zum 
Beispiel: putchar('X'); dann kommt das X nicht auf dem Hyperterminal an!
Das ist doch sehr komisch oder!?
Hat da noch jemand eine Idee dazu?

von Christian R. (supachris)


Lesenswert?

Dann ist die Baudrate falsch konfiguriert oder das Kabel kaputt oder 
sowas.
Hast du einen Quarz als Taktquelle dran? Mit dem RC-Oszillator klappt 
die UART erst nach viel rumspielen mit dem Baudratenteiler. Und dann ist 
das auch temperatur-abhängig

von Nico (Gast)


Lesenswert?

Ja, ich benutze einen 8MHz Quarz und das Kabel ist in Ordnung!
Wenn ich mir nämlich ein char-array fest definiere und dieses per 
Tastatureingabe und mit Hilfe von Interrupts ausgebe funktioniert es 
einwandfrei.
Ich habe als Referenz das "echo"-Programm von www.mathar.com genommen 
und auf eclipse+gcc angepasst.
Und wie gesagt, das läuft super mit 115200 BAUD!

Ich bin am verzweifeln! :-(

von Nico (Gast)


Lesenswert?

Also irgendwie funktioniert die Abfrage: while(!(IFG1 & UTXIFG0)); 
nicht!
Die while-Schleife wird nie verlassen. Warum nicht?
Kennt sich nicht irgendjemand damit aus?
Das ist doch eine ganz normale Schleife mit Abfrage!
Der Compiler macht auch keine Fehler.
Gibts da bei eclipse irgendwelche Besonderheiten?

Hiermal meine Initialisierung der UART:

void initUART(void)
{
  P3SEL = 0x30;           // P3.4 und P3.5 als USART0 TXD/RXD
  ME1 |= UTXE0 + URXE0;   // TX- und RX-modul erst mal anschalten
  UCTL0 |= CHAR;          // 8 data bits, 1 stop bit, no parity
  UTCTL0 |= SSEL0;        // ACLK als UCLK festlegen
   UBR00 = 0x45;           // 115200 baud aus 8MHz erzeugen
    UBR10 = 0x00;
    UMCTL0 = 0xAA;
    UCTL0 &= ~SWRST;        // USART freigeben
    IE1 |= URXIE0 +UTXIE0;  // TX- und RX-interrupts anschalten
    IFG1 &= ~UTXIFG0;       // interrupt-flag loeschen
}

von Martin Schneider (Gast)


Lesenswert?

Hmmm, zwei Anmerkungen:

ACLK als Taktquelle stimmt wirklich?

Wenn der TX Interrupt aktiv ist, wird das UTXIFG0-Flag automatisch (Ack)
beim Einsprung in die ISR gelöscht - dein Polling kann niemals ein
gesetztes Flag sehen...
Schau mal ins User Manual (SLAU49E.pdf), Seite 277.

Ahoi, Martin

von Christian R. (supachris)


Lesenswert?

Mit UTCTL0 |= SSEL1; hast du deinen 8Mhz Clock als Takt. Dann klappts 
auch mit den 115,2KBaud. Ist im C-Demo von TI falsch.

Und wie Martin schon sagte, Polling und Interrupt zusammen wird nicht 
klappen. Nur, wenn du das erste Zeichen sendest, nach dem ersten, wird 
die ISR angesprungen und das Flag gelöscht.

von Nico (Gast)


Lesenswert?

Ja, ACLK als Taktquelle stimmt!

Kannst Du mir sagen wie ich das lösen kann?

von Christian R. (supachris)


Lesenswert?

Aus dem ACLK bekommst du doch aber keine 115.200 raus. Da musst du schon 
vorher den XT2-Oszillator zuschalten und den SMCLK auf die 8MHz vom 
Quarz legen. Oder den DCO mit dem ACLK füttern, eine passende Frequenz 
einstellen und daraus die Baudrate generieren.

Schau dir mal das C-Demo fet140_uart01_0115k_2.c an.

von Nico (Gast)


Lesenswert?

Mein ACLK läuft auf 8MHz von XT1, damit speise ich den 
Baudratengenerator der mit 115200 BAUD läuft! Warum sollte das nicht 
gehen???

Kann mir jemand sagen, wie ich ohne Interrupts zu benutzen, abfragen 
kann ob der Sende-Puffer schon frei ist?

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.