Forum: Mikrocontroller und Digitale Elektronik AVR: Interrupts zwischen cli() und sei() verloren?


von Randy N. (huskynet)


Lesenswert?

Hallo,

wenn auf einem AVR die Interrupts gloabl mit cli() deaktiviert werden, 
um beispielsweise ein Codestück unterbrechungsfrei auszuführen, und 
währenddessen tritt ein Interrupt auf, wird dieser dann ausgeführt, wenn 
ich die Interrupts mit sei() wieder aktiviere, oder ist der dann 
"verloren", bzw. was kann man machen, damit diese nicht verloren gehen?

Grüße,
Randy

von Durchblicker (Gast)


Lesenswert?

Nein, die Interrupts gehen nicht verloren. Sie werden angesprungen, 
sobald sei() ausgeführt wurde. Wenn Interrupts zulange disablet werden, 
können allerdings eben doch Interrupts verloren gehen, eben dann, wenn 
in dieser Zeit mehr als ein Interrupt einer Quelle auftritt. Dann gibt 
es beispielsweise Zeichenverlust auf der seriellen Schnittstelle.

von Randy N. (huskynet)


Lesenswert?

Ok danke.

von Hmm... (Gast)


Lesenswert?

Als Lösung kann man innerhalb eines längeren Codeblocks mal eben kurz 
die Interrupts freigen:

[code]
sei
nop
cli
[\code]

Damit würden nach sei und nop jeweils Interrupts abgearbeitet werden 
können. Und für mehr als ein paar Takte muss man selten Interrupts 
verriegel.

von Randy N. (huskynet)


Lesenswert?

Ja also das reicht mir. Also bei den schnellen Interrupts ist mir das 
nicht so wichtig - da kommen dann ja auch schnell wieder neue. Aber ich 
hab auch Interrupts, die nur sehr sehr selten kommen - und solche will 
ich nicht verpassen, aber diese würden ja nach dem cli()...sei()-Block 
ausgeführt werden.

von crazy horse (Gast)


Lesenswert?

"Also bei den schnellen Interrupts ist mir das
nicht so wichtig - da kommen dann ja auch schnell wieder neue"

Das muss man sich mal auf der Zunge zergehen lassen, sehr schön. Sch.... 
auf den Int, kommt ja bald ein neuer :-)

von Randy N. (huskynet)


Lesenswert?

:-) Naja das was schnell ist ist bei mir entweder die 
Nulldurchgangserkennung, und wenn ich da mal vergesse, die Triacs zu 
zünden - das merkt keiner (zumindest so wie ich sie einsetze, als 
Dimmer, nicht), oder der IR-Empfänger, und da kann man im schlimmsten 
Fall auch einfach nochmal auf die Fernbedienung drücken :-).

von crazy horse (Gast)


Lesenswert?

mag sein, dass es Fälle gibt, wo es wirklich egal ist. Trotzdem, 
solltest du dir nicht angewöhnen. Interrupts sollten immer alle 
bearbeitet werden/können. Gerade das kann nämlich eine ziemlich 
tückische Angelegenheit werden, da hilft nur eine 
worst-case-Betrachtung. Es gibt Gerätschaften, die laufen wochenlang 
störungsfrei, dann plötzlich spinnen die. Wird dann schnell auf EMV oder 
Stress mit der Stromversorgung geschoben, ist aber eigentlich ein 
gravierender Softwarefehler.

von Hannes L. (hannes)


Lesenswert?

Randy N. wrote:
> :-) Naja das was schnell ist ist bei mir entweder die
> Nulldurchgangserkennung,

Hmmm, ääähhhh... 50 Hz (oder 100 Hz) sind bei Dir "schnell"??? Aus Sicht 
eines MCs ist das verdammt langsam...

> ..., oder der IR-Empfänger,

Naja, wirklich "schnell" sind die auch nicht.

Wenn Du Deinem Programm wegen Rechenzeitmangel bereits die Freiheit 
gönnst solche "langsamen" Ereignisse verpassen zu dürfen, dann machst Du 
irgendwas Grundlegendes falsch...

