Forum: Compiler & IDEs Verwendung von stdio.h und printf()?


von Ralf Vetterer (Gast)


Lesenswert?

Hallo zusammen,

ich möchte von PIC Controllern zum MSP430 umsteigen und habe mir die
notwendigen Tools (FET149, mspgcc,...) beschafft. Nach anfänglichen
Schwierigkeiten wurde das LED Beispiel übersetzt und ich konnte das
Ganze mit Insight auch laden und debuggen. Nun folgte der nächste
Schritt, das berühmte 'Hello World' auszugeben. Mit den IAR Tools
kein Problem, mit dem MSPGCC jedoch scheinbar eine sehr große Hürde?!?


Was ist daran falsch???

#include <stdio.h>

int main(void)
{
  // ..do anything..

  printf("Hello World\n\r");

  return 0;
}

1. Warning des Compilers "implicit declaration of function
'printf'"
2. Fehlermeldung des Linkers "undefined reference to 'printf'"

Es sieht so aus als ob die Include-Datei 'stdio.h' nicht erkannt
wird.

Hat jemand einen Tip, auch nach eingehender Internet-Recherche habe ich
nichts über die Verwendung 'printf' beim MSP430 mit dem MSPGCC
gefunden? Muss man sich diese Funktion selber schreiben?

Gruß

Ralf

von Joerg Wunsch (Gast)


Lesenswert?

Deine Schlußfolgerung ist geringfügig falsch.

Wenn <stdio.h> nicht erkannt werden würde, hättest Du eine
entsprechende Compilermeldung erhalten.  Sie existiert also
ganz offensichtlich.

Wenn er sich über eine implicit declaration beschwert, heißt das
aber, daß <stdio.h> kein printf() enthält.  Nun, durch nachsehen
in der Datei stdio.h kannst Du das sicher auch verifizieren.

Wenn Dir der Linker dann sagt, daß es eine undefined reference to
printf gibt, dann gibt es offensichtlich nicht nur in den header
files kein print(), sondern auch in der Bibliothek.

Fazit: printf() ist einfach nicht implementiert.

Das ``Helloworld''-Examples eines Microcontrollers ist übrigens
nicht die Ausgabe von "Hello, world!\n" auf einer UART, sondern
eher die blinkende LED oder sowas.  Erstens ist printf() eine recht
komplexe Funktion (die folglich auch sehr viel Code mit sich in
den ROM schleppt), zweitens braucht man noch die Pegelwandler für
die RS232.  Drittens hat ein Controller im Gegensatz zu einem
hosted environment eben von Haus aus erstmal gar nichts wie stdin
und stdout zur Verfügung.

Für die avr-libc haben wir vor einiger Zeit eine komplette printf()
Familie implementiert, die müßte eigentlich portabel sein auf
andere Controller gleichen Kalibers, sofern Deine Bibliothek ein
malloc() besitzt.  Das könntest Du Dir ja als Ausgangsbasis ja mal
ansehen.  Aber nicht erschrecken, wie ich bereits schrob: printf()
ist komplex.

von Ralf Vetterer (Gast)


Lesenswert?

Hallo Joerg,

danke für Deine rasche Antwort. Ich war schon am Verzweifeln. Die LED
blinkt ja schon mal. Und die RS232-Hardware kommuniziert mit dem PC
durch Beschreiben des TX-Buffers (getestet mit den IAR-Tools).

Beispiel-Routine:

void SendHello(void)
{
      TXBUF0='H';                // transmit "H"
      while ((UTCTL0&0x01)==0);
      TXBUF0='e';                // transmit "e"
      while ((UTCTL0&0x01)==0);
      TXBUF0='l';                // transmit "l"
      while ((UTCTL0&0x01)==0);
      TXBUF0='l';                // transmit "l"
      while ((UTCTL0&0x01)==0);
      TXBUF0='o';                // transmit "o"
      while ((UTCTL0&0x01)==0);
      TXBUF0=13;                    // send carriage return
      while ((UTCTL0&0x01)==0);
}

