-
Notifications
You must be signed in to change notification settings - Fork 0
/
si.c
527 lines (453 loc) · 17.6 KB
/
si.c
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
#include "Arduino.h"
#include "hc12.h"
#include "si.h"
#include "stm8.h"
#include <stdio.h>
#define PENDING_INTERRUPTS_CLEAR 0
#define PENDING_INTERRUPTS_KEEP 1
static uint8_t interrupt_state;
uint8_t si_hex(uint8_t nibble) {
if (nibble > 0xf)
return '.';
nibble += '0';
if (nibble > '9')
nibble += 'a' - '0' - 10;
return nibble;
}
void hexout(uint8_t byte) {
putchar(si_hex(byte >> 4));
putchar(si_hex(byte & 0xf));
}
void hexout16(uint16_t data) {
hexout(data >> 8);
hexout(data);
}
void si_debug(uint8_t c, uint8_t n) {
putchar(c); hexout(n); puts("\r");
}
static void si_err(uint8_t c) {
putchar(c);
}
static void spi_tx(uint8_t len, const uint8_t *data) {
for (uint8_t i = 0; i < len; i++) {
spi_transfer(data[i]);
}
}
static void spi_rx(uint8_t len, uint8_t *data) {
for (uint8_t i = 0; i < len; i++) {
data[i] = spi_transfer(0xFF);
}
}
static void waitCts(void) {
while (digitalRead(SI_IO1_CTS) == 0)
;
}
static void spi_select_tx(uint8_t len, const uint8_t *data) {
waitCts();
digitalWrite(SI_CS, 0);
spi_tx(len, data);
digitalWrite(SI_CS, 1);
}
// Returns 0 on timeout while waiting for cts signal.
static uint8_t si_read_cmd_buf(uint8_t len, uint8_t *dest) {
uint16_t i = 0;
uint8_t ctsVal;
do {
digitalWrite(SI_CS, 0);
spi_transfer(0x44);
ctsVal = spi_transfer(0xFF);
if (ctsVal == 0xFF) {
spi_rx(len, dest);
}
digitalWrite(SI_CS, 1);
} while (++i && ctsVal != 0xFF);
if (!i)
si_err('c');
return !!i;
}
// HC12 compatible radio params.
// GPIO config:
// GPIO0: TX_STATE (high while in TX)
// GPIO1: CTS (high when command handler is ready to receive the next signal)
// GPIO2/3: used for radio control (2: ANTENNA_1_SW, 3: ANTENNA_2_SW)
// NIRQ: active low interrupt signal
// SDO: POR (output goes low during Power-On Reset and goes high upon completion of POR)
static const uint8_t tx_config[] = {0x13, 0x60, 0x48, 0x56, 0x57, 0x67, 0x4b};
static const uint8_t rx_config[] = {0x13, 0x60, 0x48, 0x57, 0x56, 0x67, 0x4b};
// Args: Channel=2, Condition (next_state=1 (sleep/standby) << 4), tx_len (16bit_le), num_repeat
static uint8_t si_tx_cmd_buf[6] = {0x31, 2, 0x10, 0, 0, 0};
// Args: Channel=2, Start delayed, len (16bit_le)
static uint8_t si_rx_cmd_buf[8] = {0x32, 2, 0, 0, 0, SI_STATE_NO_CHANGE, SI_STATE_RX, SI_STATE_READY};
// len = 2
static uint8_t low_power_si_rx_cmd_buf[5] = {0x32, 0x02, 0, 0, 0x02};
// TX_TUNE
static uint8_t low_power_si_tx_cmd_buf[5] = {0x31, 0x02, 0x50, 0, 0x02};
static const uint8_t cmd_get_int_status_clear_pending[] = {0x20};
// Get interrupt status, but keep all pending interrupts.
static const uint8_t cmd_get_int_status_keep_pending[] = {0x20, 0xff, 0xff, 0xff};
static const uint8_t cmd_fifo_info[] = {0x15, 0};
static const uint8_t cmd_clear_fifo[] = {0x15, 3};
#define SET_PROPERTY(prop, len, ...) 0x11, (prop >> 8), len, (prop & 0xff), __VA_ARGS__
static const uint8_t cmd_set_pkt1_len[] = {SET_PROPERTY(0x120e, 1, 1)};
static const uint8_t init_commands[] = {
0x07, 0x02, 0x01, 0x00, 0x01, 0xc9, 0xc3, 0x80, // boot RF_POWER_UP
0x07, 0x13, 0x60, 0x48, 0x57, 0x56, 0x67, 0x4b, // gpio
0x02, 0x15, 0x03, // clear fifos
0x01, 0x20, // reset interrupts
0};
static const uint8_t config_common[] = {
SET_PROPERTY(0x0000, 1, 0x48),
SET_PROPERTY(0x0003, 1, 0x40),
SET_PROPERTY(0x0100, 4, 0x03, 0x38, 0x01, 0), // Packet handler interrupts (TX, RX, CRC error) pull NIRQ low, also + MODEM SYNC DETECT
SET_PROPERTY(0x0200, 4, 0x03, 0x07, 0x00, 0x00),
// Preamble-config: [6, 20, 0, 0x50, 0x31, 0, 0, 0, 0]
// 6 bytes preamble
// long preamble-timeout: 5 * 15 (=75) nibbles ~= 2.4ms
// 0x10 = First bit is 1, calculated from the calculator (default)
// 0x20 = Preamble tx_length register is in bytes
// 0x01 = Use standard preamble of 1010 (default)
SET_PROPERTY(0x1000, 0x09, 0x06, 0x14, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00),
// SYNC_CONFIG: 0x01: 16 bits sync word
SET_PROPERTY(0x1100, 0x05, 0x21, 0x89, 0x89, 0x00, 0x00),
// ITU-T CRC8, Seed = 0xFF
SET_PROPERTY(0x1200, 1, 0x81),
SET_PROPERTY(0x1206, 1, 0x02),
// Length field stored in FIFO. DataField2 will contain data.
SET_PROPERTY(0x1208, 2, 8 | 2, 0x00),
// Field 1 length: 1 (length byte) (in TX everything is stuffed into this field according to TX_LEN command option)
// CRC: enabled for this field, not checked in RX, but sent in TX
// Field 2 max length: 0x3F (0x40 - 1 length byte)
SET_PROPERTY(0x120d, 0x0c, 0x00, 0x01, 0x06, 0xa2, 0x00, 0x3f, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00),
SET_PROPERTY(0x1219, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
SET_PROPERTY(0x2000, 0x0c, 0x03, 0x00, 0x07, 0x26, 0x25, 0xa0, 0x01, 0xc9, 0xc3, 0x80, 0x00, 0x22),
SET_PROPERTY(0x200c, 1, 0x22),
SET_PROPERTY(0x2018, 0x08, 0x01, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x30),
SET_PROPERTY(0x2022, 0x09, 0x00, 0x78, 0x04, 0x44, 0x44, 0x04, 0x44, 0x02, 0x00),
SET_PROPERTY(0x202c, 0x07, 0x00, 0x23, 0x8f, 0xff, 0x00, 0xde, 0xa0),
SET_PROPERTY(0x2035, 1, 0xe2),
SET_PROPERTY(0x2038, 0x09, 0x22, 0x0d, 0x0d, 0x00, 0x1a, 0x40, 0x00, 0x00, 0x28),
SET_PROPERTY(0x2042, 0x0b, 0xa4, 0x03, 0xd6, 0x03, 0x01, 0x0a, 0x01, 0x80, 0xff, 0x0c, 0x00),
SET_PROPERTY(0x204e, 1, 0x40),
SET_PROPERTY(0x2051, 1, 0x0a),
SET_PROPERTY(0x2100, 0x0c, 0x5b, 0x47, 0x0f, 0xc0, 0x6d, 0x25, 0xf4, 0xdb, 0xd6, 0xdf, 0xec, 0xf7),
SET_PROPERTY(0x210c, 0x0c, 0xfe, 0x01, 0x15, 0xf0, 0xff, 0x03, 0x5b, 0x47, 0x0f, 0xc0, 0x6d, 0x25),
SET_PROPERTY(0x2118, 0x0c, 0xf4, 0xdb, 0xd6, 0xdf, 0xec, 0xf7, 0xfe, 0x01, 0x15, 0xf0, 0xff, 0x03),
SET_PROPERTY(0x2200, 4, 0x08, 0x7f, 0x00, 0x5d),
SET_PROPERTY(0x2300, 0x07, 0x01, 0x05, 0x0b, 0x05, 0x02, 0x00, 0x03),
SET_PROPERTY(0x3000, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
SET_PROPERTY(0x4000, 0x08, 0x38, 0x0d, 0xdd, 0xdd, 0x36, 0x9d, 0x20, 0xfe),
0
};
// packet length: 20
const uint8_t si_config_15kbit[] = {
// Length adjust: -5 so that length=0x18 (24) has a data section of (19 bytes) and thus a total packet length of 20
// for compatibility with original HC12 firmware.
SET_PROPERTY(0x120a, 1, (uint8_t) -5),
// Modem rate: 0xea60 = 60kbs, NCO mod: 0x2dc6c0 = 30e5, txosr = 40
// TX data rate = modem_rate * 30e6 / nco_mod / txosr = 15000 Hz, freq_dev=1049
SET_PROPERTY(0x2000, 12, 0x03, 0x00, 0x07, 0x00, 0xea, 0x60, 0x04, 0x2d, 0xc6, 0xc0, 0x00, 0x04),
SET_PROPERTY(0x200c, 1, 0x19),
SET_PROPERTY(0x2018, 8, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x20, 0x10),
SET_PROPERTY(0x2022, 9, 0x00, 0xa7, 0x03, 0x12, 0x6f, 0x01, 0x88, 0x02, 0xc2),
SET_PROPERTY(0x202c, 7, 0x04, 0x36, 0x80, 0x2c, 0x07, 0xe9, 0x80),
SET_PROPERTY(0x2038, 9, 0x11, 0x25, 0x25, 0x00, 0x1a, 0x80, 0x00, 0x00, 0x29),
SET_PROPERTY(0x2042, 11, 0xa4, 0x02, 0xd6, 0x83, 0x01, 0x44, 0x01, 0x80, 0xff, 0x0c, 0x00),
SET_PROPERTY(0x2100, 12, 0xcc, 0xa1, 0x30, 0xa0, 0x21, 0xd1, 0xb9, 0xc9, 0xea, 0x05, 0x12, 0x11),
SET_PROPERTY(0x210c, 12, 0x0a, 0x04, 0x15, 0xfc, 0x03, 0x00, 0xcc, 0xa1, 0x30, 0xa0, 0x21, 0xd1),
SET_PROPERTY(0x2118, 12, 0xb9, 0xc9, 0xea, 0x05, 0x12, 0x11, 0x0a, 0x04, 0x15, 0xfc, 0x03, 0x00),
SET_PROPERTY(0x2200, 4, 0x08, 0x7f, 0x00, 0x3d),
SET_PROPERTY(0x2300, 7, 0x2c, 0x0e, 0x0b, 0x04, 0x0c, 0x73, 0x03),
0
};
// packet length: 33
const uint8_t si_config_58kbit[] = {
// Length adjust: 33 - 1 - 0x18
SET_PROPERTY(0x120a, 1, 8),
// rate=116000, mod=3000000, txosr=20 → 58kb, freq_dev=2574
SET_PROPERTY(0x2000, 12, 0x03, 0x00, 0x07, 0x01, 0xc5, 0x20, 0x08, 0x2d, 0xc6, 0xc0, 0x00, 0x0a),
SET_PROPERTY(0x200c, 1, 0xec),
SET_PROPERTY(0x2018, 8, 0x01, 0x00, 0x08, 0x03, 0x80, 0x00, 0x10, 0x10),
SET_PROPERTY(0x2022, 9, 0x00, 0x56, 0x05, 0xf0, 0x6f, 0x04, 0x51, 0x02, 0x00),
SET_PROPERTY(0x202c, 7, 0x00, 0x12, 0x81, 0xfb, 0x02, 0x48, 0xa0),
SET_PROPERTY(0x2038, 9, 0x11, 0x13, 0x13, 0x00, 0x1a, 0x58, 0x47, 0x00, 0x28),
SET_PROPERTY(0x2042, 11, 0xa4, 0x03, 0xd6, 0x03, 0x01, 0xff, 0x01, 0x80, 0xff, 0x0c, 0x00),
SET_PROPERTY(0x2100, 12, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01),
SET_PROPERTY(0x210c, 12, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f, 0xff, 0xba, 0x0f, 0x51, 0xcf, 0xa9),
SET_PROPERTY(0x2118, 12, 0xc9, 0xfc, 0x1b, 0x1e, 0x0f, 0x01, 0xfc, 0xfd, 0x15, 0xff, 0x00, 0x0f),
SET_PROPERTY(0x2200, 4, 0x08, 0x7f, 0x00, 0x3d),
SET_PROPERTY(0x2300, 7, 0x00, 0x2c, 0x0e, 0x0b, 0x04, 0x0c, 0x73, 0x03, 0x00),
0
};
// packet length: 49
const uint8_t si_config_236kbit[] = {
// Length adjust: 49 - 1 - 0x18
SET_PROPERTY(0x120a, 1, 24),
// rate=2360000, mod=30e6, txosr=10 236kb
SET_PROPERTY(0x2000, 12, 0x03, 0x00, 0x07, 0x24, 0x02, 0xc0, 0x01, 0xc9, 0xc3, 0x80, 0x00, 0x20),
SET_PROPERTY(0x200c, 1, 0x39),
SET_PROPERTY(0x2018, 8, 0x01, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x30),
SET_PROPERTY(0x2022, 9, 0x00, 0x7f, 0x04, 0x07, 0x1a, 0x04, 0x08, 0x02, 0x00),
SET_PROPERTY(0x202c, 7, 0x00, 0x23, 0x8f, 0xff, 0x00, 0xde, 0xa0),
SET_PROPERTY(0x2038, 9, 0x22, 0x0e, 0x0e, 0x00, 0x1a, 0x40, 0x00, 0x00, 0x28),
SET_PROPERTY(0x2042, 11, 0xa4, 0x03, 0xd6, 0x03, 0x00, 0xfb, 0x01, 0x80, 0xff, 0x0c, 0x00),
SET_PROPERTY(0x2100, 12, 0x5b, 0x47, 0x0f, 0xc0, 0x6d, 0x25, 0xf4, 0xdb, 0xd6, 0xdf, 0xec, 0xf7),
SET_PROPERTY(0x210c, 12, 0xfe, 0x01, 0x15, 0xf0, 0xff, 0x03, 0x5b, 0x47, 0x0f, 0xc0, 0x6d, 0x25),
SET_PROPERTY(0x2118, 12, 0xf4, 0xdb, 0xd6, 0xdf, 0xec, 0xf7, 0xfe, 0x01, 0x15, 0xf0, 0xff, 0x03),
SET_PROPERTY(0x2200, 4, 0x08, 0x7f, 0x00, 0x5d),
SET_PROPERTY(0x2300, 7, 0x01, 0x05, 0x0b, 0x05, 0x02, 0x00, 0x03),
0
};
// packet length: 12
const uint8_t si_config_5kbit[] = {
// Length adjust: 49 - 1 - 0x18
SET_PROPERTY(0x120a, 1, (uint8_t) -13),
// rate=20000, mod=3e6, txosr=40 → 5kbit
SET_PROPERTY(0x2000, 12, 0x03, 0x00, 0x07, 0x00, 0x4e, 0x20, 0x04, 0x2d, 0xc6, 0xc0, 0x00, 0x04),
SET_PROPERTY(0x200c, 1, 0x19),
SET_PROPERTY(0x2018, 8, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x30, 0x20),
SET_PROPERTY(0x2022, 9, 0x01, 0x77, 0x01, 0x5d, 0x86, 0x00, 0xaf, 0x02, 0xc2),
SET_PROPERTY(0x202c, 7, 0x04, 0x36, 0x80, 0x0f, 0x13, 0x66, 0x80),
SET_PROPERTY(0x2038, 9, 0x11, 0x52, 0x52, 0x00, 0x1a, 0x18, 0x00, 0x00, 0x2a),
SET_PROPERTY(0x2042, 11, 0xa4, 0x02, 0xd6, 0x83, 0x01, 0xff, 0x01, 0x80, 0xff, 0x0c, 0x00),
SET_PROPERTY(0x2100, 12, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c),
SET_PROPERTY(0x210c, 12, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00, 0xff, 0xc4, 0x30, 0x7f, 0xf5, 0xb5),
SET_PROPERTY(0x2118, 12, 0xb8, 0xde, 0x05, 0x17, 0x16, 0x0c, 0x03, 0x00, 0x15, 0xff, 0x00, 0x00),
SET_PROPERTY(0x2200, 4, 0x08, 0x7f, 0x00, 0x3d),
SET_PROPERTY(0x2300, 7, 0x2c, 0x0e, 0x0b, 0x04, 0x0c, 0x73, 0x03),
0
};
static const uint8_t config_fu2[] = {
SET_PROPERTY(0x0001, 1, 0x01),
// WUT: 0, 15, 92, 32, 13, 1
// WUT_M: 3932 (4 * 3932 / 32.768 ~= .5s)
// WUT_R: 0x20 Go to Sleep state after WUT, R=0
// WUT_LDC: 0x0d (13) -> 4*13/ 32.768 (1.5ms)
SET_PROPERTY(0x0004, 6, 0x00, 0x0f, 0x5c, 0x20, 0x0d, 0x01),
SET_PROPERTY(0x2201, 1, 0x7f),
0
};
uint8_t si_get_property(uint16_t property) {
uint8_t cmd[] = {0x12, property >> 8, 1, property & 0xff};
uint8_t res;
spi_select_tx(sizeof(cmd), cmd);
si_read_cmd_buf(1, &res);
return res;
}
const uint8_t power_consts[] = {4, 6, 9, 13, 18, 26, 40, 127};
void si_set_tx_power(uint8_t power) {
uint8_t cmd[] = {SET_PROPERTY(0x2201, 1, power)};
spi_select_tx(sizeof(cmd), cmd);
}
void si_set_channel(uint8_t channel) {
channel++;
si_tx_cmd_buf[1] = channel;
si_rx_cmd_buf[1] = channel;
}
// Returns the chip part number, e.g. 0x4463
uint16_t si_get_chip(void) {
uint8_t cmd[] = {0x01}; // GET_CHIP_INFO
spi_select_tx(sizeof(cmd), cmd);
uint8_t chip_info[8];
uint8_t chip_info_success = si_read_cmd_buf(8, chip_info);
if (!chip_info_success)
return 0;
return chip_info[1] << 8 | chip_info[2];
}
static void si_dump_interrupt_state(uint8_t *interrupts) {
si_err('I'); // interrupt state
for (uint8_t i = 0; i < 8; i++)
hexout(interrupts[i]);
}
void si_debug_interrupts(void) {
uint8_t interrupts[8];
spi_select_tx(sizeof(cmd_get_int_status_keep_pending), cmd_get_int_status_keep_pending);
si_read_cmd_buf(8, interrupts);
si_dump_interrupt_state(interrupts);
}
void si_radio_config(const uint8_t *config_p) {
do {
uint8_t len = config_p[2] + 4;
spi_select_tx(len, config_p);
config_p += len;
} while (*config_p != 0);
}
uint8_t radio_init(const uint8_t *si_config_p) {
digitalWrite(SI_CS, 1);
pinMode(SI_CS, OUTPUT);
pinMode(SI_IRQ, INPUT);
pinMode(SI_IO1_CTS, INPUT);
#ifdef SI_RESET
digitalWrite(SI_RESET, 1);
pinMode(SI_RESET, OUTPUT);
delayMicroseconds(20);
digitalWrite(SI_RESET, 0);
#endif
spi_begin();
// Set speed to 8MHz (Si4463 is max 10MHz)
SPI_CR1 = 0x44;
// Wait until radio chip is ready to respond.
// Should this hang here. Make sure to power-cycle the chip.
waitCts();
// Sanity check to confirm that peripheral communication
// and the radio chip are working.
uint16_t chip = si_get_chip();
if (chip != 0x4463 && chip != 0x4438) {
// unexpected / unsupport chip (or read failure when 0)
si_debug('C', chip >> 8);
si_debug('C', chip & 0xff);
return 0;
}
// Send initial radio params.
const uint8_t *cmd_p = init_commands;
uint8_t len;
while ((len = *cmd_p) != 0) {
spi_select_tx(len, ++cmd_p);
cmd_p += len;
}
si_radio_config(config_common);
// Send mode specific radio params (for now only FU3 is know to be working)
si_radio_config(si_config_p);
// Reasonable default params (compatible with HC12’s AT+DEFAULT)
si_set_channel(1);
return 1;
}
void si_change_state(uint8_t state) {
digitalWrite(SI_CS, 0);
spi_transfer(0x34); // change state
spi_transfer(state);
digitalWrite(SI_CS, 1);
}
static void si_wait_interrupt_state(void) {
disableInterrupts();
while (!interrupt_state) {
wfi();
handle_events();
}
interrupt_state = 0;
enableInterrupts();
}
uint8_t si_check_interrupt(uint8_t field, uint8_t mask, uint8_t keep_pending) {
uint8_t interrupts[8];
if (keep_pending) {
spi_select_tx(sizeof(cmd_get_int_status_keep_pending), cmd_get_int_status_keep_pending);
} else {
spi_select_tx(sizeof(cmd_get_int_status_clear_pending), cmd_get_int_status_clear_pending);
}
si_read_cmd_buf(field + 1, interrupts);
return interrupts[field] & mask;
}
void si_wait_radio_tx_done(void) {
do {
si_wait_interrupt_state();
} while (!si_check_interrupt(
2, 0x20, PENDING_INTERRUPTS_CLEAR)); // PACKET_TX interrupt pending
}
void radio_gpio_rx_mode(void) {
spi_select_tx(sizeof(rx_config), rx_config);
}
void si_fill_tx_fifo(uint8_t len, const uint8_t *data) {
// Fill TX Fifo with data.
digitalWrite(SI_CS, 0);
spi_transfer(0x66); // TX_FIFO
spi_tx(len, data);
digitalWrite(SI_CS, 1);
}
void si_tx_fifo(uint8_t len) {
// radio_gpio_tx_mode
spi_select_tx(sizeof(tx_config), tx_config);
// Issue TX command
si_tx_cmd_buf[4] = len;
spi_select_tx(sizeof(si_tx_cmd_buf), si_tx_cmd_buf);
}
void radio_tx(uint8_t len, const uint8_t *data) {
si_fill_tx_fifo(len, data);
si_tx_fifo(len);
si_wait_radio_tx_done();
radio_gpio_rx_mode();
}
void si_read_rx_fifo(uint8_t len, uint8_t *dest) {
digitalWrite(SI_CS, 0);
spi_transfer(0x77); // READ_RX_FIFO
spi_rx(len, dest);
digitalWrite(SI_CS, 1);
}
static const uint8_t request_device_state[] = {0x33};
void si_start_rx(uint8_t len) {
si_rx_cmd_buf[4] = len;
if (len == 0) {
spi_select_tx(sizeof(cmd_set_pkt1_len), cmd_set_pkt1_len);
}
spi_select_tx(sizeof(si_rx_cmd_buf), si_rx_cmd_buf);
}
uint8_t si_get_state(void) {
uint8_t device_state;
spi_select_tx(1, request_device_state);
uint8_t device_state_success = si_read_cmd_buf(1, &device_state);
return device_state_success ? device_state : 0;
}
int8_t si_get_rx_fifo_size(void) {
int8_t rx_fifo_size = -1;
spi_select_tx(2, cmd_fifo_info);
if (!si_read_cmd_buf(1, &rx_fifo_size))
return -1;
return rx_fifo_size;
}
void si_clear_fifo(void) {
spi_select_tx(sizeof(cmd_clear_fifo), cmd_clear_fifo);
}
uint8_t si_wait_packet(void) {
uint8_t res;
while ((res = si_check_interrupt(2, 0x18, PENDING_INTERRUPTS_CLEAR)) ==
0) { // PACKET_RX or CRC_error interrupt pending
si_wait_interrupt_state();
}
return res;
}
void si_notify_nirq(void) {
interrupt_state = 1;
}
uint8_t radio_rx(uint8_t len, uint8_t *dest) {
if (si_get_state() != SI_STATE_RX ||
(si_rx_cmd_buf[4] != 0 && si_rx_cmd_buf[4] != len)) {
si_clear_fifo();
si_start_rx(len);
}
uint8_t int_status = si_check_interrupt(2, 0x18, PENDING_INTERRUPTS_CLEAR);
if (!int_status) {
// In case the RX fifo buffered a previous packet, retrieve this first.
// This should not happen unless the function is called with only a subset
// of the packet data to retrieve.
int8_t rxfifo = si_get_rx_fifo_size();
if (rxfifo >= (int8_t) len) {
// There’s already data there from previous packet(s) that we missed.
// retrieve this without checking CRC because those flags may be invalid.
si_read_rx_fifo(len, dest);
return len;
}
int_status = si_wait_packet();
}
int8_t rxfifo = si_get_rx_fifo_size();
if (rxfifo < 0)
return 0;
if (rxfifo < (int8_t) len)
len = rxfifo;
// Check pending interrupts to confirm that data is available and valid.
if ((int_status & 0x8) != 0) { // CRC error
// Clear fifos to discard bad data.
si_clear_fifo();
si_err('C');
return 0;
}
if ((int_status & 0x10) != 0) { // RX pending
si_read_rx_fifo(len, dest);
return len;
}
// edge case: there’s data in the fifo, but no RX pending event.
// this shouldn’t happen. The next radio_rx call will retrieve this data.
si_err('E');
return 0;
}
void radio_halt(void) {
// TODO: disable 32K osc
si_change_state(SI_STATE_SLEEP); // go to sleep
}