Es gibt einige Dinge, die nicht durch Interrupt unterbrochen werden 
dürfen, doch diese dauern immer nur wenige Takte. Im Übrigen hält man 
ISRs möglichst kurz, so dass auftretende Interrupts das Hauptprogramm 
nicht zu lange unterbrechen.

...

von Peter (Gast)


Lesenswert?

Hallo,

hätte in dem Zusammenhang auch mal eine Frage.
Habe in meiner ISR (UART Rx) eine State machin mit switch/case 
realisiert um die empfangenen Zeichen auszuwerten.
Im worst case dauert ein Aufruf 745 Takte (minimal 206 Takte).

Ist das so noch vertretbar? Oder währe es besser die Zeichen über einen 
Puffer einzulesen und im Hauptprogramm den Buffer bei jedem Durchlauf 
zyklisch abzufragen?

Was sagt Ihr aus Eurer Erfahrung? Was währe ein besserer Stiel?

Peter

von Johannes M. (johnny-m)


Lesenswert?

@Peter:
Das kann man nicht pauschal sagen. Wenn der UART-Rx-Interrupt der 
einzige aktive Interrupt ist und die ISR nicht länger dauert als der 
Abstand zwischen zwei ankommenden Zeichen, dann ist das kein Problem, 
auch wenn es kein guter Programmierstil ist.

von Peter (Gast)


Lesenswert?

