2 * Copyright © 2013 Keith Packard <keithp@keithp.com>
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.
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.
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.
19 #include <ao_cc115l.h>
21 #include <ao_telemetry.h>
24 #define AO_RADIO_MAX_SEND sizeof (struct ao_telemetry_generic)
26 static uint8_t ao_radio_mutex;
28 static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */
29 static uint8_t ao_radio_abort; /* radio operation should abort */
30 static uint8_t ao_radio_mcu_wake; /* MARC status change */
31 static uint8_t ao_radio_marcstate; /* Last read MARC state value */
33 #define CC115L_DEBUG AO_FEC_DEBUG
34 #define CC115L_TRACE 1
36 extern const uint32_t ao_radio_cal;
40 #define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_4MHz)
41 #define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
42 #define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
43 #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
44 #define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC115L_SPI_BUS)
45 #define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS)
48 ao_radio_reg_read(uint16_t addr)
50 uint8_t datao[2], datai[2];
54 printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
56 datao[0] = ((1 << CC115L_READ) |
60 ao_radio_duplex(datao, datai, 2);
63 printf (" %02x\n", datai[1]);
69 ao_radio_reg_write(uint16_t addr, uint8_t value)
75 printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
77 data[0] = ((0 << CC115L_READ) |
82 ao_radio_spi_send(data, 2);
87 ao_radio_burst_read_start (uint16_t addr)
92 data[0] = ((1 << CC115L_READ) |
96 ao_radio_spi_send(data, 1);
100 ao_radio_burst_read_stop (void)
107 ao_radio_strobe(uint8_t addr)
112 printf("\t\tao_radio_strobe (%02x): ", addr); flush();
115 ao_radio_duplex(&addr, &in, 1);
118 printf("%02x\n", in); flush();
124 ao_radio_fifo_write_start(void)
126 uint8_t addr = ((0 << CC115L_READ) |
127 (1 << CC115L_BURST) |
132 ao_radio_duplex(&addr, &status, 1);
136 static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
142 ao_radio_fifo_write(uint8_t *data, uint8_t len)
144 uint8_t status = ao_radio_fifo_write_start();
145 ao_radio_spi_send(data, len);
146 return ao_radio_fifo_write_stop(status);
150 ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
152 uint8_t status = ao_radio_fifo_write_start();
153 ao_radio_spi_send_fixed(data, len);
154 return ao_radio_fifo_write_stop(status);
158 ao_radio_tx_fifo_space(void)
160 return CC115L_FIFO_SIZE - (ao_radio_reg_read(CC115L_TXBYTES) & CC115L_TXBYTES_NUM_TX_BYTES_MASK);
164 ao_radio_status(void)
166 return ao_radio_strobe (CC115L_SNOP);
169 #define ao_radio_rdf_value 0x55
172 ao_radio_get_marcstate(void)
174 return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK;
178 ao_radio_mcu_wakeup_isr(void)
180 ao_radio_mcu_wake = 1;
181 ao_wakeup(&ao_radio_wake);
186 ao_radio_check_marcstate(void)
188 ao_radio_mcu_wake = 0;
189 ao_radio_marcstate = ao_radio_get_marcstate();
191 /* Anyt other than 'tx finished' means an error occurred */
192 if (ao_radio_marcstate != CC115L_MARCSTATE_TX_END)
199 ao_exti_disable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
201 ao_wakeup(&ao_radio_wake);
205 ao_radio_start_tx(void)
207 ao_exti_set_callback(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, ao_radio_isr);
208 ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
209 ao_exti_enable(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN);
210 ao_radio_strobe(CC115L_STX);
217 uint8_t state = ao_radio_strobe(CC115L_SIDLE);
218 if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE)
221 /* Flush any pending TX bytes */
222 ao_radio_strobe(CC115L_SFTX);
226 * Packet deviation is 20.5kHz
228 * fdev = fosc >> 17 * (8 + dev_m) << dev_e
230 * 26e6 / (2 ** 17) * (8 + 5) * (2 ** 3) = 20630Hz
233 #define PACKET_DEV_E 3
234 #define PACKET_DEV_M 5
237 * For our packet data, set the symbol rate to 38400 Baud
239 * (256 + DATARATE_M) * 2 ** DATARATE_E
240 * Rdata = -------------------------------------- * fosc
243 * (256 + 131) * (2 ** 10) / (2**28) * 26e6 = 38383
248 #define PACKET_DRATE_E 10
249 #define PACKET_DRATE_M 131
251 static const uint16_t packet_setup[] = {
252 CC115L_DEVIATN, ((PACKET_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
253 (PACKET_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
254 CC115L_MDMCFG4, ((0xf << 4) |
255 (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
256 CC115L_MDMCFG3, (PACKET_DRATE_M),
261 * RDF deviation is 5kHz
263 * fdev = fosc >> 17 * (8 + dev_m) << dev_e
265 * 26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz
272 * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
274 * (256 + DATARATE_M) * 2 ** DATARATE_E
275 * Rdata = -------------------------------------- * fosc
278 * (256 + 67) * (2 ** 6) / (2**28) * 26e6 = 2002
283 * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
285 #define RDF_DRATE_E 6
286 #define RDF_DRATE_M 67
287 #define RDF_PACKET_LEN 50
289 static const uint16_t rdf_setup[] = {
290 CC115L_DEVIATN, ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
291 (RDF_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
292 CC115L_MDMCFG4, ((0xf << 4) |
293 (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
294 CC115L_MDMCFG3, (RDF_DRATE_M),
298 * APRS deviation is the same as RDF
301 #define APRS_DEV_E RDF_DEV_E
302 #define APRS_DEV_M RDF_DEV_E
305 * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
307 * (256 + DATARATE_M) * 2 ** DATARATE_E
308 * Rdata = -------------------------------------- * fosc
311 * (256 + 131) * (2 ** 8) / (2**28) * 26e6 = 9596
317 #define APRS_DRATE_E 8
318 #define APRS_DRATE_M 131
320 static const uint16_t aprs_setup[] = {
321 CC115L_DEVIATN, ((APRS_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
322 (APRS_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
323 CC115L_MDMCFG4, ((0xf << 4) |
324 (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
325 CC115L_MDMCFG3, (APRS_DRATE_M),
328 #define AO_PKTCTRL0_INFINITE ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
329 (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \
330 (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
331 #define AO_PKTCTRL0_FIXED ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
332 (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \
333 (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
335 static uint16_t ao_radio_mode;
337 #define AO_RADIO_MODE_BITS_PACKET_TX 1
338 #define AO_RADIO_MODE_BITS_TX_BUF 2
339 #define AO_RADIO_MODE_BITS_TX_FINISH 4
340 #define AO_RADIO_MODE_BITS_RDF 8
341 #define AO_RADIO_MODE_BITS_APRS 16
342 #define AO_RADIO_MODE_BITS_INFINITE 32
343 #define AO_RADIO_MODE_BITS_FIXED 64
345 #define AO_RADIO_MODE_NONE 0
346 #define AO_RADIO_MODE_PACKET_TX_BUF (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
347 #define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
348 #define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
349 #define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
350 #define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF)
351 #define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH)
354 ao_radio_set_mode(uint16_t new_mode)
359 if (new_mode == ao_radio_mode)
362 changes = new_mode & (~ao_radio_mode);
363 if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
364 for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
365 ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
367 if (changes & AO_RADIO_MODE_BITS_TX_BUF)
368 ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR);
370 if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
371 ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX | (1 << CC115L_IOCFG_GPIO_INV));
373 if (changes & AO_RADIO_MODE_BITS_RDF)
374 for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
375 ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
377 if (changes & AO_RADIO_MODE_BITS_APRS)
378 for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2)
379 ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]);
381 if (changes & AO_RADIO_MODE_BITS_INFINITE)
382 ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_INFINITE);
384 if (changes & AO_RADIO_MODE_BITS_FIXED)
385 ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_FIXED);
387 ao_radio_mode = new_mode;
390 static const uint16_t radio_setup[] = {
391 #include "ao_rf_cc115l.h"
394 static uint8_t ao_radio_configured = 0;
401 ao_radio_strobe(CC115L_SRES);
403 for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
404 ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
410 ao_radio_configured = 1;
414 ao_radio_set_len(uint8_t len)
416 static uint8_t last_len;
418 if (len != last_len) {
419 ao_radio_reg_write(CC115L_PKTLEN, len);
425 ao_radio_get(uint8_t len)
427 static uint32_t last_radio_setting;
429 ao_mutex_get(&ao_radio_mutex);
430 if (!ao_radio_configured)
432 if (ao_config.radio_setting != last_radio_setting) {
433 ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16);
434 ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
435 ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
436 last_radio_setting = ao_config.radio_setting;
438 ao_radio_set_len(len);
441 #define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
444 ao_rdf_start(uint8_t len)
449 ao_radio_set_mode(AO_RADIO_MODE_RDF);
459 ao_arch_block_interrupts();
460 while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake)
461 ao_sleep(&ao_radio_wake);
462 ao_arch_release_interrupts();
463 if (ao_radio_mcu_wake)
464 ao_radio_check_marcstate();
473 ao_rdf_start(AO_RADIO_RDF_LEN);
475 ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN);
481 ao_radio_continuity(uint8_t c)
486 ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN);
488 status = ao_radio_fifo_write_start();
489 for (i = 0; i < 3; i++) {
490 ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
492 ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN);
494 ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN);
496 ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
497 status = ao_radio_fifo_write_stop(status);
503 ao_radio_rdf_abort(void)
506 ao_wakeup(&ao_radio_wake);
510 ao_radio_test_cmd(void)
513 static uint8_t radio_on;
515 if (ao_cmd_lex_c != '\n') {
517 mode = (uint8_t) ao_cmd_lex_u32;
520 if ((mode & 2) && !radio_on) {
522 ao_monitor_disable();
525 ao_packet_slave_stop();
528 ao_radio_strobe(CC115L_STX);
531 for (t = 0; t < 10; t++) {
532 printf ("status: %02x\n", ao_radio_status());
533 ao_delay(AO_MS_TO_TICKS(100));
540 printf ("Hit a character to stop..."); flush();
544 if ((mode & 1) && radio_on) {
555 ao_radio_wait_isr(void)
557 ao_arch_block_interrupts();
558 while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort)
559 ao_sleep(&ao_radio_wake);
560 ao_arch_release_interrupts();
561 if (ao_radio_mcu_wake)
562 ao_radio_check_marcstate();
566 ao_radio_wait_tx(uint8_t wait_fifo)
568 uint8_t fifo_space = 0;
574 fifo_space = ao_radio_tx_fifo_space();
575 } while (!fifo_space && !ao_radio_abort);
579 static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
582 ao_radio_send(const void *d, uint8_t size)
585 uint8_t *e = tx_data;
591 encode_len = ao_fec_encode(d, size, tx_data);
593 ao_radio_get(encode_len);
596 fifo_space = CC115L_FIFO_SIZE;
598 this_len = encode_len;
601 if (this_len > fifo_space) {
602 this_len = fifo_space;
603 ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
605 ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
608 ao_radio_fifo_write(e, this_len);
610 encode_len -= this_len;
616 ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
619 fifo_space = ao_radio_wait_tx(encode_len != 0);
620 if (ao_radio_abort) {
628 #define AO_RADIO_LOTS 64
631 ao_radio_send_lots(ao_radio_fill_func fill)
633 uint8_t buf[AO_RADIO_LOTS], *b;
641 fifo_space = CC115L_FIFO_SIZE;
643 cnt = (*fill)(buf, sizeof(buf));
650 /* At the last buffer, set the total length */
652 ao_radio_set_len(total & 0xff);
656 uint8_t this_len = cnt;
658 /* Wait for some space in the fifo */
659 while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
665 if (this_len > fifo_space)
666 this_len = fifo_space;
672 ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF);
674 ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH);
676 ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF);
678 ao_radio_fifo_write(b, this_len);
685 ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
687 if (ao_radio_abort) {
691 /* Wait for the transmitter to go idle */
698 static char *cc115l_state_name[] = {
699 [CC115L_STATUS_STATE_IDLE] = "IDLE",
700 [CC115L_STATUS_STATE_TX] = "TX",
701 [CC115L_STATUS_STATE_FSTXON] = "FSTXON",
702 [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE",
703 [CC115L_STATUS_STATE_SETTLING] = "SETTLING",
704 [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW",
707 static void ao_radio_show(void) {
708 uint8_t status = ao_radio_status();
712 status = ao_radio_status();
713 printf ("Status: %02x\n", status);
714 printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1);
715 printf ("STATE: %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]);
716 printf ("MARC: %02x\n", ao_radio_get_marcstate());
721 static void ao_radio_beep(void) {
725 static void ao_radio_packet(void) {
726 static const uint8_t packet[] = {
728 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
729 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
730 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
731 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
737 ao_radio_send(packet, sizeof (packet));
746 ao_packet_slave_stop();
751 static const struct ao_cmds ao_radio_cmds[] = {
752 { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" },
755 { ao_radio_aprs, "G\0Send APRS packet" },
757 { ao_radio_show, "R\0Show CC115L status" },
758 { ao_radio_beep, "b\0Emit an RDF beacon" },
759 { ao_radio_packet, "p\0Send a test packet" },
769 ao_radio_configured = 0;
770 ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN));
773 AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN));
774 for (i = 0; i < 10000; i++) {
775 if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
778 AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN);
780 ao_panic(AO_PANIC_SELF_TEST_CC115L);
783 /* Enable the EXTI interrupt for the appropriate pin */
784 ao_enable_port(AO_CC115L_INT_PORT);
785 ao_exti_setup(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN,
786 AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
789 /* Enable the hacked up GPIO3 pin */
790 ao_enable_port(AO_CC115L_MCU_WAKEUP_PORT);
791 ao_exti_setup(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN,
792 AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
793 ao_radio_mcu_wakeup_isr);
795 ao_cmd_register(&ao_radio_cmds[0]);