forked from hallard/RadioHead
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RH_MRF89.cpp
570 lines (503 loc) · 18.5 KB
/
RH_MRF89.cpp
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
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
// RH_MRF89.cpp
//
// Copyright (C) 2015 Mike McCauley
// $Id: RH_MRF89.cpp,v 1.10 2019/09/02 05:21:52 mikem Exp $
#include <RH_MRF89.h>
#define BAND_915
#define DATA_RATE_200
#define LNA_GAIN LNA_GAIN_0_DB
#define TX_POWER TX_POWER_13_DB
// Interrupt vectors for the 3 Arduino interrupt pins
// Each interrupt can be handled by a different instance of RH_MRF89, allowing you to have
// 2 or more LORAs per Arduino
RH_MRF89* RH_MRF89::_deviceForInterrupt[RH_MRF89_NUM_INTERRUPTS] = {0, 0, 0};
uint8_t RH_MRF89::_interruptCount = 0; // Index into _deviceForInterrupt for next device
// These are indexed by the values of ModemConfigChoice
// Values based on sample modulation values from MRF89XA.h
// TXIPOLFV set to be more than Fd
PROGMEM static const RH_MRF89::ModemConfig MODEM_CONFIG_TABLE[] =
{
// MODSEL, FDVAL, BRVAL, FILCREG=(PASFILV|BUTFILV), TXIPOLFV
// FSK, No Manchester, Whitening
{ RH_MRF89_MODSEL_FSK, 0x0B, 0x63, 0x40 | 0x01, 0x20 }, // FSK_Rb2Fd33
{ RH_MRF89_MODSEL_FSK, 0x0B, 0x27, 0x40 | 0x01, 0x20 }, // FSK_Rb5Fd33
{ RH_MRF89_MODSEL_FSK, 0x0B, 0x13, 0x40 | 0x01, 0x20 }, // FSK_Rb10Fd33
{ RH_MRF89_MODSEL_FSK, 0x09, 0x09, 0x70 | 0x02, 0x20 }, // FSK_Rb20Fd40
{ RH_MRF89_MODSEL_FSK, 0x04, 0x04, 0xB0 | 0x05, 0x40 }, // FSK_Rb40Fd80
{ RH_MRF89_MODSEL_FSK, 0x03, 0x03, 0xD0 | 0x06, 0x40 }, // FSK_Rb50Fd100
{ RH_MRF89_MODSEL_FSK, 0x02, 0x02, 0xE0 | 0x09, 0x60 }, // FSK_Rb66Fd133
{ RH_MRF89_MODSEL_FSK, 0x01, 0x01, 0xF0 | 0x0F, 0x80 }, // FSK_Rb100Fd200
{ RH_MRF89_MODSEL_FSK, 0x01, 0x00, 0xF0 | 0x0F, 0x80 } // FSK_Rb200Fd200
};
RH_MRF89::RH_MRF89(uint8_t csconPin, uint8_t csdatPin, uint8_t interruptPin, RHGenericSPI& spi)
:
RHNRFSPIDriver(csconPin, spi),
_csconPin(csconPin),
_csdatPin(csdatPin),
_interruptPin(interruptPin)
{
_myInterruptIndex = 0xff; // Not allocated yet
}
bool RH_MRF89::init()
{
// MRF89 data cant handle SPI greater than 1MHz.
// Sigh on teensy at 1MHz, need special delay after writes, see RHNRFSPIDriver::spiWrite
_spi.setFrequency(RHGenericSPI::Frequency1MHz);
if (!RHNRFSPIDriver::init())
return false;
// Initialise the chip select pins
pinMode(_csconPin, OUTPUT);
digitalWrite(_csconPin, HIGH);
pinMode(_csdatPin, OUTPUT);
digitalWrite(_csdatPin, HIGH);
// Determine the interrupt number that corresponds to the interruptPin
int interruptNumber = digitalPinToInterrupt(_interruptPin);
if (interruptNumber == NOT_AN_INTERRUPT)
return false;
#ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
interruptNumber = _interruptPin;
#endif
// Tell the low level SPI interface we will use SPI within this interrupt
spiUsingInterrupt(interruptNumber);
// Make sure we are not in some unexpected mode from a previous run
setOpMode(RH_MRF89_CMOD_STANDBY);
// No way to check the device type but lets trivially check there is something there
// by trying to change a register:
spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0xaa);
if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0xaa)
return false;
spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0x3); // Back to the default for FDEV
if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0x3)
return false;
// Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
// ARM M4 requires the below. else pin interrupt doesn't work properly.
// On all other platforms, its innocuous, belt and braces
pinMode(_interruptPin, INPUT);
// Set up interrupt handler
// Since there are a limited number of interrupt glue functions isr*() available,
// we can only support a limited number of devices simultaneously
// On some devices, notably most Arduinos, the interrupt pin passed in is actually the
// interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
// yourself based on knowledge of what Arduino board you are running on.
if (_myInterruptIndex == 0xff)
{
// First run, no interrupt allocated yet
if (_interruptCount <= RH_MRF89_NUM_INTERRUPTS)
_myInterruptIndex = _interruptCount++;
else
return false; // Too many devices, not enough interrupt vectors
}
_deviceForInterrupt[_myInterruptIndex] = this;
if (_myInterruptIndex == 0)
attachInterrupt(interruptNumber, isr0, RISING);
else if (_myInterruptIndex == 1)
attachInterrupt(interruptNumber, isr1, RISING);
else if (_myInterruptIndex == 2)
attachInterrupt(interruptNumber, isr2, RISING);
else
return false; // Too many devices, not enough interrupt vectors
// When used with the MRF89XAM9A module, per 75017B.pdf section 1.3, need:
// crystal freq = 12.8MHz
// clock output disabled
// frequency bands 902-915 or 915-928
// VCOT 60mV
// OOK max 28kbps
// Based on 70622C.pdf, section 3.12:
spiWriteRegister(RH_MRF89_REG_00_GCONREG, RH_MRF89_CMOD_STANDBY | RH_MRF89_FBS_950_960 | RH_MRF89_VCOT_60MV);
spiWriteRegister(RH_MRF89_REG_01_DMODREG, RH_MRF89_MODSEL_FSK | RH_MRF89_OPMODE_PACKET); // FSK, Packet mode, LNA 0dB
spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0); // Set by setModemConfig
spiWriteRegister(RH_MRF89_REG_03_BRSREG, 0); // Set by setModemConfig
spiWriteRegister(RH_MRF89_REG_04_FLTHREG, 0); // Set by setModemConfig (OOK only)
spiWriteRegister(RH_MRF89_REG_05_FIFOCREG, RH_MRF89_FSIZE_64);
spiWriteRegister(RH_MRF89_REG_06_R1CREG, 0); // Set by setFrequency
spiWriteRegister(RH_MRF89_REG_07_P1CREG, 0); // Set by setFrequency
spiWriteRegister(RH_MRF89_REG_08_S1CREG, 0); // Set by setFrequency
spiWriteRegister(RH_MRF89_REG_09_R2CREG, 0); // Frequency set 2 not used
spiWriteRegister(RH_MRF89_REG_0A_P2CREG, 0); // Frequency set 2 not used
spiWriteRegister(RH_MRF89_REG_0B_S2CREG, 0); // Frequency set 2 not used
spiWriteRegister(RH_MRF89_REG_0C_PACREG, RH_MRF89_PARC_23);
// IRQ0 rx mode: SYNC (not used)
// IRQ1 rx mode: CRCOK
// IRQ1 tx mode: TXDONE
spiWriteRegister(RH_MRF89_REG_0D_FTXRXIREG, RH_MRF89_IRQ0RXS_PACKET_SYNC | RH_MRF89_IRQ1RXS_PACKET_CRCOK | RH_MRF89_IRQ1TX);
spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, RH_MRF89_LENPLL);
spiWriteRegister(RH_MRF89_REG_0F_RSTHIREG, 0x00); // default not used if no RSSI interrupts
spiWriteRegister(RH_MRF89_REG_10_FILCREG, 0); // Set by setModemConfig
spiWriteRegister(RH_MRF89_REG_11_PFCREG, 0x38);// 100kHz, recommended, but not used, see RH_MRF89_REG_12_SYNCREG OOK only?
spiWriteRegister(RH_MRF89_REG_12_SYNCREG, RH_MRF89_SYNCREN | RH_MRF89_SYNCWSZ_32); // No polyphase, no bsync, sync, 0 errors
spiWriteRegister(RH_MRF89_REG_13_RSVREG, 0x07);//default
// spiWriteRegister(RH_MRF89_REG_14_RSTSREG, 0x00); // NO, read only
spiWriteRegister(RH_MRF89_REG_15_OOKCREG, 0x00); // Set by setModemConfig OOK only
spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG, 0x69); // Set by setSyncWords
spiWriteRegister(RH_MRF89_REG_17_SYNCV23REG, 0x81); // Set by setSyncWords
spiWriteRegister(RH_MRF89_REG_18_SYNCV15REG, 0x7E); // Set by setSyncWords
spiWriteRegister(RH_MRF89_REG_19_SYNCV07REG, 0x96); // Set by setSyncWords
// TXIPOLFV set by setModemConfig. power set by setTxPower
spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, 0xf0 | RH_MRF89_TXOPVAL_13DBM); // TX cutoff freq=375kHz,
spiWriteRegister(RH_MRF89_REG_1B_CLKOREG, 0x00); // Disable clock output to save power
spiWriteRegister(RH_MRF89_REG_1C_PLOADREG, 0x40); // payload=64bytes (no RX-filtering on packet length)
spiWriteRegister(RH_MRF89_REG_1D_NADDSREG, 0x00); // Node Address (0=default) Not used
spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, RH_MRF89_PKTLENF | RH_MRF89_PRESIZE_4 | RH_MRF89_WHITEON | RH_MRF89_CHKCRCEN | RH_MRF89_ADDFIL_OFF);
spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, 0x00); // default (FIFO access in standby=write, clear FIFO on CRC mismatch)
// Looking OK now
// Set some suitable defaults:
setPreambleLength(3); // The default
uint8_t syncwords[] = { 0x69, 0x81, 0x7e, 0x96 }; // Same as RH_MRF89XA
setSyncWords(syncwords, sizeof(syncwords));
setTxPower(RH_MRF89_TXOPVAL_1DBM);
if (!setFrequency(915.4))
return false;
// Some slow, reliable default speed and modulation
if (!setModemConfig(FSK_Rb20Fd40))
return false;
return true;
}
bool RH_MRF89::printRegisters()
{
#ifdef RH_HAVE_SERIAL
uint8_t i;
for (i = 0; i <= 0x1f; i++)
{
Serial.print(i, HEX);
Serial.print(": ");
Serial.println(spiReadRegister(i), HEX);
}
#endif
return true;
}
// C++ level interrupt handler for this instance
// MRF89XA is unusual in that it has 2 interrupt lines, and not a single, combined one.
// Only one of the several interrupt lines (IRQ1) from the RFM95 needs to be
// connnected to the processor.
// We use this to get CRCOK and TXDONE interrupts
void RH_MRF89::handleInterrupt()
{
// Serial.println("I");
if (_mode == RHModeTx)
{
// Serial.println("T");
// TXDONE
// Transmit is complete
_txGood++;
setModeIdle();
}
else if (_mode == RHModeRx)
{
// Serial.println("R");
// CRCOK
// We have received a packet.
// First byte in FIFO is packet length
// REVISIT: Capture last rssi from RSTSREG
// based roughly on Figure 3-9
_lastRssi = (spiReadRegister(RH_MRF89_REG_14_RSTSREG) >> 1) - 120;
_bufLen = spiReadData();
if (_bufLen < 4)
{
// Drain the FIFO
uint8_t i;
for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
spiReadData();
clearRxBuf();
return;
}
// Now drain all the data from the FIFO into _buf
uint8_t i;
for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++)
_buf[i] = spiReadData();
// All good. See if its for us
validateRxBuf();
if (_rxBufValid)
setModeIdle(); // Got one
}
}
// These are low level functions that call the interrupt handler for the correct
// instance of RH_MRF89.
// 3 interrupts allows us to have 3 different devices
void RH_INTERRUPT_ATTR RH_MRF89::isr0()
{
if (_deviceForInterrupt[0])
_deviceForInterrupt[0]->handleInterrupt();
}
void RH_INTERRUPT_ATTR RH_MRF89::isr1()
{
if (_deviceForInterrupt[1])
_deviceForInterrupt[1]->handleInterrupt();
}
void RH_INTERRUPT_ATTR RH_MRF89::isr2()
{
if (_deviceForInterrupt[2])
_deviceForInterrupt[2]->handleInterrupt();
}
uint8_t RH_MRF89::spiReadRegister(uint8_t reg)
{
// Tell the chip we want to talk to the configuration registers
setSlaveSelectPin(_csconPin);
digitalWrite(_csdatPin, HIGH);
return spiRead(((reg & 0x1f) << 1) | RH_MRF89_SPI_READ_MASK);
}
uint8_t RH_MRF89::spiWriteRegister(uint8_t reg, uint8_t val)
{
// Tell the chip we want to talk to the configuration registers
setSlaveSelectPin(_csconPin);
digitalWrite(_csdatPin, HIGH);
// Hmmm, on teensy 3.1, needed some special behaviour in RHNRFSPIDriver::spiWrite
// because otherwise, CSCON returns high before the final clock goes low,
// which prevents the MRF89XA spi write succeeding. Clock must be low when CSCON goes high.
return spiWrite(((reg & 0x1f) << 1), val);
}
uint8_t RH_MRF89::spiWriteData(uint8_t data)
{
spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
setSlaveSelectPin(_csdatPin);
digitalWrite(_csconPin, HIGH);
return spiCommand(data);
}
uint8_t RH_MRF89::spiWriteData(const uint8_t* data, uint8_t len)
{
spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO
setSlaveSelectPin(_csdatPin);
digitalWrite(_csconPin, HIGH);
uint8_t status = 0;
ATOMIC_BLOCK_START;
_spi.beginTransaction();
digitalWrite(_slaveSelectPin, LOW);
while (len--)
_spi.transfer(*data++);
digitalWrite(_slaveSelectPin, HIGH);
_spi.endTransaction();
ATOMIC_BLOCK_END;
return status;
}
uint8_t RH_MRF89::spiReadData()
{
spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC | RH_MRF89_FRWAXS); // Read from FIFO
setSlaveSelectPin(_csdatPin);
digitalWrite(_csconPin, HIGH);
return spiCommand(0);
}
void RH_MRF89::setOpMode(uint8_t mode)
{
// REVISIT: do we need to have time delays when switching between modes?
uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
val = (val & ~RH_MRF89_CMOD) | (mode & RH_MRF89_CMOD);
spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
}
void RH_MRF89::setModeIdle()
{
if (_mode != RHModeIdle)
{
setOpMode(RH_MRF89_CMOD_STANDBY);
_mode = RHModeIdle;
}
}
bool RH_MRF89::sleep()
{
if (_mode != RHModeSleep)
{
setOpMode(RH_MRF89_CMOD_SLEEP);
_mode = RHModeSleep;
}
return true;
}
void RH_MRF89::setModeRx()
{
if (_mode != RHModeRx)
{
setOpMode(RH_MRF89_CMOD_RECEIVE);
_mode = RHModeRx;
}
}
void RH_MRF89::setModeTx()
{
if (_mode != RHModeTx)
{
setOpMode(RH_MRF89_CMOD_TRANSMIT);
_mode = RHModeTx;
}
}
void RH_MRF89::setTxPower(uint8_t power)
{
uint8_t txconreg = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
txconreg |= (power & RH_MRF89_TXOPVAL);
spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, txconreg);
}
bool RH_MRF89::available()
{
if (_mode == RHModeTx)
return false;
setModeRx();
return _rxBufValid; // Will be set by the interrupt handler when a good message is received
}
bool RH_MRF89::recv(uint8_t* buf, uint8_t* len)
{
if (!available())
return false;
if (buf && len)
{
ATOMIC_BLOCK_START;
// Skip the 4 headers that are at the beginning of the rxBuf
if (*len > _bufLen - RH_MRF89_HEADER_LEN)
*len = _bufLen - RH_MRF89_HEADER_LEN;
memcpy(buf, _buf + RH_MRF89_HEADER_LEN, *len);
ATOMIC_BLOCK_END;
}
clearRxBuf(); // This message accepted and cleared
return true;
}
bool RH_MRF89::send(const uint8_t* data, uint8_t len)
{
if (len > RH_MRF89_MAX_MESSAGE_LEN)
return false;
waitPacketSent(); // Make sure we dont interrupt an outgoing message
setModeIdle();
if (!waitCAD())
return false; // Check channel activity
// First octet is the length of the chip payload
// 0 length messages are transmitted but never trigger a receive!
spiWriteData(len + RH_MRF89_HEADER_LEN);
spiWriteData(_txHeaderTo);
spiWriteData(_txHeaderFrom);
spiWriteData(_txHeaderId);
spiWriteData(_txHeaderFlags);
spiWriteData(data, len);
setModeTx(); // Start transmitting
return true;
}
uint8_t RH_MRF89::maxMessageLength()
{
return RH_MRF89_MAX_MESSAGE_LEN;
}
// Check whether the latest received message is complete and uncorrupted
void RH_MRF89::validateRxBuf()
{
if (_bufLen < 4)
return; // Too short to be a real message
// Extract the 4 headers
_rxHeaderTo = _buf[0];
_rxHeaderFrom = _buf[1];
_rxHeaderId = _buf[2];
_rxHeaderFlags = _buf[3];
if (_promiscuous ||
_rxHeaderTo == _thisAddress ||
_rxHeaderTo == RH_BROADCAST_ADDRESS)
{
_rxGood++;
_rxBufValid = true;
}
}
void RH_MRF89::clearRxBuf()
{
ATOMIC_BLOCK_START;
_rxBufValid = false;
_bufLen = 0;
ATOMIC_BLOCK_END;
}
bool RH_MRF89::verifyPLLLock()
{
// Verify PLL-lock per instructions in Note 1 section 3.12
// Need to do this after changing frequency.
uint8_t ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, ftpriVal | RH_MRF89_LSTSPLL); // Clear PLL lock bit
setOpMode(RH_MRF89_CMOD_FS);
unsigned long ulStartTime = millis();
while ((millis() - ulStartTime < 1000))
{
ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG);
if ((ftpriVal & RH_MRF89_LSTSPLL) != 0)
break;
}
setOpMode(RH_MRF89_CMOD_STANDBY);
return ((ftpriVal & RH_MRF89_LSTSPLL) != 0);
}
bool RH_MRF89::setFrequency(float centre)
{
// REVISIT: FSK only: its different for OOK :-(
uint8_t FBS;
if (centre >= 902.0 && centre < 915.0)
{
FBS = RH_MRF89_FBS_902_915;
}
else if (centre >= 915.0 && centre <= 928.0)
{
FBS = RH_MRF89_FBS_915_928;
}
else if (centre >= 950.0 && centre <= 960.0)
{
// Not all modules support this frequency band:
// The MRF98XAM9A does not
FBS = RH_MRF89_FBS_950_960;
}
// else if (centre >= 863.0 && centre <= 870.0)
// {
// // Not all modules support this frequency band:
// // The MRF98XAM9A does not
// FBS = RH_MRF89_FBS_950_960; // Yes same as above
// }
else
{
// Cant do this freq
return false;
}
// Based on frequency calcs done in MRF89XA.h
// uint8_t R = 100; // Recommended
uint8_t R = 119; // Also recommended :-(
uint32_t centre_kHz = centre * 1000;
uint32_t xtal_kHz = (RH_MRF89_XTAL_FREQ * 1000);
uint32_t compare = (centre_kHz * 8 * (R + 1)) / (9 * xtal_kHz);
uint8_t P = ((compare - 75) / 76) + 1;
uint8_t S = compare - (75 * (P + 1));
// Now set the new register values:
uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG);
val = (val & ~RH_MRF89_FBS) | (FBS & RH_MRF89_FBS);
spiWriteRegister(RH_MRF89_REG_00_GCONREG, val);
spiWriteRegister(RH_MRF89_REG_06_R1CREG, R);
spiWriteRegister(RH_MRF89_REG_07_P1CREG, P);
spiWriteRegister(RH_MRF89_REG_08_S1CREG, S);
return verifyPLLLock();
}
// Set one of the canned FSK Modem configs
// Returns true if its a valid choice
bool RH_MRF89::setModemConfig(ModemConfigChoice index)
{
if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
return false;
RH_MRF89::ModemConfig cfg;
memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(cfg));
// Now update the registers
uint8_t val = spiReadRegister(RH_MRF89_REG_01_DMODREG);
val = (val & ~RH_MRF89_MODSEL) | cfg.MODSEL;
spiWriteRegister(RH_MRF89_REG_01_DMODREG, val);
spiWriteRegister(RH_MRF89_REG_02_FDEVREG, cfg.FDVAL);
spiWriteRegister(RH_MRF89_REG_03_BRSREG, cfg.BRVAL);
spiWriteRegister(RH_MRF89_REG_10_FILCREG, cfg.FILCREG);
// The sample configs in MRF89XA.h all use TXIPOLFV = 0xf0 => 375kHz, which is too wide for most modulations
val = spiReadRegister(RH_MRF89_REG_1A_TXCONREG);
val = (val & ~RH_MRF89_TXIPOLFV) | (cfg.TXIPOLFV & RH_MRF89_TXIPOLFV);
spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, val);
return true;
}
void RH_MRF89::setPreambleLength(uint8_t bytes)
{
if (bytes >= 1 && bytes <= 4)
{
bytes--;
uint8_t pktcreg = spiReadRegister(RH_MRF89_REG_1E_PKTCREG);
pktcreg = (pktcreg & ~RH_MRF89_PRESIZE) | ((bytes << 5) & RH_MRF89_PRESIZE);
spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, pktcreg);
}
}
void RH_MRF89::setSyncWords(const uint8_t* syncWords, uint8_t len)
{
if (syncWords && (len > 0 and len <= 4))
{
uint8_t syncreg = spiReadRegister(RH_MRF89_REG_12_SYNCREG);
syncreg = (syncreg & ~RH_MRF89_SYNCWSZ) | (((len - 1) << 3) & RH_MRF89_SYNCWSZ);
spiWriteRegister(RH_MRF89_REG_12_SYNCREG, syncreg);
uint8_t i;
for (i = 0; i < 4; i++)
{
if (len > i)
spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG + i, syncWords[i]);
}
}
}