@johnny-m
Ja ist der einzige, aktive Interrupt. Hab den Quarz auch so gewählt das 
es mit den Takten passt (1, 8432MHz -> 1056 Takten @ 19200bps).
Aber mir geht es halt ums Prinzip. Wie macht man so was am besten damit 
es sicher läuft (auch nach Änderungen/Erweiterungen) und nach 
Möglichkeit auch noch portabel ist (leicht in andere Programme zu 
integrieren ohne viele Änderungen.

Peter

von Johannes M. (johnny-m)


Lesenswert?

Peter wrote:
> Wie macht man so was am besten damit
> es sicher läuft (auch nach Änderungen/Erweiterungen) und nach
> Möglichkeit auch noch portabel ist (leicht in andere Programme zu
> integrieren ohne viele Änderungen.
Indem im Interrupt nur das Nötigste gemacht wird (eben z.B. mit nem 
Puffer) und die Auswertung der Daten im Hauptprogramm.

von Hannes L. (hannes)


Lesenswert?

Peter wrote:
> ...
> Aber mir geht es halt ums Prinzip.

Hmmm, es kommt immer auf den Einzelfall an. Was in einem Fall 
vorteilhaft ist, kann im anderen Fall schon schädlich sein.

Wenn das Programm größer wird und mehrere Interruptquellen hat, dann ist 
ein RX-Buffer als FIFO sicher sinnvoll.

Meist wird aber an anderen Stellen Rechenzeit verschwendet, indem 
Wartezeiten durch Schleifenzählen (Zeitschleifen) oder Busywait erzeugt 
werden. Es lohnt sich also nicht, nur auf den RXC-Interrupt zu schauen 
ohne das gesamte Programm zu kennen.

...

von Peter (Gast)


Lesenswert?

@hannes

Im Moment hängt eigentlich alles nur von der ISR ab. Sind Zeichen 
empfangen und ausgewertet, wird ein entsprechendes Flag gesetzt. Das 
Hauptprogramm fragt die Flags ab und führt entsprechende Funktionen aus.
In diesem Fall macht es so evt. Sinn. Aber es ist halt geplant das ganze 
noch zu erweitern. Wie genau steht allerdings noch nicht fest. Dazu 
kommt auf jeden Fall noch SPI oder mindestens I2C.

Denke mit diesem Hintergrund währe ein Rx FIFO sinnvoll.

Danke für die Hilfe!
Peter

von Lutz (Gast)


Lesenswert?

>Nein, die Interrupts gehen nicht verloren. Sie werden angesprungen,
>sobald sei() ausgeführt wurde.

Das gilt aber nur, wenn es auch "echte" Interrupts mit eigenem Flag 
sind. Ist m.W. nicht auf allen MCs so.

Gruß
Lutz

von Simon K. (simon) Benutzerseite


Lesenswert?

Aber auf dem AVR und darum gehts doch.

von Randy N. (huskynet)


Lesenswert?

> Wenn Du Deinem Programm wegen Rechenzeitmangel bereits die Freiheit
> gönnst solche "langsamen" Ereignisse verpassen zu dürfen, dann machst Du
> irgendwas Grundlegendes falsch...

Ich habe noch nicht so einen Fall, aber angenommen ich hätte jede Stunde 
einen externen Interrupt, und der würde genau in der Zeit, in der die 
Interrupts deaktiviert sind, auftreten, dann wollte ich wissen, ob ich 
den jetzt verpasst hab (was fatale Folgen haben könnte), oder ob der 
noch nach dem sei() ausgeführt wird. Mich hatte es einfach rein 
hypothetisch interessiert, wie das der µC handhabt. Und natürlich 
verpasse ich auch die ganzen Nulldurchgänge nicht, aber das wäre (wieder 
hypothetisch) nun jetzt nicht sooo schlimm. Ob es irgendwann wirklich 
mal dazu kommt, dass der Interrupt genau dann auftritt, steht auf einem 
anderen Blatt, aber theoretisch ist es möglich. :-)

> Es gibt einige Dinge, die nicht durch Interrupt unterbrochen werden
> dürfen, doch diese dauern immer nur wenige Takte. Im Übrigen hält man
> ISRs möglichst kurz, so dass auftretende Interrupts das Hauptprogramm
> nicht zu lange unterbrechen.

Genau so ist es bei mir. Im Interrupt schreibe ich etwas in eine Queue 
und erhöhe die Position und im Hauptprogramm lese ich die Queue, 
erniedrige die Position und verarbeite das Ganze. Und das Hauptprogramm 
darf nun nicht zwischen dem Lesen der Queue und dem Ändern der Position 
unterbrochen werden, und drum muss da ein cli()...sei() rundrum. Aber da 
cli()...sei() ja für alle Interrupts gilt, wollte ich wissen, ob alle 
Interrupts, die ja durchaus in dieser Zeit auftreten können, ignoriert 
werden. Um die Interrupts nicht ganz so häufig ein- und ausschalten zu 
müssen, setze ich sogar noch eine Statusvariable ein, die angibt, ob 
Änderungen in der Queue vorliegen, und vom Interrupt gesetzt wird.

von Hannes L. (hannes)


Lesenswert?

Ein Ereignis, das einen Interrupt auslösen darf, setzt erstmal nur ein 
Interrupt-Flag. Einige spezielle Ereignisse können zusätzlich noch was 
Anderes tun, z.B. kopiert ICP noch den aktuellen Timerstand ins 
ICP-Register.

Wir haben also ein gesetztes Interrupt-Flag...

Dann gibt es für jeden möglichen Interrupt noch das 
Interrupt-Freigabe-Flag. Dieses wird vom Programm(ierer) gesetzt, wenn 
der Interrupt aktiviert/freigeschaltet werden soll.

Und zu guterletzt gibt es noch das I-Flag im SREG, das Interrupts global 
erlaubt bzw. verbietet.

Eine (Hardware-) Logik im AVR überürüft ständig (bei jedem Takt), ob 
alle drei Bedingungen (Interrupt-Freigabeflag, Interrupt-Flag und I-Flag 
im SREG) zutreffen und löst dann den (R)Call zum Interrupt-Vektor aus.

Die meisten Interrupt-Quellen setzen ihr Int-Flag und lassen es stehen. 
Es wird dann durch den Aufruf der ISR (per Hardware) gelöscht.

Es gibt aber auch Interrupt-Quellen, die ihr Interrupt-Flag wieder 
löschen, wenn die auslösende Bedingung nicht mehr vorliegt (z.B. UDRE). 
Im Einzelfall sollte man also das Datenblatt befragen, das ist da sehr 
informativ.

Zur Queue... Ich nutze (allerdings in ASM) meist einen Ringbuffer mit 
zwei Pointern. Dabei verwaltet die ISR den einen Pointer, das 
Hauptprogramm den anderen. Bei Gleichstand der Pointer ist der Buffer 
leer. Der Pointer des Datenempfängers eilt also immer dem Pointer des 
Datenlieferanten hinterher. Hierbei kommt es zu keinem Konflikt, da muss 
kein Interrupt gesperrt werden. Den Fall, wo ISR und Hauptprogramm auf 
dieselben Ressourcen zugreifen, habe ich eigentlich sehr selten.

> ..., aber angenommen ich hätte jede Stunde
> einen externen Interrupt, und der würde genau in der Zeit, in der die
> Interrupts deaktiviert sind, auftreten, ...

Dann kommt es auf den Modus des externen Interrupts an. Im Flankenmode 
verpasst Du ihn nicht. Im Level-Mode bleibt das Interrupt-Flag 
allerdings nur solange stehen (laut Datenblatt des Tiny2313), wie der 
Auslösepegel am Int-Pin anliegt. Diesen Interrupt könntest Du also 
verpassen, wenn Dein Programm den Interrupt länger sperrt als der 
Interrupt-Impuls dauert.

Also immer schön das Datenblatt befragen, das ist nicht nur für 
ASM-Programmierer interessant... ;-)

