Forum: Compiler & IDEs hallo welt


von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich wollte mal mit WINAVR "Hallo Welt" testen.
Trotz keiner Fehler oder Warnungen passiert nichts.


Dann habe ich putchar() und puts() selber geschrieben und alles läuft
super.

Ich hab mal auch ins Listing geschaut, also mit den
Bibliotheksfunktione explodiert der Code geradezu von 160Byte auf
916Byte. Da sind dann auch völlig nutzlose Funktionen includiert wie
malloc(), free() usw.

Wo liegt der Fehler ?

Anbei der Code, mit "#define SELBSTGEMACHT" gehts ja.

Und auf dem 8051 gehts auch mit den Bibliotheksfunktionen.


Peter

von Joerg Wunsch (Gast)


Lesenswert?

Erstens sind malloc() und free() sicherlich nicht nutzlos. ;-)

Zweitens ist das ungefähr so: ,,Ich habe hier ein Gerät gefunden,
da waren ein roter und ein schwarzer Knopf dran.  Ich habe auf
beide mal draufgedrückt, aber es passierte nichts.''  Das Handbuch,
das die Funktion der Knöpfe beschrieben hat, lag selbstredend
daneben...

Sprich: typischer Fall von RTFM.  Oder wie oder was soll die stdio
Bibliothek Deiner Meinung nach erraten, daß die Zeichen nun über
Deinen angeschlossenen Lochbandstanzer auszugeben sind?  Ach, Du
hast eine andere Hardware, über die Du das ausgeben willst? :-))

Vielleicht merkst Du aber mit dem Vergleich, warum ein wenig Setup
notwendig ist, bevor stdio auch was Sinnvolles tun kann...  Es
heißt eben standard IO facilities, und nicht UART0@AT90S4333
output facilities, oder LDC@PORTC output facilities oder sowas.

Daß printf() & Co Kollosse sind (naturgemäß) steht übrigens ebenfalls
in besagtem Manual.  printf("Hello world!\n"); ist halt trotz
seiner
relativen Beliebtheit alles andere als ein Minimalprogramm, und das
merkt man in einer Controller-Umgebung sehr viel deutlicher als auf
einem Allerwelts-Computer mit ...zig Megabyte RAM.  Das Hello World
der Controllerwelt ist eher die blinkende (oder an- und abschwellende
wie im Beispielcode der avr-libc) LED: wenig Code, wenig externe
Hardware (letzteres praktisch gar keine, da man außer beim ATmega128
sogar den Vorwiderstand der LED sparen kann).  Eine RS-232 dagegen
braucht sehr viel externe Hardware (RS-232 Treiber) sowie ein paar
mehr Gedanken bezüglich Takt-Setup, damit die Baudrate auch stimmt,
außerdem einiges mehr an Ansteuer-Code als die Blink-LED.  Ein LCD
braucht zwar auch nicht viel externe Hardware, aber dafür noch ein
bißchen mehr Code.  Daher sind eben beides eher nicht ganz die
klassischen Minimalprojekte.

von Peter D. (peda)


Lesenswert?

,,Ich habe hier ein Gerät gefunden,
da waren ein roter und ein schwarzer Knopf dran.  Ich habe auf
beide mal draufgedrückt, aber es passierte nichts.''

Ganz so is es ja nun nicht, sondern:

"Ich habe ein Radio (Keil C51), das geht an, wenn ich auf "An"
drücke, das neue Radio (WINAVR) aber nicht."


Ich wollte ja auch nicht, daß mir jemand das komplette Manual erklärt.
Aber da ja "Hallo Welt" das Standardprogramm ist, dachte ich, daß es
eben nur ne Kleinigkeit wäre, worans hakt, die ein GCC-Profi in einer
Zeile erklären kann.


Wenn Du mal reingeschaut hättest, dann hättest Du gemerkt, daß ich
printf() garnicht verwendet habe, sondern puts() und das sollte doch
kaum Code fressen (tuts beim Keil C51 ja auch nicht).


Mit malloc() ist nutzlos meinte ich das im Zusammenhang mit puts() und
putchar().


Das beim GCC ne Menge Sachen komplizierter gemacht sind als beim Keil
ist mir auch schon aufgefallen. Da ist eben die UART die
Standardausgabe und falls es anders gewünscht ist, muß man nur sein
eigenes putchar() einbinden.



Peter

von Peter D. (peda)


Lesenswert?

Wenn Du das hier meinst:

"5.12 Standard IO facilities"

Also jetzt bin ich noch ratloser.


Deshalb nur noch eine kurze Frage (Ja oder Nein reicht):

Wenn ich mein eigenes putchar() und getchar() definiere, kann ich dann
printf(), puts() und scanf() verwenden ?


Zumindest sprintf() und sscanf() müßten doch immer gehen ?


Peter

von Joerg Wunsch (Gast)


Angehängte Dateien:

Lesenswert?

