Forum: Compiler & IDEs Warning bei fdevopen


von Axel K. (axeman)


Lesenswert?

Hallo,
WinAVR - 20050214
gcc - 3.4.5
avrlibc - 1.4.2

stdio.h ist included

Beispiel aus AVR-gcc-Tutorial zum Senden von Zeichenketten
Ich bekommen beim kompilieren das Warning:

main.c:54: warning: passing arg 1 of `fdevopen' from incompatible
pointer type
die Zeile lautet
fdevopen(uart_putc,NULL);

Wenn ich - wie im Tutorial-Beispiel - eintrage:
fdevopen(uart_putc,NULL,0);
erhalte ich:
main.c:54: warning: passing arg 1 of `fdevopen' from incompatible
pointer type
main.c:54: error: too many arguments to function `fdevopen'

Was mache ich falsch bzw. wie mache ich es richtig?
Die erste Variante funktioniert im übrigen...

Gruß, Axel

von Karl H. (kbuchegg)


Lesenswert?

In die Doku schauen.

Der Prototype von fdevopen sieht so aus:

FILE* fdevopen  (  int(*  put)(char),
                   int(*  get)(void),
                   int opts   __attribute__((unused))
                )

Argument 1 sollte also sein:
Ein Pointer auf eine Funktion. Diese Funktion nimmt einen char
und liefert einen int zurueck.

Wie sieht Deine Funktion (aus dem Tutorial) aus:

int uart_putc(unsigned char c)
{
    while(!(USR & (1 << UDRE))); /* warte, bis UDR bereit */
    UDR = c;                     /* sende Zeichen */
    return 0;
}

Nun. Die Funktion liefert einen int wie gefordert, nimmt
aber einen unsigned char und keinen char. Damit hat diese
Funktion eine andere Signatur als fdevopen sie haben moechte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz, du hast den Prototypen von avr-libc <= 1.2.x gezeigt, Axel
benutzt offensichtlich eine 1.4.x.

Axel, das wichtigste hast du vergessen uns zu sagen: den Prototypen
(oder die Implementierung) deines uart_putc().  Ich vermute mal, du
hast den zweiten Parameter (FILE *) vergessen, der in der avr-libc 1.4
neu hinzugekommen ist.  Guck mal ins stdiodemo.  Der Sinn dieses
Parameters ist, dass man mittels fdev_set_udata() an den Stream user
data anhängen kann und innerhalb von uart_putc() mittels
fdev_get_udata() über diese verfügen kann.  Damit kann man einerseits
Dinge wie einen Gerätestatus über mehrere Aufrufe pro Stream
abspeichern, andererseits kann man ein und dieselbe backend-Funktion
(also uart_putc() in deinem Falle) für mehr als ein Gerät (z. B. zwei
verschiedene UARTs) benutzen, indem man sie anhand der user data
unterscheidbar macht.

Wenn du diese Funktionalität nicht brauchst, deklarierst du den
zweiten Parameter einfach nur als Dummy, ohne ihn zu benutzen.

von Karl H. (kbuchegg)


Lesenswert?

> Karl Heinz, du hast den Prototypen von avr-libc <= 1.2.x gezeigt,
> Axel benutzt offensichtlich eine 1.4.x.

Danke fuer den Hinweis.

von Axel K. (axeman)


Lesenswert?

Hm, ich bin wohl noch viel zu weit weg mit meinen Kenntnissen, um das so
richtig zu verstehen...
so sieht das bei mir aus (das sollte genau dem Beispiel aus dem
gcc-Tutorial entsprechen), damit erfolgt auch eine Ausgabe auf meinem
ATMega32, nur gibt es halt das entsprechende oben beschrieben
fdevopen-Warning:

void USART_Init( void )
{
UCSRB |= (1<<TXEN);//UART TX einschalten
UCSRC |= (1<<URSEL)|(3<<UCSZ0);//Asynchron 8N1

UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_CPU)>>8);
UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_CPU);
}


int uart_putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
    UDR = c;                      /* sende Zeichen */
    return 0;
}

void uart_puts (char *s)
{
    while (*s)
    {   /* so lange *s != '\0' also ungleich dem
"String-Endezeichen" */
        uart_putc(*s);
        s++;
    }
}

int main(void)
{
  USART_Init();

  fdevopen(uart_putc,NULL);
  printf("%s: stdout erfolgreich auf UART umgeleitet\n",
_FUNCTION_);

  sei();

  return 0;
}

Was muss ich in die Klammern von fdevopen schreiben, damit kein warning
mehr kommt?

Gruß, Axel

von Karl H. (kbuchegg)


Lesenswert?

OK. Du musst lernen wie man sowas loest.
Der Compiler meckert, weil der Prototyp von fdevopen
etwas andere Parameter vorschreibt also Du tatsaechlich
verwendet hast.
Das ist noch kein Beinbruch und das kriegt man raus indem
man ganz einfach mal einen Blick auf den Prototypen wirft.
Den wiederum sieht man entweder in der Doku, oder aber
was meist besser ist direkt im Code. Irgendwiw muss
der Compiler ja den Prototypen sehen und da Du ihn nicht
selbst geschrieben hast, kann er nur ueber ein #include
hineingekommen sein.
Also geht man mal alle #include durch und ueberlegt in welchen
dieser Files der Progtrammierer der Library sinnvollerweise
den Prototyp hineingegeben haben wird. Im vorliegenden Fall
ist 'stdio.h' ein heisser Kandidat.
Ich hab bei mir am Rechner eine etwas aeltere Version davon
(die findet sich bei mir unter c:\winavr\avr\includes) und bei
mir lautet der Prototyp
extern FILE *fdevopen(int (*__put)(char), int (*__get)(void),
                      int __opts);

Aber wie gesagt, ich hab eine etwas ältere Version. Deine
sieht anders aus, wie Jörg weiter oben schon ausgefuehrt hat.

Aus den Ausfuehrungen von Jörg schliesse ich folgendes:
* fdevopen uebergibt man ja 2 Funktionspointer
* diese Funktionspointer sind Zeiger auf Funktionen, die
  jeweils aufgerufen werden, wenn ein Zeichen entweder ausgegeben
  oder eingelesen werden soll.
* damit das aber funktioniert, muessen Deine Funktionen so aussehen
  wie fdevopen sich das so vorstellt. Insbesondere muessen
  die Argumente in Anzahl und Type uebereinstimmen.
* der erste Funktionszeiger im Aufruf von fdevopen ist ein Zeiger
  auf die Ausgabefunktion. In meiner Version ist das eine Funktion
  die einen char annimmt und einen int zurueckliefert. Jetzt hat Jörg
  aber gesagt, dass er diese Funktion mit einem zusaetzlichen
  Parameter, einem File-Pointer ausgestattet hat.
  d.h. Der Protoyp von fdevopen wird wahrscheinlich so aussehen
 (aber pruef das bitte nach!)

  extern FILE *fdevopen(int (*__put)(char, FILE*),
                        int (*__get)(FILE*),
                        int __opts);

* Daraus folgt: damit Deine uart_putc Funktion dazu kompatibel
  ist, muss sie daher der Signatur
              int (*put)(char, FILE*)
  genuegen.

  int uart_putc( unsigned char c, FILE* dummy )
  {
     ....
  }

  Dieser zusätzliche Parameter 'dummy' ist wohl der Stream, auf
  dem die Ausgabe tatsaechlich erfolgen soll. Da Du weist, dass
  die Ausgabe auf den UART gehen soll, ist sein Inhalt fuer Dich
  wahrscheinlich uninteressant und daher hab ich das auch 'dummy'
  genannt. Das muss aber nicht so sein. Warum soll eine einzige
  uart_putc Funktion nicht die Ausgabe auf verschiedene Geräte
  regeln können. Dann braucht man den zusätzlichen Parameter
  um die unterschiedlichen Ausgabeanfoerderungen zum richtigen
  Gerät zuordnen zu können.

Disclaimer: Wie bereits ausgefuehrt verwende ich noch eine ältere
Version der Library. Alles hier geschriebene hab ich aus Jörgs
Info von oben abgeleitet bzw. 'geraten'. Ich denke aber nicht,
daß ich alzuweit daneben liege. Alles beginnt damit, dass Du mal
den Prototypen von fdevopen() in stdio.h ueberpreufts, wie der
tatsächlich aussieht.

von Axel K. (axeman)


Lesenswert?

Uff, danke erstmal.
ja, ich muss (und will) lernen, wie man sowas löst... Der Berg ist aber
noch ganz schön hoch.
Deine Mutmaßungen bzgl. fdevopen sind alle richtig; eigentlich hatte
aber schon Dein erster Hinweis gereicht -> aus unsigned char musste
char werden. -> Jedoch ist dann auch das Beispiel im avr-gcc-Tutorial
falsch, dort steht 'int uart_putc(unsigned char c)...

Jetzt läuft es zumindest 'warning'-frei durch; auf zum nächsten
Schritt, die nächste Frage kommt bald.

Gruß, Axel

von (geloescht) (Gast)


Lesenswert?

(Dieser Beitrag wurde geloescht)

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.