...

von Philipp B. (philipp_burch)


Lesenswert?

Hannes Lux wrote:
> Zur Queue... Ich nutze (allerdings in ASM) meist einen Ringbuffer mit
> zwei Pointern. Dabei verwaltet die ISR den einen Pointer, das
> Hauptprogramm den anderen. Bei Gleichstand der Pointer ist der Buffer
> leer. Der Pointer des Datenempfängers eilt also immer dem Pointer des
> Datenlieferanten hinterher. Hierbei kommt es zu keinem Konflikt, da muss
> kein Interrupt gesperrt werden. Den Fall, wo ISR und Hauptprogramm auf
> dieselben Ressourcen zugreifen, habe ich eigentlich sehr selten.

Kannst du mal ein Beispiel deiner push- und pop-Routinen zeigen? Mich 
würde mal interessieren, wie du da die Überprüfung auf Über-/Unterlauf 
mit gewohnt Lux'scher Effizienz eingebaut hast ;)
Denn RP und WP (Readpointer und Writepointer) sind ja nun nicht nur bei 
leerem FIFO gleich, sondern auch wenn es voll ist. Daher habe ich mir 
angewöht, jeweils noch eine Variable mitzuführen, die den Füllstand 
beinhaltet, da geht auch die Auswertung relativ einfach.

von Randy N. (huskynet)


Lesenswert?

@Hannes Lux:

Joa soweit war mir das klar, aber es hätte ja sein können, dass wenn die 
Interrupts deaktiviert sind, keine Flags in die Flag-Register 
geschrieben werden. Trotzdem danke für das Posting :-).

> Zur Queue... Ich nutze (allerdings in ASM) meist einen Ringbuffer mit
> zwei Pointern. Dabei verwaltet die ISR den einen Pointer, das
> Hauptprogramm den anderen. Bei Gleichstand der Pointer ist der Buffer
> leer.

Genau so mach ichs, mit einem statischen Array und von vornherein 
festgelegter Größe, z.B. 100 Elemente. Und mit Modulo rechne ich die 
Position für das Element aus, was neu hinzugefügt werden soll. Also 
neuerIndex = (alterIndex + 1) % maximaleGröße. Nun kann es aber sein, 
dass die Queue voll ist, und das älteste Element überschrieben werden 
muss. Und DANN muss ich die Interrupts für die Zeit sperren.

von Philipp B. (philipp_burch)


Lesenswert?

> Genau so mach ichs, mit einem statischen Array und von vornherein
> festgelegter Größe, z.B. 100 Elemente. Und mit Modulo rechne ich die
> Position für das Element aus, was neu hinzugefügt werden soll. Also
> neuerIndex = (alterIndex + 1) % maximaleGröße. Nun kann es aber sein,
> dass die Queue voll ist, und das älteste Element überschrieben werden
> muss. Und DANN muss ich die Interrupts für die Zeit sperren.

