pecanpico9/tracker/software/radio.c

452 wiersze
11 KiB
C
Czysty Zwykły widok Historia

2017-05-30 22:58:20 +00:00
#include "ch.h"
#include "hal.h"
#include "defines.h"
#include "debug.h"
#include "radio.h"
#include "si4464.h"
#include "geofence.h"
#include "pi2c.h"
#include "padc.h"
#include <string.h>
#define PLAYBACK_RATE ((STM32_PCLK1) / 500) /* Samples per second (48Mhz / 250 = 192kHz) */
2017-05-30 22:58:20 +00:00
#define BAUD_RATE 1200 /* APRS AFSK baudrate */
#define SAMPLES_PER_BAUD (PLAYBACK_RATE / BAUD_RATE) /* Samples per baud (192kHz / 1200baud = 160samp/baud) */
2017-05-30 22:58:20 +00:00
#define PHASE_DELTA_1200 (((2 * 1200) << 16) / PLAYBACK_RATE) /* Delta-phase per sample for 1200Hz tone */
#define PHASE_DELTA_2200 (((2 * 2200) << 16) / PLAYBACK_RATE) /* Delta-phase per sample for 2200Hz tone */
mutex_t radio_mtx; // Radio mutex
mod_t active_mod = MOD_NOT_SET;
static uint32_t phase_delta; // 1200/2200 for standard AX.25
static uint32_t phase; // Fixed point 9.7 (2PI = TABLE_SIZE)
static uint32_t packet_pos; // Next bit to be sent out
static uint32_t current_sample_in_baud; // 1 bit = SAMPLES_PER_BAUD samples
static uint8_t current_byte;
static radioMSG_t tim_msg;
static uint8_t txs; // Serial maschine state
static uint8_t txc; // Current byte
static uint32_t txi; // Bitcounter of current byte
static uint32_t txj; // Bytecounter
static const char *getModulation(uint8_t key) {
const char *val[] = {"unknown", "OOK", "2FSK", "2GFSK", "AFSK"};
return val[key];
};
2017-05-30 22:58:20 +00:00
void initAFSK(radioMSG_t *msg) {
// Initialize radio
Si4464_Init();
setModemAFSK();
active_mod = MOD_AFSK;
2017-05-30 22:58:20 +00:00
}
void sendAFSK(radioMSG_t *msg) {
// Initialize variables for timer
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
2017-05-30 22:58:20 +00:00
phase_delta = PHASE_DELTA_1200;
phase = 0;
packet_pos = 0;
current_sample_in_baud = 0;
current_byte = 0;
// Tune
radioTune(msg->freq, 0, msg->power, 0);
// Initialize timer
2017-05-30 22:58:20 +00:00
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = 500;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
2017-05-30 22:58:20 +00:00
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
shutdownRadio();
2017-05-30 22:58:20 +00:00
}
void init2GFSK(radioMSG_t *msg) {
2017-09-02 03:43:13 +00:00
// Initialize radio
Si4464_Init();
setModem2GFSK(msg->gfsk_conf);
active_mod = MOD_2GFSK;
}
thread_reference_t feeder_ref = NULL;
/*
* Radio GPIO1 interrupt
*/
CH_IRQ_HANDLER(VectorE0) {
CH_IRQ_PROLOGUE();
chThdResumeS(&feeder_ref, MSG_OK);
EXTI->PR |= EXTI_PR_PR12;
CH_IRQ_EPILOGUE();
}
static THD_WORKING_AREA(si_fifo_feeder_wa, 10240);
THD_FUNCTION(si_fifo_feeder_thd, arg)
{
(void)arg;
2017-09-02 03:43:13 +00:00
uint16_t c = 64;
uint16_t all = (tim_msg.bin_len+7)/8;
2017-09-02 03:43:13 +00:00
// Initial FIFO fill
Si4464_writeFIFO(tim_msg.msg, c);
// Initialize interrupt
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI12_PC;
EXTI->IMR = EXTI_IMR_MR12; // Activate interrupt for chan12 (=>PC12)
EXTI->RTSR = EXTI_RTSR_TR12; // Listen on rising edge
nvicEnableVector(EXTI15_10_IRQn, 1); // Enable interrupt
2017-09-02 03:43:13 +00:00
// Transmit
radioTune(tim_msg.freq, 0, tim_msg.power, all);
2017-09-02 03:43:13 +00:00
while(c < all) { // Do while bytes not written into FIFO completely
chThdSuspendS(&feeder_ref); // Suspend until interupt resumes it
2017-09-02 03:43:13 +00:00
// Determine free memory in Si4464-FIFO
uint16_t more = Si4464_freeFIFO();
if(more > all-c)
more = all-c; // Last bytes in FIFO
//TRACE_DEBUG("fed %db %d<%d", more, c, all);
Si4464_writeFIFO(&tim_msg.msg[c], more); // Write into FIFO
2017-09-02 03:43:13 +00:00
c += more;
}
nvicDisableVector(EXTI15_10_IRQn); // Disable interrupt
shutdownRadio();
}
void send2GFSK(radioMSG_t *msg) {
// Copy data
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
// Start FIFO feeder
chThdCreateStatic(si_fifo_feeder_wa, sizeof(si_fifo_feeder_wa), HIGHPRIO+1, si_fifo_feeder_thd, NULL);
// Wait for the transmitter to start (because it is used as mutex)
while(Si4464_getState() != SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
2017-05-30 22:58:20 +00:00
/**
* Fast interrupt handler for AFSK modulation. It has has the highest priority
* in order to provide an accurate low jitter modulation.
2017-05-30 22:58:20 +00:00
*/
CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
{
if(active_mod == MOD_AFSK) // AFSK
{
2017-05-30 22:58:20 +00:00
if(packet_pos == tim_msg.bin_len) { // Packet transmission finished
2017-05-30 22:58:20 +00:00
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
}
if(current_sample_in_baud == 0) {
if((packet_pos & 7) == 0) { // Load up next byte
current_byte = tim_msg.msg[packet_pos >> 3];
2017-05-30 22:58:20 +00:00
} else { // Load up next bit
current_byte = current_byte / 2;
}
}
// Toggle tone (1200 <> 2200)
phase_delta = (current_byte & 1) ? PHASE_DELTA_1200 : PHASE_DELTA_2200;
phase += phase_delta; // Add delta-phase (delta-phase tone dependent)
RADIO_WRITE_GPIO((phase >> 16) & 1); // Set modulaton pin (connected to Si4464)
2017-05-30 22:58:20 +00:00
current_sample_in_baud++;
if(current_sample_in_baud == SAMPLES_PER_BAUD) { // Old bit consumed, load next bit
current_sample_in_baud = 0;
packet_pos++;
}
} else { // 2FSK
2017-05-30 22:58:20 +00:00
switch(txs)
{
case 6: // TX-delay
txj++;
if(txj > (uint32_t)(tim_msg.fsk_conf->predelay * tim_msg.fsk_conf->baud / 1000)) {
txj = 0;
txs = 7;
}
break;
2017-05-30 22:58:20 +00:00
case 7: // Transmit a single char
if(txj < tim_msg.bin_len/8) {
txc = tim_msg.msg[txj]; // Select char
txj++;
RADIO_WRITE_GPIO(LOW); // Start Bit (Synchronizing)
txi = 0;
txs = 8;
} else { // Finished to transmit string
RADIO_WRITE_GPIO(HIGH);
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
}
break;
2017-05-30 22:58:20 +00:00
case 8:
if(txi < tim_msg.fsk_conf->bits) {
txi++;
RADIO_WRITE_GPIO(txc & 1);
txc = txc >> 1;
} else {
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txi = 0;
txs = 9;
}
break;
case 9:
if(tim_msg.fsk_conf->stopbits == 2)
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txs = 7;
}
2017-07-24 22:46:23 +00:00
2017-05-30 22:58:20 +00:00
}
palToggleLine(LINE_IO_LED1);
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
2017-05-30 22:58:20 +00:00
}
void initOOK(radioMSG_t *msg) {
// Initialize radio
Si4464_Init();
setModemOOK();
active_mod = MOD_OOK;
2017-05-30 22:58:20 +00:00
}
/**
* Transmits binary OOK message. One bit = 20ms (1: TONE, 0: NO TONE)
*/
void sendOOK(radioMSG_t *msg) {
// Tune
radioTune(msg->freq, 0, msg->power, 0);
2017-05-30 22:58:20 +00:00
// Transmit data
uint32_t bit = 0;
systime_t time = chVTGetSystemTimeX();
while(bit < msg->bin_len) {
RADIO_WRITE_GPIO((msg->msg[bit/8] >> (bit%8)) & 0x1);
2017-05-30 22:58:20 +00:00
bit++;
time = chThdSleepUntilWindowed(time, time + MS2ST(1200 / msg->ook_conf->speed));
2017-05-30 22:58:20 +00:00
}
shutdownRadio();
2017-05-30 22:58:20 +00:00
}
void init2FSK(radioMSG_t *msg) {
// Initialize radio and tune
Si4464_Init();
setModem2FSK();
2017-05-30 22:58:20 +00:00
}
void send2FSK(radioMSG_t *msg) {
// Initialize variables for timer
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
2017-05-30 22:58:20 +00:00
txs = 6;
txc = 0;
txi = 0;
txj = 0;
// Tune
radioTune(msg->freq, msg->fsk_conf->shift, msg->power, 0);
// Initialize timer
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = STM32_PCLK1 / 16 / msg->fsk_conf->baud; // FIXME: 5625000 should be actually STM32_PCLK1
TIM7->PSC = 15;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
shutdownRadio();
2017-05-30 22:58:20 +00:00
}
void shutdownRadio(void)
{
2017-09-02 03:43:13 +00:00
// Wait for PH to finish transmission for 2GFSK
while(active_mod == MOD_2GFSK && Si4464_getState() == SI4464_STATE_TX)
{
TRACE_DEBUG("Waiting for Si4464 (state=%d, free=%d)", Si4464_getState(), Si4464_freeFIFO());
2017-09-02 03:43:13 +00:00
chThdSleepMilliseconds(5);
}
2017-09-02 03:43:13 +00:00
Si4464_shutdown();
active_mod = MOD_NOT_SET;
}
2017-05-30 22:58:20 +00:00
/**
* Returns APRS region specific frequency determined by GPS location. It will
* use the APRS default frequency set in the config file if no GPS fix has
* been received.
*/
uint32_t getAPRSRegionFrequency(void) {
2017-05-30 22:58:20 +00:00
trackPoint_t *point = getLastTrackPoint();
uint32_t freq = 0; // Position unknown
2017-05-30 22:58:20 +00:00
// America 144.390 MHz
if(isPointInAmerica(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_AMERICA;
// China 144.640 MHz
if(isPointInChina(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_CHINA;
// Japan 144.660 MHz
if(isPointInJapan(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_JAPAN;
// Southkorea 144.620 MHz
if(isPointInSouthkorea(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_SOUTHKOREA;
// Southkorea 144.620 MHz
if(isPointInSoutheastAsia(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_SOUTHEASTASIA;
// Australia 145.175 MHz
if(isPointInAustralia(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_AUSTRALIA;
// Australia 144.575 MHz
if(isPointInNewZealand(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_NEWZEALAND;
// Argentina/Paraguay/Uruguay 144.930 MHz
if(isPointInArgentina(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_ARGENTINA;
// Brazil 145.575 MHz
if(isPointInBrazil(point->gps_lat, point->gps_lon))
freq = APRS_FREQ_BRAZIL;
return freq;
}
/**
* Sends radio message into message box. This method will return false if message box is full.
*/
bool transmitOnRadio(radioMSG_t *msg, bool shutdown)
{
if(inRadioBand(msg->freq)) // Frequency in radio radio band
{
if(inRadioBand(msg->freq)) // Frequency in radio radio band
{
2017-05-30 22:58:20 +00:00
lockRadio(); // Lock radio
2017-05-30 22:58:20 +00:00
TRACE_INFO( "RAD > Transmit %d.%03d MHz, Pwr %d, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power,
getModulation(msg->mod), msg->bin_len
);
switch(msg->mod)
{
case MOD_2FSK:
if(active_mod != msg->mod)
init2FSK(msg);
send2FSK(msg);
break;
case MOD_2GFSK:
if(active_mod != msg->mod)
init2GFSK(msg);
send2GFSK(msg);
break;
case MOD_AFSK:
if(active_mod != msg->mod)
initAFSK(msg);
sendAFSK(msg);
break;
case MOD_OOK:
if(active_mod != msg->mod)
initOOK(msg);
sendOOK(msg);
break;
case MOD_NOT_SET:
TRACE_ERROR("RAD > Modulation not set");
break;
}
2017-09-02 03:43:13 +00:00
unlockRadio(); // Unlock radio
2017-05-30 22:58:20 +00:00
} else {
2017-05-30 22:58:20 +00:00
TRACE_ERROR("RAD > It is nonsense to transmit 0 bits, %d.%03d MHz, Pwr dBm, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power, getModulation(msg->mod), msg->bin_len
);
2017-05-30 22:58:20 +00:00
}
2017-05-30 22:58:20 +00:00
} else { // Frequency out of radio band
2017-05-30 22:58:20 +00:00
TRACE_ERROR("RAD > Radio cant transmit on this frequency, %d.%03d MHz, Pwr dBm, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power, getModulation(msg->mod), msg->bin_len
2017-05-30 22:58:20 +00:00
);
}
return true;
}
uint32_t getFrequency(freq_conf_t *config)
2017-05-30 22:58:20 +00:00
{
switch(config->type) {
case FREQ_APRS_REGION:; // Dynamic frequency (determined by GPS position)
uint32_t freq = getAPRSRegionFrequency();
if(!freq) // Use default frequency (if freq is not set = position unknown)
2017-05-30 22:58:20 +00:00
return config->hz;
return freq;
2017-05-30 22:58:20 +00:00
case FREQ_STATIC: // Static frequency
return config->hz;
default:
return 0;
}
}
void lockRadio(void)
{
chMtxLock(&radio_mtx);
while(active_mod != MOD_NOT_SET && Si4464_getState() == SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
void unlockRadio(void)
{
chMtxUnlock(&radio_mtx);
}