diff --git a/src/config.h b/src/config.h index 58fb916..6139fab 100644 --- a/src/config.h +++ b/src/config.h @@ -7,7 +7,7 @@ // DFM-17 transmissions, especially APRS, may not decode correctly because of incorrect timing before the internal oscillator has been calibrated. // Define radiosonde type. Remove the "//" comment to select either RS41 or DFM17. -//#define RS41 +#define RS41 //#define DFM17 #if !defined(RS41) && !defined(DFM17) @@ -21,8 +21,8 @@ // Enable semihosting to receive debug logs during development // See the README for details on how to set up debugging and debug logs with GDB // NOTE: Semihosting has to be disabled when the radiosonde is not connected to an STM32 programmer dongle, otherwise the firmware will not run. -//#define SEMIHOSTING_ENABLE -//#define LOGGING_ENABLE +#define SEMIHOSTING_ENABLE +#define LOGGING_ENABLE /** * Global configuration @@ -147,12 +147,14 @@ #define RADIO_SI4032_TX_CW_COUNT 1 #define RADIO_SI4032_TX_PIP false #define RADIO_SI4032_TX_PIP_COUNT 6 -#define RADIO_SI4032_TX_APRS true +#define RADIO_SI4032_TX_APRS false #define RADIO_SI4032_TX_APRS_COUNT 2 #define RADIO_SI4032_TX_HORUS_V1 false #define RADIO_SI4032_TX_HORUS_V1_COUNT 1 -#define RADIO_SI4032_TX_HORUS_V2 true +#define RADIO_SI4032_TX_HORUS_V2 false #define RADIO_SI4032_TX_HORUS_V2_COUNT 6 +#define RADIO_SI4032_TX_CATS true +#define RADIO_SI4032_TX_CATS_COUNT 1 // Continuous transmit mode can be enabled for *either* Horus V1 or V2, but not both. This disables all other transmission modes. // The continuous mode transmits Horus 4FSK preamble between transmissions @@ -167,6 +169,7 @@ // Use a frequency offset to place FSK tones slightly above the defined frequency for SSB reception #define RADIO_SI4032_TX_FREQUENCY_HORUS_V1 432501000 #define RADIO_SI4032_TX_FREQUENCY_HORUS_V2 432501000 +#define RADIO_SI4032_TX_FREQUENCY_CATS 430450000 /** * DFM-17 only: Built-in Si4063 radio chip transmission configuration diff --git a/src/drivers/si4032/si4032.c b/src/drivers/si4032/si4032.c index 313875e..59d5641 100644 --- a/src/drivers/si4032/si4032.c +++ b/src/drivers/si4032/si4032.c @@ -1,11 +1,15 @@ #include #include "hal/spi.h" +#include "hal/hal.h" +#include "hal/delay.h" #include "si4032.h" +#include "log.h" #define SPI_WRITE_FLAG 0x80 #define SI4032_CLOCK 26.0f +#define EXPECTED_SI4032_CLOCK 30 #define GPIO_SI4032_NSEL GPIOC #define GPIO_PIN_SI4032_NSEL GPIO_Pin_13 @@ -15,6 +19,7 @@ static inline uint8_t si4032_write(uint8_t reg, uint8_t value) { + // log_info("write %x %x\n", reg, value); return spi_send_and_receive(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL, ((reg | SPI_WRITE_FLAG) << 8U) | value); } @@ -45,6 +50,89 @@ void si4032_disable_tx() si4032_write(0x07, 0x40); } +// Returns number of bytes sent from *data +// If less than len, remaining bytes will need to be used to top up the buffer +uint16_t si4032_start_tx(uint8_t *data, int len) +{ + // Clear fifo underflow interrupt, along with other interrupts + si4032_read(0x03); + + // Clear fifo + si4032_write(0x08, 1); + si4032_write(0x08, 0); + + // Set packet length (max 255 bytes) + si4032_write(0x3E, len); + + // Fill our FIFO + int fifo_len = len; + if(fifo_len > 64) { + fifo_len = 64; + } + for(int i = 0; i < fifo_len; i++) { + si4032_write(0x7F, data[i]); + } + + // disable packet handler - just transmit whatever's in the FIFO + si4032_write(0x30, 0x08); + + // Start transmitting + si4032_write(0x07, 0x09); + + return fifo_len; +} + +// Add additional bytes to the si4032's FIFO buffer +// Needed for large packets that don't fit in its buffer. +// Keep refilling it while transmitting +// Returns number of bytes taken from *data +// If less than len, you will need to keep calling this +uint16_t si4032_refill_buffer(uint8_t *data, int len, bool *overflow) +{ + uint8_t interrupts = si4032_read(0x03); + int i = 0; + + // check for free buffer space + // TX FIFO almost full + while (!(interrupts & 0x40) && i < len) { + // check for FIFO underflow + if (interrupts & 0x80) { + *overflow = true; + return i; + } + + si4032_write(0x7F, data[i]); + + interrupts = si4032_read(0x03); + i++; + } + + if (interrupts & 0x80) { + *overflow = true; + } + + return i; +} + +int si4032_wait_for_tx_complete(int timeout_ms) +{ + for(int i = 0; i < timeout_ms; i++) { + uint8_t status = si4032_read(0x03); + + // ipksent is set + if (status & 0x04) { + return HAL_OK; + } + + delay_ms(1); + } + + // clear txon manually + si4032_write(0x07, 0x40); + + return HAL_ERROR_TIMEOUT; +} + void si4032_use_direct_mode(bool use) { if (use) { @@ -67,6 +155,15 @@ void si4032_set_tx_frequency(const float frequency_mhz) si4032_write(0x77, (uint8_t) ((uint16_t) fc & 0xff)); } +void si4032_set_data_rate(const uint32_t rate_bps) +{ + uint32_t rate = (uint64_t) rate_bps * (1 << 21) * EXPECTED_SI4032_CLOCK / 1000000 / ((uint64_t) SI4032_CLOCK); + + si4032_write(0x6E, rate >> 8); + si4032_write(0x6F, rate & 0xFF); + si4032_write(0x70, 0b00100000); +} + void si4032_set_tx_power(uint8_t power) { si4032_write(0x6D, power & 0x7U); @@ -110,6 +207,10 @@ void si4032_set_modulation_type(si4032_modulation_type type) // Direct Async Mode with FSK modulation value = 0b00010010; break; + case SI4032_MODULATION_TYPE_FIFO_FSK: + // FIFO with FSK modulation + value = 0b00100010; + break; default: return; } @@ -194,5 +295,21 @@ void si4032_init() si4032_set_frequency_offset(0); si4032_set_frequency_deviation(5); // Was: 5 for APRS in RS41HUP? + // No TX header + // Fixed packet length (don't transmit length) + // Synchronization word of 1 byte (not possible to select 0 bytes) + si4032_write(0x33, 0b00001000); + // 1 preamble byte (the minimum allowed) + // Setting this to 0 is equivalent to setting it to 1 TODO FIXME + si4032_write(0x34, 1); + // Set our sync word to 0x55, so it looks like a preamble + si4032_write(0x36, 0x55); + + // set almost full threshold to 63 (max allowed; buffer size - 1) + si4032_write(0x7C, 63); + + // enable interrupts + si4032_write(0x05, 0b11100100); + si4032_set_modulation_type(SI4032_MODULATION_TYPE_NONE); } diff --git a/src/drivers/si4032/si4032.h b/src/drivers/si4032/si4032.h index ca9de65..408d49a 100644 --- a/src/drivers/si4032/si4032.h +++ b/src/drivers/si4032/si4032.h @@ -8,14 +8,19 @@ typedef enum _si4032_modulation_type { SI4032_MODULATION_TYPE_NONE = 0, SI4032_MODULATION_TYPE_OOK, SI4032_MODULATION_TYPE_FSK, + SI4032_MODULATION_TYPE_FIFO_FSK, } si4032_modulation_type; void si4032_soft_reset(); void si4032_enable_tx(); void si4032_inhibit_tx(); void si4032_disable_tx(); +uint16_t si4032_start_tx(uint8_t *data, int len); +uint16_t si4032_refill_buffer(uint8_t *data, int len, bool *overflow); +int si4032_wait_for_tx_complete(int timeout_ms); void si4032_use_direct_mode(bool use); void si4032_set_tx_frequency(float frequency_mhz); +void si4032_set_data_rate(const uint32_t rate_bps); void si4032_set_tx_power(uint8_t power); void si4032_set_frequency_offset(uint16_t offset); void si4032_set_frequency_offset_small(uint8_t offset); diff --git a/src/radio.c b/src/radio.c index c848e1a..795f457 100644 --- a/src/radio.c +++ b/src/radio.c @@ -164,6 +164,20 @@ radio_transmit_entry radio_transmit_schedule[] = { .fsk_encoder_api = &mfsk_fsk_encoder_api, }, #endif +#if RADIO_SI4032_TX_CATS + { + .enabled = RADIO_SI4032_TX_CATS, + .radio_type = RADIO_TYPE_SI4032, + .data_mode = RADIO_DATA_MODE_CATS, + .transmit_count = RADIO_SI4032_TX_CATS_COUNT, + .time_sync_seconds = CATS_TIME_SYNC_SECONDS, + .time_sync_seconds_offset = CATS_TIME_SYNC_OFFSET_SECONDS, + .frequency = RADIO_SI4032_TX_FREQUENCY_CATS, + .tx_power = RADIO_SI4032_TX_POWER, + .payload_encoder = &radio_cats_payload_encoder, + .fsk_encoder_api = &raw_fsk_encoder_api, + }, +#endif #endif #endif diff --git a/src/radio_si4032.c b/src/radio_si4032.c index b8f2f93..aff0cec 100644 --- a/src/radio_si4032.c +++ b/src/radio_si4032.c @@ -31,6 +31,7 @@ static bool si4032_use_dma = false; // TODO: Add support for multiple APRS baud rates // This delay is for RS41 radiosondes #define symbol_delay_bell_202_1200bps_us 823 +#define SI4032_DEVIATION_HZ_625_CATS 8 // 4800 / 625 static volatile bool radio_si4032_state_change = false; static volatile uint32_t radio_si4032_freq = 0; @@ -41,8 +42,11 @@ uint16_t radio_si4032_fill_pwm_buffer(uint16_t offset, uint16_t length, uint16_t bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { uint16_t frequency_offset; + uint32_t frequency_deviation = 5; + uint32_t data_rate = 0; si4032_modulation_type modulation_type; bool use_direct_mode; + bool use_fifo_mode = false; switch (entry->data_mode) { case RADIO_DATA_MODE_CW: @@ -80,6 +84,14 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state data_timer_init(entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder)); break; } + case RADIO_DATA_MODE_CATS: + frequency_offset = 0; + frequency_deviation = SI4032_DEVIATION_HZ_625_CATS; + modulation_type = SI4032_MODULATION_TYPE_FIFO_FSK; + use_direct_mode = false; + use_fifo_mode = true; + data_rate = 9600 * 8; // TODO why a factor of 5 here? + break; default: return false; } @@ -88,8 +100,14 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state si4032_set_tx_power(entry->tx_power); si4032_set_frequency_offset(frequency_offset); si4032_set_modulation_type(modulation_type); + si4032_set_frequency_deviation(frequency_deviation); - si4032_enable_tx(); + if(use_fifo_mode) { + si4032_set_data_rate(data_rate); + } + else { + si4032_enable_tx(); + } if (use_direct_mode) { spi_uninit(); @@ -122,6 +140,9 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state system_disable_tick(); shared_state->radio_interrupt_transmit_active = true; break; + case RADIO_DATA_MODE_CATS: + shared_state->radio_fifo_transmit_active = true; + break; default: break; } @@ -196,6 +217,45 @@ static void radio_handle_main_loop_manual_si4032(radio_transmit_entry *entry, ra system_enable_tick(); } +void radio_handle_fifo_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { + log_debug("Start FIFO TX\n"); + fsk_encoder_api *fsk_encoder_api = entry->fsk_encoder_api; + fsk_encoder *fsk_enc = &entry->fsk_encoder; + + uint8_t *data = fsk_encoder_api->get_data(fsk_enc); + uint16_t len = fsk_encoder_api->get_data_len(fsk_enc); + + uint16_t written = si4032_start_tx(data, len); + data += written; + len -= written; + + bool overflow = false; + + while(len > 0) { + uint16_t written = si4032_refill_buffer(data, len, &overflow); + data += written; + len -= written; + + // log_info("FIFO wrote %d bytes\n", written); + + /*if(overflow) { + log_info("FIFO underflow - Aborting\n"); + shared_state->radio_transmission_finished = true; + + return; + }*/ + } + + int err = si4032_wait_for_tx_complete(10000); // TODO FIXME + if(err != HAL_OK) { + log_info("Error waiting for tx complete: %d\n", err); + } + + log_debug("Finished FIFO TX\n"); + + shared_state->radio_transmission_finished = true; +} + void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { if (entry->radio_type != RADIO_TYPE_SI4032 || shared_state->radio_interrupt_transmit_active) { @@ -207,6 +267,11 @@ void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_sta return; } + if (shared_state->radio_fifo_transmit_active) { + radio_handle_fifo_si4032(entry, shared_state); + return; + } + if (radio_si4032_state_change) { radio_si4032_state_change = false; pwm_timer_set_frequency(radio_si4032_freq);