Jein (sofern Du Dich an das API hälst -- beachte bitte die
Rückkehrwerte).  Ja insofern, daß Deine eigenen Funktionen dafür
ungefähr die richtigen sind.  Nein in der Beziehung, daß putchar()
und getchar() vordefiniert sind und der Standard vorschreibt, daß
Du keine eigenen Funktionen mit diesem Namen haben darfst.  (Der
Compiler ersetzt intern per Standard gleichwertige Funktionen
teilweise durch Alternativen, und der Standard erlaubt sowas
ausdrücklich.)  Benenne sie my_putchar() und my_getchar(), dann
ist es OK.

Ja.  Letztere brauchen übrigens auch kein malloc(), mit Ausnahme
der floating point Varianten.

Für die Eingaben mußt Du Dir überlegen, ob Du die Zeile editieren
können willst oder nicht.  Je nachdem, ist my_getchar() u. U. gar
nicht mehr so einfach...  Ich hänge mal einen Prototypen dran von
etwas, was mal ein etwas besseres stdio-Beispiel werden sollte,
den Weg in die offizielle Beispielsammlung aber noch nicht
gefunden hat.  Wie gesagt, ist unvollendet, aber die physischen
EA-Routinen sind soweit Ok und getestet.

Der Vergleich mit dem C51 hinkt schon deshalb, weil der AVR u. U.
ja zwei USARTs haben kann.  Welche soll der Default sein?  UART0?
Die kollidiert aber beim ATmega128 mit dem ISP-Interface, so daß
viele wohl eher UART1 bevorzugen würden...  Außerdem wollte ich
das stdio wirklich generisch schreiben und nicht auf eine UART
festbrennen -- was hätte sonst der nächste Anwender mit seiner
LCD gemacht?  Und der übernächste mit einem Parallel-Interface
à la Centronics?  (*) Da aber generisch, ist halt auch ein malloc()
vonnöten, sorry.  (Ich hatte schon an eine Abrüstvariante ohne
malloc() gedacht, die dann nur stdin/stdout/stderr und nicht
mehr kann, könnte vielen eventuell ja genügen.)

(*) Natürlich auch der Fall, daß stdout ein LCD ist und stderr als
Debug-Schnittstelle eine UART...  Wie macht man sowas beim Keil
C51?

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

@Joerg,

vielen Dank für die Antwort.
Sieht ja doch wesentlich komplizierter aus, als ich dachte.
Da wird wohl sprintf() + my_putchar() die praktikablere Lösung für mich
sein. Anbei die entsprechend modifizierte "hallo.c".


"Wie macht man sowas beim Keil C51?"

Beim Keil machen die das mit einer einzigen Variablen, d.h. vor
printf() muß man diese Variable auf den richtigen Port setzen:

http://www.keil.com/support/docs/1324.htm


Peter

von Joerg Wunsch (Gast)


Lesenswert?

Na Moment, kompliziert ist doch eigentlich nur die Variante für
die Eingabe, da sie auch Zeilenpufferung und primitive Editier-
funktionen bietet.  Die Variante für die Ausgabe besteht aus:

int
uart_putchar(char c)
{

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

Was ist daran kompliziert?  (Die Umwandlung von \n in \r\n kannst
Du
auch weglassen, wenn Du stattdessen in allen Strings, in denen Du
das brauchst, \r\n explizit schreibst.  Ich habe die Unix-Option
bevorzugt, bei der diese Umwandlung erst vom Gerätetreiber vorge-
nommen wird.)

Wenn Du bei der Eingabe mit Einzelzeicheneingabe leben kannst, sieht
deren Funktion nicht viel schlimmer aus.

Alles was Du sonst noch brauchst ist halt der Aufruf für fdevopen()
(das ich als Variante von fopen() für physische Geräte sehe, aber
mit einer anderen Syntax/Semantik, da ich nicht erst noch anfangen
wollte, ,,Datei''namen zu verwalten für die Geräte.)

Die Variante vom Keil ist erstens Q&D, zweitens was machst Du, wenn
Du Dein printf() auf ein LCD oder eine 5x7-LED-Matrix lenken willst?
Sorry, sowas wollte ich nicht schreiben.  Daß malloc() manch einem
ein bißchen zu viel ist, vor allem auf kleinen Controllern, sehe
ich ja ein, und wie schon geschrieben, werde ich nach einem
Workaround suchen, aber Q&D Code mag ich nicht zum Bestandteil einer
offiziellen Library machen, dafür müßte ich mich hinterher schämen.

(Q&D? -- quick and dirty)

von Peter D. (peda)


Lesenswert?

Ich hab mir das Listing mal etwas näher angesehen und da ne Menge ICALLs
gefunden.

Ich vermute mal, daß die ganzen I/O-Funktionen erst zur Laufzeit
aufgelöst werden und deshalb auch das malloc() benötigt wird.

Ich befürchte bloß, daß mich das einen riesen Flash, CPU-Zeit und
SRAM-Aufwand kosten wird. Und da ich nicht immer Mega-Boliden, sondern
auch Tiny26 / 2313 verwende, dürfte denen dann schnell mal die Puste
ausgehen.

Deshalb will ich, daß sämtliche Referenzen, die schon zur Compilezeit
bekannt sind, dann gefälligst auch fix und fertig aufgelöst werden.
Es ist ja nicht wie bei PCs, wo man verschiedene Programme laden und
ausführen können muß.
D.h. sämtliche Routinen, die laufen müssen und deren RAM-Bedarf sind ja
bereits zur Compilezeit bekannt und entweder sie passen rein oder
nicht.
Es ist mir auch tausendmal lieber, der Linker meckert bereits, als daß
dann später beim Kunden ein malloc() gegen den Baum fährt und die ganze
Anwendung abschmiert.


Nichts gegen Deine Arbeit, aber für die meisten meiner Anwendungen ist
das einfach mehrere Nummern zu groß gedacht.


Ich hab auch gesehen, das sprintf() fputc() benutzt, was wiederum
ICALLs  ausführt.
Das verstehe ich nicht, sprintf() schreibt doch immer in den SRAM,
warum sind dann für jede Byteausgabe umständliche ICALLs notwendig ?


Peter

von Joerg Wunsch (Gast)


Lesenswert?

> Ich vermute mal, daß die ganzen I/O-Funktionen erst zur Laufzeit
> aufgelöst werden ...

Ja.

> und deshalb auch das malloc() benötigt wird.

Nein.  Vielleicht solltest Du ja statt Kaffeesatzlesen aus dem
Listing lieber das Lesen des Sourcecodes vorziehen? ;-)

