Ich beschäftige mich schon geraume Zeit mit der Thematik, wie ich die
Daten aus dem RFM01 per Interrupt auslesen kann. Leider komme ich nicht
recht voran!
Ich habe mir eine Lösung für den RFM12 angeschaut, dort wird nach in der
ISR der Befehl 0xB0 zum RFM12 geschickt und danach ein Byte aus dem FIFO
ausgelesen! Daraus schlussfolgere ich, dass jedes einzelne Byte einen
Interrupt auslösen muss! Hierbei werden keine Statusbytes gelesen und
verarbeitet.
Im Vergleich dazu wird in der Polling Prozedur aus Benedikts Source Code
nachdem das SDO Pin Low ist 16 Bit (Statusregister ausgelesen und
verworfen, danach wird ein Byte gelesen und verarbeitet! Wenn die
Paketgröße also 16 Byte beträgt, wird die Statusbytes 16mal gelesen und
verworfen.
Woher kommt dieser Unterschied? Warum muss ich beim RFM12 erst noch
einen Befehl hinschicken, und beim RFM01 erst Statusdaten lesen?
Bei der Polling Struktur ist natürlich immer klar, wann ein Paket zu
Ende ist, das ist bei der Interruptstruktur nur mit Hilfe einer
statischen Hilfsvariable möglich. Das heißt, dass Risiko ist hoch, dass
ein Byte verlorengeht und dann keine sinnvolle Information zu Stande
kommt.
Kann mir jemand erklären, wie man es richtig macht?
ArduStemmi schrieb:> Warum muss ich beim RFM12 erst noch> einen Befehl hinschicken, und beim RFM01 erst Statusdaten lesen?
Der RFM12 ist ein Transceiver, d.h. er kann senden und empfangen. Der
RFM01 ist ein reiner Empfänger und deswegen muss man ihm nicht
mitteilen, das er empfangen soll.
ArduStemmi schrieb:> Das heißt, dass Risiko ist hoch, dass> ein Byte verlorengeht und dann keine sinnvolle Information zu Stande> kommt.
Gerade bei der Interuptsteuerung geht sehr wenig verloren, weil die
Prioritäten hoch sind.
Leider ist der Grossteil meines IRQ gesteuerten RFM Moduls verloren
gegangen, aber das Prinzip geht so:
* Pinchange Interrupt für den Pin, an dem nIRQ vom Modul dran ist,
einrichten, aber noch nicht freigeben.
* (an den RFM sende ich dazu : 0xc0a2, 0xce88, 0xce8b)
* Empfänger initialisieren und Fifo anschalten. Datenpointer
vorbereiten.
* ( RFM bekommt 0xc0a3)
* Pinchange Interrupt freigeben.
Im Interrupt:
Daten zum RFM01 dauerhaft lo setzen.
CS des RFM auf aktiv setzen, also RFM anwählen.
Jetzt 16 bit vom RFM Datenpin lesen, dabei mit der Clk weitertakten,
also immer high, dann low, dann lesen. Das ist das Statusword. Wenn
dadrin das FIFO_IT Bit gesetzt ist (das oberste Bit15), das Datenbyte
einlesen, sonst den Empfang abbrechen.
Byte einlesen mit Clk Hi, einige µs warten, Datenbit lesen, Clk low und
8 mal wiederholen, dann das Byte im Buffer speichern.
Ganz am Ende dann CS wieder deaktivieren. Das wars.
im Hauptprogramm den Datenpointer checken. Wenn die Paketlänge komplett
ist, Fifo resetten und wieder von vorne.
*
Ich kann dir ja mal ein paar Tipps wie ich es gelöst hatte.
du must die zwei Interrupt Routinen
ISR (INTx_vect){
}
und
ISR (SPI_STC_vect) {
}
aus programmieren.
zusätzlich brauchst du hilf Funktionen die du aus deinem Hauptprogramm
aufrufst.
1. Es werden immer 16 Bit per SPI übertragen. Der AVR-SPI-Kontroller
arbeitet aber nur mit 8 Bits, daher musst du ihn immer zweimal
'anwerfen'.
ich mach das immer so:
1
volatileuint8_tbusyFlag_ISR;
2
volatileuint8_trxBuf_ISR;
3
volatileuint8_tmode16Flag_ISR;
4
volatileuint8_trxBufHi_ISR;
5
volatileuint8_ttxBuf_ISR;
6
7
8
voidSpi_init(){
9
SPSR=0x01;
10
SPCR=0xD1;
11
}
12
13
ISR(SPI_STC_vect)
14
{
15
if(mode16Flag_ISR){
16
mode16Flag_ISR=0;
17
rxBufHi_ISR=SPDR;
18
SPDR=txBuf_ISR;
19
}else{
20
rxBuf_ISR=SPDR;
21
/* SS to Hi */
22
SPI_NSEL_PORT|=(1<<SPI_NSEL_PIN);
23
busyFlag_ISR=0;
24
rfm_code_here();
25
}
26
}
27
28
uint8_tSpi_16(uint16_tdata){
29
uint8_tresult;
30
cli();
31
result=Spi_16_ISR(data);
32
sei();
33
returnresult;
34
}
35
36
uint8_tSpi_16_ISR(uint16_tdata){
37
if(busyFlag_ISR){
38
return0;
39
}
40
mode16Flag_ISR=1;
41
busyFlag_ISR=1;
42
txBuf_ISR=data;
43
//SS to Low
44
SPI_NSEL_PORT&=~(1<<SPI_NSEL_PIN);
45
SPDR=(data>>8);
46
return1;
47
}
48
49
voidSpi_16W(uint16_tdata){
50
while(1){
51
cli();
52
if(busyFlag_ISR){
53
sei();
54
continue;
55
}
56
break;
57
}
58
Spi_16_ISR(data);
59
sei();
60
}
61
62
uint8_tSpi_8(uint8_tdata){
63
uint8_tresult;
64
cli();
65
result=Spi_8_ISR(data);
66
sei();
67
returnresult;
68
}
69
70
uint8_tSpi_8_ISR(uint8_tdata){
71
if(busyFlag_ISR){
72
return0;
73
}
74
busyFlag_ISR=1;
75
//SS to Low
76
SPI_NSEL_PORT&=~(1<<SPI_NSEL_PIN);
77
SPDR=data;
78
return1;
79
}
80
81
voidSpi_8W(uint8_tdata){
82
while(1){
83
cli();
84
if(busyFlag_ISR){
85
sei();
86
continue;
87
}
88
break;
89
}
90
Spi_8_ISR(data);
91
sei();
92
}
93
94
uint8_tSpi_get(void){
95
returnrxBuf_ISR;
96
}
97
98
uint8_tSpi_getHi(void){
99
returnrxBufHi_ISR;
100
}
101
102
uint8_tSpi_isBusy(void){
103
returnbusyFlag_ISR;
104
}
Du kannst ersmal deine Software SPI damit ersetzen, in dem du:
schreibst.
Der Hardware SPI ist um ein vielfaches schneller als über Software,
daher ist es kein Problem jedes Mal das komplette Statusregister des RFM
auszulesen.
Die gesamte Ansteuerung kommst in die "rfm_code_here()".
Die Interrupt Routine ist bei mir auch sehr klein, da sie nur den SPI
startet:
1
volatileuint8_tinterruptFlag_ISR=0;
2
3
/*
4
* si4420Physical_state_ISR:
5
* 0 => not init
6
* 1 => idle
7
* 2 => listening
8
* 3 => sending
9
*/
10
volatileuint8_tstate_ISR=0;
11
12
/* si4420Physical_SPIAction_ISR:
13
* 0 => none
14
* 1 => status read
15
* 2 => receiver read
16
*/
17
volatileuint8_tspiAction_ISR=0;
18
19
ISR(INT0_vect)
20
{
21
if(Spi_16_ISR(0x0000)){
22
spiAction_ISR=1;
23
}else{
24
interruptFlag_ISR=1;
25
}
26
}
Nicht wundern si4420 ist der eigentliche Chip auf den RFMs.
interruptFlag_ISR ist ein Merker, das nach der aktuell laufenden
SPI-Übertragung wieder das Staus Register ausgelesen werden muss.
Hallo Matthias Sch. Ich habe mal versucht, Dein Rezept umzusetzen,
leider bisher ohne Erfolg.
Matthias Sch. schrieb:> Gerade bei der Interuptsteuerung geht sehr wenig verloren, weil die> Prioritäten hoch sind.> Leider ist der Grossteil meines IRQ gesteuerten RFM Moduls verloren> gegangen, aber das Prinzip geht so:> * Pinchange Interrupt für den Pin, an dem nIRQ vom Modul dran ist,> einrichten, aber noch nicht freigeben.
1
EICRA|=(1<<ISC01);// The falling edge of INTx generates an interrupt request
Zu Beginn des Programmes! Erste Zeile nach main.
> * (an den RFM sende ich dazu : 0xc0a2, 0xce88, 0xce8b)
1
RF_PORT=(1<<CS);
2
RF_DDR=(1<<SDI)|(1<<SCK)|(1<<CS);
3
4
for(i=0;i<16;i++)
5
_delay_ms(10);// wait until POR done
6
7
rf01_trans(0xC2E0);// AVR CLK: 10MHz
8
rf01_trans(0xC42B);// Data Filter: internal
9
rf01_trans(0xCE88);// FIFO mode
10
rf01_trans(0xC6F7);// AFC settings: autotuning: -10kHz...+7,5kHz
11
rf01_trans(0xE000);// disable wakeuptimer
12
rf01_trans(0xCC00);// disable low duty cycle
13
14
rf01_trans(0xC0E9);//|((sgain&3)<<4)|((sdrssi&7)<<1)); // RX on
15
rf01_trans(0xCE89);// set FIFO mode
16
rf01_trans(0xCE8B);// enable FIFO
Die komplette Initialisierung des RFM 01 incl der letzten drei Zeilen,
die RX anschalten, den FIFO Mode setzen und das den FIFO einschalten.
> * Empfänger initialisieren und Fifo anschalten. Datenpointer> vorbereiten.> * ( RFM bekommt 0xc0a3)> * Pinchange Interrupt freigeben.
1
EIMSK|=(1<<INT0);// Turns on INT0
2
3
4
sei();// turn on interrupts
Hier den Interrupt aktiviert und globale Interrupts freigegeben.
>> Im Interrupt:> Daten zum RFM01 dauerhaft lo setzen.> CS des RFM auf aktiv setzen, also RFM anwählen.> Jetzt 16 bit vom RFM Datenpin lesen, dabei mit der Clk weitertakten,> also immer high, dann low, dann lesen. Das ist das Statusword. Wenn> dadrin das FIFO_IT Bit gesetzt ist (das oberste Bit15), das Datenbyte> einlesen, sonst den Empfang abbrechen.> Byte einlesen mit Clk Hi, einige µs warten, Datenbit lesen, Clk low und> 8 mal wiederholen, dann das Byte im Buffer speichern.
1
ISR(INT0_vect)
2
{
3
4
uart_puts("Interrupt");
5
unsignedcharc;
6
7
cbi(RF_PORT,SDI);
8
sbi(RF_PORT,CS);//low avctiv
9
cbi(RF_PORT,SCK);
10
sbi(RF_PORT,SDO);//low activ
11
_delay_ms(2);
12
13
cbi(RF_PORT,CS);//RFM ausgewählt
14
15
for(j=0;j<16;j++)// lies Status Register
16
{
17
StatusFlag<<=1;
18
19
sbi(RF_PORT,SCK);
20
_delay_us(0.2);
21
cbi(RF_PORT,SCK);
22
23
if(RF_PIN&(1<<SDO))
24
{
25
StatusFlag|=1;
26
}
27
}
28
29
c=0;
30
for(j=0;j<8;j++)// lies Daten aus dem FIFO
31
{
32
c<<=1;
33
34
sbi(RF_PORT,SCK);
35
_delay_us(0.2);
36
cbi(RF_PORT,SCK);
37
38
if(RF_PIN&(1<<SDO))
39
{
40
c|=1;
41
}
42
43
}
44
45
data[nummer]=c;
46
nummer+=1;
47
48
}
Meine komplette Interrupt Routine!
>> Ganz am Ende dann CS wieder deaktivieren. Das wars.
Der Interrupt wird niemals ausgelöst! Dass heißt nIRQ bleibt konstant
auf High-Level. Wenn ich den Interrupt mit der Hand auslöse, GND an
INT0, bleibt das Programm im ISR hängen, was ich nun gar nicht verstehe!
Nachdem es mir in endlicher Zeit und mit vertretbarem Aufwand nicht
gelungen war, eine Interruptroutine für den RFM02 zu programmieren,
hatte ich beschlossen, den Datenempfang in einen Attiny zu verlagern.
Dieser sollte dann mit Polling arbeiten und die Daten an den Atmega328p
übergeben. Im 328er wollte ich eine Interrupt Routine für den Empfang
der Daten aus dem Attiny programmieren. Somit hätte der 328er ohne
Störung mit schon geprüften Daten arbeiten können.
Also habe ich mein Programm wieder auf Poling umgestrickt. Dabei habe
ich die in diesem thread besprochenen Fehler vermieden
(Frequenzfestlegung und Frequenzberechnung). Das Ergebnis war
ernüchternd. Wieder dauert es zwischen 3 Sekunden und 3 Minuten bis der
Empfänger sicher Daten empfängt. Das ist aus meiner Sicht inakzeptabel.
Aus diesem Grund mache ich jetzt Schluss mit RFM01 und RFM02!
Kann mir einer von euch eine Alternative nennen. Ich möchte eine
Datenverbindung im Sinne des broadcasting für Innenräume aufbauen. Nach
wie vor möchte ich mich nicht mit Hochfrequenztechnik beschäftigen. Es
wäre sehr schön wenn es für diese Alternative Bibliotheken gäbe. Ich
muss die verlorene Zeit wieder aufholen.
Ich danke euch für eure Hinweise.