Urks, bitte nicht. Wenn du schon unbedingt modulo verwenden willst, dann 
bitte mit Grössen als Zweierpotenzen (z.B. 128 Byte statt 100). Den 
Index könntest du aber auch einfach auf die Länge des Puffers prüfen und 
gegebenenfalls nullen. Dann gibt's auch keine Probleme mit Überläufen 
(gibt's mit Zweierpotenzen allerdings auch nicht).

Wenn ich's mir so überlege, ist die Verwendung von einem frei 
durchlaufenden Index, der mit modulo (Bzw. optimiert natürlich dann 
einfach and) zurückgesetzt wird sogar noch besser als mit einem 
Vergleich, spart zwei Takte pro Zugriff...
1
inc rp
2
cpi rp, DATALENGTH
3
brne PC + 2
4
  clr rp
5
6
;gegen
7
8
inc rp
9
andi rp, DATALENGTH - 1

von Hannes L. (hannes)


Lesenswert?

> Kannst du mal ein Beispiel deiner push- und pop-Routinen zeigen? Mich
> würde mal interessieren, wie du da die Überprüfung auf Über-/Unterlauf
> mit gewohnt Lux'scher Effizienz eingebaut hast ;)

Ein neues ("effizientes") Beispiel habe ich jetzt nicht greifbar...
Beim Lesen vom Buffer vergleiche ich vorher den Low-Teil beider Pointer 
und breche bei Gleichstand ab (Buffer leer). Beim Schreiben auf den 
Buffer erhöhe ich eine Kopie vom Schreibpointer um 1 und vergleiche die 
mit dem Lesepointer, bei Gleichheit (die eigentlich nie eintreten 
sollte) wird bis zur Ungleichheit gewartet (Buffer voll). Mit Push und 
Pop hat das nix zu tun.

> Wenn ich's mir so überlege, ist die Verwendung von einem frei
> durchlaufenden Index, der mit modulo (Bzw. optimiert natürlich dann
> einfach and) zurückgesetzt wird sogar noch besser als mit einem
> Vergleich, spart zwei Takte pro Zugriff...

Ach Du meinst das Begrenzen des Pointers auf Buffergröße... Das geht 
besonders einfach, wenn man den Buffer auf (SRAM-)Seitenanfang legt.


> ... z.B. 100 Elemente.

Auch beim Benutzen einer Hochsprache sollte man nicht vergessen, dass 
der AVR intern binär arbeitet und ihm Rechnerei mit "100" bedeutend mehr 
Arbeit bereitet als Buffergrößen in Zweierpotenzen.

> Und mit Modulo ...

Der AVR kann nicht (per Hardware) dividieren. Buffergrößen in 
Zweierpotenzen erlauben das Anwenden der AND-Verknüpfung zur 
Pointerbegrenzung, was (im günstigsten Fall (ANDI)) einen einzigen 
Prozessortakt kostet.

Man kann in einer Hochsprache durch unbedachte Wahl der Funktionen bzw. 
Algorithmen einem AVR schon eine Menge unnötige Arbeit aufhalsen. 8-)

...

von Philipp B. (philipp_burch)


Lesenswert?

Hannes Lux wrote:
>> Kannst du mal ein Beispiel deiner push- und pop-Routinen zeigen? Mich
>> würde mal interessieren, wie du da die Überprüfung auf Über-/Unterlauf
>> mit gewohnt Lux'scher Effizienz eingebaut hast ;)
>
> Ein neues ("effizientes") Beispiel habe ich jetzt nicht greifbar...
> Beim Lesen vom Buffer vergleiche ich vorher den Low-Teil beider Pointer
> und breche bei Gleichstand ab (Buffer leer). Beim Schreiben auf den
> Buffer erhöhe ich eine Kopie vom Schreibpointer um 1 und vergleiche die
> mit dem Lesepointer, bei Gleichheit (die eigentlich nie eintreten
> sollte) wird bis zur Ungleichheit gewartet (Buffer voll). Mit Push und
> Pop hat das nix zu tun.
>
>> Wenn ich's mir so überlege, ist die Verwendung von einem frei
>> durchlaufenden Index, der mit modulo (Bzw. optimiert natürlich dann
>> einfach and) zurückgesetzt wird sogar noch besser als mit einem
>> Vergleich, spart zwei Takte pro Zugriff...
>
> Ach Du meinst das Begrenzen des Pointers auf Buffergröße... Das geht
> besonders einfach, wenn man den Buffer auf (SRAM-)Seitenanfang legt.

