Forum: Mikrocontroller und Digitale Elektronik RFM12B portierte Library will auf dem Tiny nicht mehr


von Mike M. (mikeii)


Lesenswert?

Ich habe mir vor einiger Zeit mal eine Arduino Library für das RFM12B 
Funkmodul an den Atmega 2560 angepasst, so dass ich das Arduino Gedöns 
nicht brauche.
Um sie universeller für die Zukunft zu halte, habe ich auch noch 
Bitbanging eingebaut. Die Library funktioniert einwandfrei.

Jetzt wollte ich das sie gerade mit einem Attiny2313 verwenden, aber das 
ganze macht keinen Mucks. Am Atmega2560 funktioniert ein uns das  selbe 
Funkmodul aber.

nSel ist an B4
SCK ist an B7
SDO ist an B5
SDI ist an B6
nIRQ ist an D2

Controller läuft mit 8 Mhz ohne Vorteiler.
Die Änderungen die ich vorgenommen habe, inkl. original Code habe ich 
mit dem Kommentar "//Für den Tiny geändert" versehen.

Es kommen Daten aus dem DO des Tiny raus, und ein Clock Signal kommt 
auch an. Nur kommen augenscheinlich keine Daten aus dem Funkmodul zurück 
(hab leider nur ein Oszi, weil LA kaputt).

In der Funktion void SendWait(uint8_t waitMode) hängt das ganze, weil 
kein Interrupt vom Funkmodul erzeugt wird und somit die Variable für den 
aktuellen Zustand (rxstate) nicht verändert wird.
Der Interrupt funktioniert. Es wird in den Handler gesprungen, wenn ich 
den Pin manuell auf low ziehe.


Wenn sich jemand den Code ansehen könnte, ob die Änderungen von mir alle 
so korrekt sind, das wäre nett.

Hier ist mal die c und die h Datei angehängt.



