-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRFM69_JeeLib.h
489 lines (409 loc) · 14.7 KB
/
RFM69_JeeLib.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
/*
* Native mode RFM69 driver.
*
* Derived from the JeeLabs file rf69.h, with some small changes (RSSI)
*
* OpenEnergyMonitor version V1.0.0
*
* Note 1: Default transmit power reduced to +7 dBm to avoid damage to RFM if no effective antenna is present.
* Note 2: This is not related to the JeeLib file RF69.h [RF in capital letters]
*/
#include <util/crc16.h>
#ifndef chThdYield
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
#endif
#ifdef EMONPI2
#define SSpin PIN_PA7
#define MOSIpin PIN_PA4
#define MISOpin PIN_PA5
#define SCKpin PIN_PA6
#endif
#ifdef EMONTX5
#define SSpin PIN_PA7
#define MOSIpin PIN_PA4
#define MISOpin PIN_PA5
#define SCKpin PIN_PA6
#endif
#ifdef EMONTX4
#define SSpin PIN_PB5
#define MOSIpin PIN_PC0
#define MISOpin PIN_PC1
#define SCKpin PIN_PC2
#endif
#define RF69_433MHZ 43
#define RF69_868MHZ 86
#define RF69_915MHZ 91
class RFM69 {
public:
void format(uint8_t radioFormat = 2);
bool initialize (uint8_t freq, uint8_t id, uint8_t group);
void send (uint8_t header, const void* buffer, uint8_t bufferSize);
bool receiveDone();
void encrypt (const char* key);
int16_t readRSSI(bool forceTrigger=false);
uint8_t DATA[62];
uint8_t RSSI;
uint8_t DATALEN;
uint8_t PAYLOADLEN;
uint8_t TARGETID;
uint8_t SENDERID;
uint8_t _address;
uint8_t _parity;
uint8_t _radioFormat = 2;
protected:
enum {
REG_FIFO = 0x00,
REG_OPMODE = 0x01,
REG_FRFMSB = 0x07,
REG_PALEVEL = 0x11,
REG_RSSIVALUE = 0x24,
REG_IRQFLAGS1 = 0x27,
REG_IRQFLAGS2 = 0x28,
REG_SYNCVALUE1 = 0x2F,
REG_SYNCVALUE2 = 0x30,
REG_PACKETCONFIG2 = 0x3D,
REG_AESKEY1 = 0x3E,
RF69_MODE_SLEEP = 0<<2,
RF69_MODE_STANDBY = 1<<2,
RF69_MODE_TX = 3<<2,
RF69_MODE_RX = 4<<2,
RF_IRQFLAGS1_MODEREADY = 0x80,
RF_IRQFLAGS1_RXREADY = 0x40,
RF_IRQFLAGS2_PACKETSENT = 0x08,
RF_IRQFLAGS2_PAYLOADREADY = 0x04,
REG_RSSICONFIG = 0x23,
RF_RSSI_START = 0x01,
RF_RSSI_DONE = 0x02,
RF_PACKET2_RXRESTART = 0x04,
RF_PALEVEL_PA0_ON = 0x80,
RF69_MAX_DATA_LEN = 61,
CSMA_LIMIT = -97,
RF69_CSMA_LIMIT_MS = 25
};
void spi_init();
uint8_t spi_transfer (uint8_t data);
void select();
void unselect();
uint8_t readReg (uint8_t addr);
void writeReg (uint8_t addr, uint8_t value);
void setMode (uint8_t newMode);
void configure (const uint8_t* p);
void setFrequency (uint32_t freq);
void interruptHandler();
void receiveBegin();
void wait_clear();
void sendFrame_v2 (uint8_t header, const void* buffer, uint8_t bufferSize);
void sendFrame_v1 (uint8_t header, const void* buffer, uint8_t bufferSize);
volatile uint8_t _mode;
};
void RFM69::setFrequency (uint32_t hz) {
// accept any frequency scale as input, including kHz and MHz
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
while (hz < 100000000)
hz *= 10;
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
// due to this, the lower 6 bits of the calculated factor will always be 0
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
// 434.0 MHz = 0x6C8000.
uint32_t frf = (hz << 2) / (32000000L >> 11);
writeReg(REG_FRFMSB, frf >> 10);
writeReg(REG_FRFMSB+1, frf >> 2);
writeReg(REG_FRFMSB+2, frf << 6);
}
static const uint8_t configRegs_v2 [] = {
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
0x01, 0x04, // RegOpMode: Sequencer on, listen off, standby mode
0x02, 0x00, // RegDataModul: Packet mode, FSK modulation, no modulation shaping
0x03, 0x02, // BitRateMsb, 0x02 << 8 = 512
0x04, 0x8A, // BitRateLsb, 0x8A = 138, (512+138)=650, 32 MHz / 650 = 49,230 bits/s
0x05, 0x05, // RegFdevMsb, 0x05 << 8 = 1280
0x06, 0xC3, // RegFdevLsb, 0xC3 << 0 = 195, (1280+195)=1475, 1475 x FSTEP (61Hz) = 90 kHz (should probably be half this!?)
0x0B, 0x20, // RegAfcCtrl, Improved AFC routine (default is standard 0x00)
0x11, 0x99, // OutputPower = +7 dBm - was default = max = +13 dBm
0x19, 0x42, // RegRxBw 32000000÷(16×2^(2+2)) = 125 kHz but could be set to 100 kHz 0x4a rfm69nTxLib: 0x4A (100kHz)
//0x1A, 0x42, // AfcBw 125 kHz rfm69nTxLib: 0x42 (125kHz)
0x1E, 0x2C, // AfcAutoclearOn, AfcAutoOn 0x80
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
0x26, 0x07, // disable clkout
0x29, 0xA0, // RssiThresh -80 dB 0xC8 (-100 dB)
0x2D, 0x05, // PreambleSize = 5
0x2E, 0x88, // SyncConfig = sync on, sync size = 2
0x2F, 0x2D, // SyncValue1 = 0x2D
0x37, 0xD0, // PacketConfig1 = variable, white, no filtering
0x38, 0x42, // PayloadLength = 0, unlimited
0x3C, 0x8F, // FifoThresh, not empty, level 15
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
0x6F, 0x20, // TestDagc ...
0x71, 0x02, // RegTestAfc
0
};
static const uint8_t configRegs_v1 [] = {
0x01, 0x04, // RegOpMode: Sequencer on, listen off, standby mode
0x02, 0x00, // RegDataModul: Packet mode, FSK modulation, no modulation shaping
0x03, 0x02, // BitRateMsb, 0x02 << 8 = 512
0x04, 0x8A, // BitRateLsb, 0x8A = 138, (512+138)=650, 32 MHz / 650 = 49,230 bits/s
0x05, 0x05, // RegFdevMsb, 0x05 << 8 = 1280
0x06, 0xC3, // RegFdevLsb, 0xC3 << 0 = 195, (1280+195)=1475, 1475 x FSTEP (61Hz) = 90 kHz (should probably be half this!?)
0x11, 0x99, // OutputPower = +7 dBm - was default = max = +13 dBm
0x1E, 0x2C, // AfcAutoclearOn, AfcAutoOn
0x25, 0x80,
0x26, 0x03, // disable clkout
0x28, 0x00,
0x2E, 0x88, // SyncConfig = sync on, sync size = 2
0x2F, 0x2D, // SyncValue1 = 0x2D
0x37, 0x00, // PacketConfig1 = variable, white, no filtering
0
};
void RFM69::format (uint8_t radioFormat) {
_radioFormat = radioFormat;
}
bool RFM69::initialize (uint8_t freq, uint8_t ID, uint8_t group) {
_address = ID;
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
_parity = group ^ (group << 4);
_parity = (_parity ^ (_parity << 2)) & 0xC0;
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
spi_init();
uint32_t start = millis();
uint8_t timeout = 50;
do writeReg(REG_SYNCVALUE1, 0xAA); while (readReg(REG_SYNCVALUE1) != 0xaa && millis()-start < timeout);
start = millis();
do writeReg(REG_SYNCVALUE1, 0x55); while (readReg(REG_SYNCVALUE1) != 0x55 && millis()-start < timeout);
if (_radioFormat==1) {
configure(configRegs_v1);
configure(configRegs_v1);
} else if (_radioFormat==2) {
configure(configRegs_v2);
configure(configRegs_v2);
}
setFrequency(434);
writeReg(REG_SYNCVALUE2, group);
uint8_t powerLevel = 25; // could be up to 31... -18 + dBm with PA0 = 7dBm
if (powerLevel>31) powerLevel = 31;
writeReg(0x11,RF_PALEVEL_PA0_ON | powerLevel);
setMode(RF69_MODE_STANDBY);
start = millis();
while (((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00) && millis()-start < timeout); // wait for ModeReady
if (millis()-start >= timeout)
return false;
return true;
}
void RFM69::setMode (uint8_t newMode) {
if (newMode == _mode)
return;
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
// we are using packet mode, so this check is not really needed
// but waiting for mode ready is necessary when going from sleep because the FIFO may not be immediately available from previous mode
while (_mode == RF69_MODE_SLEEP && (readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
_mode = newMode;
}
void RFM69::interruptHandler() {
if (_mode == RF69_MODE_RX && (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY))
{
setMode(RF69_MODE_STANDBY);
select();
spi_transfer(REG_FIFO & 0x7F);
PAYLOADLEN = spi_transfer(0);
PAYLOADLEN = PAYLOADLEN > 66 ? 66 : PAYLOADLEN; // precaution
uint8_t dest = spi_transfer(0);
if ((dest & 0xC0) != _parity) {
unselect();
receiveBegin();
return;
}
TARGETID = dest & 0x3F;
if (TARGETID != _address && TARGETID != 0 && _address != 63) {
unselect();
receiveBegin();
return;
}
SENDERID = spi_transfer(0);
DATALEN = PAYLOADLEN - 3;
for (uint8_t i = 0; i < DATALEN; i++) DATA[i] = spi_transfer(0);
DATA[DATALEN] = 0; // add null at end of string // add null at end of string
unselect();
setMode(RF69_MODE_RX);
RSSI = readRSSI();
}
}
void RFM69::receiveBegin() {
DATALEN = 0;
PAYLOADLEN = 0;
RSSI = 0;
SENDERID = 0;
TARGETID = 0;
if (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY)
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
// writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01); // set DIO0 to "PAYLOADREADY" in receive mode
setMode(RF69_MODE_RX);
}
// checks if a packet was received and/or puts transceiver in receive (ie RX or listen) mode
bool RFM69::receiveDone() {
interruptHandler();
if (_mode == RF69_MODE_RX && PAYLOADLEN > 0)
{
setMode(RF69_MODE_STANDBY); // enables interrupts
return true;
}
else if (_mode == RF69_MODE_RX) // already in RX no payload yet
{
return false;
}
receiveBegin();
return false;
}
void RFM69::wait_clear() {
if (RF69_CSMA_LIMIT_MS > 0){
setMode(RF69_MODE_RX);
uint32_t now = millis();
while ((millis()-now) < RF69_CSMA_LIMIT_MS) {
while((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0);
if (readRSSI() < CSMA_LIMIT) break;
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // Restart the receiver
}
}
}
void RFM69::send (uint8_t header, const void* buffer, uint8_t bufferSize) {
wait_clear();
if (_radioFormat==1) {
sendFrame_v1(header,buffer,bufferSize);
} else if (_radioFormat==2) {
sendFrame_v2(header,buffer,bufferSize);
}
}
void RFM69::sendFrame_v2 (uint8_t header, const void* buffer, uint8_t bufferSize) {
setMode(RF69_MODE_STANDBY); // turn off receiver to prevent reception while filling fifo
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
if (bufferSize > RF69_MAX_DATA_LEN) bufferSize = RF69_MAX_DATA_LEN;
// write to FIFO
select();
spi_transfer(REG_FIFO | 0x80);
spi_transfer(bufferSize + 2);
spi_transfer((header & 0x3F) | _parity);
spi_transfer((header & 0xC0) | _address);
for (uint8_t i = 0; i < bufferSize; i++)
spi_transfer(((uint8_t*) buffer)[i]);
unselect();
setMode(RF69_MODE_TX);
while ((readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PACKETSENT) == 0x00); // wait for packet sent
setMode(RF69_MODE_STANDBY);
}
void RFM69::sendFrame_v1 (uint8_t header, const void* buffer, uint8_t bufferSize) {
setMode(RF69_MODE_STANDBY); // turn off receiver to prevent reception while filling fifo
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
if (bufferSize > RF69_MAX_DATA_LEN) bufferSize = RF69_MAX_DATA_LEN;
uint8_t group = 210;
writeReg(0x30, group);
uint16_t crc = _crc16_update(~0, group);
// write to FIFO
select();
spi_transfer(REG_FIFO | 0x80);
spi_transfer(_address & 0x1F);
crc = _crc16_update(crc, _address & 0x1F);
spi_transfer(bufferSize);
crc = _crc16_update(crc, bufferSize);
for (uint8_t i = 0; i < bufferSize; i++) {
spi_transfer(((uint8_t*) buffer)[i]);
crc = _crc16_update(crc, ((uint8_t*) buffer)[i]);
}
spi_transfer((byte)crc);
spi_transfer((byte)(crc>>8));
spi_transfer((byte)(crc>>8));
spi_transfer(0xAA);
unselect();
setMode(RF69_MODE_TX);
while ((readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PACKETSENT) == 0x00); // wait for packet sent
setMode(RF69_MODE_STANDBY);
}
// To enable encryption: radio.encrypt("ABCDEFGHIJKLMNOP");
// To disable encryption: radio.encrypt(null) or radio.encrypt(0)
// KEY HAS TO BE 16 bytes !!!
void RFM69::encrypt (const char* key) {
if (_radioFormat==1) return;
setMode(RF69_MODE_STANDBY);
uint8_t validKey = key != 0 && strlen(key)!=0;
if (validKey)
{
select();
spi_transfer(REG_AESKEY1 | 0x80);
for (uint8_t i = 0; i < 16; i++)
spi_transfer(key[i]);
unselect();
}
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFE) | (validKey ? 1 : 0));
}
// get the received signal strength indicator (RSSI)
int16_t RFM69::readRSSI(bool forceTrigger) {
int16_t rssi = 0;
if (forceTrigger)
{
// RSSI trigger not needed if DAGC is in continuous mode
writeReg(REG_RSSICONFIG, RF_RSSI_START);
while ((readReg(REG_RSSICONFIG) & RF_RSSI_DONE) == 0x00); // wait for RSSI_Ready
}
rssi = -readReg(REG_RSSIVALUE);
rssi >>= 1;
return rssi;
}
uint8_t RFM69::readReg(uint8_t addr)
{
select();
spi_transfer(addr & 0x7F);
uint8_t regval = spi_transfer(0);
unselect();
return regval;
}
void RFM69::writeReg(uint8_t addr, uint8_t value)
{
select();
spi_transfer(addr | 0x80);
spi_transfer(value);
unselect();
}
void RFM69::select() {
digitalWriteFast(SSpin, 0);
}
void RFM69::unselect() {
digitalWriteFast(SSpin, 1);
}
void RFM69::spi_init() {
pinMode(SSpin, OUTPUT);
pinMode(MOSIpin, OUTPUT);
pinMode(SCKpin, OUTPUT);
unselect();
#ifdef EMONPI2
PORTMUX.SPIROUTEA = SPI_MUX | (PORTMUX.SPIROUTEA & (~PORTMUX_SPI0_gm));
SPI0.CTRLB |= (SPI_SSD_bm);
SPI0.CTRLA |= (SPI_ENABLE_bm | SPI_MASTER_bm);
#endif
#ifdef EMONTX4
PORTMUX.SPIROUTEA = SPI_MUX | (PORTMUX.SPIROUTEA & (~PORTMUX_SPI1_gm));
SPI1.CTRLB |= (SPI_SSD_bm);
SPI1.CTRLA |= (SPI_ENABLE_bm | SPI_MASTER_bm);
#endif
}
uint8_t RFM69::spi_transfer (uint8_t data) {
asm volatile("nop");
#ifdef EMONPI2
SPI0.DATA = data;
while ((SPI0.INTFLAGS & SPI_RXCIF_bm) == 0); // wait for complete send
return SPI0.DATA; // read data back
#endif
#ifdef EMONTX4
SPI1.DATA = data;
while ((SPI1.INTFLAGS & SPI_RXCIF_bm) == 0); // wait for complete send
return SPI1.DATA; // read data back
#endif
return 0;
}
void RFM69::configure (const uint8_t* p) {
while (true) {
uint8_t cmd = p[0];
if (cmd == 0)
break;
writeReg(cmd, p[1]);
p += 2;
}
}