Hey,
irgendwie habe ich gerade ein Problem, welches ich nicht so ganz
verstehe. Ich bin gerade dabei mich ein bisschen vertrauter mit der
USART Schnittstelle des ATmega88 machen und wollte dies
Interrupt-Basiert machen. Ich habe hier nun einen Puffer. Befülle ich
den mittels Indexen, funktioniert alles.
Also, einfach ein kleiner Echo. Zum testen erst einmal ohne Boundary
Check & RingBuffer.
Realisiere ich dies mit Zeigern, dann bekomme ich nichts zurück auf der
seriellen Schnittstelle.
volatileuint8_t*out;// <-- Zeiger für main-loop zum lesen
14
15
intmain(void){
16
17
UBRR0=UBRR_VAL;
18
19
UCSR0A=0;
20
UCSR0B=((1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0));
21
UCSR0C=((1<<UCSZ01)|(1<<UCSZ00));
22
23
in=buffer;// Anfang natürlich auf Buffer-Anfang
24
out=buffer;
25
26
sei();
27
28
while(1){
29
if(in!=out){
30
while(!(UCSR0A&(1<<UDRE0)));
31
UDR0=*(out++);// Stelle des Zeigers dereferenzieren und danach eins weiter
32
}
33
}
34
35
}
36
37
ISR(USART_RX_vect){
38
*(in++)=UDR0;// Dito wie in main-loop
39
}
Es ist keine große Änderung dabei. Wo Änderungen sind, sind Kommentare.
Also, entweder stehe ich gerade auf dem Schlauch, oder es ist schon
spät.
Wäre für eure Hilfe echt dankbar.
Viele Grüße
sep
sep schrieb:> volatile uint8_t *in; // <-- Zeiger für Interrupt> volatile uint8_t *out; // <-- Zeiger für main-loop zum lesen
Hier hast du zwei Zeiger auf volatile uint8_t. Aber die Zeiger selbst
sind nicht volatile.
Hmm, gut, das kannte ich noch nicht.
Aber das funktioniert und klingt auch recht logisch wenn man dies
bedenkt: http://en.wikipedia.org/wiki/Const-correctness
Danke. Also mit dem folgenden funktioniert es nun.
Ich bevorzuge die Indexe.
Einen Index kann man leicht auf Bereichsüberschreitung testen, einen
wild gewordenen Pointer nicht so leicht.
Und wenn der Puffer <= 256 Byte ist, kann der Index 8-bittig sein, d.h.
man spart deutlich Code gegenüber Pointern.
Damit sich die Indexe/Pointer nicht überholen, kann der 256 Byte Puffer
nur mit max 255 Byte befüllt werden. Aber da die HW-UART 3 Byte puffert,
hat man effektiv 258 Byte Puffer.
Hey Peter,
Ich wollte einfach mal schauen, wie es denn umgesetzt wird. Auch wenn
die Pointer 16 Bittig sind, habe ich weniger Code als mit Indexen. In
diesem Bsp waren das knapp 10%.
Das wird wohl daher kommen, dass die Pointer immer im 1 Byte weiter
bewegt werden. Das kann mit dem st X+, r24 bsp passieren. Da hab ich
zwei Takte und der Zeiger ist gleich weiter bewegt. An sich find ich das
eigentlich ganz kompakt.
Weiterhin, ob mein Index wild geworden ist, oder mein Zeiger... Das ist
meiner Meinung nach nur ne Geschmacksfrage.
Viele Grüße
sep
sep schrieb:> Auch wenn> die Pointer 16 Bittig sind, habe ich weniger Code als mit Indexen.
Dein Code ist ja auch nicht vollständig. Du schreibst Dir ja den ganzen
RAM zu. Es fehlt noch der Rücksprung zum Anfang.
sep schrieb:> Weiterhin, ob mein Index wild geworden ist, oder mein Zeiger... Das ist> meiner Meinung nach nur ne Geschmacksfrage.
Für mich nicht. Wenn ein Pointer überschrieben wurde, zeigt er
irgendwohin im RAM und Du suchst den Fehler an einer völlig falschen
Stelle.
Z.B. Funktion X will ein int16_t schreiben und Du übergibst aber
versehentlich die Adresse eines int8_t. Dahinter steht zufällig Dein
Pointer für Puffer Y, der nun zerstört wird. Er zeigt dadurch in den RAM
der Funktion Z, wo sich der Fehler bemerbar macht.
Du suchst Dich also dumm und dämlich in Funktion Z, statt im Aufruf von
X.
Das: "Was wäre wenn" ist ziemlich sinnlos, wenn der Code unvollständig
ist.
Spätestens, wenn die Zeiger verglichen werden müssen, neigt sich die
Waage schnell in Richtung Indexvergleich.
Klar *Zeiger++= sieht nach fast nix aus schau Dir aber mal an was
dahintersteht.
Es gibt natürlich eine Ausnahme: Bei einem Riesenpuffer (>255)geht's
auch beim Index richtig zur Sache.
Peter Dannegger schrieb:> Ich bevorzuge die Indexe.> Einen Index kann man leicht auf Bereichsüberschreitung testen, einen> wild gewordenen Pointer nicht so leicht.>> Und wenn der Puffer <= 256 Byte ist, kann der Index 8-bittig sein, d.h.> man spart deutlich Code gegenüber Pointern.
Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und
zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code
erzeugt?
Peter Dannegger schrieb:> Z.B. Funktion X will ein int16_t schreiben und Du übergibst aber> versehentlich die Adresse eines int8_t. Dahinter steht zufällig Dein> Pointer für Puffer Y, der nun zerstört wird. Er zeigt dadurch in den RAM> der Funktion Z, wo sich der Fehler bemerbar macht.> Du suchst Dich also dumm und dämlich in Funktion Z, statt im Aufruf von> X.
Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein
Index steht und eben dadurch an die falsche Adresse geschrieben wird?
>Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und>zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code>erzeugt?
Das stimmt zwar, aber diese Addition erfolgt nur bei einem "echten"
Zugriff alles andere spielt sich im Bereich 0 ... 255 ab.
Leer- und Vollabfrage, Indexarithmetik usw. sind Einfachstrechnungen.
Wie gesagt: nur beim "echten" Lesen und Schreiben wird's langwieriger.
In der Praxis wird wohl die Leerabfrage am häufigsten sein. Ein
einfacher Indexvergleich (Anfang==Ende).
Übrigens die Frage ob ein wildgewordener Index oder Zeiger schlimmer
ist, halte ich für unsinnig.
Naja. Nen Puffer muss man ja nicht immer als Ringpuffer implementieren.
Für meine Anwendung (auch wenn der eine Zugriff nicht atomar war), waren
es 10% weniger Code mit Zeigern.
Amateur schrieb:> Das: "Was wäre wenn" ist ziemlich sinnlos, wenn der Code unvollständig> ist.> Spätestens, wenn die Zeiger verglichen werden müssen, neigt sich die> Waage schnell in Richtung Indexvergleich.> Klar *Zeiger++= sieht nach fast nix aus schau Dir aber mal an was> dahintersteht.> Es gibt natürlich eine Ausnahme: Bei einem Riesenpuffer (>255)geht's> auch beim Index richtig zur Sache.
Ich vergleiche keine Zeiger. Ich setze sie nur. Der Puffer ist groß
genug sodass alle Zeichen platz finden. Wenn nicht, habe ich mit einem
Ringpuffer auch Probleme, dass meine älteren Zeichen überschrieben
werden, oder die neueren weg fliegen, oder oder oder... Auch alles
Probleme welche sich nicht leicht finden lassen.
> *Zeiger++ =
st X+, r16 <-- Mehr steckt nicht dahinter... Wenn denn der Compiler
ordentlich optimiert, oder ich das mit inline ASM mache.
Amateur schrieb:>>Bei jedem Zugriff auf ein Elemen muß dafür aber der Index erweitert und>>zur Basisadresse dazuaddiert werden. Ob das wirklich weniger Code>>erzeugt?>> Das stimmt zwar, aber diese Addition erfolgt nur bei einem "echten"> Zugriff alles andere spielt sich im Bereich 0 ... 255 ab.
Ich spiele nicht mit den Zeigern rum, ich brauche sie nur als Hinweis,
wo ich mich gerade mit dem schreiben, oder lesen befinde...
Also, ich denke schon, dass das ordentliche Arbeiten mit Zeigern kleiner
und schneller ist. Zumindest für meinen Anwendungszweck. Kommt halt
immer drauf an was man machen will.
Aber seis drum. Ich habs nun noch ein bisschen geändert und hab halt den
einen Zeiger in den Registern zu liegen (So läuft der RX Int in ca 30
Takten durch). Das gesamte Ergebnis schaut nun gerade so aus.
Für mich läuft es ziemlich gut.
1
#ifndef UART_RX_BUFFER_H_
2
#define UART_RX_BUFFER_H_
3
4
#include<avr/common.h>
5
#include<avr/interrupt.h>
6
#include<avr/io.h>
7
8
#ifndef UART_RX_BUFSIZ
9
#error "Define UART_RX_BUFSIZ!"
10
#elif !defined( UART_RX_INREG )
11
#error "Define UART_RX_INREG. A register where in_buffer is stored."
sep schrieb:> Ich vergleiche keine Zeiger. Ich setze sie nur. Der Puffer ist groß> genug sodass alle Zeichen platz finden. Wenn nicht, habe ich mit einem> Ringpuffer auch Probleme, dass meine älteren Zeichen überschrieben> werden, oder die neueren weg fliegen, oder oder oder...
Es ist ein Unterschied, ob ein FIFO überläuft (d.h. alte empfangene
Zeichen überschrieben werden) oder ob das gesamte RAM überschrieben
wird. Für letzteres sorgt Deine Vorgehensweise.
Klar, Datenverlust beim Empfang ist ein Fehler, aber der Rest des
Programmes könnte ja störungsfrei weiterlaufen. Killt man aber das RAM,
geht gar nichts mehr.
Setzt Du denn jemals Deine Zeiger wieder zurück? Oder gehst Du davon
aus, daß Du während der Laufzeit Deines Programmes nie mehr als x Bytes
an Daten empfangen möchtest?
Ich setze den zurück. Jeweils wenn ein Befehl, welcher über den USART
rein gekommen ist abgearbeitet ist, oder wenn ein Timeout eingetreten
ist. Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl
auf einmal gesendet wird.
sep schrieb:> Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl> auf einmal gesendet wird.
Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?
Was, wenn durch eine Störung die UART "Dreck" empfängt?
Rufus Τ. Firefly schrieb:> sep schrieb:>> Durch das Protokoll ist sichergestellt, dass immer nur ein Befehl>> auf einmal gesendet wird.>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?> Was, wenn durch eine Störung die UART "Dreck" empfängt?
Dann werden die Zeichen sofort aus dem Puffer gelöscht.
Johann L. schrieb:> Und schon hat man einen Fehler im Code durch unnötige Inline Asm> Kapriolen...>> Viel Spaß beim Suchen :-P
Währe gut zu wissen wo.
Aber so viel inline Assembler ist es ja nicht. Und hier in dem Interrupt
muss ich das SREG nicht sichern, da weder push, pop, lds, movw, noch st
X+ Einfluss auf das SREG haben. höchstens reti. Aber das soll ja so! ;)
Wenn ich was übersehen habe, würde ich dir dankbar sein, wenn du mir
sagst, was es ist. ^^
Sep hat entschieden das Zeiger weniger Platz und Rechenzeit benötigen.
Vor allem dann, wenn unbegrenzt Speicher, wie bei den Meisten hier
verwendeten Prozessoren (*Tiny, *Mega, PIC und Konsorten), vorhanden
ist.
Was genau ist der Grund für die Assembler-Origie.
Ich seh da noch keinen wirklichen Grund dafür?
So ineffizient kann doch der Compiler ein
1
ISR(USART_RX_vect){
2
*(in++)=UDR0;// Dito wie in main-loop
3
}
gar nicht auflösen.
Wenn der volatile Pointer stört, dann caste das volatile weg oder benutz
eine Hilfsvariable, die keine volatiles hat (optimiert der COmpiler
sowieso wieder raus)
Karl Heinz Buchegger schrieb:> Was genau ist der Grund für die Assembler-Origie.
Erstens einmal eine Entscheidung von mir aus.
Zweitens wird so weder SREG noch r1 gepusht/pepoppt weswegen auch noch
einmal ein bisschen was weg fällt. Wie ich auch gleich unten schreibe,
ich brauche Platz auf dem Flash.
Amateur schrieb:> Sep hat entschieden das Zeiger weniger Platz und Rechenzeit benötigen.> Vor allem dann, wenn unbegrenzt Speicher, wie bei den Meisten hier> verwendeten Prozessoren (*Tiny, *Mega, PIC und Konsorten), vorhanden> ist.
Sry, ich weiß ja nicht, was los ist. Aber ich denke mal, dass dies jeder
für sich entscheiden muss und sollte. In meinem Fall kann ich nun einmal
in paar mehr Bytes im RAM ablegen. Kleiner und schneller ist es in
meinem Fall auch noch. Da ich mehr auf den Flash achten muss, als auf
den SRAM. Wenn ich dort was zur Verfügung habe, warum sollte ich das
denn brach liegen lassen. Wenn ich im Flash Notstand habe, warum sollte
ich den dann verschwenden.
Eigentlich hatte ich das µC-Forum einmal anders in Erinnerung. Geholfen
wurde mir ja nach meinem Anfangspost. Nun weiß ich aber nicht, was das
Problem ist.
Ich klink mich dann hier erst einmal aus.
Grüße
sep
Rolf Magnus schrieb:> Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein> Index steht und eben dadurch an die falsche Adresse geschrieben wird?
Ich muß ja den Index mit der Größe des Ringpuffers vergleichen, um ihn
wieder auf Anfang zu setzen. Und wenn man das geschickter Weise vor dem
Schreibzugriff macht, können keine Daten außerhalb des Puffers
überschrieben werden.
Aber das habe ich auch erst lernen müssen, daß man sichere Konstrukte
bevorzugen sollte.
Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der
falschen Stelle sucht und dann am liebsten in die Tischkante beißen
würde ob der eigenen Dämlichkeit.
sep schrieb:> asm volatile(
Ehe ich die gefährliche Asm-Axt ansetze, überlege ich mir 100-mal, ob
das unbedingt nötig ist und die 3 gesparten Bytes/Zyklen wirklich über
Wohl und Wehe des Programms entscheiden.
Um Himmels Wilhelm.
Du sollst Dir doch keine andere Meinung aufzwingen lassen. Wenn in
Deinem speziellen Fall, das mit den Zeigern besser ist... Warum nicht!
Allerdings würde mich der Algorithmus hinter der folgenden Aussage mal
interessieren:
>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?>> Was, wenn durch eine Störung die UART "Dreck" empfängt?>Dann werden die Zeichen sofort aus dem Puffer gelöscht.
...
Amateur schrieb:> Allerdings würde mich der Algorithmus hinter der folgenden Aussage mal> interessieren:>>>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?>>> Was, wenn durch eine Störung die UART "Dreck" empfängt?>>>Dann werden die Zeichen sofort aus dem Puffer gelöscht.
Es steckt eine FSM dahinter.
sep schrieb:>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?>> Was, wenn durch eine Störung die UART "Dreck" empfängt?>> Dann werden die Zeichen sofort aus dem Puffer gelöscht.
Deine Empfangsroutine --die Du hier gepostet hast-- macht das nicht. Wer
setzt dann den Pointer wieder zurück?
Peter Dannegger schrieb:> Rolf Magnus schrieb:>> Und inwiefern wäre das nun anders, wenn an der besagten Adresse ein>> Index steht und eben dadurch an die falsche Adresse geschrieben wird?>> Ich muß ja den Index mit der Größe des Ringpuffers vergleichen, um ihn> wieder auf Anfang zu setzen.
Und wenn ich stattdessen einen Pointer nehme, muß ich ihn halt mit
Puffer-Ende vergleichen.
> Und wenn man das geschickter Weise vor dem> Schreibzugriff macht, können keine Daten außerhalb des Puffers> überschrieben werden.>> Aber das habe ich auch erst lernen müssen, daß man sichere Konstrukte> bevorzugen sollte.
Ich sehe das etwas anders. "Selbstheilender" Code kann einem die Sache
auch erschweren. Lieber habe ich ein Programm, das definiert abstürzt,
als eins, das zwar unauffällig weiterläuft, aber immer wieder
zwischendrin mal ein falsches Ergebnis produziert, weil duch einen
Fehler irgendwo ein falscher Index steht, der aber immer gleich wieder
repariert wird. Da ist der Fehler dann nämlich noch schwerer zu finden.
Viele machen bei Prüfung auf Puffer-Ende lieber ein <= als ein !=, damit
sich das Programm "regeneriert", falls der Index mal zu groß ist. Ich
mache gerade das nicht, denn ich will lieber den Grund für den falschen
Index finden, was meist leichter ist, wenn der Fehler nicht vertuscht
wird.
Peter Dannegger schrieb:> Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der> falschen Stelle sucht und dann am liebsten in die Tischkante beißen> würde ob der eigenen Dämlichkeit.
Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder
gar behoben, sondern einfach nur etwas besser versteckt. Da ist er immer
noch.
Rolf Magnus schrieb:> Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder> gar behoben, sondern einfach nur etwas besser versteckt.
Nö, ich habe verhindert, daß er andere Routinen stört. Ich bin also mit
der Fehlersuche ein Stufe näher an der Ursache dran.
Würde er z.B. die RTC stören, würde ich erstmal ewig im Timerinterrupt
rumsuchen.
Rufus Τ. Firefly schrieb:> sep schrieb:>>> Aha. Und was geschieht, wenn die Gegenseite das Protokoll nicht einhält?>>> Was, wenn durch eine Störung die UART "Dreck" empfängt?>>>> Dann werden die Zeichen sofort aus dem Puffer gelöscht.>> Deine Empfangsroutine --die Du hier gepostet hast-- macht das nicht. Wer> setzt dann den Pointer wieder zurück?
Aber sie bietet die Funktionalität. mittels reset kannst du nämlich den
Puffer zurück setzen. Alles andere geschieht eine Schicht weiter oben.
Wie gesagt, steht dort eine FSM. Diese kennt das Protokoll und kann
entsprechend reagieren. Der Puffer kann dies nicht. Weil dieser nicht
weiß, was gesprochen wird. So bleibt alles schön modular.
Rolf Magnus schrieb:> Peter Dannegger schrieb:>> Ist bestimmt jedem mal passiert, daß man tagelang den Fehler an der>> falschen Stelle sucht und dann am liebsten in die Tischkante beißen>> würde ob der eigenen Dämlichkeit.>> Ja. Aber mit deiner Methode hast du diesen Fehler nicht gefunden oder> gar behoben, sondern einfach nur etwas besser versteckt. Da ist er immer> noch.
Sry, wenn ich nun doppelt poste. Ich halte das auch so wie Rolf. Denn es
sollte ein Modul für genau eine Aufgabe geben. Sollten bei der Benutzung
Fehler auftreten, dann kann man die nach verschiedenen Vorgehensweisen
suchen. Das ist auch wieder Geschmackssache. Aber das Fehler durch
untere Module ausgebügelt werden halte ich auch für nicht richtig. Denn
der Fehler muss in der Benutzung gefunden und behoben werden.
Zur Nutzung von Modulen gibt es Dokumentationen. Daran sollte man sich
dann auch halten. Diese ist in meinem Falle nun auch vorhanden. Dort
stehen auch Hinweise darauf dass man halt den Puffer zurück setzen muss.
Wer sich nicht daran hält, muss halt mit den Konsequenzen hier rechnen.
Aber so ist das halt. Wer das eine will muss das andere mögen.
Grüße
sep
Ich glaube PeDa geht es einfach nur um robuste Routinen. Solche, die
nicht gleich komplett ausrasten, wenn irgendwo ganz anders etwas falsch
läuft oder gleich wie ein Kartenhaus zusammenfallen, wenn man wo ganz
anders eine kleine Änderung macht.
Wenn ich mir aber die "C-Lösung" oben anschaue, dann frage ich mich:
warum nicht gleich als S-File?
sep schrieb:> Aber das Fehler durch> untere Module ausgebügelt werden halte ich auch für nicht richtig.
Wird er ja nicht, sie wird weiterhin falsche Daten liefern.
Aber sie wird nicht eine völlig unbeteiligte Routine stören und dadurch
den Fehler verschleiern.
Es ist schon ein großer Unterschied, ob ein Fehler sich nur in der
beteiligten Routine bemerkbar macht oder sich irgendwo ein Zufallsopfer
aussucht. Und auch, wenn man den Verdacht hat, daß ein Pointer Unfug
treibt, weiß man noch lange nicht, welcher.
sep schrieb:> Der Puffer kann dies nicht. Weil dieser nicht> weiß, was gesprochen wird. So bleibt alles schön modular.
In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.
Peter Dannegger schrieb:> sep schrieb:>> Aber das Fehler durch>> untere Module ausgebügelt werden halte ich auch für nicht richtig.>> Wird er ja nicht, sie wird weiterhin falsche Daten liefern.> Aber sie wird nicht eine völlig unbeteiligte Routine stören und dadurch> den Fehler verschleiern.
Wie kommst du darauf? Dein Sendepuffer wird immer noch Mist machen. Du
hast zwar keinen out-of-Bounds-Zugriff mehr, aber wenn der Index
überschrieben wird, können zwei Dinge passieren: Der Wert ist "out of
bounds", dann wird er hart auf 0 gesetzt, oder er ist zufällig im
gültigen Bereich, dann wird halt an dieser zufälligen Stelle im Puffer
weitergemacht. In beiden Fällen sind irgendwelche wilden Sprünge im
Puffer das Resultat. Und damit hast du dann genau gar nichts gewonnen.
Es gibt immer noch merkwürdige Fehler in der unbeteiligten Routine.
> Es ist schon ein großer Unterschied, ob ein Fehler sich nur in der> beteiligten Routine bemerkbar macht oder sich irgendwo ein Zufallsopfer> aussucht.
Da gebe ich dir recht.
Bastler schrieb:> Wenn ich mir aber die "C-Lösung" oben anschaue, dann frage ich mich:> warum nicht gleich als S-File?
Naja. Hier steht nun alles in einer Header-File. Ich kann das Ding
nacher in eine Lib auslagern und kann Projektspezifisch die Interrupts
zentral "binden". (So hab ich alle Interrupts auf einen Blick)
Dies geschieht in diesem Falle durch
1
ASSIGN_UART_RX_FAST(USART_RX_vect,UDR0)
Weiterhin sind die Funktionen alle geinlined. Da die ja doch recht
kompakt sind, bzw in der Regel nur einmal aufgerufen werden.
Rufus Τ. Firefly schrieb:> sep schrieb:>> Der Puffer kann dies nicht. Weil dieser nicht>> weiß, was gesprochen wird. So bleibt alles schön modular.>> In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.
Da ich euch nicht mein Programm zeigen möchte und meine
Rufus Τ. Firefly schrieb:> sep schrieb:>> Der Puffer kann dies nicht. Weil dieser nicht>> weiß, was gesprochen wird. So bleibt alles schön modular.>> In meinen Augen ist ein Puffer ohne Überlaufsicherung ein Designfehler.
In meinen Augen sollte man die Anforderungen und das gesamte System
abschätzen. Ansonsten ist es ein Fehler solch eine Designentscheidung
als Fehler abzutun.
Die Ursprungsfrage drehte sich um etwas ganz anderes. Aber trotzdem muss
jeder (auch wenn er keine Ahnung von den Randbedingungen hat), seinen
Senf dazu geben. Wie gesagt ist es meine Entscheidung und die Größe und
Performance zahlen sich bei mir hier sehr viel besser aus, als die
Sicherung.
Weiterhin ist es ja nun nicht so dass es gänzlich nichts gibt was den
Puffer zurück setzt. Es steht eine FSM dahinter. die das Protokoll kennt
und sehr viel besser reagieren kann als der Puffer selbst.
Die Überlaufsicherung passiert halt nur eine Ebene weiter oben. Die
Grundfunktionalität reicht für den Puffer aber vollkommen aus.
Weiter habe ich nun langsam wirklich keine Lust mehr mich zu
rechtfertigen. Ich habe nun schon des öfteren beschrieben warum ich
meine Designentscheidung so gewählt habe, wie ich es getan hab. Wenn ihr
das nicht versteht, dann fragt ordentlich oder behaltet eure Meinung für
euch.
Grüße
sep
sep schrieb:> Wenn ihr das nicht versteht, dann fragt ordentlich oder> behaltet eure Meinung für euch.
Bist Du Dir sicher, daß das der geeignete Umgangston für dieses Forum
hier ist?
sep schrieb:> irgendwie habe ich gerade ein Problem, welches ich nicht so ganz> verstehe.
Aber ich verstehe dein Problem: Du hast einfch drauflos programmiert,
ohne vorher nachzudenken.
Ein Ringpuffer dient eigentlich IMMER dazu, zwei asynchron laufende
Domänen voneinander zu trennen, hier z.B. deine Endlosschleife vom
Interrupt-Geschehen. (Gilt auch für Hardware-FIFO).
Also soll jede Domäne NUR den Teil sehen, der ihr auch zusteht. Das wäre
für die Schleife in main eine Funktion im UART-Treiber, die meinetwegen
char GetCharFromUART(void);
heißt. Und bei dieser Funktion MUSS !! man VORHER bedenken, wie sie sich
verhalten soll, wenn grad nix anliegt. Genauso MUSS man vorher bedenken,
wie sich die Interrupt-Routine benehmen soll, wenn der Puffer immer noch
voll ist.
Ansonsten ist das Arbeiten mit Indizes bedeutend günstiger, denn man muß
ja ne Rechnung aufmachen, welchen Wert man beim Beladen und Entladen des
Ringpuffers in den jeweiligen Index zurückschreibt. EInen Index setzt
man nach Überschreiten des letzten Elements im Array auf 0 zurück, das
ist einfacher als das Beladen eines Zeigers mit ner Startadresse. Wenn
man strikt 2er Potenzen für die Puffergröße nimmt, reicht dafür ein AND
aus, bei krummen Puffergrößen muß man MOD benutzen. All das fehlt deinem
Beispiel.
Johann L. schrieb:> Auf in muß atomar zugegriffen werden!
Das ist fast ein Unfug, weil viel zu grob dargestellt. Richtig ist, daß
man von jeder Seite aus auf den Index oder Pointer der Gegenseite nur
lesend zugreifen darf und dieses sollte dann auch atomar geschehen.
Wenn also ein µC einen Pointer nicht in einem Maschinenbefehl lesen
kann, sondern dafür 2 oder 3 Zugriffe braucht, dann scheidet die
Pointerversion schon deshalb aus - der Interrupt könnte ja
dazwischenkommen. Indizes über übliche Puffer sind meist bloß 8 Bittig
und das kann eigentlich jeder µC mit einem Befehl.
W.S.
Rolf Magnus schrieb:> Wie kommst du darauf? Dein Sendepuffer wird immer noch Mist machen.
Ganz genau!
Sag ich doch schon die ganze Zeit.
Nur der macht Mist und nicht irgendwas zufälliges anderes, wo ich erst
unnötig Zeit bräuchte, um dahinter zu kommen, daß es eigentlich die
UART-Routine ist, bzw. daß sie gestört wird.
Ich spare also einen aufwendigen Debugschritt ein.
Das was Peter und andere hier propagieren, nenne ich gerne "defensives
programmieren".
Was wenn du an deiner FSM schraubst, und die halt mal zufällig den
Überlauf nicht abfängt? oder wenn du irgendwann den Code entdeckst, und
den low-level-teil ohne FSM als super geeignet für ein anderes Projekt
erachtest?
ich würde die Aussage "In meinen Augen ist ein Puffer ohne
Überlaufsicherung ein Designfehler." sogar noch verstärken: eine schwere
Designsünde, und im Wiederholungsfall ein Kündigungsgrund.
> Was wenn du an deiner FSM schraubst, und die halt mal zufällig den Überlauf
nicht abfängt?
Dann werd ich wohl in der FSM nen Fehler haben und diesen beheben.
> oder wenn du irgendwann den Code entdeckst, und den low-level-teil ohne FSM als
super geeignet für ein anderes Projekt erachtest?
Dann werd ich das wohl für dieses Projekt wieder verwenden.
W.S. schrieb:> Ein Ringpuffer dient eigentlich IMMER dazu,
Wer schreibt denn vor, dass ein IMMER ein Ringpuffer verwendet wird.
Natürlich hat der Ringpuffer seine Vorteile. Jedoch sollte man diese
genauso abwägen. Dieser ist zwar sicherer als ein einfacher Puffer, aber
zum einen auch komplexer (damit Fehleranfälliger) und auch um einiges
langsamer und größer (da immer wieder die Grenzen geprüft werden
müssen). Ein einfacher Puffer ist um einiges schneller, simpler und
einfacher. Dafür fehlt aber einem die Sicherheit. Diese muss man dann
wieder an anderen Stellen investieren. Es ist hier auch wieder eine
Abwägung welche natürlich geprüft werden muss. In meinem Falle ist es
nun aber einmal so dass ein Rücksetzen des Puffers alle Nachteile wieder
wett macht.
Wer sagt, dass ich einen Puffer verwende der kleiner als 256 Bytes ist?
Ich brauche dort etwas mehr und dies passt alles ordentlich in den RAM
(der ist ja bei weitem größer als 256 Byte!) und nein, zufälligerweise
ist das nun auch leider keine Zweierpotenz mehr. Somit würde ich dort
auch wieder auf 16 Bit ausweichen müssen und dann fallen mir alle
arithmetischen Operationen wieder zur Last.
W.S. schrieb:> Wenn man strikt 2er Potenzen für die Puffergröße nimmt, reicht dafür ein> AND aus, bei krummen Puffergrößen muß man MOD benutzen. All das fehlt> deinem Beispiel.
Wie gesagt. keine 2er Potenzen. Mod würde ich so oder so nicht
verwenden. Da dies viel zu aufwendig ist. lieber würde ich den Zeiger
auf den Anfang setzen oder dann den Index auf 0. Da so oder so nur
Byte-weise und nicht Blockweise geschrieben wird. Ist um einiges
effizienter als ein MOD.
Grüße
sep
Mal eine Frage: Wieviel tausend Stück willst du produzieren, dass sich
der ganze Aufwand lohnt, um jedes einzelne Byte zu kämpfen, anstatt nen
größeren Controller zu nehmen?
sep schrieb:> Dieser ist zwar sicherer als ein einfacher Puffer, aber> zum einen auch komplexer (damit Fehleranfälliger)
Du bist lustig. Fehleranfällig ist vor allem Dein Puffer ohne
Grenzprüfung.
> und auch um einiges langsamer und größer (da immer wieder> die Grenzen geprüft werden müssen).
Sofern die Puffergröße eine Zweierpotenz ist, und mit einem Index
gearbeitet wird, ist eine einzige AND-Verknüpfung erforderlich. Auch
eine simple Werteabfrage mit nachfolgender Zuweisung oder eine
Modulo-Anweisung ist kein Beinbruch, und bei Pufferung von mit seriellen
Schnittstellen empfangenen Daten ist Geschwindigkeit nicht so kritisch,
daß es auf ein paar Taktzyklen mehr oder weniger ankommt.
Tut es das wider Erwarten doch, liegt möglicherweise ein Problem im
Gesamtdesign vor.
Auch hier: Wenn die Verwendung einer anderen Adressierungsart durch den
Controller bereits ein Geschwindigkeitsproblem darstellt, liegt ein
Problem im Gesamtdesign vor.
sep schrieb:> Somit würde ich dort auch wieder auf 16 Bit ausweichen> müssen und dann fallen mir alle arithmetischen Operationen> wieder zur Last.
Wenn simple 16-Bit-Operationen "zur Last fallen", liegt definitiv ein
Problem im Gesamtdesign vor.
Mit welcher Datenrate bitte empfängst Du Daten auf Deiner seriellen
Schnittstelle, und welche Wunderdinge stellt Deine "FSM" damit an?
sep schrieb:> Wenn ich was übersehen habe, würde ich dir dankbar sein, wenn du mir> sagst, was es ist. ^^
Im ersten asm wird ein Operand gesetzt / verändert, obwohl er
Inlput-Operand ist.