kopia lustrzana https://github.com/kamocat/uSDX
RX ready for testing
rodzic
dede6b7bef
commit
3aed3f52f5
|
@ -33,6 +33,14 @@
|
|||
* @name ISRs suppressed in standard drivers
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM1_SUPPRESS_ISR
|
||||
#define STM32_TIM2_SUPPRESS_ISR
|
||||
#define STM32_TIM3_SUPPRESS_ISR
|
||||
#define STM32_TIM14_SUPPRESS_ISR
|
||||
#define STM32_TIM15_SUPPRESS_ISR
|
||||
#define STM32_TIM16_SUPPRESS_ISR
|
||||
#define STM32_TIM17_SUPPRESS_ISR
|
||||
|
||||
#define STM32_USART1_SUPPRESS_ISR
|
||||
#define STM32_USART2_SUPPRESS_ISR
|
||||
#define STM32_USART3_SUPPRESS_ISR
|
||||
|
|
|
@ -120,7 +120,13 @@ LDSCRIPT= $(STARTUPLD)/STM32F051x8.ld
|
|||
# setting.
|
||||
CSRC = $(ALLCSRC) \
|
||||
$(TESTSRC) \
|
||||
main.c radio/rx.c drivers/si5351.c trig.c
|
||||
main.c \
|
||||
radio/hilbert.c \
|
||||
radio/rx.c \
|
||||
radio/ssb.c \
|
||||
drivers/si5351.c \
|
||||
drivers/speaker.c \
|
||||
trig.c
|
||||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
* @brief Enables the GPT subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_GPT FALSE
|
||||
#define HAL_USE_GPT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
#define STM32_GPT_USE_TIM1 FALSE
|
||||
#define STM32_GPT_USE_TIM2 FALSE
|
||||
#define STM32_GPT_USE_TIM3 FALSE
|
||||
#define STM32_GPT_USE_TIM14 FALSE
|
||||
#define STM32_GPT_USE_TIM14 TRUE
|
||||
#define STM32_GPT_TIM1_IRQ_PRIORITY 2
|
||||
#define STM32_GPT_TIM2_IRQ_PRIORITY 2
|
||||
#define STM32_GPT_TIM3_IRQ_PRIORITY 2
|
||||
|
|
|
@ -31,11 +31,16 @@ void synthStop(struct synth * cfg){
|
|||
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
|
||||
}
|
||||
|
||||
void synthSetPhase(struct synth * cfg, uint8_t phase){
|
||||
cfg->phase = phase;
|
||||
void synthSetPhase(struct synth * cfg, float degrees){
|
||||
if(degrees < 0)
|
||||
degrees += 360; // Only allow positive delay
|
||||
//FIXME: If more than 180 degrees, we should use the clock invert feature
|
||||
// Since the delay is before the divider, we scale by the divide ratio
|
||||
degrees *= ((1./360)*(cfg->reg.synth))/(1<<25);
|
||||
cfg->phase = degrees > 127 ? 127 : degrees;
|
||||
uint8_t buf[2];
|
||||
buf[0]=cfg->channel+165;
|
||||
buf[1]=phase;
|
||||
buf[1]=cfg->phase;
|
||||
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
|
||||
pll_reset(cfg->PLLx);
|
||||
}
|
||||
|
@ -55,6 +60,11 @@ void synthWriteParam(uint8_t reg, uint64_t val, uint8_t div){
|
|||
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 9, NULL, 0, TIME_MS2I(100));
|
||||
}
|
||||
|
||||
void synthWriteConfig(struct synth * cfg){
|
||||
synthWriteParam(cfg->PLLx, cfg->reg.pll, 0);
|
||||
synthWriteParam(cfg->channel+2, cfg->reg.synth, cfg->reg.divide);
|
||||
}
|
||||
|
||||
void synthSetCarrier(struct synth * cfg, float carrier){
|
||||
// Calculate PLL and Multisynth values
|
||||
const double xtal = 27e6;
|
||||
|
@ -81,9 +91,7 @@ void synthSetCarrier(struct synth * cfg, float carrier){
|
|||
cfg->reg.shift = delta_shift;
|
||||
cfg->reg.divide = div2;
|
||||
|
||||
// Write changes to chip
|
||||
synthWriteParam(cfg->PLLx, cfg->reg.pll, 0);
|
||||
synthWriteParam(cfg->channel+2, cfg->reg.synth, cfg->reg.divide);
|
||||
synthWriteConfig(cfg);
|
||||
}
|
||||
|
||||
void synthSetBaseband(struct synth * cfg, int32_t baseband){
|
||||
|
|
|
@ -29,7 +29,13 @@ void synthStart(struct synth * cfg);
|
|||
void synthSetCarrier(struct synth * cfg, float carrier);
|
||||
void synthSetBaseband(struct synth * cfg, int32_t baseband);
|
||||
void synthWriteParam(uint8_t reg, uint64_t val, uint8_t div);
|
||||
void synthSetPhase(struct synth * cfg, uint8_t phase);
|
||||
void synthWriteConfig(struct synth * cfg);
|
||||
/* Sets the initial phase offset
|
||||
*
|
||||
* Note that this has limited resolution and range. The maximum delay is 31.75x the PLL clock period,
|
||||
* which means you will have a difficult time getting a 90-degree phase shift on the 80m band.
|
||||
*/
|
||||
void synthSetPhase(struct synth * cfg, float degrees);
|
||||
void synthStop(struct synth * cfg);
|
||||
|
||||
|
||||
|
|
|
@ -17,20 +17,67 @@
|
|||
* advantages like increased dynamic range and noise rejection.
|
||||
*/
|
||||
|
||||
uint16_t spkr_dma_buf[spkr_buffer_len];
|
||||
objects_fifo_t spkr_fifo;
|
||||
|
||||
void speakerUpdate(int16_t data, int len);
|
||||
void speakerInit(void){
|
||||
size_t objsize = spkr_buffer_len*sizeof(int16_t);
|
||||
msg_t * msg_buf = chCoreAllocFromBase(spkr_fifo_len*sizeof(msg_t), sizeof(msg_t), 0);
|
||||
void * obj_buf = chCoreAllocFromBase(objsize*spkr_fifo_len, 4, 0);
|
||||
chFifoObjectInit(&spkr_fifo, objsize, spkr_fifo_len, obj_buf, msg_buf);
|
||||
}
|
||||
|
||||
msg_t speakerUpdate(int16_t * data, int len){
|
||||
static int16_t * buf = 0;
|
||||
static size_t i = 0;
|
||||
for(int n = 0; n < len; ++n){
|
||||
if(!buf){
|
||||
i = 0;
|
||||
buf = (int16_t *)chFifoTakeObjectTimeout(&spkr_fifo, TIME_IMMEDIATE);
|
||||
if(!buf)
|
||||
return MSG_TIMEOUT;
|
||||
}
|
||||
buf[i]=data[n];
|
||||
++i;
|
||||
if(i>=spkr_buffer_len){
|
||||
chFifoSendObject(&spkr_fifo, (void*)buf);
|
||||
buf=0;
|
||||
}
|
||||
}
|
||||
return MSG_OK;
|
||||
}
|
||||
|
||||
/** Copies audio data to PWM
|
||||
*
|
||||
*/
|
||||
void speaker_callback(PWMDriver * pwmp){
|
||||
//FIXME: Copy data into DMA buffer. Or interrupt every period.
|
||||
void speaker_callback(GPTDriver * gptp){
|
||||
static size_t i=0;
|
||||
static uint16_t * buf = 0;
|
||||
(void)gptp; // We don't use the gpt driver info
|
||||
if(!buf){
|
||||
//get next buffer
|
||||
msg_t result = chFifoReceiveObjectTimeout(&spkr_fifo, (void**)&buf, TIME_IMMEDIATE);
|
||||
//If fifo is empty, return.
|
||||
if(MSG_TIMEOUT == result)
|
||||
return;
|
||||
}
|
||||
// Convert signed integer to unsigned with offset
|
||||
buf[i] += 0x8000;
|
||||
// Scale to 11 bits
|
||||
buf[i]>>=(16-11);
|
||||
// Update PWM
|
||||
pwmEnableChannel(&PWMD3, 0, buf[i]);
|
||||
// increment to next sample
|
||||
++i;
|
||||
if(i >= spkr_buffer_len){
|
||||
//Free this buffer
|
||||
chFifoReturnObject(&spkr_fifo, buf);
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PWMConfig spkr = {
|
||||
.frequency = 4096000, /* 4MHz PWM clock frequency. */
|
||||
.period = 4096, /* Initial PWM period 1ms. */
|
||||
.frequency = 32e6, // Run at full processor speed
|
||||
.period = (1<<10), // 11 bits of data to give a 16 kHz update rate
|
||||
.callback = NULL,
|
||||
.channels = {
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, NULL},
|
||||
|
@ -39,32 +86,22 @@ static PWMConfig spkr = {
|
|||
{PWM_OUTPUT_DISABLED, NULL}
|
||||
},
|
||||
.cr2 = 0,
|
||||
.dier = STM32_TIM_DIER_CC1DE, // DMA request on channel 1 compare match
|
||||
.dier = 0,
|
||||
};
|
||||
|
||||
const uint32_t dma_config = 0
|
||||
| STM32_DMA_CR_DIR_M2P // Memory to peripheral
|
||||
| STM32_DMA_CR_CIRC // Circular buffer
|
||||
| STM32_DMA_CR_MINC // Memory Increment Mode
|
||||
| STM32_DMA_CR_PSIZE_WORD // 16-bit peripheral space
|
||||
| STM32_DMA_CR_MSIZE_WORD // 16-bit buffer width in RAM
|
||||
| STM32_DMA_CR_PL(3) // High priority
|
||||
| STM32_DMA_CR_TCIE // Transfer Complete Interrupt
|
||||
| STM32_DMA_CR_HTIE // Half-transfer Interrupt
|
||||
;
|
||||
static GPTConfig gpt_config = {
|
||||
.callback = speaker_callback,
|
||||
};
|
||||
|
||||
void speakerStart(void){
|
||||
static stm32_dma_stream_t * stream =
|
||||
dmaStreamAlloc(STM32_PWM_TIM3_DMA_STREAM, 2, speaker_callback, NULL);
|
||||
dmaStreamSetPeripheral(stream, &TIM3->DMAR);
|
||||
dmaStreamSetMemory0(stream, spkr_dma_buf);
|
||||
dmaStreamSetTransactionSize(stream, spkr_buffer_len);
|
||||
dmaStreamSetMode(stream, dma_config);
|
||||
dmaStreamEnable(stream);
|
||||
void speakerStart(float sample_rate){
|
||||
pwmStart(&PWMD3, &spkr);
|
||||
gpt_config.frequency = sample_rate;
|
||||
gptStart(&GPTD14, &gpt_config);
|
||||
gptStartContinuous(&GPTD14, 1);
|
||||
}
|
||||
|
||||
void speakerStop(void){
|
||||
pwmStop(&PWMD3);
|
||||
dmaStreamDisable(stream);
|
||||
gptStopTimer(&GPTD14);
|
||||
gptStop(&GPTD14);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,41 @@
|
|||
#include "hal.h"
|
||||
#include <stdint.h>
|
||||
|
||||
const size_t spkr_buffer_len = 64;
|
||||
/// The number of samples in one fifo element. This is used to reduce overhead.
|
||||
static const size_t spkr_buffer_len = 16;
|
||||
/// The number of elements in the fifo. This must be at least 3.
|
||||
static const size_t spkr_fifo_len = 4;
|
||||
|
||||
void speakerStart(void);
|
||||
/** Starts the speaker PWM
|
||||
*
|
||||
* @param sample_rate How quickly you want the samples to be played.
|
||||
*/
|
||||
void speakerStart(float sample_rate);
|
||||
/* Stops the speaker PWM
|
||||
*
|
||||
*/
|
||||
void speakerStop(void);
|
||||
void speakerUpdate(int16_t data, int len);
|
||||
|
||||
/** Copies data into speaker fifo
|
||||
*
|
||||
* @param data 16-bit signed data to write to speaker. Note that the audio might not be 16-bit resolution
|
||||
* @param len The number of samples top copy
|
||||
*
|
||||
* @returns MSG_OK on sucess, MSG_TIMEOUT if not all data fit
|
||||
*/
|
||||
msg_t speakerUpdate(int16_t * data, int len);
|
||||
|
||||
|
||||
/** Copies audio data to PWM
|
||||
*
|
||||
* This function is called via a timer callback, and should
|
||||
* not be called explicitly under normal circumstances.
|
||||
*/
|
||||
void speaker_callback(GPTDriver *gpt);
|
||||
|
||||
/** Allocates memory for speaker fifo
|
||||
*
|
||||
* This function must only be called once.
|
||||
* It is not automatically called by halInit().
|
||||
*/
|
||||
void speakerInit(void);
|
||||
|
|
|
@ -5,16 +5,20 @@
|
|||
* Author: marshal
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hilbert.h"
|
||||
|
||||
int16_t hilbert19(int16_t * src){
|
||||
const size_t M = 19/2;
|
||||
const int32_t coef[M/2+1];
|
||||
/* Hilbert coeffecients with a hamming window
|
||||
* Values are 2/pi, 2/3pi, 2/7pi, 2/9pi, etc
|
||||
* Stored in 15.16 fixed-point
|
||||
*/
|
||||
const int16_t coef[] = {40564, 10709, 3839, 1118, 371};
|
||||
int32_t sum;
|
||||
sum = (src[M-1]-src[M+1]) * coef[0];
|
||||
sum += (src[M-3]-src[M+3]) * coef[1];
|
||||
sum += (src[M-5]-src[M+5]) * coef[2];
|
||||
sum += (src[M-7]-src[M+7]) * coef[3];
|
||||
sum += (src[M-9]-src[M+9]) * coef[4];
|
||||
sum = (src[M-1]-src[M+1]) * (int32_t)coef[0];
|
||||
sum += (src[M-3]-src[M+3]) * (int32_t)coef[1];
|
||||
sum += (src[M-5]-src[M+5]) * (int32_t)coef[2];
|
||||
sum += (src[M-7]-src[M+7]) * (int32_t)coef[3];
|
||||
sum += (src[M-9]-src[M+9]) * (int32_t)coef[4];
|
||||
return sum>>16;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/** 19-element Hilbert transform
|
||||
*
|
||||
* Generated with hamming window
|
||||
*/
|
||||
int16_t hilbert19(int16_t * src);
|
|
@ -8,10 +8,16 @@
|
|||
#include "rx.h"
|
||||
#include "ssb.h"
|
||||
#include "../drivers/speaker.h"
|
||||
#include "../drivers/si5351.h"
|
||||
|
||||
/** Mailbox for received data */
|
||||
mailbox_t new_sample;
|
||||
|
||||
struct{
|
||||
float frequency;
|
||||
enum radio_mode mode;
|
||||
}rx_cfg;
|
||||
|
||||
THD_WORKING_AREA(waradio_rx, 128);
|
||||
THD_FUNCTION(radio_rx, arg){
|
||||
(void)arg;
|
||||
|
@ -26,9 +32,9 @@ THD_FUNCTION(radio_rx, arg){
|
|||
}
|
||||
/** Process the received data */
|
||||
int16_t out[len];
|
||||
ssb(out, data, len);
|
||||
ssb_rx(out, data, len);
|
||||
/** Fill buffer for audio out */
|
||||
speaker_update(out, len);
|
||||
speakerUpdate(out, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,3 +123,27 @@ void adc_rx_init(void){
|
|||
adcStartConversion(&ADCD1, &qsd_in, samples, len);
|
||||
|
||||
}
|
||||
|
||||
void rxStart(enum radio_mode mode, float frequency){
|
||||
rx_cfg.mode = mode;
|
||||
rx_cfg.frequency = frequency;
|
||||
|
||||
// Set up the clocks with 90-degree phase offset
|
||||
struct synth clk1;
|
||||
synthInit(&clk1, 0, 0);
|
||||
synthSetCarrier(&clk1, frequency);
|
||||
synthStart(&clk1);
|
||||
struct synth clk2 = clk1; // Re-use the frequency calculation
|
||||
synthInit(&clk2, 1, 0);
|
||||
synthWriteConfig(&clk2);
|
||||
synthSetPhase(&clk2, 90);
|
||||
synthStart(&clk2);
|
||||
|
||||
adc_rx_init();
|
||||
speakerStart(5e3); // 5kHz audo sample rate
|
||||
}
|
||||
|
||||
void rxStop(void){
|
||||
adcStop(&ADCD1);
|
||||
speakerStop();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* Created on: Jul 9, 2020
|
||||
* Author: marshal
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "ssb.h"
|
||||
|
||||
void ssb_rx(int16_t * dest, int16_t * src, size_t qty){
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* Created on: Jul 9, 2020
|
||||
* Author: marshal
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/** Single sideband decoder
|
||||
*
|
||||
|
|
Ładowanie…
Reference in New Issue