Achso, du verwendest jeweils "echte" Pointer. Bei mir ist das jeweils 
eine 8-Bit-Variable als Index. Erfordert dann natürlich noch etwas mehr 
Rechnerei.
Mit push und pop meinte ich eigentlich das Laden eines Bytes in den 
Puffer, bzw. das Holen davon (Wie beim Stack halt). Mit Busy-Wait 
rumhängen bis wieder Platz ist, ist natürlich auch eine Idee ;)

von Simon K. (simon) Benutzerseite


Lesenswert?

Philipp Burch wrote:
> Achso, du verwendest jeweils "echte" Pointer. Bei mir ist das jeweils
> eine 8-Bit-Variable als Index. Erfordert dann natürlich noch etwas mehr
> Rechnerei.

Ja, und zwar jedes mal wenn du schreibst/liest. Also jedesmal wenn du 
das Array indizierst... Nicht so toll :-) Zwei einzelne Pointer finde 
ich besser.

von Hannes L. (hannes)


Lesenswert?

> Mit Busy-Wait
> rumhängen bis wieder Platz ist, ist natürlich auch eine Idee ;)

Das schmeckt mir auch nicht, deshalb habe ich bei den LCD-Routinen bei 
der alten Zünduhr auch darauf verzichtet. Der Buffer umfasst aber eine 
ganze Page, da ist Überlauf mehr als unwahrscheinlich. Wenn es zum 
Überlauf kommen kann, dann klemmt irgendwas Anderes im Programm. Dann 
sollte man das Konzept überprüfen. ;-)

Ich hatte auch bisher keine Anwendung, wo der AVR z.B. so schnell mit 
UART-Daten zugeschüttet wird, dass ihm die Zeit fehlt, sie zu 
verarbeiten.

In einem Fall (die neue Zünduhr) schaufelt ein PC-Programm die Zünddaten 
aus einer EXCEL-Datei per UART in den Mega8535 der Zünduhr, der sie dann 
ins eigene EEP brennt. Da dies (durch das EEP-Brennen) länger dauert, 
hält das PC-Programm Pausen zwischen den Datensätzen ein. Es kommt eben 
darauf an, dass sich beide Kommunikationspartner verstehen, hohe 
Datenübertragungsrate alleine ist eben nicht alles...

...

von Hannes L. (hannes)


Lesenswert?

Simon Küppers wrote:
> Philipp Burch wrote:
>> Achso, du verwendest jeweils "echte" Pointer. Bei mir ist das jeweils
>> eine 8-Bit-Variable als Index. Erfordert dann natürlich noch etwas mehr
>> Rechnerei.
>
> Ja, und zwar jedes mal wenn du schreibst/liest. Also jedesmal wenn du
> das Array indizierst... Nicht so toll :-) Zwei einzelne Pointer finde
> ich besser.

Ich halte die L-Bytes der Pointer auch im SRAM, der H-Teil des gerade 
benötigten Pointers wird immidiate gesetzt, der Buffer reicht ja nicht 
über Pagegrenzen. Natürlich nutze ich zum Indizieren "echte Pointer", 
also die letzten Registerpaare des AVRs. Da diese auch noch andere 
Aufgaben haben, werden sie mit Push/Pop gesichert.

...

von Philipp B. (philipp_burch)


Lesenswert?

