Hallo, ich möchte bei einem Mega168 den Usart und den TWI bidirektional über Streams wahlweise mit Keyboard(3x4) und LCD verbinden, die üer stdin und stdout betrieben werden. Über udata des Files wird ein fifo eingebunden. Eine Routing-Funktion soll wahlweise die Streams durch zeicheweises Kopieren verbinden (USART->LCD oder TWI->LCD oder sogar USART->TWI und umgekehrt). Variante 1) je ein Stream für Receiver und Transmitter, Streams müsssen beide trotzdem RW sein, weil ich put und get beide benötige für den fifo-Zugriff. Variante 2) ein RW-Stream, wobei die Hardware direkt in das Fifo arbeitet, nur für das Routing der Streams benutze ich die fgetc/fputc-Funktionen. Was ist hier die "politisch korrekte Vorgehensweise", wofür steht der RW-Zugriff der Streams, etwa nur für die Zugriffssteuerung auf put und get ? Gruß, Michael
Ich würd den Aufwand gar nicht machen, die stdin/stdout so zu untermauern, dass du mit den normalen C-Stream-Funktionen arbeiten kannst. Lohnt normalerweise den Aufwand einfach nicht. Für jedes Device eine get/put Funktion (je nach Devicetyp), dazu noch eine allgemeine FIFO und du bist im Rennen. Recht viel mehr als zeichenweise umkopieren wirst du in deiner Applikation ja nicht benötigen.
Michael Appelt wrote:
> Was ist hier die "politisch korrekte Vorgehensweise"
Keine Streams zu benutzen.
Ich habs noch nie gemacht auf nem MC und sehe darin auch keinen Sinn.
Auch wenns irgendwie lustig klingt, alles scheinbar gleich zu behandeln,
man verhuddelt sich da sehr schnell und Reccourcen verbraucht es auch
viel mehr.
Ein LCD wird ja grundsätzlich völlig anders angesprochen als die UART.
Es möchte gerne eine Position haben, wo der Text stehen soll, es
versteht keinen Zeilenvorschub, es kann sich Text nicht merken, d.h. er
muß für eine Zeitdauer stehen bleiben, damit er gelesen werden kann usw.
Das Keyboard verhält sich auch völlig anders als die UART, Du kannst ja
mit 3*4 keinen Text eingeben, die Tasten erhalten erst im konkreten
Funktionskontext ihre Bedeutung.
Oftmals muß der Keyboardtreiber Die Druckdauer (kurz, lang) getrennt
behandeln. Die geht aber verloren, wenn die Tasten in ner FIFO landen.
Ne Tasten-FIFO ist also in der Regel Unsinn.
Der User erwartet, daß Tasten zeitnah behandelt werden.
Und das I2C ist wieder ne andere Baustelle, Du hast spezielle Ereignisse
(Adressierung, Start, Stop, Arbitrierung), die bei der UART fehlen.
Du wirst also auf dem I2C ein völlig anderes Protokoll fahren müssen,
das geht Dir dann im Stream auch verloren.
Ich benutze das I2C paketorientiert, d.h. statt ner FIFO habe ich einen
Paketpuffer, der 1 oder 2 Pakete zwischenspeichert (ähnlich CAN).
Nimm extra Funktionen für UART (RX, TX), I2C, Keyboard und das LCD.
Das ist die übliche Vorgehensweise.
Peter
Peter Dannegger wrote: > > Auch wenns irgendwie lustig klingt, alles scheinbar gleich zu behandeln, > man verhuddelt sich da sehr schnell und Reccourcen verbraucht es auch > viel mehr. > Hallo, genau darum geht es mir. Natürlich muß man Einschränkungen hinnehmen, wenn man alle Schnittstellen aufs Zeichenorientierte Minimum beschränkt. Ich wollte halt wegen des geringen Aufwands alle meine kleinen Spielreien per I2C auf dieses Miniterminal führen, wo ich dann von beliebigen Debugausgaben bis zu ganzen Setup-Menüs alles durchführen kann. Ich nutze fürs LCD z.B. den seriellen Standard von Parallax, d.h. goto geht per 1 Byte. Die LCD-Ausganbe arbeitet auf ein Array als Puffer, und per Interrupt werden alle Änderungen zum LCD übertragen. Eine Statusmaschine für Ansi-Escapesequenzen habe ich auch in Arbeit. Ich verstehe einerseits, daß Entwickler immer Ihr Ziel und dessen sparsame Realisierung im Auge haben, andererseits sind ihnen, besonders wenn sie E-teschnisch vorbelastet sind, aber auch viele Grundprinzipien garnicht bekannt, so daß erstmal hoher Aufwand vermutet wird, der aber garnicht zutrifft. In meinem Fall ging es auch um die Implementierung einer dezentralen Architektur, d.h die leicht erhöhte CPU-elastung und der längere Code fällt bei 18,432MHz und Anschluss über relativ langsame serielle Schnittstellen garnicht auf. 3€ MEhrkosten/CPU sind Peanuts bei Einzelstücken. Und ab einem gewissen Punkt geht die Entwicklung von Applikationen schneller und fehlerfreier.... Ich habe übrigens festgestellt, daß ich 2 Streams für den Usart btrauche, weil das Put bereits direkt den Transmitterinterrupt freigeben muss falls das Fifo leer ist. Gruß, Michael
Peter Dannegger wrote: > Ich habs noch nie gemacht auf nem MC und sehe darin auch keinen Sinn. Das sehe ich ganz anders, ich sehe es als höchst sinnvoll an, Streams zu benutzen, und zwar genau dann, wenn man wiederverwendbare Ausgabe-Formatierungsfunktionen schreiben und auf beliebige Ausgabe-Devices anwenden möchte, speziell, wenn man diese Ausgabe-Funktionen in eigene Bibliotheken packen möchte (was oft sinnvoll ist); dann kann man für ein spezielles Device einfach eine zeichenorientierte Ausgabe-Funktion (z.B. für LCD-Display) oder Ein/Ausgabe-Funktion (z.B. für UART, I2C/TWI, SPI) schreiben und in einen Stream einbetten, und kann dann Funktionen damit verwenden, die unabhängig vom speziellem Device sind. >Ein LCD wird ja grundsätzlich völlig anders angesprochen als die UART. >Es möchte gerne eine Position haben, wo der Text stehen soll, es >versteht keinen Zeilenvorschub, es kann sich Text nicht merken, d.h. er >muß für eine Zeitdauer stehen bleiben, damit er gelesen werden kann usw. Nicht unbedingt wird ein LCD völlig anders angesprochen als ein UART; klar muß man die Cursor-Positionierung mit speziellen LCD-Funktionen machen; aber für Ausgaben, die ja wie beim UART zeichenweise geschehen, kann man die gleichen Funktionen verwenden. Das praktiziere ich schon seit 15 jahren so. Ich habe mal ein kleines Code-Fragment angehängt, in dem beschrieben ist, wie man vorgehen kann (bzw. wie ich es mache). Dort steht beispielhaft eine Funktion "DspCharStr", mit der man eine Kette gleicher ASCII-Zeichen bestimmter Länge ausgeben kann (und diese Funktion ist noch einfach; da sind komplexere Funktionen denkbar, z.B. Fehlertext-Ausgaben mit Fremdsprachen-Unterstützung o.ä., die man wahlweise am LCD und/oder auf einem ASCII-Terminal anzeigt, oder die Ausgabe eines formatierten Zeit/Datumstrings von der RTC, usw.).
Günter R. wrote: > Peter Dannegger wrote: >> Ich habs noch nie gemacht auf nem MC und sehe darin auch keinen Sinn. > > Das sehe ich ganz anders, ich sehe es als höchst sinnvoll an, Streams zu > benutzen, und zwar genau dann, wenn man wiederverwendbare > Ausgabe-Formatierungsfunktionen schreiben und auf beliebige > Ausgabe-Devices anwenden möchte, speziell, wenn man diese > Ausgabe-Funktionen in eigene Bibliotheken packen möchte (was oft > sinnvoll ist); dann kann man für ein spezielles Device einfach eine > zeichenorientierte Ausgabe-Funktion (z.B. für LCD-Display) oder > Ein/Ausgabe-Funktion (z.B. für UART, I2C/TWI, SPI) schreiben und in > einen Stream einbetten, und kann dann Funktionen damit verwenden, die > unabhängig vom speziellem Device sind. Die Ideologie ist gut, aber Streams braucht man da trotzdem nicht für. Ich mache es oft so, dass ein bestimmtes CodeModul (beispielsweise ein Dateisystem) als "Eingang" genau zwei definierte LowLevel Routinen braucht (zum Lesen und zum Schreiben). Statt jetzt einen Stream zu benutzen (was Overhead verursacht), lagere ich die Funktionsdefinitionen als #define in die Konfigurations-Header-Datei des Modules (hier: Dateisystem) einfach aus. Dann hat man im Endeffekt einen Stream, der nur im C-Code vorhanden ist und nicht mehr im Maschinencode. Somit spart man sich sämtlichen Overhead.
Da gebe ich Dir recht; diese Methode kenne ich auch, ich verwende sie auch, allerdings für andere Dinge (Auslagerung hardware-bezogener Funktionen, um eine Funktion hardware-unabhängig zu machen und in eine Bibliothek aufzunehmen, z.B. RS485-Enable/Disabling für eine Block-Puffer-I/O). Die von Dir beschriebene Methode spart Ressourcen, das stimmt. Sie ist allerdings m.E. etwas "unschön", da sie für Zeichen-I/O zwei Funktionen-Zeiger benötigt. Die Stream-Methode kommt mit einem Zeiger (auf das "Dev0") aus, was beim Weiterreichen von verschachtelten Funktionen der Übersichtlichkeit sehr dienlich ist. Und wenn man einen großzügigen Prozessor hat (z.B. ATmega128), kommt es nicht auf das letzte Byte an. Ich habe mir im übrigen auch einen eigenen File-Control-Block FILEX ("eXtended") definiert, der drei Funktionen verwaltet, nämlich "serin", "serout" und "serstat"; mit "serstat" kann man abfragen, ob beim UART-Empfänger ein Zeichen vorliegt; wichtig für Multitasking-Anwendungen, bei denen man z.B. mit ESC etwas abbrechen möchte. Auch hier kann man mit nur einem Zeiger über beliebig viele Ebenen den Stream weiterreichen. Und solche Funktionen sind hardware-unabhängig, sie lassen sich leicht in Bibliotheken unterbringen (okay: Deine Methode auch). Klar, kostet wieder etwas Ressourcen. Aber wie überall: der persönliche Geschmack entscheidet. Somit gibt es nicht "Besser" oder "Schlechter".
Hallo, die Idee mit den defines ist hier nicht nutzbar weil ich per Menü dynamisch vom lokalen Betrieb auf RS232 und I2C umschalten will. Dazu eignet sich ein select oder eben eine function-referenz. Mehr macht aber ein Stream aber auch nicht, ein pointer für put und einer für get. Der Overhead wird meines Erachtens völlig überbewertet, vermutlich weil printf mit formatstrings gelegentlich sehr aufwändig arbeitet. Gruß, Michael
Michael Appelt wrote: > Dazu eignet sich ein select oder eben eine function-referenz. Mehr macht > aber ein Stream aber auch nicht, ein pointer für put und einer für get. Da wär ich mir nicht so sicher. Ein Stream muss auch noch Buchführen ob er eof ist, ob er geöffnet wurde, in welchem Modus er geöffnet wurde, eventuelle Daten die er vom Betriebssystem hat (Filehandle od dgl.) Datenbuffer etc. Wieviel davon auf dem WinAvr vorhanden ist, kann ich nicht sagen. Einfach mal in stdio.h reinschauen, da müsste die FILE-Struktur deklariert sein. Auf einem PC (VC++) sieht sie zb so aus
1 | struct _iobuf { |
2 | char *_ptr; |
3 | int _cnt; |
4 | char *_base; |
5 | int _flag; |
6 | int _file; |
7 | int _charbuf; |
8 | int _bufsiz; |
9 | char *_tmpfname; |
10 | };
|
11 | typedef struct _iobuf FILE; |
Karl heinz Buchegger wrote: > > Auf einem PC (VC++) sieht sie zb so aus > [/C] Hallo, Ja, aber das läuft dann auf dem Mega168 wirklich nicht mehr, evtl habe ich das am Anfang nicht so klar erwähnt. BTW ist VC++ kein sehr gutes Beispiel, ich schau da lieber in die Linux Kernelquellen ;-) Also die Idee mit dem eigenen filedescriptor ist nicht blöd, andererseits liebe ich function refs, und kann mit der stdio des AVR bisher gut auskommen. Ach so, I2C ist tatsächlich erstmal kein typisches Streamdevice, aber wenn es um die LCD-Ausgabe geht, dann führt die Definition von Paketen doch immer wieder auf einen Strom von N bytes. Gruß, Michael
Karl heinz Buchegger wrote: > Ein Stream muss auch noch Buchführen ob er eof ist, ob er geöffnet > wurde, in welchem Modus er geöffnet wurde, eventuelle Daten die er vom > Betriebssystem hat (Filehandle od dgl.) Datenbuffer etc. Nein, so viel Overhead treibt avr-libc da nicht. EOF kommt direkt von der lowlevel-Funktion, gepuffert wird (derzeit) rein gar nichts. Der Stream ist also in der Tat eine nette Abstraktion, wenn man verschiedene IO-Kanäle auf obererer Ebene mit einem gemeinsamen API benutzen möchte, und man hat den Vorteil der formatierten Ausgabe über fprintf().
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.