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
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().
Eine eigene Frage?
Dieser Thread ist über 17 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.