Da ich aber später Messwerte über die serielle Schnittstelle übertragen
will, sowie 'printf' auch ausgiebig zu Debug-Zwecken nutzte, vermisse
ich diese Funktion sehr. Mir ist bewußt, dass 'printf' sehr komplex
ist. Mal sehen ob ich mir eine Portierung antue.  Fürs erste muss eine
einfache Ausgabe reichen. Eventuell hat hier jemand eine Idee wie man
elegant Text über die serielle Schnittstelle sendet ohne 'printf' zu
benützen.

Gruß

Ralf

von Matthias (Gast)


Lesenswert?

Hi

C-String ausgeben:

void rs232_print(unsigned char *s)
{
    while(*s)
    {
        rs232_putc(*s);
        s++;
    }
}

Matthias

von Joerg Wunsch (Gast)


Lesenswert?

Nicht vergessen, aus \n noch ein \r\n zu machen.  Ich empfehle die
folgende uart_putchar() Funktion als minimale Funktion für meine
stdio-Implementierung in der Doku der avr-libc:

int
uart_putchar(char c)
{

  if (c == '\n')
    uart_putchar('\r');
  loop_until_bit_is_set(UCSRA, UDRE);
  UDR = c;
  return 0;
}

Diese Funktion ist dort als Parameter an fdevopen() zu übergeben.

Ralf, wenn Du schon ein malloc() hast, dann ist die Portierung
dieser printf-Familie wahrscheinlich nur wenig mehr als das
Zimmern des entsprechenden Makefiles.  Habe gerade mal ein
fgrep '#include <avr/' dort gemacht, das sollte die benutzten
AVR-spezifischen Features einigermaßen zutage fördern.  Da ist
nur <avr/pgmspace.h> benutzt, der Zugriff auf die ROM-Konstanten
(hier ROM-Strings) im AVR.  Das mußt Du Dir für den MSP430 sicher
wirklich ändern, manches wird vielleicht auch einfacher aufgrund
dessen von-Neumann-Architektur, mit der der gcc ja besser klarkommt.
Einige wenige Funktionen sind auch als Assembler-Stubs geschrieben:
getc() und putc(), da steht nur ein Sprung nach fgetc() und fputc()
drin.  Sicher auch einfach zu portieren.

von Matthias (Gast)


Lesenswert?

Hi

warum sollte man aus '\n' "\r\n" machen? Ein einzelnes '\n'
kann auch durchaus mal beabsichtig sein um eine entsprechende Ausgabe
zu erreichen. Wenn man diese Art des Zeilenumbruchs will sollte man das
im String auch explizit angeben.

Just my 2 Cent

Matthias

von Joerg Wunsch (Gast)


Lesenswert?

Unix kann seit 25 Jahren damit leben, daß das so gemacht wird. ;-)
Ich habe noch keinen einzigen ernsthaften Anwendungsfall gesehen,
wo man wirklich einen einzelnen Zeilenvorschub benötigen würde.  Den
umgekehrten Fall, ein einzelnes \r, braucht man gelegentlich mal.

Klar, man kann das auch im String angeben, aber erstens ist es
C-Konvention, nur \n zu nehmen, zweitens spart es paar Bytes.

von Matthias (Gast)


Lesenswert?

Hi

Geschmackssache. Ich geb es eben lieber expliziet an.

Matthias

von Ralf Vetterer (Gast)


Lesenswert?

Hallo,

ich habe die Funktion von Mathias implementiert und es funktioniert.
Für die Datenkonvertierung sowie der Ausgabe der Messwerte muss ich mir
noch ein paar Gedanken machen.

@ Matthias
Vielen Dank für die Routine, ich wäre wahrscheinlich zuerst mit der
Kirche ums Dorf gegangen.

