1d28148ba2b4a74366a87c345cad72e6f9ebb02d
[fw/altos] / src / drivers / ao_cc1120.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_cc1120.h>
20 #include <ao_exti.h>
21 #include <ao_fec.h>
22 #include <ao_packet.h>
23
24 uint8_t ao_radio_wake;
25 uint8_t ao_radio_mutex;
26 uint8_t ao_radio_abort;
27 uint8_t ao_radio_in_recv;
28
29 #define CC1120_DEBUG    AO_FEC_DEBUG
30 #define CC1120_TRACE    0
31
32 const uint32_t  ao_radio_cal = 0x6ca333;
33
34 #define FOSC    32000000
35
36 #define ao_radio_select()       ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_SPI_SPEED_1MHz)
37 #define ao_radio_deselect()     ao_spi_put_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS)
38 #define ao_radio_spi_send(d,l)  ao_spi_send((d), (l), AO_CC1120_SPI_BUS)
39 #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1120_SPI_BUS)
40 #define ao_radio_spi_recv(d,l)  ao_spi_recv((d), (l), AO_CC1120_SPI_BUS)
41 #define ao_radio_duplex(o,i,l)  ao_spi_duplex((o), (i), (l), AO_CC1120_SPI_BUS)
42
43 static uint8_t
44 ao_radio_reg_read(uint16_t addr)
45 {
46         uint8_t data[2];
47         uint8_t d;
48
49 #if CC1120_TRACE
50         printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
51 #endif
52         if (CC1120_IS_EXTENDED(addr)) {
53                 data[0] = ((1 << CC1120_READ)  |
54                            (0 << CC1120_BURST) |
55                            CC1120_EXTENDED);
56                 data[1] = addr;
57                 d = 2;
58         } else {
59                 data[0] = ((1 << CC1120_READ)  |
60                            (0 << CC1120_BURST) |
61                            addr);
62                 d = 1;
63         }
64         ao_radio_select();
65         ao_radio_spi_send(data, d);
66         ao_radio_spi_recv(data, 1);
67         ao_radio_deselect();
68 #if CC1120_TRACE
69         printf (" %02x\n", data[0]);
70 #endif
71         return data[0];
72 }
73
74 static void
75 ao_radio_reg_write(uint16_t addr, uint8_t value)
76 {
77         uint8_t data[3];
78         uint8_t d;
79
80 #if CC1120_TRACE
81         printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
82 #endif
83         if (CC1120_IS_EXTENDED(addr)) {
84                 data[0] = ((0 << CC1120_READ)  |
85                            (0 << CC1120_BURST) |
86                            CC1120_EXTENDED);
87                 data[1] = addr;
88                 d = 2;
89         } else {
90                 data[0] = ((0 << CC1120_READ)  |
91                            (0 << CC1120_BURST) |
92                            addr);
93                 d = 1;
94         }
95         data[d] = value;
96         ao_radio_select();
97         ao_radio_spi_send(data, d+1);
98         ao_radio_deselect();
99 }
100
101 static void
102 ao_radio_burst_read_start (uint16_t addr)
103 {
104         uint8_t data[2];
105         uint8_t d;
106
107         if (CC1120_IS_EXTENDED(addr)) {
108                 data[0] = ((1 << CC1120_READ)  |
109                            (1 << CC1120_BURST) |
110                            CC1120_EXTENDED);
111                 data[1] = addr;
112                 d = 2;
113         } else {
114                 data[0] = ((1 << CC1120_READ)  |
115                            (1 << CC1120_BURST) |
116                            addr);
117                 d = 1;
118         }
119         ao_radio_select();
120         ao_radio_spi_send(data, d);
121 }
122
123 static void
124 ao_radio_burst_read_stop (void)
125 {
126         ao_radio_deselect();
127 }
128
129
130 static uint8_t
131 ao_radio_strobe(uint8_t addr)
132 {
133         uint8_t in;
134
135 #if CC1120_TRACE
136         printf("\t\tao_radio_strobe (%02x): ", addr); flush();
137 #endif
138         ao_radio_select();
139         ao_radio_duplex(&addr, &in, 1);
140         ao_radio_deselect();
141 #if CC1120_TRACE
142         printf("%02x\n", in); flush();
143 #endif
144         return in;
145 }
146
147 static uint8_t
148 ao_radio_fifo_read(uint8_t *data, uint8_t len)
149 {
150         uint8_t addr = ((1 << CC1120_READ)  |
151                         (1 << CC1120_BURST) |
152                         CC1120_FIFO);
153         uint8_t status;
154
155         ao_radio_select();
156         ao_radio_duplex(&addr, &status, 1);
157         ao_radio_spi_recv(data, len);
158         ao_radio_deselect();
159         return status;
160 }
161
162 static uint8_t
163 ao_radio_fifo_write(uint8_t *data, uint8_t len)
164 {
165         uint8_t addr = ((0 << CC1120_READ)  |
166                         (1 << CC1120_BURST) |
167                         CC1120_FIFO);
168         uint8_t status;
169
170         ao_radio_select();
171         ao_radio_duplex(&addr, &status, 1);
172         ao_radio_spi_send(data, len);
173         ao_radio_deselect();
174         return status;
175 }
176
177 static uint8_t
178 ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
179 {
180         uint8_t addr = ((0 << CC1120_READ)  |
181                         (1 << CC1120_BURST) |
182                         CC1120_FIFO);
183         uint8_t status;
184
185         ao_radio_select();
186         ao_radio_duplex(&addr, &status, 1);
187         ao_radio_spi_send_fixed(data, len);
188         ao_radio_deselect();
189         return status;
190 }
191
192 static uint8_t
193 ao_radio_tx_fifo_space(void)
194 {
195         return CC1120_FIFO_SIZE - ao_radio_reg_read(CC1120_NUM_TXBYTES);
196 }
197
198 static uint8_t
199 ao_radio_status(void)
200 {
201         return ao_radio_strobe (CC1120_SNOP);
202 }
203
204 void
205 ao_radio_recv_abort(void)
206 {
207         ao_radio_abort = 1;
208         ao_wakeup(&ao_radio_wake);
209 }
210
211 #define ao_radio_rdf_value 0x55
212
213 static uint8_t
214 ao_radio_marc_status(void)
215 {
216         return ao_radio_reg_read(CC1120_MARC_STATUS1);
217 }
218
219 static void
220 ao_radio_tx_isr(void)
221 {
222         ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
223         ao_radio_wake = 1;
224         ao_wakeup(&ao_radio_wake);
225 }
226
227 static void
228 ao_radio_start_tx(void)
229 {
230         ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_tx_isr);
231         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
232         ao_radio_strobe(CC1120_STX);
233 }
234
235 static void
236 ao_radio_idle(void)
237 {
238         for (;;) {
239                 uint8_t state = ao_radio_strobe(CC1120_SIDLE);
240                 if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE)
241                         break;
242         }
243 }
244
245 /*
246  * Packet deviation is 20.5kHz
247  *
248  *      fdev = fosc >> 24 * (256 + dev_m) << dev_e
249  *
250  *      32e6Hz / (2 ** 24) * (256 + 80) * (2 ** 5) = 20508Hz
251  */
252
253 #define PACKET_DEV_E    5
254 #define PACKET_DEV_M    80
255
256 /*
257  * For our packet data, set the symbol rate to 38360 Baud
258  *
259  *              (2**20 + DATARATE_M) * 2 ** DATARATE_E
260  *      Rdata = -------------------------------------- * fosc
261  *                           2 ** 39
262  *
263  *
264  *      DATARATE_M = 239914
265  *      DATARATE_E = 9
266  */
267 #define PACKET_DRATE_E  9
268 #define PACKET_DRATE_M  239914
269
270 static const uint16_t packet_setup[] = {
271         CC1120_DEVIATION_M,     PACKET_DEV_M,
272         CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
273                                  (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
274                                  (PACKET_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
275         CC1120_DRATE2,          ((PACKET_DRATE_E << CC1120_DRATE2_DATARATE_E) |
276                                  (((PACKET_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
277         CC1120_DRATE1,          ((PACKET_DRATE_M >> 8) & 0xff),
278         CC1120_DRATE0,          ((PACKET_DRATE_M >> 0) & 0xff),
279         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
280                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
281         CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
282                                  (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
283                                  (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
284                                  (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
285         CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
286                                  (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
287                                  (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
288                                  (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
289                                  (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
290 };
291
292 static const uint16_t packet_tx_setup[] = {
293         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
294                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
295         CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG,
296 };
297
298 static const uint16_t packet_rx_setup[] = {
299         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
300                                  (CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)),
301         CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT,
302 };
303
304 /*
305  * RDF deviation is 5kHz
306  *
307  *      fdev = fosc >> 24 * (256 + dev_m) << dev_e
308  *
309  *      32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989
310  */
311
312 #define RDF_DEV_E       3
313 #define RDF_DEV_M       71
314 #define RDF_PACKET_LEN  50
315
316 /*
317  * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
318  *
319  *              (2**20 - DATARATE_M) * 2 ** DATARATE_E
320  *      Rdata = -------------------------------------- * fosc
321  *                           2 ** 39
322  *
323  *      DATARATE_M = 511705
324  *      DATARATE_E = 6
325  *
326  * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
327  */
328 #define RDF_DRATE_E     5
329 #define RDF_DRATE_M     25166
330 #define RDF_PACKET_LEN  50
331
332 static const uint16_t rdf_setup[] = {
333         CC1120_DEVIATION_M,     RDF_DEV_M,
334         CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
335                                  (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
336                                  (RDF_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
337         CC1120_DRATE2,          ((RDF_DRATE_E << CC1120_DRATE2_DATARATE_E) |
338                                  (((RDF_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
339         CC1120_DRATE1,          ((RDF_DRATE_M >> 8) & 0xff),
340         CC1120_DRATE0,          ((RDF_DRATE_M >> 0) & 0xff),
341         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
342                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
343         CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
344                                  (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
345                                  (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
346                                  (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
347         CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
348                                  (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
349                                  (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
350                                  (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
351                                  (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
352 };
353
354 static uint8_t ao_radio_mode;
355
356 #define AO_RADIO_MODE_BITS_PACKET       1
357 #define AO_RADIO_MODE_BITS_PACKET_TX    2
358 #define AO_RADIO_MODE_BITS_TX_BUF       4
359 #define AO_RADIO_MODE_BITS_TX_FINISH    8
360 #define AO_RADIO_MODE_BITS_PACKET_RX    16
361 #define AO_RADIO_MODE_BITS_RDF          32
362
363 #define AO_RADIO_MODE_NONE              0
364 #define AO_RADIO_MODE_PACKET_TX_BUF     (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
365 #define AO_RADIO_MODE_PACKET_TX_FINISH  (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
366 #define AO_RADIO_MODE_PACKET_RX         (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX)
367 #define AO_RADIO_MODE_RDF               (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
368
369 static void
370 ao_radio_set_mode(uint8_t new_mode)
371 {
372         uint8_t changes;
373         int i;
374
375         if (new_mode == ao_radio_mode)
376                 return;
377
378         changes = new_mode & (~ao_radio_mode);
379         if (changes & AO_RADIO_MODE_BITS_PACKET)
380                 for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
381                         ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
382
383         if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
384                 for (i = 0; i < sizeof (packet_tx_setup) / sizeof (packet_tx_setup[0]); i += 2)
385                         ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]);
386                 
387         if (changes & AO_RADIO_MODE_BITS_TX_BUF)
388                 ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR);
389
390         if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
391                 ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
392
393         if (changes & AO_RADIO_MODE_BITS_PACKET_RX)
394                 for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2)
395                         ao_radio_reg_write(packet_rx_setup[i], packet_rx_setup[i+1]);
396                 
397         if (changes & AO_RADIO_MODE_BITS_RDF)
398                 for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
399                         ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
400         ao_radio_mode = new_mode;
401 }
402
403 static const uint16_t radio_setup[] = {
404 #include "ao_cc1120_CC1120.h"
405 };
406
407 static uint8_t  ao_radio_configured = 0;
408
409 static void
410 ao_radio_setup(void)
411 {
412         int     i;
413
414         ao_radio_strobe(CC1120_SRES);
415
416         for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
417                 ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
418
419         ao_radio_mode = 0;
420
421         ao_config_get();
422
423         ao_radio_configured = 1;
424 }
425
426 static void
427 ao_radio_get(uint8_t len)
428 {
429         static uint32_t last_radio_setting;
430         static uint8_t  last_len;
431
432         ao_mutex_get(&ao_radio_mutex);
433         if (!ao_radio_configured)
434                 ao_radio_setup();
435         if (ao_config.radio_setting != last_radio_setting) {
436                 ao_radio_reg_write(CC1120_FREQ2, ao_config.radio_setting >> 16);
437                 ao_radio_reg_write(CC1120_FREQ1, ao_config.radio_setting >> 8);
438                 ao_radio_reg_write(CC1120_FREQ0, ao_config.radio_setting);
439                 last_radio_setting = ao_config.radio_setting;
440         }
441         if (len != last_len) {
442                 ao_radio_reg_write(CC1120_PKT_LEN, len);
443                 last_len = len;
444         }
445 }
446
447 #define ao_radio_put()  ao_mutex_put(&ao_radio_mutex)
448
449 void
450 ao_radio_rdf(uint8_t len)
451 {
452         int i;
453
454         ao_radio_abort = 0;
455         ao_radio_get(len);
456
457         ao_radio_set_mode(AO_RADIO_MODE_RDF);
458         ao_radio_wake = 0;
459
460         ao_radio_fifo_write_fixed(ao_radio_rdf_value, len);
461
462         ao_radio_start_tx();
463
464         cli();
465         while (!ao_radio_wake && !ao_radio_abort)
466                 ao_sleep(&ao_radio_wake);
467         sei();
468         if (!ao_radio_wake)
469                 ao_radio_idle();
470         ao_radio_put();
471 }
472
473 void
474 ao_radio_rdf_abort(void)
475 {
476         ao_radio_abort = 1;
477         ao_wakeup(&ao_radio_wake);
478 }
479
480 static void
481 ao_radio_test(void)
482 {
483         uint8_t mode = 2;
484         uint8_t radio_on;
485         ao_cmd_white();
486         if (ao_cmd_lex_c != '\n') {
487                 ao_cmd_decimal();
488                 mode = (uint8_t) ao_cmd_lex_u32;
489         }
490         mode++;
491         if ((mode & 2) && !radio_on) {
492 #if HAS_MONITOR
493                 ao_monitor_disable();
494 #endif
495 #if PACKET_HAS_SLAVE
496                 ao_packet_slave_stop();
497 #endif
498                 ao_radio_get(0xff);
499                 ao_radio_strobe(CC1120_STX);
500 #if CC1120_TRACE
501                 { int t; 
502                         for (t = 0; t < 10; t++) {
503                                 printf ("status: %02x\n", ao_radio_status());
504                                 ao_delay(AO_MS_TO_TICKS(100));
505                         }
506                 }
507 #endif
508                 radio_on = 1;
509         }
510         if (mode == 3) {
511                 printf ("Hit a character to stop..."); flush();
512                 getchar();
513                 putchar('\n');
514         }
515         if ((mode & 1) && radio_on) {
516                 ao_radio_idle();
517                 ao_radio_put();
518                 radio_on = 0;
519 #if HAS_MONITOR
520                 ao_monitor_enable();
521 #endif
522         }
523 }
524
525 void
526 ao_radio_send(const void *d, uint8_t size)
527 {
528         uint8_t         marc_status;
529         static uint8_t  encode[256];
530         uint8_t         *e = encode;
531         uint8_t         encode_len;
532         uint8_t         this_len;
533         uint8_t         started = 0;
534         uint8_t         fifo_space;
535
536         encode_len = ao_fec_encode(d, size, encode);
537
538         ao_radio_get(encode_len);
539
540         started = 0;
541         fifo_space = CC1120_FIFO_SIZE;
542         while (encode_len) {
543                 this_len = encode_len;
544
545                 if (this_len > fifo_space) {
546                         this_len = fifo_space;
547                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
548                 } else {
549                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
550                 }
551
552                 ao_radio_fifo_write(e, this_len);
553                 e += this_len;
554                 encode_len -= this_len;
555
556                 if (!started) {
557                         ao_radio_start_tx();
558                         started = 1;
559                 } else {
560                         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
561                 }
562                         
563                 do {
564                         ao_radio_wake = 0;
565                         cli();
566                         while (!ao_radio_wake)
567                                 ao_sleep(&ao_radio_wake);
568                         sei();
569                         if (!encode_len)
570                                 break;
571                         fifo_space = ao_radio_tx_fifo_space();
572                 } while (!fifo_space);
573         }
574         ao_radio_put();
575 }
576
577 #define AO_RADIO_MAX_RECV       90
578
579 static uint8_t  rx_data[(AO_RADIO_MAX_RECV + 4) * 2 * 8];
580 static uint16_t rx_data_count;
581 static uint16_t rx_data_consumed;
582 static uint16_t rx_data_cur;
583 static uint8_t  rx_ignore;
584 static uint8_t  rx_waiting;
585
586 #if AO_PROFILE
587 static uint32_t rx_start_tick, rx_packet_tick, rx_done_tick, rx_last_done_tick;
588
589 uint32_t        ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
590
591 #include <ao_profile.h>
592 #endif
593
594 static void
595 ao_radio_rx_isr(void)
596 {
597         uint8_t d;
598
599         d = stm_spi2.dr;
600         stm_spi2.dr = 0;
601         if (rx_ignore == 0) {
602                 if (rx_data_cur >= rx_data_count)
603                         ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
604                 else
605                         rx_data[rx_data_cur++] = d;
606                 if (rx_waiting && rx_data_cur - rx_data_consumed >= AO_FEC_DECODE_BLOCK) {
607 #if AO_PROFILE
608                         if (!rx_packet_tick)
609                                 rx_packet_tick = ao_profile_tick();
610                         if (rx_data_cur < rx_data_count)
611                                 return;
612 #endif
613                         rx_waiting = 0;
614                         ao_wakeup(&ao_radio_wake);
615                 }
616         } else {
617                 --rx_ignore;
618         }
619 }
620
621 static uint16_t
622 ao_radio_rx_wait(void)
623 {
624         cli();
625         rx_waiting = 1;
626         while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
627                !ao_radio_abort) {
628                 ao_sleep(&ao_radio_wake);
629         }
630         rx_waiting = 0;
631         sei();
632         if (ao_radio_abort)
633                 return 0;
634         rx_data_consumed += AO_FEC_DECODE_BLOCK;
635 #if AO_PROFILE
636         return rx_data_cur - rx_data_consumed;
637 #endif
638         return AO_FEC_DECODE_BLOCK;
639 }
640
641 uint8_t
642 ao_radio_recv(__xdata void *d, uint8_t size)
643 {
644         uint8_t         len;
645         uint16_t        i;
646         uint8_t         rssi;
647         uint8_t         ret;
648         static int been_here = 0;
649
650         size -= 2;                      /* status bytes */
651         if (size > AO_RADIO_MAX_RECV) {
652                 ao_delay(AO_SEC_TO_TICKS(1));
653                 return 0;
654         }
655 #if AO_PROFILE
656         rx_start_tick = ao_profile_tick();
657         rx_packet_tick = 0;
658 #endif
659         len = size + 2;                 /* CRC bytes */
660         len += 1 + ~(len & 1);          /* 1 or two pad bytes */
661         len *= 2;                       /* 1/2 rate convolution */
662         rx_data_count = len * 8;        /* bytes to bits */
663         rx_data_cur = 0;
664         rx_data_consumed = 0;
665         rx_ignore = 2;
666
667         ao_radio_abort = 0;
668         ao_radio_in_recv = 1;
669         /* configure interrupt pin */
670         ao_radio_get(len);
671         ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
672
673         ao_radio_wake = 0;
674
675         stm_spi2.cr2 = 0;
676
677         /* clear any RXNE */
678         (void) stm_spi2.dr;
679
680         ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
681         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
682
683         ao_radio_strobe(CC1120_SRX);
684
685         ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT);
686
687         ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait);
688
689         ao_radio_burst_read_stop();
690
691         ao_radio_strobe(CC1120_SIDLE);
692
693         /* Convert from 'real' rssi to cc1111-style values */
694
695         rssi = (((int8_t) ao_radio_reg_read(CC1120_RSSI1)) + 74) * 2;
696
697         ao_radio_put();
698
699         /* Store the received RSSI value; the crc-OK byte is already done */
700
701         ((uint8_t *) d)[size] = (uint8_t) rssi;
702
703         ao_radio_in_recv = 0;
704
705         if (ao_radio_abort)
706                 ao_delay(1);
707
708 #if AO_PROFILE
709         rx_last_done_tick = rx_done_tick;
710         rx_done_tick = ao_profile_tick();
711
712         ao_rx_start_tick = rx_start_tick;
713         ao_rx_packet_tick = rx_packet_tick;
714         ao_rx_done_tick = rx_done_tick;
715         ao_rx_last_done_tick = rx_last_done_tick;
716 #endif
717
718         return ret;
719 }
720
721
722 #if CC1120_DEBUG
723 static char *cc1120_state_name[] = {
724         [CC1120_STATUS_STATE_IDLE] = "IDLE",
725         [CC1120_STATUS_STATE_RX] = "RX",
726         [CC1120_STATUS_STATE_TX] = "TX",
727         [CC1120_STATUS_STATE_FSTXON] = "FSTXON",
728         [CC1120_STATUS_STATE_CALIBRATE] = "CALIBRATE",
729         [CC1120_STATUS_STATE_SETTLING] = "SETTLING",
730         [CC1120_STATUS_STATE_RX_FIFO_ERROR] = "RX_FIFO_ERROR",
731         [CC1120_STATUS_STATE_TX_FIFO_ERROR] = "TX_FIFO_ERROR",
732 };
733
734 struct ao_cc1120_reg {
735         uint16_t        addr;
736         char            *name;
737 };
738
739 const static struct ao_cc1120_reg ao_cc1120_reg[] = {
740         { .addr = CC1120_IOCFG3,        .name = "IOCFG3" },
741         { .addr = CC1120_IOCFG2,        .name = "IOCFG2" },
742         { .addr = CC1120_IOCFG1,        .name = "IOCFG1" },
743         { .addr = CC1120_IOCFG0,        .name = "IOCFG0" },
744         { .addr = CC1120_SYNC3, .name = "SYNC3" },
745         { .addr = CC1120_SYNC2, .name = "SYNC2" },
746         { .addr = CC1120_SYNC1, .name = "SYNC1" },
747         { .addr = CC1120_SYNC0, .name = "SYNC0" },
748         { .addr = CC1120_SYNC_CFG1,     .name = "SYNC_CFG1" },
749         { .addr = CC1120_SYNC_CFG0,     .name = "SYNC_CFG0" },
750         { .addr = CC1120_DEVIATION_M,   .name = "DEVIATION_M" },
751         { .addr = CC1120_MODCFG_DEV_E,  .name = "MODCFG_DEV_E" },
752         { .addr = CC1120_DCFILT_CFG,    .name = "DCFILT_CFG" },
753         { .addr = CC1120_PREAMBLE_CFG1, .name = "PREAMBLE_CFG1" },
754         { .addr = CC1120_PREAMBLE_CFG0, .name = "PREAMBLE_CFG0" },
755         { .addr = CC1120_FREQ_IF_CFG,   .name = "FREQ_IF_CFG" },
756         { .addr = CC1120_IQIC,  .name = "IQIC" },
757         { .addr = CC1120_CHAN_BW,       .name = "CHAN_BW" },
758         { .addr = CC1120_MDMCFG1,       .name = "MDMCFG1" },
759         { .addr = CC1120_MDMCFG0,       .name = "MDMCFG0" },
760         { .addr = CC1120_DRATE2,        .name = "DRATE2" },
761         { .addr = CC1120_DRATE1,        .name = "DRATE1" },
762         { .addr = CC1120_DRATE0,        .name = "DRATE0" },
763         { .addr = CC1120_AGC_REF,       .name = "AGC_REF" },
764         { .addr = CC1120_AGC_CS_THR,    .name = "AGC_CS_THR" },
765         { .addr = CC1120_AGC_GAIN_ADJUST,       .name = "AGC_GAIN_ADJUST" },
766         { .addr = CC1120_AGC_CFG3,      .name = "AGC_CFG3" },
767         { .addr = CC1120_AGC_CFG2,      .name = "AGC_CFG2" },
768         { .addr = CC1120_AGC_CFG1,      .name = "AGC_CFG1" },
769         { .addr = CC1120_AGC_CFG0,      .name = "AGC_CFG0" },
770         { .addr = CC1120_FIFO_CFG,      .name = "FIFO_CFG" },
771         { .addr = CC1120_DEV_ADDR,      .name = "DEV_ADDR" },
772         { .addr = CC1120_SETTLING_CFG,  .name = "SETTLING_CFG" },
773         { .addr = CC1120_FS_CFG,        .name = "FS_CFG" },
774         { .addr = CC1120_WOR_CFG1,      .name = "WOR_CFG1" },
775         { .addr = CC1120_WOR_CFG0,      .name = "WOR_CFG0" },
776         { .addr = CC1120_WOR_EVENT0_MSB,        .name = "WOR_EVENT0_MSB" },
777         { .addr = CC1120_WOR_EVENT0_LSB,        .name = "WOR_EVENT0_LSB" },
778         { .addr = CC1120_PKT_CFG2,      .name = "PKT_CFG2" },
779         { .addr = CC1120_PKT_CFG1,      .name = "PKT_CFG1" },
780         { .addr = CC1120_PKT_CFG0,      .name = "PKT_CFG0" },
781         { .addr = CC1120_RFEND_CFG1,    .name = "RFEND_CFG1" },
782         { .addr = CC1120_RFEND_CFG0,    .name = "RFEND_CFG0" },
783         { .addr = CC1120_PA_CFG2,       .name = "PA_CFG2" },
784         { .addr = CC1120_PA_CFG1,       .name = "PA_CFG1" },
785         { .addr = CC1120_PA_CFG0,       .name = "PA_CFG0" },
786         { .addr = CC1120_PKT_LEN,       .name = "PKT_LEN" },
787         { .addr = CC1120_IF_MIX_CFG,    .name = "IF_MIX_CFG" },
788         { .addr = CC1120_FREQOFF_CFG,   .name = "FREQOFF_CFG" },
789         { .addr = CC1120_TOC_CFG,       .name = "TOC_CFG" },
790         { .addr = CC1120_MARC_SPARE,    .name = "MARC_SPARE" },
791         { .addr = CC1120_ECG_CFG,       .name = "ECG_CFG" },
792         { .addr = CC1120_SOFT_TX_DATA_CFG,      .name = "SOFT_TX_DATA_CFG" },
793         { .addr = CC1120_EXT_CTRL,      .name = "EXT_CTRL" },
794         { .addr = CC1120_RCCAL_FINE,    .name = "RCCAL_FINE" },
795         { .addr = CC1120_RCCAL_COARSE,  .name = "RCCAL_COARSE" },
796         { .addr = CC1120_RCCAL_OFFSET,  .name = "RCCAL_OFFSET" },
797         { .addr = CC1120_FREQOFF1,      .name = "FREQOFF1" },
798         { .addr = CC1120_FREQOFF0,      .name = "FREQOFF0" },
799         { .addr = CC1120_FREQ2, .name = "FREQ2" },
800         { .addr = CC1120_FREQ1, .name = "FREQ1" },
801         { .addr = CC1120_FREQ0, .name = "FREQ0" },
802         { .addr = CC1120_IF_ADC2,       .name = "IF_ADC2" },
803         { .addr = CC1120_IF_ADC1,       .name = "IF_ADC1" },
804         { .addr = CC1120_IF_ADC0,       .name = "IF_ADC0" },
805         { .addr = CC1120_FS_DIG1,       .name = "FS_DIG1" },
806         { .addr = CC1120_FS_DIG0,       .name = "FS_DIG0" },
807         { .addr = CC1120_FS_CAL3,       .name = "FS_CAL3" },
808         { .addr = CC1120_FS_CAL2,       .name = "FS_CAL2" },
809         { .addr = CC1120_FS_CAL1,       .name = "FS_CAL1" },
810         { .addr = CC1120_FS_CAL0,       .name = "FS_CAL0" },
811         { .addr = CC1120_FS_CHP,        .name = "FS_CHP" },
812         { .addr = CC1120_FS_DIVTWO,     .name = "FS_DIVTWO" },
813         { .addr = CC1120_FS_DSM1,       .name = "FS_DSM1" },
814         { .addr = CC1120_FS_DSM0,       .name = "FS_DSM0" },
815         { .addr = CC1120_FS_DVC1,       .name = "FS_DVC1" },
816         { .addr = CC1120_FS_DVC0,       .name = "FS_DVC0" },
817         { .addr = CC1120_FS_LBI,        .name = "FS_LBI" },
818         { .addr = CC1120_FS_PFD,        .name = "FS_PFD" },
819         { .addr = CC1120_FS_PRE,        .name = "FS_PRE" },
820         { .addr = CC1120_FS_REG_DIV_CML,        .name = "FS_REG_DIV_CML" },
821         { .addr = CC1120_FS_SPARE,      .name = "FS_SPARE" },
822         { .addr = CC1120_FS_VCO4,       .name = "FS_VCO4" },
823         { .addr = CC1120_FS_VCO3,       .name = "FS_VCO3" },
824         { .addr = CC1120_FS_VCO2,       .name = "FS_VCO2" },
825         { .addr = CC1120_FS_VCO1,       .name = "FS_VCO1" },
826         { .addr = CC1120_FS_VCO0,       .name = "FS_VCO0" },
827         { .addr = CC1120_GBIAS6,        .name = "GBIAS6" },
828         { .addr = CC1120_GBIAS5,        .name = "GBIAS5" },
829         { .addr = CC1120_GBIAS4,        .name = "GBIAS4" },
830         { .addr = CC1120_GBIAS3,        .name = "GBIAS3" },
831         { .addr = CC1120_GBIAS2,        .name = "GBIAS2" },
832         { .addr = CC1120_GBIAS1,        .name = "GBIAS1" },
833         { .addr = CC1120_GBIAS0,        .name = "GBIAS0" },
834         { .addr = CC1120_IFAMP, .name = "IFAMP" },
835         { .addr = CC1120_LNA,   .name = "LNA" },
836         { .addr = CC1120_RXMIX, .name = "RXMIX" },
837         { .addr = CC1120_XOSC5, .name = "XOSC5" },
838         { .addr = CC1120_XOSC4, .name = "XOSC4" },
839         { .addr = CC1120_XOSC3, .name = "XOSC3" },
840         { .addr = CC1120_XOSC2, .name = "XOSC2" },
841         { .addr = CC1120_XOSC1, .name = "XOSC1" },
842         { .addr = CC1120_XOSC0, .name = "XOSC0" },
843         { .addr = CC1120_ANALOG_SPARE,  .name = "ANALOG_SPARE" },
844         { .addr = CC1120_PA_CFG3,       .name = "PA_CFG3" },
845         { .addr = CC1120_WOR_TIME1,     .name = "WOR_TIME1" },
846         { .addr = CC1120_WOR_TIME0,     .name = "WOR_TIME0" },
847         { .addr = CC1120_WOR_CAPTURE1,  .name = "WOR_CAPTURE1" },
848         { .addr = CC1120_WOR_CAPTURE0,  .name = "WOR_CAPTURE0" },
849         { .addr = CC1120_BIST,  .name = "BIST" },
850         { .addr = CC1120_DCFILTOFFSET_I1,       .name = "DCFILTOFFSET_I1" },
851         { .addr = CC1120_DCFILTOFFSET_I0,       .name = "DCFILTOFFSET_I0" },
852         { .addr = CC1120_DCFILTOFFSET_Q1,       .name = "DCFILTOFFSET_Q1" },
853         { .addr = CC1120_DCFILTOFFSET_Q0,       .name = "DCFILTOFFSET_Q0" },
854         { .addr = CC1120_IQIE_I1,       .name = "IQIE_I1" },
855         { .addr = CC1120_IQIE_I0,       .name = "IQIE_I0" },
856         { .addr = CC1120_IQIE_Q1,       .name = "IQIE_Q1" },
857         { .addr = CC1120_IQIE_Q0,       .name = "IQIE_Q0" },
858         { .addr = CC1120_RSSI1, .name = "RSSI1" },
859         { .addr = CC1120_RSSI0, .name = "RSSI0" },
860         { .addr = CC1120_MARCSTATE,     .name = "MARCSTATE" },
861         { .addr = CC1120_LQI_VAL,       .name = "LQI_VAL" },
862         { .addr = CC1120_PQT_SYNC_ERR,  .name = "PQT_SYNC_ERR" },
863         { .addr = CC1120_DEM_STATUS,    .name = "DEM_STATUS" },
864         { .addr = CC1120_FREQOFF_EST1,  .name = "FREQOFF_EST1" },
865         { .addr = CC1120_FREQOFF_EST0,  .name = "FREQOFF_EST0" },
866         { .addr = CC1120_AGC_GAIN3,     .name = "AGC_GAIN3" },
867         { .addr = CC1120_AGC_GAIN2,     .name = "AGC_GAIN2" },
868         { .addr = CC1120_AGC_GAIN1,     .name = "AGC_GAIN1" },
869         { .addr = CC1120_AGC_GAIN0,     .name = "AGC_GAIN0" },
870         { .addr = CC1120_SOFT_RX_DATA_OUT,      .name = "SOFT_RX_DATA_OUT" },
871         { .addr = CC1120_SOFT_TX_DATA_IN,       .name = "SOFT_TX_DATA_IN" },
872         { .addr = CC1120_ASK_SOFT_RX_DATA,      .name = "ASK_SOFT_RX_DATA" },
873         { .addr = CC1120_RNDGEN,        .name = "RNDGEN" },
874         { .addr = CC1120_MAGN2, .name = "MAGN2" },
875         { .addr = CC1120_MAGN1, .name = "MAGN1" },
876         { .addr = CC1120_MAGN0, .name = "MAGN0" },
877         { .addr = CC1120_ANG1,  .name = "ANG1" },
878         { .addr = CC1120_ANG0,  .name = "ANG0" },
879         { .addr = CC1120_CHFILT_I2,     .name = "CHFILT_I2" },
880         { .addr = CC1120_CHFILT_I1,     .name = "CHFILT_I1" },
881         { .addr = CC1120_CHFILT_I0,     .name = "CHFILT_I0" },
882         { .addr = CC1120_CHFILT_Q2,     .name = "CHFILT_Q2" },
883         { .addr = CC1120_CHFILT_Q1,     .name = "CHFILT_Q1" },
884         { .addr = CC1120_CHFILT_Q0,     .name = "CHFILT_Q0" },
885         { .addr = CC1120_GPIO_STATUS,   .name = "GPIO_STATUS" },
886         { .addr = CC1120_FSCAL_CTRL,    .name = "FSCAL_CTRL" },
887         { .addr = CC1120_PHASE_ADJUST,  .name = "PHASE_ADJUST" },
888         { .addr = CC1120_PARTNUMBER,    .name = "PARTNUMBER" },
889         { .addr = CC1120_PARTVERSION,   .name = "PARTVERSION" },
890         { .addr = CC1120_SERIAL_STATUS, .name = "SERIAL_STATUS" },
891         { .addr = CC1120_RX_STATUS,     .name = "RX_STATUS" },
892         { .addr = CC1120_TX_STATUS,     .name = "TX_STATUS" },
893         { .addr = CC1120_MARC_STATUS1,  .name = "MARC_STATUS1" },
894         { .addr = CC1120_MARC_STATUS0,  .name = "MARC_STATUS0" },
895         { .addr = CC1120_PA_IFAMP_TEST, .name = "PA_IFAMP_TEST" },
896         { .addr = CC1120_FSRF_TEST,     .name = "FSRF_TEST" },
897         { .addr = CC1120_PRE_TEST,      .name = "PRE_TEST" },
898         { .addr = CC1120_PRE_OVR,       .name = "PRE_OVR" },
899         { .addr = CC1120_ADC_TEST,      .name = "ADC_TEST" },
900         { .addr = CC1120_DVC_TEST,      .name = "DVC_TEST" },
901         { .addr = CC1120_ATEST, .name = "ATEST" },
902         { .addr = CC1120_ATEST_LVDS,    .name = "ATEST_LVDS" },
903         { .addr = CC1120_ATEST_MODE,    .name = "ATEST_MODE" },
904         { .addr = CC1120_XOSC_TEST1,    .name = "XOSC_TEST1" },
905         { .addr = CC1120_XOSC_TEST0,    .name = "XOSC_TEST0" },
906         { .addr = CC1120_RXFIRST,       .name = "RXFIRST" },
907         { .addr = CC1120_TXFIRST,       .name = "TXFIRST" },
908         { .addr = CC1120_RXLAST,        .name = "RXLAST" },
909         { .addr = CC1120_TXLAST,        .name = "TXLAST" },
910         { .addr = CC1120_NUM_TXBYTES,   .name = "NUM_TXBYTES" },
911         { .addr = CC1120_NUM_RXBYTES,   .name = "NUM_RXBYTES" },
912         { .addr = CC1120_FIFO_NUM_TXBYTES,      .name = "FIFO_NUM_TXBYTES" },
913         { .addr = CC1120_FIFO_NUM_RXBYTES,      .name = "FIFO_NUM_RXBYTES" },
914 };
915
916 #define AO_NUM_CC1120_REG       (sizeof ao_cc1120_reg / sizeof ao_cc1120_reg[0])
917
918 static void ao_radio_show(void) {
919         uint8_t status = ao_radio_status();
920         int     i;
921
922         ao_radio_get(0xff);
923         status = ao_radio_status();
924         printf ("Status:   %02x\n", status);
925         printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1);
926         printf ("STATE:    %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]);
927         printf ("MARC:     %02x\n", ao_radio_marc_status());
928
929         for (i = 0; i < AO_NUM_CC1120_REG; i++)
930                 printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name);
931         ao_radio_put();
932 }
933
934 static void ao_radio_beep(void) {
935         ao_radio_rdf(RDF_PACKET_LEN);
936 }
937
938 static void ao_radio_packet(void) {
939         static const uint8_t packet[] = {
940 #if 1
941                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
942                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
943                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
944                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
945 #else
946                 3, 1, 2, 3
947 #endif
948         };
949
950         ao_radio_send(packet, sizeof (packet));
951 }
952
953 void
954 ao_radio_test_recv()
955 {
956         uint8_t bytes[34];
957         uint8_t b;
958
959         if (ao_radio_recv(bytes, 34)) {
960                 if (bytes[33] & 0x80)
961                         printf ("CRC OK");
962                 else
963                         printf ("CRC BAD");
964                 printf (" RSSI %d", (int16_t) ((int8_t) bytes[32] >> 1) - 74);
965                 for (b = 0; b < 32; b++)
966                         printf (" %02x", bytes[b]);
967                 printf ("\n");
968         }
969 }
970
971 #endif
972
973 static const struct ao_cmds ao_radio_cmds[] = {
974         { ao_radio_test,        "C <1 start, 0 stop, none both>\0Radio carrier test" },
975 #if CC1120_DEBUG
976         { ao_radio_show,        "R\0Show CC1120 status" },
977         { ao_radio_beep,        "b\0Emit an RDF beacon" },
978         { ao_radio_packet,      "p\0Send a test packet" },
979         { ao_radio_test_recv,   "q\0Recv a test packet" },
980 #endif
981         { 0, NULL }
982 };
983
984 void
985 ao_radio_init(void)
986 {
987         int     i;
988
989         ao_radio_configured = 0;
990         ao_spi_init_cs (AO_CC1120_SPI_CS_PORT, (1 << AO_CC1120_SPI_CS_PIN));
991
992         AO_CC1120_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC1120_SPI_CS_PIN));
993         for (i = 0; i < 10000; i++) {
994                 if ((SPI_2_GPIO->idr & (1 << SPI_2_MISO)) == 0)
995                         break;
996         }
997         AO_CC1120_SPI_CS_PORT->bsrr = (1 << AO_CC1120_SPI_CS_PIN);
998         if (i == 10000)
999                 ao_panic(AO_PANIC_SELF_TEST);
1000
1001         /* Enable the EXTI interrupt for the appropriate pin */
1002         ao_enable_port(AO_CC1120_INT_PORT);
1003         ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
1004                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
1005                       ao_radio_tx_isr);
1006
1007         ao_cmd_register(&ao_radio_cmds[0]);
1008 }