C-Datei
1
#include "RFM12B.h"
2
#include "avr/io.h"
3
#include "string.h"
4
5
#define F_CPU 8000000UL
6
#include <util/delay.h>
7
8
void SPIInit() {
9
10
  SS_DDR |= (1 << cs_pin);
11
  //DI
12
  DDRB &= ~(1 << 5);
13
  //DO
14
  DDRB = (1 << 6);
15
  //SCK
16
  DDRB |= (1 << 7);
17
  //Interrupt Pin has Pullup
18
  DDRD &= ~(1 << 2);
19
  PORTD |= (1 << 2);
20
}
21
22
23
uint8_t Byte(uint8_t out){
24
25
  uint8_t returnvalue = 0;
26
  int8_t i;
27
  for (i = 7;i>=0;i--){
28
29
    //Bit low or high?
30
    if(out & (1<<i))
31
      PORTB |= (1<<6);
32
    else
33
      PORTB &= ~(1<<6);
34
35
    //Clock High
36
    PORTB |= (1 << 7);
37
    _delay_us(1); 
38
39
    //Clock Low
40
    PORTB &= ~(1 << 7); 
41
42
    //Read input    
43
    if ( PINB & (1<<PINB5) ) {
44
      returnvalue |= (1<<i);
45
    }   
46
   _delay_us(1);
47
  }
48
  return returnvalue;
49
}
50
51
52
uint16_t XFERSlow(uint16_t cmd) {
53
54
  SS_PORT &= ~(1 << cs_pin);
55
  uint16_t reply = Byte(cmd >> 8) << 8;
56
  reply |= Byte(cmd);
57
  SS_PORT |= (1 << cs_pin);
58
  return reply;
59
}
60
61
void XFER(uint16_t cmd) {
62
  SS_PORT &= ~(1 << cs_pin);
63
  Byte(cmd >> 8) << 8;
64
  Byte(cmd);
65
  SS_PORT |= (1 << cs_pin);
66
}
67
68
void Initialize(uint8_t ID, uint8_t networkid)
69
{
70
71
72
  Data = rf12_data;
73
  DataLen = &rf12_buf[3];
74
75
  uint8_t txPower = 0;
76
  uint8_t airKbps = 0x08;
77
  uint8_t lowVoltageThreshold = RF12_2v75;
78
79
80
  cs_pin = SS_BIT;
81
  nodeID = ID;
82
  networkID = networkid;
83
  SPIInit();
84
  XFER(0x0000); // intitial SPI transfer added to avoid power-up problem
85
  XFER(RF_SLEEP_MODE); // DC (disable clk pin), enable lbd
86
87
  // wait until RFM12B is out of power-up reset, this takes several *seconds*
88
  XFER(RF_TXREG_WRITE); // in case we're still in OOK mode
89
  //Für den Tiny geändert: while (!(PIND & (1 << 3) ))
90
  while (!(PIND & (1 << 2) )) //             digitalRead(18) == 0)
91
    XFER(0x0000);
92
93
  XFER(0x80C7 | (RF12_868MHZ << 4)); // EL (ena TX), EF (ena RX FIFO), 12.0pF
94
  XFER(0xA640); // Frequency is exactly 434/868/915MHz (whatever freqBand is)
95
  XFER(0xC600 + airKbps);   //Air transmission baud rate: 0x08= ~38.31Kbps
96
  XFER(0x94A2);             // VDI,FAST,134kHz,0dBm,-91dBm
97
  XFER(0xC2AC);             // AL,!ml,DIG,DQD4
98
  if (networkID != 0) {
99
    XFER(0xCA83);           // FIFO8,2-SYNC,!ff,DR
100
    XFER(0xCE00 | networkID); // SYNC=2DXX;
101
  } else {
102
    XFER(0xCA8B); // FIFO8,1-SYNC,!ff,DR
103
    XFER(0xCE2D); // SYNC=2D;
104
  }
105
106
107
  XFER(0xC483); // @PWR,NO RSTRIC,!st,!fi,OE,EN
108
  XFER(0x9850 | (txPower > 7 ? 7 : txPower)); // !mp,90kHz,MAX OUT               //last byte=power level: 0=highest, 7=lowest
109
  XFER(0xCC77); // OB1,OB0, LPX,!ddy,DDIT,BW0
110
  XFER(0xE000); // NOT USE
111
  XFER(0xC800); // NOT USE
112
  XFER(0xC043); // Clock output (1.66MHz), Low Voltage threshold (2.55V)
113
114
  rxstate = TXIDLE;
115
116
  //Für den Tiny geändert
117
  //Interupt enable
118
  //EICRA |= (1 << ISC31);
119
  //EICRA &= ~(1 << ISC30);
120
  //EIMSK |= (1 << INT3);
121
  MCUCR |= (1<<ISC01);
122
  MCUCR &= ~(1<<ISC00);
123
  GIMSK |= (1<<INT0);
124
125
  sei();
126
127
}
128
129
130
// access to the RFM12B internal registers with interrupts disabled
131
uint16_t Control(uint16_t cmd) {
132
  //Für den Tiny geändert
133
  //EIMSK &= ~(1 << INT0);
134
  GIMSK &= ~(1<<INT0);
135
136
  uint16_t r = XFERSlow(cmd);
137
  
138
  //Für den Tiny geändert
139
  //EIMSK |= (1 << INT0);
140
  GIMSK |= (1<<INT0);
141
  return r;
142
}
143
144
145
146
void InterruptHandler() {
147
  XFER(0x0000);
148
149
  if (rxstate == TXRECV) {
150
    uint8_t in = XFERSlow(RF_RX_FIFO_READ);
151
152
    if (rxfill == 0 && networkID != 0)
153
      rf12_buf[rxfill++] = networkID;
154
155
    rf12_buf[rxfill++] = in;
156
    rf12_crc = _crc16_update(rf12_crc, in);
157
158
    if (rxfill >= rf12_len + 6 || rxfill >= RF_MAX)
159
      XFER(RF_IDLE_MODE);
160
  } else {
161
    uint8_t out;
162
163
    if (rxstate < 0) {
164
      uint8_t pos = 4 + rf12_len + rxstate++;
165
      out = rf12_buf[pos];
166
      rf12_crc = _crc16_update(rf12_crc, out);
167
    } else
168
      switch (rxstate++) {
169
      case TXSYN1: out = 0x2D; break;
170
      case TXSYN2: out = networkID; rxstate = -(3 + rf12_len); break;
171
      case TXCRC1: out = rf12_crc; break;
172
      case TXCRC2: out = rf12_crc >> 8; break;
173
      case TXDONE: XFER(RF_IDLE_MODE);
174
      default:     out = 0xAA;
175
      }
176
177
    XFER(RF_TXREG_WRITE + out);
178
  }
179
}
180
181
//Für den Tiny geändert:
182
//ISR(INT3_vect) {
183
ISR(INT0_vect) {
184
185
  InterruptHandler();
186
}
187
188
189
void ReceiveStart() {
190
  rxfill = rf12_len = 0;
191
  rf12_crc = ~0;
192
  if (networkID != 0)
193
    rf12_crc = _crc16_update(~0, networkID);
194
  rxstate = TXRECV;
195
  XFER(RF_RECEIVER_ON);
196
}
197
198
char ReceiveComplete() {
199
  if (rxstate == TXRECV && (rxfill >= rf12_len + 6 || rxfill >= RF_MAX)) {
200
    rxstate = TXIDLE;
201
    if (rf12_len > RF12_MAXDATA)
202
      rf12_crc = 1;
203
    if (RF12_DESTID == 0 || RF12_DESTID == nodeID) {
204
      if (!rf12_crc)
205
        rf12_seq = -1;
206
      return 1;
207
    }
208
  }
209
  if (rxstate == TXIDLE)
210
    ReceiveStart();
211
  return 0;
212
}
213
214
char CanSend() {
215
216
  if (rxstate == TXRECV && rxfill == 0 && (Byte(0x00) & (RF_RSSI_BIT >> 8)) == 0) {
217
    XFER(RF_IDLE_MODE);
218
    rxstate = TXIDLE;
219
    return 1;
220
  }
221
  return 0;
222
}
223
224
void SendStart(uint8_t toNodeID, char requestACK, char sendACK) {
225
  rf12_hdr1 = toNodeID | (sendACK ? RF12_HDR_ACKCTLMASK : 0);
226
  rf12_hdr2 = nodeID | (requestACK ? RF12_HDR_ACKCTLMASK : 0);
227
  rf12_crc = ~0;
228
  rf12_crc = _crc16_update(rf12_crc, networkID);
229
  rxstate = TXPRE1;
230
  XFER(RF_XMITTER_ON);
231
}
232
233
void SendStart2(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, char requestACK, char sendACK, uint8_t waitMode) {
234
  rf12_len = sendLen;
235
  memcpy((void*) rf12_data, sendBuf, sendLen);
236
237
238
239
  SendStart(toNodeID, requestACK, sendACK);
240
241
  SendWait(waitMode);
242
}
243
244
245
void SendACK2(const void* sendBuf, uint8_t sendLen, uint8_t waitMode) {
246
  while (!CanSend()) ReceiveComplete();
247
  SendStart2(RF12_SOURCEID, sendBuf, sendLen, 0, 1, waitMode);
248
}
249
250
251
252
void SendACK() {
253
  const void* sendBuf = "";
254
  uint8_t sendLen = 0;
255
  uint8_t waitMode = SLEEP_MODE_IDLE;
256
  while (!CanSend()) ReceiveComplete();
257
  SendStart2(RF12_SOURCEID, sendBuf, sendLen, 0, 1, waitMode);
258
}
259
260
261
262
void Send(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, char requestACK)
263
{
264
  uint8_t waitMode = SLEEP_MODE_STANDBY;
265
  while (!CanSend()) ReceiveComplete();
266
  SendStart2(toNodeID, sendBuf, sendLen, requestACK, 0, waitMode);
267
}
268
269
void SendWait(uint8_t waitMode) {
270
  while (rxstate != TXIDLE);
271
  
272
}
273
274
void OnOff(uint8_t value) {
275
  XFER(value ? RF_XMITTER_ON : RF_IDLE_MODE);
276
}
277
278
void Sleep2(char n) {
279
  if (n < 0)
280
    Control(RF_IDLE_MODE);
281
  else {
282
    Control(RF_WAKEUP_TIMER | 0x0500 | n);
283
    Control(RF_SLEEP_MODE);
284
    if (n > 0)
285
      Control(RF_WAKEUP_MODE);
286
  }
287
  rxstate = TXIDLE;
288
}
289
void Sleep() { Sleep2(0); }
290
void Wakeup() { Sleep2(-1); }
291
292
char LowBattery() {
293
  return (Control(0x0000) & RF_LBD_BIT) != 0;
294
}
295
296
uint8_t GetSender() {
297
  return RF12_SOURCEID;
298
}
299
300
uint8_t * GetData() { return rf12_data; }
301
302
303
uint8_t GetDataLen() { return *DataLen; }
304
char ACKRequested() { return RF12_WANTS_ACK; }
305
306
char ACKReceived(uint8_t fromNodeID) {
307
  if (ReceiveComplete())
308
    return CRCPass() &&
309
           RF12_DESTID == nodeID &&
310
           (RF12_SOURCEID == fromNodeID || fromNodeID == 0) &&
311
           (rf12_hdr1 & RF12_HDR_ACKCTLMASK) &&
312
           !(rf12_hdr2 & RF12_HDR_ACKCTLMASK);
313
  return 0;
314
}
315
316
char CRCPass() {
317
  return rf12_crc == 0;
318
}

