When the radio drops out of RX or TX mode due to an error, it changes
the MARC status, and sends pulse down a configured GPIO. Use this to
tell when something 'bad' happened during TX or RX so that we can
recover from losing the SPI bus in the middle of transmission or
reception.
Without this, the radio would change state and we'd never know,
leaving the radio code waiting for an interrupt that would never arrive.
Signed-off-by: Keith Packard <keithp@keithp.com>
#define AO_RADIO_MAX_RECV sizeof(struct ao_packet)
#define AO_RADIO_MAX_SEND sizeof(struct ao_packet)
#define AO_RADIO_MAX_RECV sizeof(struct ao_packet)
#define AO_RADIO_MAX_SEND sizeof(struct ao_packet)
-uint8_t ao_radio_wake;
-uint8_t ao_radio_mutex;
-uint8_t ao_radio_abort;
-uint8_t ao_radio_in_recv;
+static uint8_t ao_radio_mutex;
+
+static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */
+static uint8_t ao_radio_abort; /* radio operation should abort */
+static uint8_t ao_radio_mcu_wake; /* MARC status change */
+static uint8_t ao_radio_marc_status; /* Last read MARC status value */
#define CC1120_DEBUG AO_FEC_DEBUG
#define CC1120_TRACE 0
#define CC1120_DEBUG AO_FEC_DEBUG
#define CC1120_TRACE 0
#define ao_radio_rdf_value 0x55
static uint8_t
#define ao_radio_rdf_value 0x55
static uint8_t
-ao_radio_marc_status(void)
+ao_radio_get_marc_status(void)
{
return ao_radio_reg_read(CC1120_MARC_STATUS1);
}
static void
{
return ao_radio_reg_read(CC1120_MARC_STATUS1);
}
static void
+ao_radio_mcu_wakeup_isr(void)
+{
+ ao_radio_mcu_wake = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+
+static void
+ao_radio_check_marc_status(void)
+{
+ ao_radio_mcu_wake = 0;
+ ao_radio_marc_status = ao_radio_get_marc_status();
+
+ /* Anyt other than 'tx/rx finished' means an error occurred */
+ if (ao_radio_marc_status & ~(CC1120_MARC_STATUS1_TX_FINISHED|CC1120_MARC_STATUS1_RX_FINISHED))
+ ao_radio_abort = 1;
+}
+
+static void
+ao_radio_isr(void)
{
ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
ao_radio_wake = 1;
{
ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
ao_radio_wake = 1;
static void
ao_radio_start_tx(void)
{
static void
ao_radio_start_tx(void)
{
- ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_tx_isr);
+ ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_isr);
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+ ao_exti_enable(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN);
ao_radio_strobe(CC1120_STX);
}
ao_radio_strobe(CC1120_STX);
}
if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE)
break;
}
if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE)
break;
}
+ /* Flush any pending TX bytes */
+ ao_radio_strobe(CC1120_SFTX);
(0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
(0 << CC1120_PKT_CFG0_UART_MODE_EN) |
(0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
(0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
(0 << CC1120_PKT_CFG0_UART_MODE_EN) |
(0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
+ AO_CC1120_MARC_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP,
};
static const uint16_t packet_tx_setup[] = {
CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
(CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
};
static const uint16_t packet_tx_setup[] = {
CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
(CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
- CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG,
+ AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG,
};
static const uint16_t packet_rx_setup[] = {
CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
(CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)),
};
static const uint16_t packet_rx_setup[] = {
CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
(CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)),
- CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT,
+ AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT,
ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]);
if (changes & AO_RADIO_MODE_BITS_TX_BUF)
ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]);
if (changes & AO_RADIO_MODE_BITS_TX_BUF)
- ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR);
+ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR);
if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
- ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
+ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
if (changes & AO_RADIO_MODE_BITS_PACKET_RX)
for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2)
if (changes & AO_RADIO_MODE_BITS_PACKET_RX)
for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2)
ao_radio_start_tx();
ao_arch_block_interrupts();
ao_radio_start_tx();
ao_arch_block_interrupts();
- while (!ao_radio_wake && !ao_radio_abort)
+ while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake)
ao_sleep(&ao_radio_wake);
ao_arch_release_interrupts();
ao_sleep(&ao_radio_wake);
ao_arch_release_interrupts();
+ if (ao_radio_mcu_wake)
+ ao_radio_check_marc_status();
if (!ao_radio_wake)
ao_radio_idle();
ao_radio_put();
if (!ao_radio_wake)
ao_radio_idle();
ao_radio_put();
static void
ao_radio_wait_isr(void)
{
static void
ao_radio_wait_isr(void)
{
ao_arch_block_interrupts();
ao_arch_block_interrupts();
+ while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort)
ao_sleep(&ao_radio_wake);
ao_arch_release_interrupts();
ao_sleep(&ao_radio_wake);
ao_arch_release_interrupts();
+ if (ao_radio_mcu_wake)
+ ao_radio_check_marc_status();
if (!wait_fifo)
return 0;
fifo_space = ao_radio_tx_fifo_space();
if (!wait_fifo)
return 0;
fifo_space = ao_radio_tx_fifo_space();
+ } while (!fifo_space && !ao_radio_abort);
while (encode_len) {
this_len = encode_len;
while (encode_len) {
this_len = encode_len;
if (this_len > fifo_space) {
this_len = fifo_space;
ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
if (this_len > fifo_space) {
this_len = fifo_space;
ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
}
fifo_space = ao_radio_wait_tx(encode_len != 0);
}
fifo_space = ao_radio_wait_tx(encode_len != 0);
+ if (ao_radio_abort) {
+ ao_radio_idle();
+ break;
+ }
uint8_t this_len = cnt;
/* Wait for some space in the fifo */
uint8_t this_len = cnt;
/* Wait for some space in the fifo */
- while ((fifo_space = ao_radio_tx_fifo_space()) == 0) {
+ while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
- ao_arch_block_interrupts();
- while (!ao_radio_wake)
- ao_sleep(&ao_radio_wake);
- ao_arch_release_interrupts();
+ if (ao_radio_abort)
+ break;
if (this_len > fifo_space)
this_len = fifo_space;
if (this_len > fifo_space)
this_len = fifo_space;
} else
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
}
} else
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
}
+ if (ao_radio_abort) {
+ ao_radio_idle();
+ break;
+ }
/* Wait for the transmitter to go idle */
/* Wait for the transmitter to go idle */
ao_radio_wait_isr();
}
ao_radio_put();
ao_radio_wait_isr();
}
ao_radio_put();
static uint16_t
ao_radio_rx_wait(void)
{
static uint16_t
ao_radio_rx_wait(void)
{
- ao_arch_block_interrupts();
- rx_waiting = 1;
- while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
- !ao_radio_abort) {
- ao_sleep(&ao_radio_wake);
- }
- rx_waiting = 0;
- ao_arch_release_interrupts();
+ do {
+ if (ao_radio_mcu_wake)
+ ao_radio_check_marc_status();
+ ao_arch_block_interrupts();
+ rx_waiting = 1;
+ while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
+ !ao_radio_abort &&
+ !ao_radio_mcu_wake)
+ {
+ if (ao_sleep(&ao_radio_wake))
+ ao_radio_abort = 1;
+ }
+ rx_waiting = 0;
+ ao_arch_release_interrupts();
+ } while (ao_radio_mcu_wake);
if (ao_radio_abort)
return 0;
rx_data_consumed += AO_FEC_DECODE_BLOCK;
if (ao_radio_abort)
return 0;
rx_data_consumed += AO_FEC_DECODE_BLOCK;
rx_data_consumed = 0;
rx_ignore = 2;
rx_data_consumed = 0;
rx_ignore = 2;
+ /* Must be set before changing the frequency; any abort
+ * after the frequency is set needs to terminate the read
+ * so that the registers can be reprogrammed
+ */
/* configure interrupt pin */
ao_radio_get(len);
ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
ao_radio_wake = 0;
/* configure interrupt pin */
ao_radio_get(len);
ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
ao_radio_wake = 0;
stm_spi2.cr2 = 0;
/* clear any RXNE */
(void) stm_spi2.dr;
stm_spi2.cr2 = 0;
/* clear any RXNE */
(void) stm_spi2.dr;
- ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
+ /* Have the radio signal when the preamble quality goes high */
+ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_PQT_REACHED);
+ ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
+ AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH);
+ ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_isr);
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+ ao_exti_enable(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN);
ao_radio_strobe(CC1120_SRX);
ao_radio_strobe(CC1120_SRX);
+ /* Wait for the preamble to appear */
+ ao_radio_wait_isr();
+ if (ao_radio_abort)
+ goto abort;
+
+ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT);
+ ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
+
+ ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
+ ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+
ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT);
ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait);
ao_radio_burst_read_stop();
ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT);
ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait);
ao_radio_burst_read_stop();
ao_radio_strobe(CC1120_SIDLE);
/* Convert from 'real' rssi to cc1111-style values */
ao_radio_strobe(CC1120_SIDLE);
/* Convert from 'real' rssi to cc1111-style values */
((uint8_t *) d)[size] = (uint8_t) rssi;
((uint8_t *) d)[size] = (uint8_t) rssi;
- ao_radio_in_recv = 0;
-
- if (ao_radio_abort)
- ao_delay(1);
-
#if AO_PROFILE
rx_last_done_tick = rx_done_tick;
rx_done_tick = ao_profile_tick();
#if AO_PROFILE
rx_last_done_tick = rx_done_tick;
rx_done_tick = ao_profile_tick();
printf ("Status: %02x\n", status);
printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1);
printf ("STATE: %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]);
printf ("Status: %02x\n", status);
printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1);
printf ("STATE: %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]);
- printf ("MARC: %02x\n", ao_radio_marc_status());
+ printf ("MARC: %02x\n", ao_radio_get_marc_status());
for (i = 0; i < AO_NUM_CC1120_REG; i++)
printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name);
for (i = 0; i < AO_NUM_CC1120_REG; i++)
printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name);
ao_enable_port(AO_CC1120_INT_PORT);
ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
ao_enable_port(AO_CC1120_INT_PORT);
ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
+ ao_radio_isr);
+
+ /* Enable the hacked up GPIO3 pin */
+ ao_enable_port(AO_CC1120_MCU_WAKEUP_PORT);
+ ao_exti_setup(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
+ ao_radio_mcu_wakeup_isr);
ao_cmd_register(&ao_radio_cmds[0]);
}
ao_cmd_register(&ao_radio_cmds[0]);
}
static __xdata uint8_t ao_m25_instruction[4];
static __xdata uint8_t ao_m25_instruction[4];
-#if HAS_BOOT_RADIO
-/* Kick any radio listeners off so the flash can be written */
-extern uint8_t ao_radio_in_recv;
-
-static void ao_boot_radio(void) {
- if (ao_radio_in_recv)
- ao_radio_recv_abort();
-}
-#else
-#define ao_boot_radio()
-#endif
-
-#define M25_SELECT(cs) do { ao_boot_radio(); ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST); } while (0)
+#define M25_SELECT(cs) ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST)
#define M25_DESELECT(cs) ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
#define M25_BLOCK_SHIFT 16
#define M25_DESELECT(cs) ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
#define M25_BLOCK_SHIFT 16
#define AO_CC1120_INT_PIN 14
#define AO_CC1120_INT_GPIO 2
#define AO_CC1120_INT_PIN 14
#define AO_CC1120_INT_GPIO 2
-#define HAS_BOOT_RADIO 1
/*
* Profiling Viterbi decoding
/*
* Profiling Viterbi decoding
ao_exti_init();
ao_adc_init();
ao_exti_init();
ao_adc_init();
ao_cmd_init();
#if HAS_MS5607
ao_cmd_init();
#if HAS_MS5607
#define HAS_EEPROM 1
#define USE_INTERNAL_FLASH 0
#define HAS_USB 1
#define HAS_EEPROM 1
#define USE_INTERNAL_FLASH 0
#define HAS_USB 1
#define HAS_RADIO 1
#define HAS_TELEMETRY 1
#define HAS_APRS 1
#define HAS_RADIO 1
#define HAS_TELEMETRY 1
#define HAS_APRS 1
#define AO_CC1120_SPI_CS_PIN 5
#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15
#define AO_CC1120_SPI_CS_PIN 5
#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15
-#define AO_CC1120_INT_PORT (&stm_gpioc)
-#define AO_CC1120_INT_PIN 14
+#define AO_CC1120_INT_PORT (&stm_gpioc)
+#define AO_CC1120_INT_PIN 14
+#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc)
+#define AO_CC1120_MCU_WAKEUP_PIN (0)
#define AO_CC1120_INT_GPIO 2
#define AO_CC1120_INT_GPIO 2
-#define HAS_BOOT_RADIO 1
+#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2
+
+#define AO_CC1120_MARC_GPIO 3
+#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3
+
+
+#define HAS_BOOT_RADIO 0
/*
* Mag sensor (hmc5883)
/*
* Mag sensor (hmc5883)