kopia lustrzana https://github.com/mikaelnousiainen/RS41ng
848 wiersze
24 KiB
C
848 wiersze
24 KiB
C
/**
|
|
* The DFM17 radiosonde-compatible Si4063 driver code has been inspired by:
|
|
* - pAVAR9: https://github.com/Upuaut/pAVAR9/
|
|
* - uTrak: https://github.com/thasti/utrak/
|
|
* - DFM17 APRS/RTTY firmware by Derek Rowland
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stm32f10x_gpio.h>
|
|
#include <stm32f10x_tim.h>
|
|
|
|
#include "hal/hal.h"
|
|
#include "hal/delay.h"
|
|
#include "hal/spi.h"
|
|
#include "si4063.h"
|
|
#include "gpio.h"
|
|
#include "log.h"
|
|
|
|
#define SI4063_CLOCK 25600000UL
|
|
|
|
#define GPIO_SI4063_SDN GPIOC
|
|
#define GPIO_PIN_SI4063_SDN GPIO_Pin_3
|
|
|
|
#define GPIO_SI4063_NSEL GPIOB
|
|
#define GPIO_PIN_SI4063_NSEL GPIO_Pin_2
|
|
|
|
#define GPIO_SI4063_SDI GPIOA
|
|
#define GPIO_PIN_SI4063_SDI GPIO_Pin_7
|
|
|
|
#define GPIO_SI4063_GPIO2 GPIOD
|
|
#define GPIO_PIN_SI4063_GPIO2 GPIO_Pin_0
|
|
|
|
#define GPIO_SI4063_GPIO3 GPIOA
|
|
#define GPIO_PIN_SI4063_GPIO3 GPIO_Pin_4
|
|
|
|
#define SI4063_COMMAND_PART_INFO 0x01
|
|
#define SI4063_COMMAND_POWER_UP 0x02
|
|
#define SI4063_COMMAND_SET_PROPERTY 0x11
|
|
#define SI4063_COMMAND_GPIO_PIN_CFG 0x13
|
|
#define SI4063_COMMAND_FIFO_INFO 0x15
|
|
#define SI4063_COMMAND_GET_INT_STATUS 0x20
|
|
#define SI4063_COMMAND_START_TX 0x31
|
|
#define SI4063_COMMAND_REQUEST_DEVICE_STATE 0x33
|
|
#define SI4063_COMMAND_CHANGE_STATE 0x34
|
|
#define SI4063_COMMAND_GET_ADC_READING 0x14
|
|
#define SI4063_COMMAND_READ_CMD_BUFF 0x44
|
|
#define SI4063_COMMAND_WRITE_TX_FIFO 0x66
|
|
|
|
#define SI4063_STATE_SLEEP 0x01
|
|
#define SI4063_STATE_SPI_ACTIVE 0x02
|
|
#define SI4063_STATE_READY 0x03
|
|
#define SI4063_STATE_READY2 0x04
|
|
#define SI4063_STATE_TX_TUNE 0x05
|
|
#define SI4063_STATE_TX 0x07
|
|
|
|
/**
|
|
* Modulation settings from Derek Rowland's firmware
|
|
*/
|
|
|
|
#define SI4063_DATA_RATE_APRS 4400
|
|
|
|
/**
|
|
* Filters from uTrak: https://github.com/thasti/utrak
|
|
*
|
|
* These filters consist of low-pass filters for harmonics of the square wave modulation waveform
|
|
* and a high-pass filter for APRS pre-emphasis.
|
|
*/
|
|
|
|
/**
|
|
* 6dB@1200 Hz, 2400 Hz
|
|
*/
|
|
uint8_t si4063_filter_6db_1200_2400[9] = {0x1d, 0xe5, 0xb8, 0xaa, 0xc0, 0xf5, 0x36, 0x6b, 0x7f};
|
|
/**
|
|
* 3db@1200 Hz, 2400 Hz
|
|
*/
|
|
uint8_t si4063_filter_3db_1200_2400[9] = {0x07, 0xde, 0xbf, 0xb9, 0xd4, 0x05, 0x40, 0x6d, 0x7f};
|
|
/**
|
|
* LP only, 2400 Hz
|
|
*/
|
|
uint8_t si4063_filter_lp_2400[9] = {0xfa, 0xe5, 0xd8, 0xde, 0xf8, 0x21, 0x4f, 0x71, 0x7f};
|
|
/**
|
|
* LP only, 4800 Hz
|
|
*/
|
|
uint8_t si4063_filter_lp_4800[9] = {0xd9, 0xf1, 0x0c, 0x29, 0x44, 0x5d, 0x70, 0x7c, 0x7f};
|
|
/**
|
|
* LP only, 4400 Hz
|
|
*/
|
|
uint8_t si4063_filter_lp_4400[9] = {0xd5, 0xe9, 0x03, 0x20, 0x3d, 0x58, 0x6d, 0x7a, 0x7f};
|
|
/**
|
|
* 6dB@1200Hz, 4400 Hz (bad stopband)
|
|
*/
|
|
uint8_t si4063_filter_6db_1200_4400[9] = {0x81, 0x9f, 0xc4, 0xee, 0x18, 0x3e, 0x5c, 0x70, 0x76};
|
|
|
|
uint32_t current_frequency_hz = 434000000UL;
|
|
uint32_t current_deviation_hz = 0;
|
|
|
|
static inline void si4063_set_chip_select(bool select)
|
|
{
|
|
spi_set_chip_select(GPIO_SI4063_NSEL, GPIO_PIN_SI4063_NSEL, select);
|
|
|
|
// Output enable time, 20ns
|
|
for (uint32_t i = 0; i < 0xFFFF; i++);
|
|
}
|
|
|
|
static int si4063_wait_for_cts()
|
|
{
|
|
uint16_t timeout = 0xFFFF;
|
|
uint8_t response;
|
|
|
|
// Poll CTS over SPI
|
|
do
|
|
{
|
|
si4063_set_chip_select(true);
|
|
spi_send(SI4063_COMMAND_READ_CMD_BUFF);
|
|
response = spi_read();
|
|
si4063_set_chip_select(false);
|
|
} while (response != 0xFF && timeout--);
|
|
|
|
if (timeout == 0) {
|
|
log_error("ERROR: Si4063 timeout\n");
|
|
}
|
|
|
|
return timeout > 0 ? HAL_OK : HAL_ERROR;
|
|
}
|
|
|
|
static int si4063_read_response(uint8_t length, uint8_t *data)
|
|
{
|
|
uint16_t timeout = 0xFFFF;
|
|
uint8_t response;
|
|
|
|
// Poll CTS over SPI
|
|
do {
|
|
si4063_set_chip_select(true);
|
|
spi_send(SI4063_COMMAND_READ_CMD_BUFF);
|
|
response = spi_read();
|
|
if (response == 0xFF) {
|
|
break;
|
|
}
|
|
si4063_set_chip_select(false);
|
|
|
|
delay_us(10);
|
|
} while(timeout--);
|
|
|
|
if (timeout == 0) {
|
|
log_error("ERROR: Si4063 timeout\n");
|
|
si4063_set_chip_select(false);
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
// Read the requested data
|
|
while (length--) {
|
|
*(data++) = spi_read();
|
|
}
|
|
|
|
si4063_set_chip_select(false);
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
static void si4063_send_command(uint8_t command, uint8_t length, uint8_t *data)
|
|
{
|
|
si4063_wait_for_cts();
|
|
|
|
si4063_set_chip_select(true);
|
|
|
|
spi_send(command);
|
|
|
|
while (length--) {
|
|
spi_send(*(data++));
|
|
}
|
|
|
|
si4063_set_chip_select(false);
|
|
}
|
|
|
|
static int si4063_power_up()
|
|
{
|
|
si4063_wait_for_cts();
|
|
|
|
uint8_t data[] = {
|
|
0x01, // 0x01 = FUNC PRO - Power the chip up into EZRadio PRO functional mode.
|
|
0x01, // 0x01 = Reference signal is derived from an external TCXO.
|
|
(SI4063_CLOCK >> 24) & 0xFF, // VCXO frequency
|
|
(SI4063_CLOCK >> 16) & 0xFF,
|
|
(SI4063_CLOCK >> 8) & 0xFF,
|
|
SI4063_CLOCK & 0xFF
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_POWER_UP, sizeof(data), data);
|
|
|
|
return si4063_wait_for_cts();
|
|
}
|
|
|
|
static void si4603_set_shutdown(bool active)
|
|
{
|
|
if (active) {
|
|
GPIO_SetBits(GPIO_SI4063_SDN, GPIO_PIN_SI4063_SDN);
|
|
} else {
|
|
GPIO_ResetBits(GPIO_SI4063_SDN, GPIO_PIN_SI4063_SDN);
|
|
}
|
|
}
|
|
|
|
static void si4063_set_state(uint8_t state)
|
|
{
|
|
log_debug("Si4063: Set state %02x\n", state);
|
|
si4063_send_command(SI4063_COMMAND_CHANGE_STATE, 1, &state);
|
|
}
|
|
|
|
void si4063_enable_tx()
|
|
{
|
|
log_debug("Si4063: Enable TX\n");
|
|
si4063_set_state(SI4063_STATE_TX);
|
|
}
|
|
|
|
void si4063_inhibit_tx()
|
|
{
|
|
log_debug("Si4063: Inhibit TX\n");
|
|
si4063_set_state(SI4063_STATE_READY);
|
|
}
|
|
|
|
void si4063_disable_tx()
|
|
{
|
|
// Is this needed?
|
|
si4063_set_state(SI4063_STATE_SLEEP);
|
|
}
|
|
|
|
// 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 si4063_start_tx(uint8_t *data, int len)
|
|
{
|
|
// Clear TX FIFO
|
|
uint8_t fifo_clear_data[] = {1};
|
|
si4063_send_command(SI4063_COMMAND_FIFO_INFO, 1, fifo_clear_data);
|
|
si4063_wait_for_cts();
|
|
|
|
// Add our data to the TX FIFO
|
|
int fifo_len = len;
|
|
if(fifo_len > 64) {
|
|
fifo_len = 64;
|
|
}
|
|
si4063_send_command(SI4063_COMMAND_WRITE_TX_FIFO, fifo_len, data);
|
|
|
|
//si4063_get_int_status();
|
|
|
|
// Start transmitting
|
|
uint8_t tx_cmd[] = {
|
|
0, // channel
|
|
SI4063_STATE_SLEEP << 4,
|
|
len >> 8,
|
|
len & 0xFF,
|
|
0 // delay
|
|
};
|
|
si4063_send_command(SI4063_COMMAND_START_TX, sizeof(tx_cmd), tx_cmd);
|
|
si4063_wait_for_cts();
|
|
|
|
//si4063_get_int_status();
|
|
|
|
return fifo_len;
|
|
}
|
|
|
|
// Add additional bytes to the si4063'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 si4063_refill_buffer(uint8_t *data, int len)
|
|
{
|
|
uint8_t response[2];
|
|
|
|
// Check how many bytes we have free
|
|
si4063_send_command(SI4063_COMMAND_FIFO_INFO, 0, NULL);
|
|
si4063_read_response(sizeof(response), response);
|
|
|
|
uint8_t free_space = response[1];
|
|
if(free_space < len) {
|
|
len = free_space;
|
|
}
|
|
|
|
si4063_send_command(SI4063_COMMAND_WRITE_TX_FIFO, len, data);
|
|
|
|
return len;
|
|
}
|
|
|
|
// Wait for our buffer to be emptied, and for the si4063 to leave TX mode
|
|
// If timeout, we force it to sleep
|
|
int si4063_wait_for_tx_complete(int timeout_ms)
|
|
{
|
|
for(int i = 0; i < timeout_ms; i++) {
|
|
uint8_t status = 0;
|
|
si4063_send_command(SI4063_COMMAND_REQUEST_DEVICE_STATE, 0, NULL);
|
|
si4063_read_response(1, &status);
|
|
|
|
if(status == SI4063_STATE_SLEEP ||
|
|
status == SI4063_STATE_READY ||
|
|
status == SI4063_STATE_READY2 ||
|
|
status == SI4063_STATE_SPI_ACTIVE) {
|
|
return HAL_OK;
|
|
}
|
|
|
|
delay_ms(1);
|
|
}
|
|
|
|
si4063_disable_tx();
|
|
si4063_wait_for_cts();
|
|
|
|
return HAL_ERROR_TIMEOUT;
|
|
}
|
|
|
|
static int si4063_get_outdiv(const uint32_t frequency_hz)
|
|
{
|
|
// Select the output divider according to the recommended ranges in the Si406x datasheet
|
|
if (frequency_hz < 177000000UL) {
|
|
return 24;
|
|
} else if (frequency_hz < 239000000UL) {
|
|
return 16;
|
|
} else if (frequency_hz < 353000000UL) {
|
|
return 12;
|
|
} else if (frequency_hz < 525000000UL) {
|
|
return 8;
|
|
} else if (frequency_hz < 705000000UL) {
|
|
return 6;
|
|
}
|
|
|
|
return 4;
|
|
}
|
|
|
|
static int si4063_get_band(const uint32_t frequency_hz)
|
|
{
|
|
if (frequency_hz < 177000000UL) {
|
|
return 5;
|
|
} else if (frequency_hz < 239000000UL) {
|
|
return 4;
|
|
} else if (frequency_hz < 353000000UL) {
|
|
return 3;
|
|
} else if (frequency_hz < 525000000UL) {
|
|
return 2;
|
|
} else if (frequency_hz < 705000000UL) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool si4063_fifo_underflow()
|
|
{
|
|
uint8_t data[] = {0xFF, 0xFF, ~0x20}; // Clear underflow status
|
|
si4063_send_command(SI4063_COMMAND_GET_INT_STATUS, sizeof(data), data);
|
|
uint8_t response[7];
|
|
si4063_read_response(sizeof(response), response);
|
|
|
|
bool fifo_underflow_pending = response[6] & 0x20;
|
|
return fifo_underflow_pending;
|
|
}
|
|
|
|
void si4063_set_tx_frequency(const uint32_t frequency_hz)
|
|
{
|
|
uint8_t outdiv, band;
|
|
uint32_t f_pfd, n, m;
|
|
float ratio, rest;
|
|
|
|
log_debug("Si4063: Set frequency %lu\n", frequency_hz);
|
|
|
|
outdiv = si4063_get_outdiv(frequency_hz);
|
|
band = si4063_get_band(frequency_hz);
|
|
|
|
f_pfd = 2 * SI4063_CLOCK / outdiv;
|
|
n = frequency_hz / f_pfd - 1;
|
|
|
|
ratio = (float) frequency_hz / f_pfd;
|
|
rest = ratio - n;
|
|
|
|
m = rest * 524288UL;
|
|
|
|
// Set the frequency band
|
|
{
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x01, // Set 1 property
|
|
0x51, // 0x51 = MODEM_CLKGEN_BAND
|
|
0x08 + band // 0x08 = SY_SEL: High Performance mode (fixed prescaler = Div-by-2). Finer tuning.
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
// Set the PLL parameters
|
|
{
|
|
uint8_t data[] = {
|
|
0x40, // 0x40 = Group FREQ_CONTROL
|
|
0x06, // Set 6 properties
|
|
0x00, // 0x00 = Start from FREQ_CONTROL_INTE
|
|
n, // 0 (FREQ_CONTROL_INTE): Frac-N PLL Synthesizer integer divide number.
|
|
(m >> 16) & 0xFF, // 1 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
|
(m >> 8) & 0xFF, // 2 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
|
m & 0xFF, // 3 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
|
0x00, // 4 (FREQ_CONTROL_CHANNEL_STEP_SIZE): EZ Frequency Programming channel step size.
|
|
0x02 // 5 (FREQ_CONTROL_CHANNEL_STEP_SIZE): EZ Frequency Programming channel step size.
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
current_frequency_hz = frequency_hz;
|
|
|
|
// Deviation depends on the frequency band
|
|
si4063_set_frequency_deviation(current_deviation_hz);
|
|
}
|
|
|
|
void si4063_set_data_rate(const uint32_t rate_bps)
|
|
{
|
|
int rate = rate_bps * 10;
|
|
// Set TX_NCO_MODE to our crystal frequency, as recommended by the data sheet for rates <= 200kbps
|
|
// Set MODEM_DATA_RATE to rate_bps * 10 (will get downsampled because NCO_MODE defaults to 10x)
|
|
uint8_t data[] = {
|
|
0x20, // Group
|
|
0x07, // Set 7 properties
|
|
0x03, // Start from MODEM_DATA_RATE
|
|
(rate >> 16) & 0xFF,
|
|
(rate >> 8) & 0xFF,
|
|
rate & 0xFF,
|
|
(SI4063_CLOCK >> 24) & 0xFF,
|
|
(SI4063_CLOCK >> 16) & 0xFF,
|
|
(SI4063_CLOCK >> 8) & 0xFF,
|
|
SI4063_CLOCK & 0xFF,
|
|
};
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
void si4063_set_tx_power(uint8_t power)
|
|
{
|
|
uint8_t data[] = {
|
|
0x22, // 0x20 = Group PA
|
|
0x01, // Set 1 property
|
|
0x01, // 0x01 = PA_PWR_LVL
|
|
power & 0x7F // Power level from 00..7F
|
|
};
|
|
|
|
log_debug("Si4063: Set TX power %02x\n", power);
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
void si4063_set_frequency_offset(uint16_t offset)
|
|
{
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x02, // Set 2 properties (2 bytes)
|
|
0x0D, // 0x0D = MODEM_FREQ_OFFSET
|
|
offset >> 8, // Upper 8 bits of the offset
|
|
offset & 0xFF // Lower 8 bits of the offset
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
static uint32_t si4063_calculate_deviation(uint32_t deviation_hz)
|
|
{
|
|
uint8_t outdiv = si4063_get_outdiv(current_frequency_hz);
|
|
|
|
// SY_SEL = Div-by-2
|
|
return (uint32_t) (((double) (1 << 19) * outdiv * deviation_hz) / (2 * SI4063_CLOCK));
|
|
}
|
|
|
|
void si4063_set_frequency_deviation(uint32_t deviation_hz)
|
|
{
|
|
uint32_t deviation = si4063_calculate_deviation(deviation_hz);
|
|
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x03, // Set 3 properties (3 bytes)
|
|
0x0A, // 0x0A = MODEM_FREQ_DEV
|
|
(deviation >> 16) & 0xFF,
|
|
(deviation >> 8) & 0xFF,
|
|
deviation & 0xFF
|
|
};
|
|
|
|
log_info("Si4063: Set frequency deviation to value %lu with %lu Hz\n", deviation, deviation_hz);
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
|
|
current_deviation_hz = deviation_hz;
|
|
}
|
|
|
|
void si4063_set_modulation_type(si4063_modulation_type type)
|
|
{
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x01, // Set 1 property
|
|
0x00, // 0x00 = MODEM_MOD_TYPE
|
|
0x80 | // 0x80 = Direct async mode (MCU-controlled)
|
|
0x60 | // 0x60 = Use GPIO3 as source for direct mode modulation
|
|
0x08 // 0x08 = Direct modulation source (MCU-controlled)
|
|
};
|
|
|
|
log_debug("Si4063: Set modulation type %d\n", type);
|
|
|
|
switch (type) {
|
|
case SI4063_MODULATION_TYPE_CW:
|
|
// Pure carrier wave modulation (for modulating via frequency offset, e.g. for RTTY)
|
|
data[3] |= 0x00;
|
|
break;
|
|
case SI4063_MODULATION_TYPE_OOK:
|
|
// Direct Async Mode with OOK modulation
|
|
data[3] |= 0x01;
|
|
break;
|
|
case SI4063_MODULATION_TYPE_FSK:
|
|
// Direct Async Mode with FSK modulation
|
|
data[3] |= 0x02;
|
|
break;
|
|
case SI4063_MODULATION_TYPE_FIFO_FSK:
|
|
// FIFO with FSK modulation
|
|
data[3] = 0x02;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
int32_t si4063_read_temperature_celsius_100()
|
|
{
|
|
uint8_t response[6];
|
|
int32_t temperature;
|
|
uint8_t data[] = {
|
|
0x10, // Measure internal temperature ADC reading only
|
|
0x00
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_GET_ADC_READING, sizeof(data), data);
|
|
|
|
si4063_read_response(sizeof(response), response);
|
|
|
|
// Calculate the temperature in C * 10
|
|
temperature = (response[4] << 8) | response[5];
|
|
temperature *= 568;
|
|
temperature /= 256;
|
|
temperature -= 2970;
|
|
|
|
return temperature * 10;
|
|
}
|
|
|
|
uint16_t si4063_read_part_info()
|
|
{
|
|
uint8_t response[8];
|
|
|
|
si4063_send_command(SI4063_COMMAND_PART_INFO, 0, NULL);
|
|
|
|
si4063_read_response(sizeof(response), response);
|
|
|
|
// Return part number
|
|
return response[1] << 8 | response[2];
|
|
}
|
|
|
|
inline void si4063_set_direct_mode_pin(bool high)
|
|
{
|
|
if (high) {
|
|
GPIO_SetBits(GPIO_SI4063_GPIO3, GPIO_PIN_SI4063_GPIO3);
|
|
} else {
|
|
GPIO_ResetBits(GPIO_SI4063_GPIO3, GPIO_PIN_SI4063_GPIO3);
|
|
}
|
|
}
|
|
|
|
void si4063_configure()
|
|
{
|
|
{
|
|
uint8_t data[] = {
|
|
0x00, // 0x00 = Group GLOBAL
|
|
0x01, // Set 1 property
|
|
0x00, // 0x00 = GLOBAL_XO_TUNE
|
|
0x62 // Value determined for DFM17 radiosondes
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
uint8_t data[] = {
|
|
0x00, // 0x00 = Group GLOBAL
|
|
0x01, // Set 1 property
|
|
0x01, // 0x00 = GLOBAL_CLK_CFG
|
|
0x00 // No clock output needed
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
uint8_t data[] = {
|
|
0x00, // 0x00 = Group GLOBAL
|
|
0x01, // Set 1 property
|
|
0x03, // 0x03 = GLOBAL_CONFIG
|
|
0x40 | // 0x40 = Reserved, needs to be set to 1
|
|
0x20 | // 0x20 = Fast sequencer mode
|
|
0x10 | // 129-byte FIFO
|
|
0x00 // High-performance mode
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
uint8_t data[] = {
|
|
0x01, // 0x01 = Group INT_CTL
|
|
0x01, // Set 1 property
|
|
0x00, // 0x00 = INT_CTL_ENABLE
|
|
0x00 // 0x00 = Disable all hardware interrupts
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
uint8_t data[] = {
|
|
0x02, // 0x02 = Group FRR_CTL
|
|
0x04, // Set 4 properties
|
|
0x00, // 0x00 = FRR_CTL_A_MODE
|
|
0x00, // Disable all FRR values
|
|
0x00,
|
|
0x00,
|
|
0x00
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
// Used only in synchronous mode (for GFSK modulation/filtering)
|
|
uint8_t data[] = {
|
|
0x10, // 0x10 = Group PREAMBLE
|
|
0x01, // Set 1 property
|
|
0x00, // 0x00 = PREAMBLE_TX_LENGTH
|
|
0x00 // 0x00 = Disable preamble
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
// Used only in synchronous mode (for GFSK modulation/filtering)
|
|
uint8_t data[] = {
|
|
0x11, // 0x11 = Group SYNC
|
|
0x01, // Set 1 property
|
|
0x00, // 0x00 = SYNC_CONFIG
|
|
0x80 // 0x80 = Sync word is not transmitted
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
uint8_t data[] = {
|
|
0x22, // 0x22 = Group PA
|
|
0x01, // Set 1 property
|
|
0x02, // 0x02 = PA_BIAS_CLKDUTY
|
|
0x00 // 0x00 = Complementary drive signals, 50% duty cycle. For high-power applications.
|
|
// Alternative: 0xC0 = Single-ended drive signal, 25% duty cycle. For low-power applications.
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
{
|
|
/*
|
|
2. Detailed Errata Descriptions
|
|
2.1 If Configured to Skip Sync and Preamble on Transmit, the TX Data from the FIFO is Corrupted
|
|
Description of Errata
|
|
If preamble and sync word are excluded from the transmitted data (PREMABLE_TX_LENGTH = 0 and SYNC_CONFIG: SKIP_TX = 1), data from the FIFO is not transmitted correctly.
|
|
Affected Conditions / Impacts
|
|
Some number of missed bytes will occur at the beginning of the packet and some number of repeated bytes at the end of the packet.
|
|
Workaround
|
|
Set PKT_FIELD_1_CRC_CONFIG: CRC_START to 1. This will trigger the packet handler and result in transmitting the correct data,
|
|
while still not sending a CRC unless enabled in a FIELD configuration. A fix has been identified and will be included in a future release
|
|
*/
|
|
|
|
// In other words, without this, the FIFO buffer gets corrupted while TXing, because we're not using
|
|
// the preamble/sync word stuff
|
|
// To be clear - we're not doing any CRC stuff! This is just the recommended workaround
|
|
uint8_t data[] = {
|
|
0x12, // Group
|
|
0x01, // Set 1 property
|
|
0x10, // PKT_FIELD_1_CRC_CONFIG
|
|
0x80, // CRC_START
|
|
};
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
}
|
|
|
|
// Not used yet, for future use
|
|
void si4063_set_filter(const uint8_t *filter)
|
|
{
|
|
uint8_t data[12] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x09, // Set 9 properties
|
|
0x0F // 0x0F = MODEM_TX_FILTER_COEFF_8
|
|
};
|
|
|
|
for (uint8_t i = 0; i < 9; i++) {
|
|
data[3 + i] = filter[i];
|
|
}
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
void si4063_configure_data_rate(uint32_t data_rate)
|
|
{
|
|
// Used only for GFSK mode filtering
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x03, // Set 3 properties
|
|
0x03, // 0x03 = MODEM_DATA_RATE
|
|
(data_rate >> 16) & 0xFF,
|
|
(data_rate >> 8) & 0xFF,
|
|
data_rate & 0xFF
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
// Not used yet, for future use
|
|
void si4063_configure_aprs()
|
|
{
|
|
// For APRS in direct mode with GFSK modulation
|
|
si4063_configure_data_rate(SI4063_DATA_RATE_APRS);
|
|
|
|
// Used only for GFSK mode filtering
|
|
uint32_t nco_mod = SI4063_CLOCK / 10;
|
|
uint8_t data[] = {
|
|
0x20, // 0x20 = Group MODEM
|
|
0x04, // Set 4 properties
|
|
0x06, // 0x06 = MODEM_TX_NCO_MODE
|
|
0x00 | // 0x00 = TX Gaussian filter oversampling ratio is 10x
|
|
((nco_mod >> 24) & 0x03),
|
|
(nco_mod >> 16) & 0xFF,
|
|
(nco_mod >> 8) & 0xFF,
|
|
nco_mod & 0xFF
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
|
}
|
|
|
|
// Not used yet, for future use
|
|
void si4063_configure_rtty()
|
|
{
|
|
// For RTTY:
|
|
// NOTE: 0x22 sets shift at about 450 Hz for RTTY
|
|
si4063_set_frequency_deviation(0x22);
|
|
}
|
|
|
|
void si4063_configure_gpio(uint8_t gpio0, uint8_t gpio1, uint8_t gpio2, uint8_t gpio3, uint8_t drive_strength) {
|
|
uint8_t data[] = {
|
|
gpio0,
|
|
gpio1,
|
|
gpio2,
|
|
gpio3,
|
|
0x00, // NIRQ = Do nothing
|
|
11, // SDO 11 = Outputs the Serial Data Out (SDO) signal for the SPI bus
|
|
drive_strength
|
|
};
|
|
|
|
si4063_send_command(SI4063_COMMAND_GPIO_PIN_CFG, sizeof(data), data);
|
|
}
|
|
|
|
int si4063_init()
|
|
{
|
|
GPIO_InitTypeDef gpio_init;
|
|
|
|
// Si4063 shutdown pin
|
|
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_SDN;
|
|
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
|
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(GPIO_SI4063_SDN, &gpio_init);
|
|
|
|
// Si4063 chip select pin
|
|
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_NSEL;
|
|
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
|
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(GPIO_SI4063_NSEL, &gpio_init);
|
|
|
|
// Si4063 GPIO3 pin for direct mode transmission
|
|
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_GPIO3;
|
|
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
|
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(GPIO_SI4063_GPIO3, &gpio_init);
|
|
|
|
si4063_set_direct_mode_pin(false);
|
|
|
|
si4603_set_shutdown(false);
|
|
delay_us(50);
|
|
|
|
si4063_wait_for_cts();
|
|
|
|
si4603_set_shutdown(true);
|
|
delay_us(20);
|
|
si4603_set_shutdown(false);
|
|
delay_us(50);
|
|
|
|
if (si4063_power_up() != HAL_OK) {
|
|
log_error("ERROR: Error powering up Si4063\n");
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
// Assume Si4063 part number
|
|
uint16_t part = si4063_read_part_info();
|
|
if (part != 0x4063) {
|
|
log_error("ERROR: Unknown or missing Si4063 part number: 0x%04x\n", part);
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
si4063_configure();
|
|
|
|
si4063_configure_gpio(
|
|
0x00, // GPIO0: Do nothing
|
|
0x00, // GPIO1: Do nothing
|
|
0x00, // GPIO2: Do nothing
|
|
0x04, // GPIO3: Pin is configured as a CMOS input for direct mode transmissions.
|
|
0x00 // Drive strength: HIGH
|
|
);
|
|
|
|
si4063_set_tx_power(0x00);
|
|
|
|
si4063_set_frequency_offset(0);
|
|
|
|
// Set deviation to zero for non-FSK modulations
|
|
si4063_set_frequency_deviation(0);
|
|
|
|
si4063_set_modulation_type(SI4063_MODULATION_TYPE_CW);
|
|
|
|
si4063_set_state(SI4063_STATE_READY);
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
void TIM1_BRK_TIM15_IRQHandler(void)
|
|
{
|
|
#ifdef DFM17
|
|
static bool pin_state = false;
|
|
#endif
|
|
|
|
if (TIM_GetITStatus(TIM15, TIM_IT_Update) != RESET) {
|
|
TIM_ClearITPendingBit(TIM15, TIM_IT_Update);
|
|
#ifdef DFM17
|
|
// Restrict the interrupt to DFM17 only just in case this ISR gets called on RS41
|
|
pin_state = !pin_state;
|
|
si4063_set_direct_mode_pin(pin_state);
|
|
#endif
|
|
}
|
|
}
|