H-Datei
1
#ifndef RFM12B_h
2
#define RFM12B_h
3
4
#include <inttypes.h>
5
#include <avr/io.h>
6
#include <util/crc16.h>
7
#include <avr/eeprom.h>
8
#include <avr/sleep.h>
9
10
#include <avr/interrupt.h>
11
12
13
14
15
16
17
#define RF12_MAXDATA    128
18
#define RF_MAX          (RF12_MAXDATA + 6)
19
20
#define RF12_315MHZ     0
21
#define RF12_433MHZ     1
22
#define RF12_868MHZ     2
23
#define RF12_915MHZ     3
24
25
#define RF12_2v25       0
26
#define RF12_2v55       3
27
#define RF12_2v65       4
28
#define RF12_2v75       5
29
#define RF12_3v05       8
30
#define RF12_3v15       9
31
#define RF12_3v25       10
32
33
#define RF12_HDR_IDMASK      0x7F
34
#define RF12_HDR_ACKCTLMASK  0x80
35
#define RF12_DESTID   (rf12_hdr1 & RF12_HDR_IDMASK)
36
#define RF12_SOURCEID (rf12_hdr2 & RF12_HDR_IDMASK)
37
38
#define RF12_WANTS_ACK ((rf12_hdr2 & RF12_HDR_ACKCTLMASK) && !(rf12_hdr1 & RF12_HDR_ACKCTLMASK))
39
40
#define RF12_SLEEP   0
41
#define RF12_WAKEUP -1
42
43
#define rf12_grp        rf12_buf[0]
44
#define rf12_hdr1        rf12_buf[1]
45
#define rf12_hdr2        rf12_buf[2]
46
47
#define rf12_len        rf12_buf[3]
48
#define rf12_data       (rf12_buf + 4)
49
50
#define bitSet_(x,y)  (x) |= ((1<<y))
51
52
#define SS_DDR      DDRB //Für den Tiny geändert: Vorher DDRD
53
#define SS_PORT     PORTB //Für den Tiny geändert:Vorher DDRD
54
#define SS_BIT      4  ////Für den Tiny geändert: Vorher 2
55
56
57
#define RF_RECEIVER_ON  0x82DD
58
#define RF_XMITTER_ON   0x823D
59
#define RF_IDLE_MODE    0x820D
60
#define RF_SLEEP_MODE   0x8205
61
#define RF_WAKEUP_MODE  0x8207
62
#define RF_TXREG_WRITE  0xB800
63
#define RF_RX_FIFO_READ 0xB000
64
#define RF_WAKEUP_TIMER 0xE000
65
66
#define RF_LBD_BIT      0x0400
67
#define RF_RSSI_BIT     0x0100
68
69
70
enum {
71
  TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE,
72
  TXRECV,
73
  TXPRE1, TXPRE2, TXPRE3, TXSYN1, TXSYN2
74
};
75
76
uint8_t rf12_buf[RF_MAX];
77
uint8_t rxfill;
78
volatile int8_t rxstate;
79
uint16_t rf12_crc;
80
uint32_t seqNum;
81
uint32_t cryptKey[4];
82
long rf12_seq;
83
uint8_t cs_pin;
84
uint8_t Byte(uint8_t out);
85
uint16_t XFERSlow(uint16_t cmd);
86
void XFER(uint16_t cmd);
87
void SPIInit(void);
88
uint8_t networkID;
89
uint8_t nodeID;
90
uint8_t* Data;
91
uint8_t* DataLen;
92
void InterruptHandler(void);
93
void Initialize(uint8_t nodeid, uint8_t groupid);
94
void ReceiveStart(void);
95
char ReceiveComplete(void);
96
char CanSend(void);
97
uint16_t Control(uint16_t cmd);
98
void SendStart(uint8_t toNodeId, char requestACK, char sendACK);
99
void SendStart2(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, char requestACK, char sendACK, uint8_t waitMode);
100
void SendACK2(const void* sendBuf, uint8_t sendLen, uint8_t waitMode);
101
void SendACK(void);
102
void Send(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, char requestACK);
103
void SendWait(uint8_t waitMode);
104
void OnOff(uint8_t value);
105
void Sleep2(char n);
106
void Sleep(void);
107
void Wakeup(void);
108
uint8_t * GetData(void);
109
uint8_t GetDataLen(void);
110
uint8_t GetSender(void);
111
char LowBattery(void);
112
char ACKRequested(void);
113
char ACKReceived(uint8_t fromNodeID);
114
115
char CRCPass(void);
116
117
118
119
120
#endif