@ Joerg
Da ich noch einiges an anderer Hardware austesten will und mit der
Entwicklungsumgebung (make file, macros,...) nicht so vertraut bin,
werde ich mit der Portierung von 'printf' noch etwas warten.
Ansonsten vielen Dank für Deinen Beitrag bei meinem Problem.

An alle anderen, die in der gleichen Lage sind oder glauben zu sein,
hier mein Beitrag zur seriellen Ausgabe von Text:

---------------------------------------------------------------
void rs232_print(unsigned char *s); // string is sent via RS232

int main(void)
{   unsigned int i,k;
    unsigned char *my_string="Hello World\r\n";

    Init_OSC();                     // Initialization of oscillator
    Init_UART();                    // Initialization of UART
    P1DIR = BIT0;                   // P1.0 output

    while (1)
    {
       P1OUT |= BIT0;               // Set P1.0

       rs232_print(&my_string[0]);  // send string over rs232
       for (i=0;i<=20000;i++)       // wait loop
           k++;
       P1OUT &= ~BIT0;              // Clear P1.0
       for (i=0;i<=20000;i++);      // wait loop
           k--;
    }
 return 0;
}

void rs232_print(unsigned char *s)
{
    while(*s)
    {
        TXBUF0 = *s;
        while ((UTCTL0&0x01)==0);
        s++;
    }
}
--------------------------------------------------------


Gruß

Ralf

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Spar dir die Portierung:

http://mspgcc.sourceforge.net/manual/x1108.html:

The function

    uprintf(void (*func)(char c), const char *fmt,...);

is similar to "sprintf()", except that caller provides an output
function for printing, rather than an output buffer. This function must
accept a single "char" parameter, and return "void" (for example to
send a character to a UART). The user function is responsible for
mutexes, slow interfaces, etc. "uprintf()" will not return until all
characters have been printed.

von Joerg Wunsch (Gast)


Lesenswert?

Macht die eigentlich auch floating point?  Dann würde mich mal
interessieren, wie groß das Resultat ist.  Mir war es auf dem AVR
zu groß, als daß ich FP immer dabei haben möchte, so daß es zwei
Alternativen gibt: mit und ohne FP.  (Noch eine dritte, die fast
nichts mehr kann und noch ein wenig kleiner ist, wenn man mal arg
knapp dran ist mit dem Platz und es für ein paar Debug-Ausgaben
braucht.)

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Float ist nicht drin, die Funktion ist ca. 1450 Byte groß (wenn man sie
mit AVR-GCC kompiliert sind es sogar 30 Bytes mehr).

http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/mspgcc/msp430-libc/src/stdlib/uprintf.c?rev=1.5&content-type=text/vnd.viewcvs-markup

Das Schöne am MSP430 ist, dass man sich die Verrenkungen beim
ROM-Zugriff sparen kann.

von Joerg Wunsch (Gast)


Lesenswert?

Ah, diese Version hat er als Basis genommen.  Alexander Popov hatte
dann eine neuere Variante daraus gezimmert, die für den AVR besser
optimiert war.  Das war dann meine Basis für das jetztige vfprintf().

von Ralf Vetterer (Gast)


Lesenswert?

Hallo Andreas,

danke für den Tipp! Nach einigen Versuchen läuft nun auch die
Textausgabe mit 'uprintf'. :-)

Gruß

Ralf

von Dominik (Gast)


Lesenswert?

Hallo ich hab ein problem ich möchte eine float zahl über die serielle
schnittstelle senden, nun bekomme ich immer den fehler

float NOFLOAT option
kann man mit dem befehl

printf("Hallo" , float_zahl) das nicht realisieren?

von Jörg Wunsch (Gast)


Lesenswert?

Erstens hast du einen uralten Thread gekapert und zweitens redest du
mit an Sicherheit grenzender Wahrscheinlichkeit nicht über
{AVR,ARM,MSP430}-GCC.

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.