hallo zusammen,
ich habe folgendes Problem. Zurzeit versuche ich (mithilfe der jeelib)
Daten über 2 RFM12b (868) von einen Arduino auf einen anderen zu
übertragen. Dabei funktioniert das ganze über Polling ohne Probleme.
Nun möchte ich das ganze auf Interrupt umbauen.
Aktuell löst der Interrupt verlässlich aus, sobald der Sender Daten
funkt, diesen überprüfe ich in der ISR mithilfe des Statusbits und lese
bei 0x8xxx den FIFO aus. (0x8xxx, da die 8 für das FFIT steht, bekomme
aber meistens 0x801E, selten z.b. 0x8060 und fast nie 0x8000)
Leider bekomme ich je nach Status unterschiedliche (schlechte) Werte,
welche ich über 0xB000 auslese.
Beim meistens gelesenem 0x801E bekomme ich immer (unabhängig von
gesendeten Daten) 31dez, bei den anderen Kombinationen andere,
verschiedene falsche Werte.
Meine aktuelle Config sieht folgendermaßen aus:
0x80E8
0x82D9
0xA640
0xC606
0x97A2
0xC22C
0xCA81
0xCED4
0xC467
0x9800
0xCC77
0xB8AA
0xC80E
0xCA83
Grüße, Manfred
Ist zwar bei mir mit dem RFM12 schon etwas her, aber ich hatte damals
meine Ansteuerung auch komplett über Interrupts gelöst.
Immer wenn das RFM-Modul ein Interrupt ausgelöst hat habe ich per SPI
das Statusregister ausgelesen. Und je nach dem in welchen Zustand die
Software war darauf reagiert.
Meinen Code von damals will ich hier nicht posten, aber so hatte ich ihn
konfiguriert:
Hab für das Modul 3 Oberzustände: Idle, Listening, und Sending
Nach dem ich die obige Konfiguration gesetzt habe, kommt noch ein
abschließendes 0x8201 um in den Idel-Zustand zu gehen.
Hab hier noch mal nachgeschaut
https://www.silabs.com/documents/public/data-sheets/Si4420.pdf
Das letzte POWER_MANAGMENT Command setzt das Modul wieder in den
Tiefschlaf
Zur Vollständigkeit:
Für den ListeningMode sende ich
0x82C8 und 0xCA83
und für den SendingMode
0x8238
Ich benutze den RFM12b, aber der sollte ja bis auf die
Versorgungsspannung weitestgehend identisch sein. leider ist der FIFO
lesebefehl nicht sehr ausführlich.
Irgendetwas liegt bei mir noch im Argen...mit deiner Config passiert wie
gesagt überhaupt nichts. Ich betreibe sie sogar jeweils fest als Sender
und den anderen als Empfänger, da muss das doch nochmal ein Stück
einfacher sein.
Hast du die Daten auch per 0xB000 ausgelesen?
Der nIRQ-Ausgang bleibt solange Hi, bis alle 6-Interrupt-Bits gelöscht
sind.
Während die Bits 1-5 durch das Auslesen des Statusregister gelöscht
werden, wird Bit 0 (also RGIT/FFIT) nur gelöscht wenn:
* der FIFO beim Empfangen-Modus unterhalb des Limits (Parameter "f", bei
mir 8 Bit) ist
* und beim Senden-Modus der FIFO NICHT voll.
Wie gesagt lese ich immer das ganze Statusregister aus, wenn nIRQ auf Hi
geht.
EDIT:
Mein Code ist vom 2010, und habe jetzt beim Überfliegen einen Bug
entdeckt, wenn im FIFO genau 16 Bit liegen (also voll aber noch kein
overflow) bleibt meine State-Maschine hängen, da ich 8 Bits auslese,
bleiben ja noch 8 Bits im Fifo und nSEL geht nicht auf LOW. Aber ich
reagiere nur auf Steigende Flanken.
Da meine Software aber den Fifo immer direkt ausliest kommt es nie zu
diesem Problem. Zumindest in der Anwendung damals.
Danke für die Info. Ich habe das Datenblatt bereits gelesen, nur kriege
ich den Baustein damit nicht zum Laufen.
Den nsel benutze ich nicht. Im Grunde hat er ja die gleiche Funktion wie
nIRQ, nur andersherum gerichtet.
Mir ist aufgefallen dass du nirq auf steigende Flanke auswertest. Beim
RFM12b muss ich auf fallende Flanke (oder direkt low) abfragen. Ich
werde mir heute Abend mal zusätzlich das Datenblatt vom RFM12 ansehen.
Manfred Bär schrieb:> Den nsel benutze ich nicht. Im Grunde hat er ja die gleiche Funktion wie> nIRQ, nur andersherum gerichtet.
Mein Fehler, ich meinte natürlich nIRQ.
Manfred Bär schrieb:> Beim> RFM12b muss ich auf fallende Flanke
Auch da hast du recht. Es ist natürlich die fallende Flanke auf die
getriggert werden muss. Wie gesagt das Projekt liegt bei mir bereits 7
Jahre zurück.
Kein Problem, ich bin für jede Hilfe dankbar.
Leider bin ich bis morgen Abend nicht zuhause, aber mir ist folgendes
klargeworden:
Beim Auslesen der Interrupts habe ich nur diejenigen mit 0x8xxx
"durchgelassen" (und in der Konsole ausgegeben.) Nun erinnere ich mich
aber daran -wenn ich alle ankommenden Interrupts angezeigt habe- dass
jeder zweite 0x2xxx (oder 0xAxxx) war. In beiden Fällen ist das
FIFO-Überlaufbit gesetzt...und das obwohl ich es auslese...Demzufolge
müsste ja der FIFO-Read-Befehl (von jeelib) nicht richtig funktionieren.
Das wiederum kann ich mir nicht vorstellen.
Wieviele Bits werden denn mit einem Befehl ausgelesen? Im Datenblatt
wird nicht darauf eingegangen. Sollten es z.b. 8Bit sein müsste ich es
ja 2x hintereinander auslesen um es komplett zu leeren (falls es
komplett voll war).
Naja, ich werde das ganze verifizieren sobald ich Zeit finde.
Also, scheinbar funktioniert doch fast(!) alles wie es soll. Es ist nur
so, dass der empfangene Wert "33" eben nicht die gewollten Daten sind
sondern HDR (wie hier zu sehen:
http://jeelabs.org/2011/06/09/rf12-packet-format-and-design/index.html#comments).
...nur wie schaffe ich es nun stattdessen auf meine Daten zuzugreifen?
Mit der RF12 library hab ich keine Erfahrung.
Hab da alles selber gemacht. Inclusive eines Package-Format: 6Byte
Ziel-Adresse, 6Byte Source-Adresse, 5 Bit Typ, 11 Bit Länge, Payload und
2 Byte CRC
Hab mich da an Ethernet Orientiert.
Bin mir gerade nicht sicher was du vor hast, willst du von der jeelib
weg und alles selber machen, oder die Lib umbauen?
das ist mein erstes Projekt mit Funkmodulen und ich will dafür
eigentlich nur Temperaturdaten von einem zum anderen senden. Für dieses
mal reicht mir auch einfach eine funktionierenden fertige Library, nur
leider bekomme ich die Übertragung über Interrupt nicht hin.
Kurz: Ich will sowenig wie möglich umbauen, sondern nur eine einfache
Funkstrecke über Interrupts.
Hallo zusammen,
Gut, um meine Frage zu präzisieren:
Angenommen ich sende ein Paket mit einer großen Datenmenge, z.B. 8Byte.
Der FIFO kann ja nur 2Byte halten. Natürlich kann ich die ersten 2Byte
über den Interrupt zügig abholen, aber liefert der Sender nicht die
8Byte in einem Schwung?
Selbstverständlich lässt sich bei ein großes Paket in einzelne kleine
Pakete aufteilen, um die Signale geordnet abzuarbeiten. Dieses Beispiel
soll aber nur mein Problem verdeutlichen.
In meinem Fall wird ein 16Bit-Wert übertragen, aber vor den Daten werden
Syncbyte etc. übertragen, diese landen nicht im FIFO, das habe ich
verstanden. Aber direkt vor meinen Daten werden noch Informationen
bezüglich Länge der Daten, Acknowledge und die NodeID übertragen, nun
schaffe ich es nur genau diese Daten auszulesen. Die (vermutlich) ohne
Unterbrechung danach gesendeten für mich wichtigen Daten würden den FIFO
während ich noch in der ISR bin überschreiben.
Nun habe ich einen Versuch gestartet den FIFO erst beim 2. Auffüllen
(was meine Daten sein sollten) auszulesen, auch hier empfange ich leider
das gleiche wie zuvor.
Gruß, Manfred
Manfred Bär schrieb:> Angenommen ich sende ein Paket mit einer großen Datenmenge, z.B. 8Byte.> Der FIFO kann ja nur 2Byte halten.
Maximal!
Das Interruptflag wird aber gesetzt, sobald er das erste Byte im Sack
hat. Die restlichen Bits des FIFO sind nur der Puffer, der es dir
überhaupt ermöglicht, dieses eine Byte abzuholen, bevor das erste Bit
des nächsten da ist und in das Empfangsregister übernommen werden
müsste...
Sprich: das ist eigentlich ein klassischer double buffer. Jedenfalls in
dieser Anwendung. Man kann die RFM12 auch anders verwenden und den FIFO
tatsächlich bitweise verwenden, aber das ist wohl weit jenseits deiner
Liga und vor allem: für deine Anwendung auch nicht nützlich...
> Natürlich kann ich die ersten 2Byte> über den Interrupt zügig abholen
Das darfst du nicht! Du würdest einfach das gleiche Byte noch einmal
bekommen. Erst wenn das nächste Mal das Interruptflag gesetzt ist, sind
die nächsten 8 empfangenen Bits in dem Register gelandet, was du
auslesen kannst.
> aber liefert der Sender nicht die> 8Byte in einem Schwung?
Ja klar, jedenfalls, wenn du den Sender so programmiert hast, dass er
das tun soll...
> Selbstverständlich lässt sich bei ein großes Paket in einzelne kleine> Pakete aufteilen, um die Signale geordnet abzuarbeiten.
Das ist nicht nötig. Man muß einfach nur das eine Paket richtig
verarbeiten, das ist alles.
> Aber direkt vor meinen Daten werden noch Informationen> bezüglich Länge der Daten, Acknowledge und die NodeID übertragen, nun> schaffe ich es nur genau diese Daten auszulesen. Die (vermutlich) ohne> Unterbrechung danach gesendeten für mich wichtigen Daten würden den FIFO> während ich noch in der ISR bin überschreiben.
Dann ist deine ISR schlicht zu langsam. Wenn du nicht in der Lage bist,
daran etwas zu ändern, hilft nur eins: du mußt das gesamte Datenpaket im
RAM puffern und ansonsten nix in der ISR tun. Maximal die Länge des zu
erwartetenden Paketes aus den empfangenen Daten extrahieren, wenn diese
Länge nicht fix ist.
Erst wenn das Paket vollständig empfangen ist, geht's weiter. Aber nicht
in der ISR. Oder zumindest nicht ohne Ende der exclusiven
Codeabarbeitung in dieser letzten ISR-Instanz des Paketes...
Danke für deine schnelle Antwort.
c-hater schrieb:> Maximal!>> Das Interruptflag wird aber gesetzt, sobald er das erste Byte im Sack> hat. Die restlichen Bits des FIFO sind nur der Puffer, der es dir> überhaupt ermöglicht, dieses eine Byte abzuholen, bevor das erste Bit> des nächsten da ist und in das Empfangsregister übernommen werden> müsste...
Meine Datenvariable ist derzeit ein int mit 16 Bit, also past genau in
den FIFO. Ein Anfang wäre es also, stattdessen ein unit8_t zu verwenden?
Für meine Daten wäre das kein Problem. Mein Interrupt "feuert" bereits
nach den ersten 8 Bit, demzufolge ging ich davon aus, dass es klappen
sollte.
c-hater schrieb:> Das darfst du nicht! Du würdest einfach das gleiche Byte noch einmal> bekommen. Erst wenn das nächste Mal das Interruptflag gesetzt ist, sind> die nächsten 8 empfangenen Bits in dem Register gelandet, was du> auslesen kannst.
Ich ging davon aus, dass beim Auslesen die Daten im FIFO jeweils um 1
Byte weitergeschoben werden. Danke für die Aufklärung. Jetzt macht auch
die Aussage mit dem Dummybyte zum Schluss (über die ich hin und wieder
gestolpert bin) Sinn. Vielen Dank für die Erleuchtung.
c-hater schrieb:>> aber liefert der Sender nicht die>> 8Byte in einem Schwung?>> Ja klar, jedenfalls, wenn du den Sender so programmiert hast, dass er> das tun soll...
das stimmt natürlich, ich wollte damit auf das zeitliche Problem -dass
nach meinem Verständnis auftritt- verdeutlichen. Ich verwende die
Jeelib, und habe vor allem was den Aufbau der gesendeten Pakete angeht
wenig Überblick.
c-hater schrieb:> Dann ist deine ISR schlicht zu langsam. Wenn du nicht in der Lage bist,> daran etwas zu ändern, hilft nur eins: du mußt das gesamte Datenpaket im> RAM puffern und ansonsten nix in der ISR tun. Maximal die Länge des zu> erwartetenden Paketes aus den empfangenen Daten extrahieren, wenn diese> Länge nicht fix ist.
meine ISR ist zugegebenermaßen relativ lang, allerdings wird der
Großteil nur ausgeführt wenn ich wirklich die Nutzdaten empfangen habe.
Soweit der Plan. Abgesehen davon ist sie vernachlässigbar im Bezug auf
die zeitlichen Abstände zwischen den Paketen.
Mein letzter Versuch war folgender:
Um auf die Daten die später übertragen werden zugreifen zu können, habe
ich beim ersten Interrupt nur einen Counter inkrementiert, den FIFO
ausgelesen um den FFIT abzuarbeiten und den FIFO neu gestartet. Viel
kürzer lässt sich der ISR nicht erledigen oder?
Erst wenn der Counter eine gewisse Anzahl erreicht hat, werden die Daten
erfasst und weiterverarbeitet.
Ist dies prinzipiell so machbar? Wenn ja hat sich einfach noch ein
Fehler eingeschlichen, wenn nein wäre ich über Aufklärung dankbar :)
Manfred Bär schrieb:> Um auf die Daten die später übertragen werden zugreifen zu können, habe> ich beim ersten Interrupt nur einen Counter inkrementiert, den FIFO> ausgelesen um den FFIT abzuarbeiten
Bis hierher schick und richtig.
> und den FIFO neu gestartet
Vollkommen falsch. Der muß einfach weiter laufen, um die nächsten 8 Bits
zu empfangen. Einige davon hat er mit einiger Wahrscheinlichkeit bereits
empfangen, während du noch das Flagregister und das empfangene Byte
liest. Wenn du jetzt den Fifo zurücksetzt, sind diese Bits wech und der
ganze Rest, den du ausliest, wird Müll sein...
c-hater schrieb:>> und den FIFO neu gestartet>> Vollkommen falsch. Der muß einfach weiter laufen, um die nächsten 8 Bits> zu empfangen. Einige davon hat er mit einiger Wahrscheinlichkeit bereits> empfangen, während du noch das Flagregister und das empfangene Byte> liest. Wenn du jetzt den Fifo zurücksetzt, sind diese Bits wech und der> ganze Rest, den du ausliest, wird Müll sein...
meine Intention dahinter war, den FIFO für den nächsten Interrupt zu
befähigen.
Dafür schreibe ich die Befehle
0xCA81 und danach 0xCA83
Eventuell habe ich mich einfach schlecht/falsch ausgedrückt.
Ok, ich habe das Datenblatt nochmal zu Rate gezogen. In der Tat
unterbrechen diese das Auffüllen des FIFO für eine kurze Zeit. Womöglich
habe ich etwas falsch interpretiert. Das bedeutet, durch das Auslesen
allein wird der Interrupt zurückgesetzt und ist wieder startbereit.
Manfred Bär schrieb:> Das bedeutet, durch das Auslesen> allein wird der Interrupt zurückgesetzt und ist wieder startbereit.
Genauso ist das. Analog dazu funktioniert auch das Senden, hier setzt
der Schreibvorgang den Interrupt zurück.
Alle anderen Interruptbits werden hingegen allein schon durch das Lesen
des Statusregisters zurückgesetzt. Das Schema der Interruptbehandlung
ist also ziemlich einfach.
Allerdings gibt es eine Sache, die es dann doch wieder komplizierter
macht. Nämlich den Fall, dass ein Interrupt auftritt, während du gerade
beim Datenaustausch mit dem RFM bist, also gerade ein SPI-Vorgang läuft.
Dann darf der NICHT einfach abgebrochen werden, um das Statusregister
zu lesen, sondern sollte schön bis zum Ende ausgeführt werden. Erst
danach dann das Statusregister lesen.
Das RFM12(B) hat im Gegensatz zu seinem mittlerweile ganz gut
etablierten Nachfolger RFM69CW den Nachteil, dass man den Empfang
aufgrund des sehr kleinen FIFOs quasi in Echtzeit abwickeln muss: Das
Byte vom Sender kommt rein, das Modul signalisiert das, man muss die
Daten umgehend abholen und dann heißt es darauf zu warten, bis das
nächste eintrudelt, womit die Story wieder von vorne beginnt. Wie
schnell hintereinander die Daten eintreffen, liegt logischerweise an der
Datenrate des Senders.
Ein Neustart des FIFO zwischen den Bytes darf nicht erfolgen, denn damit
sperrt man die Annahme von Bytes so lange, bis die beiden Schlüsselbytes
(beim RFM12 0x2D, 0xD4) wieder gesendet werden, um den FIFO wieder für
den Datenempfang zu öffnen. Erst wenn das komplette Datenpaket empfangen
wurde, sollte man den Empfänger neu starten.
Hallo Felix, das stimmt...man verliert nicht nur die Daten während der
FIFO ausgeschaltet ist, sondern das komplette restliche Paket. Danke für
den Hinweis.
dank c-hater konnte ich gestern auch meinen Empfang relativ stabil auf
meine Daten umstellen. Das bedeutet: Löst der Interrupt aus bekomme ich
bis auf seltene Ausnahmen (fehlerhafte Übertragung?) richtige Daten am
Empfänger. Vielen Dank!
Ein kleiner Dämpfer ist die Tatsache, dass die Interrupts nicht immer
mitspielen. Damit meine ich nicht, dass ein paar verschluckt werden,
vielmehr geht nach dem Kompilieren und Überspielen des Codes entweder
(fast) alles reibungslos oder garnichts. Auch wenn ich den Arduino
während er fleissig empfängt abstecke und wieder anstecke ist nicht
sicher ob er wirklich wieder Ints registriert.
Ich habe schon mitbekommen, dass die RFMs zickig seien, was die
Versorgungsspannung angeht, aber das genannte Verhalten spricht meiner
Meinung nach nicht für unsaubere Versorgung.
Manfred Bär schrieb:> Ein kleiner Dämpfer ist die Tatsache, dass die Interrupts nicht immer> mitspielen. Damit meine ich nicht, dass ein paar verschluckt werden,> vielmehr geht nach dem Kompilieren und Überspielen des Codes entweder> (fast) alles reibungslos oder garnichts.
Bekanntes Problem. Drei mögliche Ursachen:
1)
Du hast den Reset des RFM nicht mit dem Reset deines Steuercontrollers
gekoppelt (und benutzt die Hardware-SPI-Schnittstelle deines Controllers
zur Kommunikation mit dem RFM und hast keine Vorsorge im Sinne der
Eigensicherheit getroffen, also AVR-SS/RFM-SEL nicht per physischem
Widerstand auf Low gezogen).
Dann hast du dem RFM u.U. implizit DURCH das Programmieren völligen
Gurkensalat gesendet, von dem er sich nur schwer wieder erholt. Beachte:
SPI und ISP benutzen bei den meisten Megas die gleichen Pins...
2)
Du hast den Reset des RFM mit dem Reset deines Controllers mit dem Reset
des RFM gekoppelt.
Dann hast du zwar nicht das Gurkensalat-Problem, aber dafür ein anderes:
Nach einem Reset des RFM mußt du eine gewisse, recht lange Zeit warten,
bevor du ihn das erste Mal mit irgendeiner SPI-Aktivität behelligen
darfst, sonst landet er auch wieder im Wald und findet nicht mehr raus.
Ich kann mich nicht mehr erinnern, was das DB dazu sagte (auf jeden Fall
wurde das Problem dort erwähnt, da bin ich ziemlich sicher), ich warte
in meinem Code jedenfalls in dieser Situation mindestens 50ms. Und er
funktioniert sehr zuverlässig, auch nach einem Reset. ;o)
3)
Du benutzt einfach den falschen Typ Interrupt in deinem AVR. Die
Interrupts des RFM sind vom Charakter her Level-Interrupts, also so
lange Low, wie irgendeine Interruptursache besteht, also nicht alle
Interrupts zurückgesetzt sind. Dementsprechend kannst du auf AVR-Seite
auch nur entweder einen LowLevel-Interrupt verwenden oder du mußt die
ISR eines Edge-Interrupts so gestalten, dass effektiv die Wirkung eines
LowLevel-Interrupts erzielt wird.
Ich mache das in meinem Code allerdings ziemlich anders. Bei mir läuft
die gesamte Logik der Kommunikation mit dem RFM in einer Statemachine in
der SPI-ISR. Den externen Interrupt des RFM benutze ich nur dazu, um den
SPI-Interrupt zu enablen, wenn er das gerade nicht ist. Die SPI-ISR
ihrerseit muss dann natürlich immer am Ende eines Datenaustauschs
entscheiden, was sie weiter tut. Dazu schaut sie auf den Pegel des
Interruptpins. Ist der Low, macht sie eine Abfrage des Statusregisters
des RFM, ansonsten disabled sie ihren eigenen Interrupt.
Das Ergebnis ist das gleiche, wie bei Verwendung eines
LowLevel-Interupts, die Sache ist nur deutlich effizienter und hat den
Vorteil, dass das Problem des RFM-Interrupts bei laufender
SPI-Kommunikation ohne jeden zusätzlichen Verwaltungsaufwand gleich mit
abgehandelt wird.
hallo c-hater,
danke für die ausführliche Antwort!
ich habe tatsächlich die Resets nicht gekoppelt.
Das heißt ich verbinde die beiden Pins und lege sie über Pulldown auf
Masse?
c-hater schrieb:> Nach einem Reset des RFM mußt du eine gewisse, recht lange Zeit warten,> bevor du ihn das erste Mal mit irgendeiner SPI-Aktivität behelligen> darfst, sonst landet er auch wieder im Wald und findet nicht mehr raus.
da das Projekt später in der Regel durchlaufen soll und dementsprechend
nicht oft einen Reset erfährt ist die Wartezeit unproblematisch, ich
gönne ihm aktuell sogar 200ms :)
c-hater schrieb:> Ich mache das in meinem Code allerdings ziemlich anders. Bei mir läuft> die gesamte Logik der Kommunikation mit dem RFM in einer Statemachine in> der SPI-ISR. Den externen Interrupt des RFM benutze ich nur dazu, um den> SPI-Interrupt zu enablen, wenn er das gerade nicht ist. Die SPI-ISR> ihrerseit muss dann natürlich immer am Ende eines Datenaustauschs> entscheiden, was sie weiter tut. Dazu schaut sie auf den Pegel des> Interruptpins. Ist der Low, macht sie eine Abfrage des Statusregisters> des RFM, ansonsten disabled sie ihren eigenen Interrupt.
das klingt nach einer gut durchdachten Möglichkeit, allerdings traue ich
mich noch nicht dran, sowas in der Art selbst umzusetzen.
Nach längerer Zeit habe ich nun wieder Zeit gefunden.
Ich hab nun die beiden Reset-Pins miteinander verbunden, leider
funktioniert nun garnichts mehr. Auch mit einem Testprogramm zum
Überprüfen der Funktionalität der Module wird nichts empfangen. Es
scheint so als ob durch die Verbindung der Resets der Bausteil zerstört
wurde?