und die Main
1
#include "RFM12B.h"
2
#include <stdio.h>
3
#define F_CPU 8000000UL
4
#include <util/delay.h>
5
6
#define NODEID        2  //network ID used for this unit
7
#define NETWORKID    99  //the network ID we are on
8
#define GATEWAYID     1  //the node ID we're sending to
9
10
11
12
13
14
int main(void) {
15
16
17
  Initialize(NODEID, NETWORKID);
18
  for (;;) {
19
   
20
    Send(GATEWAYID, "abc", 3, 0);
21
    _delay_ms(1000);
22
  }
23
  return 0;
24
25
}

: Bearbeitet durch User
von Felix P. (fixxl)


Lesenswert?

Das Problem liegt vermutlich hier:
1
void SPIInit() {
2
  ...
3
  DDRB = (1 << 6);
4
  ...
5
}

Da fehlt ein |, wodurch der CS-Pin wieder zum Eingang wird.

von Mike M. (mikeii)


Lesenswert?

Felix P. schrieb:
> Das Problem liegt vermutlich hier:
>
1
> void SPIInit() {
2
>   ...
3
>   DDRB = (1 << 6);
4
>   ...
5
> }
6
>
>
> Da fehlt ein |, wodurch der CS-Pin wieder zum Eingang wird.