Hannes Lux wrote:
>> Mit Busy-Wait
>> rumhängen bis wieder Platz ist, ist natürlich auch eine Idee ;)
>
> Das schmeckt mir auch nicht, deshalb habe ich bei den LCD-Routinen bei
> der alten Zünduhr auch darauf verzichtet. Der Buffer umfasst aber eine
> ganze Page, da ist Überlauf mehr als unwahrscheinlich. Wenn es zum
> Überlauf kommen kann, dann klemmt irgendwas Anderes im Programm. Dann
> sollte man das Konzept überprüfen. ;-)
>
> Ich hatte auch bisher keine Anwendung, wo der AVR z.B. so schnell mit
> UART-Daten zugeschüttet wird, dass ihm die Zeit fehlt, sie zu
> verarbeiten.

Kann aber noch kommen. Du wolltest die Firmware für die Drucker doch 
selbst schreiben, nicht wahr? ^^
Dort musste ich mir mit Flusskontrolle behelfen (Daher ist RTS auf dem 
Schema auch verbunden), um mit der Datenmenge zurechtzukommen, da die 
Druckvorgänge je nach "Pixeldichte" unterschiedlich lange dauern.

> In einem Fall (die neue Zünduhr) schaufelt ein PC-Programm die Zünddaten
> aus einer EXCEL-Datei per UART in den Mega8535 der Zünduhr, der sie dann
> ins eigene EEP brennt. Da dies (durch das EEP-Brennen) länger dauert,
> hält das PC-Programm Pausen zwischen den Datensätzen ein. Es kommt eben
> darauf an, dass sich beide Kommunikationspartner verstehen, hohe
> Datenübertragungsrate alleine ist eben nicht alles...
>
> ...

Auch eine Idee. Hardware-Flusskontroller war keine Alternative?

Simon Küppers wrote:
> Philipp Burch wrote:
>> Achso, du verwendest jeweils "echte" Pointer. Bei mir ist das jeweils
>> eine 8-Bit-Variable als Index. Erfordert dann natürlich noch etwas mehr
>> Rechnerei.
>
> Ja, und zwar jedes mal wenn du schreibst/liest. Also jedesmal wenn du
> das Array indizierst... Nicht so toll :-) Zwei einzelne Pointer finde
> ich besser.

Ach, so tragisch ist das auch wieder nicht. Wenn etwas zeitkritisch ist, 
dann programmiere ich es in Assembler (Das ganze Prog) und optimiere 
auch dementsprechend. In C ist es mir aber lieber, ich kann ein Array 
über die Indizes ansprechen, anstatt an den Pointern rumzubasteln.

von Hannes L. (hannes)


Lesenswert?

> Kann aber noch kommen.

Das kommt mit Sicherheit.

> Du wolltest die Firmware für die Drucker doch
> selbst schreiben, nicht wahr? ^^

Jou... Spätestens dann kommt das. Trotzdem versuche ich im Vorfeld, 
solche Probleme zu vermeiden. Das ist aber nicht immer möglich (z.B. bei 
dem Drucker).

> Dort musste ich mir mit Flusskontrolle behelfen (Daher ist RTS auf dem
> Schema auch verbunden), um mit der Datenmenge zurechtzukommen, da die
> Druckvorgänge je nach "Pixeldichte" unterschiedlich lange dauern.

Prima Idee, werde ich dann vermutlich auch nutzen, hoffentlich klappt 
das auch noch hinter einem USB-SER-Adapter... ;-)

> Auch eine Idee. Hardware-Flusskontroller war keine Alternative?

In diesem Fall nicht, bei der ersten Version war kein Pin mehr frei, bei 
der aktuellen Version war es nicht nötig. Ich habe es sogar noch 
zusätzlich ausgebremst, damit man die Fortschrittsanzeige auch 
wahrnehmen kann. ;-) Und bei 512 Bytes Nutzdaten (allerdings HEX-codiert 
übertragen) interessiert das Tempo nicht wirklich... Als es noch 
schneller ging, war der Benutzer nie sicher, ob die Daten auch 
übertragen wurden. Nun sieht er anhand der Fortschrittsanzeige (xxx / 
170), dass das System (explosive) Daten schaufelt.

...

von Philipp B. (philipp_burch)


Lesenswert?