Das malloc wird benötigt, damit man keine Beschränkung in der
Anzahl offener Filedescriptoren (mit Ausnahme des vorhandenen RAM
natürlich) in der Bibliothek implizieren muß.  Daher ja auch mein
Workaround für die ,,Sparvariante'': Beschränkung auf die stdin,
stdout, stderr.

Ich gebe natürlich gern zu, daß ich printf() ohnehin als so
schwergewichtig ansehe, daß ich davon ausgegangen war, daß in
vielen derartigen Applikationen auch bereits in malloc() in
Benutzung ist, so daß das egal wäre.  Ich habe selbst schließlich
schon weit mehr Bedarf für malloc() in meinen Applikationen gehabt
als Platz für printf(). ;)

Die paar indirect calls machen das Kraut nicht fett, das ist ja
kein 1 MHz Z80 und keine alte PDP-11 -- wenngleich die Architektur
der jetzigen stdio-Implementierung sehr stark an die der ersten
Unixe auf der PDP-11 angelehnt ist.

>Ich hab auch gesehen, das sprintf() fputc() benutzt, was wiederum
>ICALLs  ausführt.

Wiederum: UTSL.  Dann würdest Du nämlich auch sehen, daß im Falle
von sprintf() kein indirect call je erreicht wird.  Ich möchte
aber nicht den gesamten schwergewichtigen Code für die printf-
Formatauswertung einmal für sprintf, einmal für fprintf, einmal
für printf usw. haben, weil dann eine Applikation, die sowohl
printf als auch sprintf benutzt, den ganzen Salat gleich doppelt
hätte.  Von daher habe ich mich in der Architektur an gängige
Implementierungen gehalten (die im Falle der PDP-11 eben sogar
resourcenmäßig durchaus vergleichbar mit einem AVR sind, d. h. ich
habe mich vorsätzlich eher an 20 Jahre alten Implementierungen denn
an multimegabyte-verwöhnten aktuellen Unixen orientiert), und da
liegt die zentrale Formatierroutine dann eben in vfprintf() mit
einer Laufzeitbindung für die eigentliche Entscheidung, was dann
wohin geschrieben wird.

Daß man eventuell aus Performancegründen noch eine Pufferung in
stdio haben möchte, habe ich einigermaßen im Hinterkopf behalten,
aber in der ersten Version aus Aufwandsgründen noch nicht konkret
angedacht.  Die Performanceverbesserung wird dabei in jedem Falle
mit Codegröße zu bezahlen sein (schon deshalb wollte ich lieber
erstmal ungepuffert arbeiten, da das für die gängigen UART, LCD
& Co. praktisch ausreicht), aber das um die Ecke lugende Ethernet
(in Form preiswerter TCP/IP-Hardwarelösungen) winkt hier natürlich
schon mit dem Zaunspfahl...  Dort ist es eben nicht mehr egal, ob
jedes Byte einzeln in ein TCP-Paket verpackt würde oder stattdessen
so viel wie möglich gesammelt werden.

Wenn Du aber an derartig kleine Controller denkst, ist printf wohl
ohnehin nur maßgeschneidert zu vertragen, d. h. es werden nur die
Konvertierungen und Hilfskonstrukte eingebunden, die auch
tatsächlich notwendig sind für die Applikation.  Das ist praktisch
mit einer Bibliothek nicht zu realisieren, wenngleich es Dir
natürlich komplett freisteht, den Sourcecode des existierenden
vfprintf() zu nehmen und so weit einzukürzen, bis er für Dich paßt.
Ich würde auf solchen Controllern aber vermutlich eher zu den
vorhandenen Sonder-Konvertierungs-Funktionen greifen wie itoa() &
Co.

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.