JAAAAAAAAAAAAAAAAAAAAAA, das ist es!!!!!
Felix, ich sitze an dem Ding jetzt seit 10 Stunden.... denkst du das 
hätte ich gesehen? Oh mein Gott, du hast mir gerade den Tag versüßt, 
jetzt kann ich beruhigt schlafen.

Danke dir tausendfach!

von Mike M. (mikeii)


Lesenswert?

Tja leider war das zu voreilig... Es geht leider doch nicht... Nach wie 
vor hängt es in der gleichen Funktion.

von Mike M. (mikeii)


Lesenswert?

Hab den Fehler immer noch nicht gefunden.
Slave Select toggelt, Clock ist da und Daten auch. Modul antwortet aber 
am Tiny nicht, am Mega schon.
Gibts da irgendwelche Eigenheiten oder reagiert das Teil sensibel auf 
irgendwas?

von c-hater (Gast)


Lesenswert?

Mike M. schrieb:

> Gibts da irgendwelche Eigenheiten oder reagiert das Teil sensibel auf
> irgendwas?

Ja, es reagiert sehr sensibel auf SPI-Zugriffe, bevor der im DB 
definierte Startup-Timeout abgelaufen ist. Und macht dann in der Folge 
oft nur noch völligen Quatsch und ist aus dieser Condition nur durch 
einen erneuten Reset zu erlösen.