Hannes Lux wrote:
>> Dort musste ich mir mit Flusskontrolle behelfen (Daher ist RTS auf dem
>> Schema auch verbunden), um mit der Datenmenge zurechtzukommen, da die
>> Druckvorgänge je nach "Pixeldichte" unterschiedlich lange dauern.
>
> Prima Idee, werde ich dann vermutlich auch nutzen, hoffentlich klappt
> das auch noch hinter einem USB-SER-Adapter... ;-)

Tut es. Zumindest bei meinem (Prolific).

>> Auch eine Idee. Hardware-Flusskontroller war keine Alternative?
>
> In diesem Fall nicht, bei der ersten Version war kein Pin mehr frei, bei
> der aktuellen Version war es nicht nötig. Ich habe es sogar noch
> zusätzlich ausgebremst, damit man die Fortschrittsanzeige auch
> wahrnehmen kann. ;-) Und bei 512 Bytes Nutzdaten (allerdings HEX-codiert
> übertragen) interessiert das Tempo nicht wirklich... Als es noch
> schneller ging, war der Benutzer nie sicher, ob die Daten auch
> übertragen wurden. Nun sieht er anhand der Fortschrittsanzeige (xxx /
> 170), dass das System (explosive) Daten schaufelt.

g

3...
2...
1...
0...
BOOOM

von Hannes L. (hannes)


Lesenswert?

> 3...
> 2...
> 1...
> 0...
> BOOOM

Genau, und das mit bis zu 170 BOOOMs auf die Zehntelsekunde genau...
Nur eben nicht beim Datentransfer, sondern später auf dem Abbrennplatz.
;-)

...

von Simon K. (simon) Benutzerseite


Lesenswert?

Wo liegt denn eigentlich genau euer Problem? Einen Überlauf beim FIFO 
mit einzelnem Read/Write Pointer herauszufinden?

Man kann doch, bevor man das neue Byte reinschiebt, gucken ob die 
beiden Pointer direkt nebeneinander platziert sind. Sprich: Wenn ich 
jetzt den Write-Pointer inkrementiere, würden beide Zeiger 
"kollidieren".

Zack: Funktion weiß: Überlauf ist passiert.

Oder habe ich was verpasst? :-)

von Philipp B. (philipp_burch)


Lesenswert?

Simon Küppers wrote:
> Wo liegt denn eigentlich genau euer Problem? Einen Überlauf beim FIFO
> mit einzelnem Read/Write Pointer herauszufinden?
>
> Man kann doch, bevor man das neue Byte reinschiebt, gucken ob die
> beiden Pointer direkt nebeneinander platziert sind. Sprich: Wenn ich
> jetzt den Write-Pointer inkrementiere, würden beide Zeiger
> "kollidieren".
>
> Zack: Funktion weiß: Überlauf ist passiert.
>
> Oder habe ich was verpasst? :-)

Problem haben wir eigentlich gar keins, ich hab' nur etwas laut gedacht 
und ihr habt geantwortet ;)

Wenn die Daten z.B. per RS232 reinkommen, dann ist das ja nicht so ein 
Problem, wenn das FIFO mal überläuft, dann kann ich die Daten einfach 
verwerfen, da sich der Datenstrom eh nicht einfach so unterbrechen lässt 
("Wenn man mal ein Interrupt verpasst ist das ja nicht so schlimm, kommt 
ja bald wieder ein neuer" ;D ). In anderen Fällen wird aber ein FIFO auf 
Anforderung gefüllt und da hatte ich jeweils einfach keine gescheite 
Idee, wie FIFO_Push nun zurückliefern soll, dass der Puffer voll ist. 
Denn da ist es Voraussetzung, dass das gerade übergebene Byte in jedem 
Fall noch geschrieben wird, es muss also noch Platz sein. Daher habe ich 
mir dann angewöhnt, einfach den Füllstand jeweils noch zu aktualisieren, 
da ich damit zu jeder Zeit schauen kann, ob noch Platz ist und ob Daten 
zum Auslesen vorhanden sind.

Aber egal, das geht hier langsam off-topic ;)

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.