Also muss man nach Reset des RFM12 (natürlich auch nach PowerOn-Reset!) 
einfach mal diese Zeit warten, bevor man versucht, per SPI mit dem Teil 
zu kommunizieren. Datenblatt lesen bildet...

Dass es beim Mega funktioniert und beim Tiny nicht, liegt mit einiger 
Wahrscheinlichkeit daran, dass du den Mega mit Quarz laufen läßt (und 
deswegen sinnvollerweise mit recht hohem Startup-Delay), den Tiny 
hingegen mit dem internen RC-Oszillator praktisch ohne Startup-Delay. 
Also: Mindestens das, was du bei der Konfiguration des Taktsystems des 
Mega als Startup-Delay per Fuse vorgegeben hast, musst du beim Tiny in 
Software warten, bevor du das erste Mal mit dem RFM12 kommunizierst bzw. 
eigentlich, bevor du überhaupt die Pins für das Software-SPI 
konfigurierst.

Und selbst dann kann es noch gelegentlich Reset-Probleme geben (wenn der 
Reset für beide ICs quasi gleichzeitig in einem "ungünstigen" Moment 
während der laufenden Kommunikation erfolgt). Dagegen hilft dann nur 
noch ein physischer PullUp am nSel-Pin des RFM12. Diese Sache kann 
übrigens genausogut auch mit einem ATMega und Hardware-SPI zum Problem 
werden...

Der eigentlich springende Punkt ist nämlich der Zeitpunkt der ersten 
H/L-Flanke an nSel nach dem Ende eines Reset des RFM12. Dafür gibt es 
einen wohldefinierten Mindestwert. Und der steht, wie schon oben gesagt, 
im Datenblatt...

von Mike M. (mikeii)


Lesenswert?

Das mit dem nSel mit Pullup habe ich gerade getestet, hat nicht 
geholfen.
Ein Delay beim Starten hatte ich auch schon eingebaut gehabt.
Setup- und Holdtimes laut Datenblatt sollten mehr als eingehalten sein.

Ich habe mir gestern einen neuen Logic Analyser gekauft, ich werde jetzt 
wohl mal damit drauf gehen, und schauen ob damit etwas zu finden ist.

von c-hater (Gast)


Lesenswert?

Mike M. schrieb:

> Ich habe mir gestern einen neuen Logic Analyser gekauft

OMG. Ein LA kann niemals Sachverstand und DB-Lektüre ersetzen!

Ein LA ist ein sehr praktisches Tool, um logische Fehler in der 
Kommunikation über einen "laufenden" Bus aufzuspüren, aber völlig 
ungeeignet, um Fehler bei der Businitialisierung zu entlarven.

Dazu müsste das Teil nämlich die Eigenheiten aller Busteilnehmer kennen, 
was ich gerade im Bereich SPI für einigermaßen unmöglich halten würde...

von Mike M. (mikeii)


Lesenswert?

Was willst du mir jetzt sagen? Das der LA nicht geeignet ist um 
herauszufinden was laut Datenblatt aus meinem Controller kommen soll 
oder wie?
DB habe ich gelesen, Sachverstand vorhanden. Menschen machen Fehler, den 
suche ich, und was machst du so schönes?

von Mike M. (mikeii)


Lesenswert?

So, Fehler gefunden:

Warum auch immer die Library auf dem Atmega geht, auf dem Tiny geht sie 
nicht.
Und zwar aus mehreren Gründen:

Der "InterruptHandler" sollte so lange laufen, wie der Pin auf Low ist, 
damit er seine Statemachine abhandeln kann. Dann wurden statt 16bit nur 
8 Bit Statusinfo überprüft....

Die zwei Sachen geändert und schon läuft es.

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.