Porównaj commity

...

2 Commity

Autor SHA1 Wiadomość Data
Arjan te Marvelde 3df0a38ecf
V3.09 2022-08-13 16:25:47 +02:00
ArjanteMarvelde a712c1c29b V3.09
Solved I2C issue
Added uSDR.h containing the system wide definitions.
2022-08-13 16:08:08 +02:00
13 zmienionych plików z 181 dodań i 173 usunięć

Wyświetl plik

@ -1,26 +1,26 @@
# uSDR-pico
This Git repository contains a Micro-SDR implementation, based on a RP2040 Pi Pico. The project is highly experimental, foremost intended to investigate how the Pico HW and SDK work with an application like this. Also it is a platform to experiment with digital signal processing techniques. The repository contains the code for an experimental implementation of the control and signal processing for a QSD/QSE based transceiver.
Furthermore, the repository contains the electronic design of some modules that cover the mixing, filtering and RF amplification.
This Git repository contains a Micro-SDR implementation, based on a RP2040 Pi Pico.
The ZIP files contain a consistent package, but the latest code with all the bug fixes and some new features is in the files in the main directory.
Starting with the V3.00 package **uSDR-pico** contains *two signal processing engines*, selectable with a compile switch in dsp.h. The first engine is the old time domain processor, more or less as in V2.00, and the second engine is a new FFT-based processor.
For a more detailed description of the software and the hardware, please refer to the elaborate documentation.
The project is highly experimental, foremost intended to investigate how the Pico HW and SDK work with an application like this. Also, it is a platform to experiment with digital signal processing techniques. The repository contains the code for an experimental implementation of the control and signal processing for a Quadrature Sampling Detector (QSD) and - Exciter (QSE) based transceiver.
For completeness, the repository contains the electronic design of some modules that cover the mixing, filtering and RF amplification, as I have implemented in my prototype. See the *doc* subdirectory for full documentation.
The platform used is a Pi Pico module with an RP2040 processor. This processor has dual cores, running default at 125MHz each, and a very configurable I/O which eases the HW design. The platform can be overclocked, but some functions seem to become unstable when pushed too far.
The software is distributed over the two cores: *core0* takes care of all user I/O and control functions, while *core1* performs all of the signal processing. The *core1* functionality consists of a TX-branch and an RX-branch, each called from a function that is synchronized by a timer every 64usec. Hence the signal processing rythm on *core1* effectively is 15.625kHz.
The ZIP files contain a consistent package, but the latest code with all the bug fixes and some new features is contained in the files in the main directory.
Starting with the V3.00 package **uSDR-pico** contains *two signal processing engines*, selectable with a compile switch in uSDR.h. The first engine is the time domain processor, more or less as in V2.00, and the second engine is a new FFT-based frequency domain processor.
For a more detailed description of the software and the hardware, again refer to the elaborate documentation.
The processor platform is a Pi Pico module, with an RP2040 device. This processor has dual cores running at 125MHz each, and a very configurable I/O which eases the HW design enormously. The platform can be overclocked, but some functions seem to become unstable when pushed too far. It is one of the topics for further investigation, although performance-wise not neccessary at the moment.
The software is distributed over the two cores: *core0* takes care of all user I/O and control functions, while *core1* performs all of the signal processing. The *core1* functionality consists of a TX-branch and an RX-branch, each invoked by a function that is synchronized by a timer every 64usec. Hence the signal processing rythm on *core1* effectively is 15.625kHz.
On *core1* the three ADC channels are continuously sampled at maximum speed in round-robin mode. Samples are therefore taken every 6usec for each channel, maximum jitter between I and Q channels is 2usec, which has a negligible effect in the audio domain.
For the time domain processing the TX and RX functions are called every timeslot, but for the frequency domain processing the samples are collected until half an FFT buffer is filled (512 samples), and hence this happens every 32msec.
For the time domain processing the TX and RX functions are executed within every 64usec timeslot, but for the frequency domain processing the samples are collected until half an FFT buffer is filled (512 samples), and hence this happens every 32msec (in background).
On *core0* the main loop takes care of user I/O, all other controls and the monitor port. There is also a LED flashing timer callback functioning as a heartbeat.
The Pico controls an Si5351A clock module to obtain the switching clock for the QSE and QSD. The module outputs two synchronous square wave clocks on ch 0 and 1, whith selectable phase difference (0, 90, 180 or 270 degrees). The clock on ch2 is free to be used for other goals. The module is controlled over the **i2c0** channel.
The display is a standard 16x2 LCD, but with an I2C interface. The display is connected through the **i2c1** channel, as well as the bus expanders for controlling the various relays.
The Pico controls an Si5351A clock module to obtain the switching clock for the QSE and QSD. The module outputs two synchronous square wave clocks on ch 0 and 1, whith selectable phase difference (0, 90, 180 or 270 degrees). The clock on ch2 is free to be used for other goals. The module is controlled over one of the I2C channels.
The display is a standard 16x2 LCD, but with an I2C interface. The display is connected through the other I2C channel, as well as the bus expanders for controlling the various relays.
## Open issues:
- [ ] implement proper AGC
- [x] implement RSSI and S-meter
- [x] improve FFT-based signal processing
- [x] revisit Si5351A driver
- [x] automatic bandfilter switching
- [ ] improve on TX audio quality
- [ ] implement better AGC
## Installing and using the SDK for Windows:
For setting up the C/C++ build environment for Windows, you can follow the procedure as described in the Raspberry [Getting Started](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) document. This document also refers to a [setup script](https://github.com/ndabas/pico-setup-windows). In case this does not work, follow the instructions below.

91
dsp.c
Wyświetl plik

@ -24,11 +24,11 @@
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "uSDR.h"
#include "dsp.h"
#include "hmi.h"
#include "fix_fft.h"
#define GP_PTT 15 // PTT pin 20 (GPIO 15)
volatile bool tx_enabled; // TX branch active
volatile uint32_t dsp_overrun; // Overrun counter
@ -354,14 +354,14 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
if (tx_enabled)
{
A_buf[dsp_active][dsp_tick] = (int16_t)(tx_agc*adc_result[2]);
pwm_set_gpio_level(21, I_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output I to DAC
pwm_set_gpio_level(20, Q_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output Q to DAC
pwm_set_gpio_level(DAC_I, I_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output I to DAC
pwm_set_gpio_level(DAC_Q, Q_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output Q to DAC
}
else
{
I_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[1]);
Q_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[0]);
pwm_set_gpio_level(22, A_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output A to DAC
pwm_set_gpio_level(DAC_A, A_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output A to DAC
}
// When sample buffer is full, move pointer to next and signal the DSP loop
@ -378,12 +378,12 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
if (tx_enabled)
{
a_sample = tx_agc * adc_result[2]; // Store A for DSP use
pwm_set_gpio_level(21, i_sample); // Output I to DAC
pwm_set_gpio_level(20, q_sample); // Output Q to DAC
pwm_set_gpio_level(DAC_I, i_sample); // Output I to DAC
pwm_set_gpio_level(DAC_Q, q_sample); // Output Q to DAC
}
else
{
pwm_set_gpio_level(22, a_sample); // Output Q to DAC
pwm_set_gpio_level(DAC_A, a_sample); // Output Q to DAC
q_sample = rx_agc * adc_result[0]; // Store Q for DSP use
i_sample = rx_agc * adc_result[1]; // Store I for DSP use
}
@ -413,15 +413,15 @@ void __not_in_flash_func(dsp_loop)()
* default mode is free running,
* A and B pins are output
*/
gpio_set_function(20, GPIO_FUNC_PWM); // GP20 is PWM for Q DAC (Slice 2, Channel A)
gpio_set_function(21, GPIO_FUNC_PWM); // GP21 is PWM for I DAC (Slice 2, Channel B)
dac_iq = pwm_gpio_to_slice_num(20); // Get PWM slice for GP20 (Same for GP21)
gpio_set_function(DAC_Q, GPIO_FUNC_PWM); // GP20 is PWM for Q DAC (Slice 2, Channel A)
gpio_set_function(DAC_I, GPIO_FUNC_PWM); // GP21 is PWM for I DAC (Slice 2, Channel B)
dac_iq = pwm_gpio_to_slice_num(DAC_Q); // Get PWM slice for GP20 (Same for GP21)
pwm_set_clkdiv_int_frac (dac_iq, 1, 0); // clock divide by 1: full system clock
pwm_set_wrap(dac_iq, DAC_RANGE-1); // Set cycle length; nr of counts until wrap, i.e. 125/DAC_RANGE MHz
pwm_set_enabled(dac_iq, true); // Set the PWM running
gpio_set_function(22, GPIO_FUNC_PWM); // GP22 is PWM for Audio DAC (Slice 3, Channel A)
dac_audio = pwm_gpio_to_slice_num(22); // Find PWM slice for GP22
gpio_set_function(DAC_A, GPIO_FUNC_PWM); // GP22 is PWM for Audio DAC (Slice 3, Channel A)
dac_audio = pwm_gpio_to_slice_num(DAC_A); // Find PWM slice for GP22
pwm_set_clkdiv_int_frac (dac_audio, 1, 0); // clock divide by 1: full system clock
pwm_set_wrap(dac_audio, DAC_RANGE-1); // Set cycle length; nr of counts until wrap, i.e. 125/DAC_RANGE MHz
pwm_set_enabled(dac_audio, true); // Set the PWM running
@ -431,9 +431,9 @@ void __not_in_flash_func(dsp_loop)()
* samples are stored in array through IRQ callback
*/
adc_init(); // Initialize ADC to known state
adc_gpio_init(26); // GP26 is ADC 0 for Q channel
adc_gpio_init(27); // GP27 is ADC 1 for I channel
adc_gpio_init(28); // GP28 is ADC 2 for Audio channel
adc_gpio_init(ADC_Q); // ADC GPIO for Q channel
adc_gpio_init(ADC_I); // ADC GPIO for I channel
adc_gpio_init(ADC_A); // ADC GPIO for Audio channel
adc_set_round_robin(0x01+0x02+0x04); // Sequence ADC 0-1-2 (GP 26, 27, 28) free running
adc_select_input(0); // Start with ADC0
adc_fifo_setup(true,true,3,false,false); // IRQ result, DMA req, fifo thr=3: xfer per 3 x 16 bits
@ -506,7 +506,7 @@ void __not_in_flash_func(dsp_loop)()
rx(); // Do RX signal processing
}
/////// This is a trap, ptt remains active after once asserted: to be checked!
/** !!! This is a trap, ptt remains active after once asserted: TO BE CHECKED! **/
tx_enabled = vox_active || ptt_active; // Check RX or TX
#if DSP_FFT == 1
@ -527,63 +527,4 @@ void dsp_init()
/* DMA EXAMPLE, should convert to chained DMA to reload after 3 words
// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.
adc_gpio_init(26 + CAPTURE_CHANNEL);
adc_init();
adc_select_input(CAPTURE_CHANNEL);
adc_fifo_setup(
true, // Write each completed conversion to the sample FIFO
true, // Enable DMA data request (DREQ)
1, // DREQ (and IRQ) asserted when at least 1 sample present
false, // We won't see the ERR bit because of 8 bit reads; disable.
true // Shift each sample to 8 bits when pushing to FIFO
);
// Divisor of 0 -> full speed. Free-running capture with the divider is
// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`
// cycles (div not necessarily an integer). Each conversion takes 96
// cycles, so in general you want a divider of 0 (hold down the button
// continuously) or > 95 (take samples less frequently than 96 cycle
// intervals). This is all timed by the 48 MHz ADC clock.
adc_set_clkdiv(0);
printf("Arming DMA\n");
sleep_ms(1000);
// Set up the DMA to start transferring data as soon as it appears in FIFO
uint dma_chan = dma_claim_unused_channel(true);
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
// Reading from constant address, writing to incrementing byte addresses
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
channel_config_set_read_increment(&cfg, false);
channel_config_set_write_increment(&cfg, true);
// Pace transfers based on availability of ADC samples
channel_config_set_dreq(&cfg, DREQ_ADC);
dma_channel_configure(dma_chan, &cfg,
capture_buf, // dst
&adc_hw->fifo, // src
CAPTURE_DEPTH, // transfer count
true // start immediately
);
printf("Starting capture\n");
adc_run(true);
// Once DMA finishes, stop any new conversions from starting, and clean up
// the FIFO in case the ADC was still mid-conversion.
dma_channel_wait_for_finish_blocking(dma_chan);
printf("Capture finished\n");
adc_run(false);
adc_fifo_drain();
// Print samples to stdout so you can display them in pyplot, excel, matlab
for (int i = 0; i < CAPTURE_DEPTH; ++i) {
printf("%-3d, ", capture_buf[i]);
if (i % 10 == 9)
printf("\n");
}
*/

4
dsp.h
Wyświetl plik

@ -13,10 +13,6 @@
*
*/
#define DSP_FFT 1
/*
* Callback timeout is TIM_US, value in usec

Wyświetl plik

@ -53,7 +53,7 @@
*
*/
#include "uSDR.h"
/*
* FFT buffer allocation

Wyświetl plik

@ -28,6 +28,7 @@
*
*/
#include "uSDR.h"
/*
* Low pass FIR filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/)

13
hmi.c
Wyświetl plik

@ -29,10 +29,11 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "uSDR.h"
#include "lcd.h"
#include "hmi.h"
#include "dsp.h"
@ -40,15 +41,8 @@
#include "relay.h"
/*
* GPIO assignments
* GPIO masks
*/
#define GP_ENC_A 2
#define GP_ENC_B 3
#define GP_AUX_0 6 // Enter, Confirm
#define GP_AUX_1 7 // Escape, Cancel
#define GP_AUX_2 8 // Left move
#define GP_AUX_3 9 // Right move
#define GP_PTT 15
#define GP_MASK_IN ((1<<GP_ENC_A)|(1<<GP_ENC_B)|(1<<GP_AUX_0)|(1<<GP_AUX_1)|(1<<GP_AUX_2)|(1<<GP_AUX_3)|(1<<GP_PTT))
#define GP_MASK_PTT (1<<GP_PTT)
@ -340,7 +334,6 @@ void hmi_evaluate(void)
dsp_setvox(hmi_sub[HMI_S_VOX]);
dsp_setagc(hmi_sub[HMI_S_AGC]);
relay_setband(hmi_bpf[hmi_sub[HMI_S_BPF]]);
sleep_ms(1); // I2C doesn't work without...
relay_setattn(hmi_pre[hmi_sub[HMI_S_PRE]]);
hmi_update = false;
}

2
hmi.h
Wyświetl plik

@ -9,8 +9,6 @@
* See hmi.c for more information
*/
#define CARRIER_OFFSET 3500
extern bool ptt_active;
void hmi_init(void);

49
lcd.c
Wyświetl plik

@ -4,11 +4,10 @@
* Created: Mar 2021
* Author: Arjan te Marvelde
*
* --> Set I2C address below!
* --> Select LCD_TYPE below!
*
* Driver for 16x2 HD44780 based LCD displays.
* There exist many different types, so you may need to adapt some of the code.
* => LCD Address and Type are chosen in uSDR.h!
*
* This file contains the driver for 16x2 HD44780 based LCD displays.
* Many different types exist, so you may need to adapt some of the code.
* Most notably the startup sequence and the way bytes are sent over the I2C interface.
* But also the register mappings as described below.
*
@ -46,19 +45,10 @@
#include "hardware/i2c.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "uSDR.h"
#include "lcd.h"
/** User selectable definitions **/
// Set I2C address for your device
#define I2C_LCD 0x3E // Grove: 0x3E, 8574 backpack: 0x20..0x27
// Select LCD type to match your device,
// or define a new one when code changes are needed.
#define LCD_1804 0 // Seeed / Grove
#define LCD_8574_ADA 1 // Adafruit I2C backpack
#define LCD_8574_GEN 2 // Generic I2C backpack
#define LCD_TYPE LCD_1804
/** Generic HD44780 interface **/
// commands
@ -100,19 +90,20 @@
#define LCD_5x8DOTS 0x00
/** I2C interface specific mappings **/
// 1804-based specific bitmasks
// 1804-based specific bitmasks (Seeed/Grove)
#define LCD_COMMAND 0x80
#define LCD_DATA 0x40
#define LCD_INIT_1804 (LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE | LCD_5x8DOTS)
// 8574-based specific bitmasks
// 8574-based specific bitmasks (Adafruit)
#define LCD_COMMAND_ADA 0x00
#define LCD_DATA_ADA 0x02
#define LCD_BACKLIGHT_ADA 0x80
#define LCD_ENABLE_ADA 0x04
#define LCD_INIT_ADA (LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS)
// 8574-based specific bitmasks (Generic)
#define LCD_COMMAND_GEN 0x00
#define LCD_DATA_GEN 0x01
#define LCD_ENABLE_GEN 0x04
@ -131,9 +122,6 @@
#endif
/** Other definitions **/
#define LCD_DELAY 100 // Delay for regular write
/*
* User defined (CGRAM) characters
@ -164,8 +152,7 @@ void lcd_sendbyte(uint8_t command, uint8_t data)
// Write command/data flag and data byte
txdata[0] = (command?LCD_COMMAND:LCD_DATA);
txdata[1] = data;
i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, txdata, 2, false);
#endif
#if LCD_TYPE == LCD_8574_ADA
@ -175,15 +162,15 @@ void lcd_sendbyte(uint8_t command, uint8_t data)
// Write high nibble
high |= LCD_ENABLE_ADA;
i2c_write_blocking(i2c1, I2C_LCD, &high, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &high, 1, false);
high &= ~LCD_ENABLE_ADA;
i2c_write_blocking(i2c1, I2C_LCD, &high, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &high, 1, false);
// Write low nibble
low |= LCD_ENABLE_ADA;
i2c_write_blocking(i2c1, I2C_LCD, &low, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &low, 1, false);
low &= ~LCD_ENABLE_ADA;
i2c_write_blocking(i2c1, I2C_LCD, &low, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &low, 1, false);
#endif
#if LCD_TYPE == LCD_8574_GEN
@ -193,15 +180,15 @@ void lcd_sendbyte(uint8_t command, uint8_t data)
// Write high nibble
high |= LCD_ENABLE_GEN;
i2c_write_blocking(i2c1, I2C_LCD, &high, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &high, 1, false);
high &= ~LCD_ENABLE_GEN;
i2c_write_blocking(i2c1, I2C_LCD, &high, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &high, 1, false);
// Write low nibble
low |= LCD_ENABLE_GEN;
i2c_write_blocking(i2c1, I2C_LCD, &low, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &low, 1, false);
low &= ~LCD_ENABLE_GEN;
i2c_write_blocking(i2c1, I2C_LCD, &low, 1, false); sleep_us(LCD_DELAY);
i2c_put_data(i2c1, I2C_LCD, &low, 1, false);
#endif
}

Wyświetl plik

@ -14,6 +14,7 @@
#include <string.h>
#include "pico/stdlib.h"
#include "uSDR.h"
#include "lcd.h"
#include "si5351.h"
#include "dsp.h"
@ -21,12 +22,15 @@
#include "monitor.h"
// Some special character ASCII codes
#define CR 13
#define LF 10
#define SP 32
#define CMD_LEN 80
#define CMD_ARGS 16
char mon_cmd[CMD_LEN+1]; // Command string buffer
char *argv[CMD_ARGS]; // Argument pointers
int nargs; // Nr of arguments

17
relay.c
Wyświetl plik

@ -25,12 +25,11 @@
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "uSDR.h"
#include "relay.h"
/* I2C address and pins */
#define I2C_RX 0x21
#define I2C_BPF 0x20
void relay_setband(int val)
@ -39,8 +38,8 @@ void relay_setband(int val)
int ret;
data[0] = ((uint8_t)val)&0x1f;
if (i2c_write_blocking(i2c1, I2C_BPF, data, 1, false) < 0)
i2c_write_blocking(i2c1, I2C_BPF, data, 1, false);
if (i2c_put_data(i2c1, I2C_BPF, data, 1, false) < 0)
i2c_put_data(i2c1, I2C_BPF, data, 1, false);
sleep_ms(1);
}
@ -49,7 +48,7 @@ int relay_getband(void)
uint8_t data[2];
int ret;
ret = i2c_read_blocking(i2c1, I2C_BPF, data, 1, false);
ret = i2c_get_data(i2c1, I2C_BPF, data, 1, false);
if (ret>=0)
ret=data[0];
return(ret);
@ -60,8 +59,8 @@ void relay_setattn(int val)
uint8_t data[2];
data[0] = ((uint8_t)val)&0x07;
if (i2c_write_blocking(i2c1, I2C_RX, data, 1, false) < 0)
i2c_write_blocking(i2c1, I2C_RX, data, 1, false);
if (i2c_put_data(i2c1, I2C_RX, data, 1, false) < 0)
i2c_put_data(i2c1, I2C_RX, data, 1, false);
sleep_ms(1);
}
@ -70,7 +69,7 @@ int relay_getattn(void)
uint8_t data[2];
int ret;
ret = i2c_read_blocking(i2c1, I2C_RX, data, 1, false);
ret = i2c_get_data(i2c1, I2C_RX, data, 1, false);
if (ret>=0)
ret=data[0];
return(ret);

Wyświetl plik

@ -149,9 +149,10 @@ Control Si5351 (see AN619):
#include "hardware/i2c.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "uSDR.h"
#include "si5351.h"
#define I2C_VFO 0x60 // I2C address
// SI5351 register address definitions
#define SI_CLK_OE 3
@ -234,14 +235,14 @@ void si_enable(int i, bool en)
if ((i<0)||(i>1)) return; // Check VFO range
data[0] = SI_CLK_OE; // Read OE register
i2c_write_blocking(i2c0, I2C_VFO, &data[0], 1, true);
i2c_put_data(i2c0, I2C_VFO, &data[0], 1, true);
i2c_read_blocking(i2c0, I2C_VFO, &data[1], 1, false);
if (i==0)
data[1] = en ? data[1]&~SI_VFO0_DISABLE : data[1]|SI_VFO0_DISABLE; // clk0 and clk1
else
data[1] = en ? data[1]&~SI_VFO1_DISABLE : data[1]|SI_VFO1_DISABLE; // clk2
i2c_write_blocking(i2c0, I2C_VFO, &data[0], 2, false);
i2c_put_data(i2c0, I2C_VFO, &data[0], 2, false);
}
/*
@ -251,9 +252,9 @@ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len)
{
int ret;
ret = i2c_write_blocking(i2c0, I2C_VFO, &reg, 1, true);
ret = i2c_put_data(i2c0, I2C_VFO, &reg, 1, true);
if (ret<0) printf ("I2C write error\n");
ret = i2c_read_blocking(i2c0, I2C_VFO, data, len, false);
ret = i2c_get_data(i2c0, I2C_VFO, data, len, false);
if (ret<0) printf ("I2C read error\n");
return(len);
}
@ -299,7 +300,7 @@ void si_setmsn(int i)
data[6] = ((SI_PLL_C & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[7] = (P2 & 0x0000FF00) >> 8;
data[8] = (P2 & 0x000000FF);
i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
i2c_put_data(i2c0, I2C_VFO, data, 9, false);
}
/*
@ -338,59 +339,56 @@ void si_setmsi(int i)
data[6] = 0x00;
data[7] = 0x00;
data[8] = 0x00;
i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
i2c_put_data(i2c0, I2C_VFO, data, 9, false);
// If vfo[0] also set clk 1 and phase offset, (integer mode(?) and high drive current for low phase noise).
if (i==0)
{
data[0] = SI_SYNTH_MS1; // Same data in synthesizer
i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
i2c_put_data(i2c0, I2C_VFO, data, 9, false);
if ((vfo[0].phase==PH090)||(vfo[0].phase==PH270)) // Phase is 90 or 270 deg?
{
data[0] = SI_CLK1_PHOFF;
data[1] = vfo[0].msi; // offset == MSi for 90deg
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
}
else // Phase is 0 or 180 deg
{
data[0] = SI_CLK1_PHOFF;
data[1] = 0; // offset == 0 for 0deg
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
}
sleep_us(500);
if ((vfo[0].phase==PH180)||(vfo[0].phase==PH270)) // Phase is 180 or 270 deg?
{
data[0] = SI_CLK0_CTL; // set the invert flag
data[1] = SI_VFO0CTL; // CLK0: nonINV
data[2] = SI_VFO0CTL | SI_CLK_INV; // CLK1: INV
i2c_write_blocking(i2c0, I2C_VFO, data, 3, false);
i2c_put_data(i2c0, I2C_VFO, data, 3, false);
}
else
{
data[0] = SI_CLK0_CTL; // set the invert flag
data[1] = SI_VFO0CTL; // CLK0: nonINV
data[2] = SI_VFO0CTL; // CLK1: nonINV
i2c_write_blocking(i2c0, I2C_VFO, data, 3, false);
i2c_put_data(i2c0, I2C_VFO, data, 3, false);
}
// Reset PLL A (use with care, this causes a click)
sleep_us(500);
data[0] = SI_PLL_RESET;
data[1] = SI_PLLA_RST|SI_PLLB_RST;
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
}
else
{
data[0] = SI_CLK2_CTL; // set the invert flag
data[1] = SI_VFO1CTL; // CLK2: nonINV
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
// Reset PLL B (use with care, this causes a click)
sleep_us(500);
data[0] = SI_PLL_RESET;
data[1] = SI_PLLA_RST|SI_PLLB_RST;
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
}
}
@ -457,14 +455,14 @@ void si_init(void)
// Disable spread spectrum (startup state is undefined)
data[0] = SI_SS_EN;
data[1] = 0x00;
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
i2c_put_data(i2c0, I2C_VFO, data, 2, false);
// First time init of clock control registers
data[0] = SI_CLK0_CTL;
data[1] = SI_VFO0CTL;
data[2] = SI_VFO0CTL;
data[3] = SI_VFO1CTL;
i2c_write_blocking(i2c0, I2C_VFO, data, 4, false);
i2c_put_data(i2c0, I2C_VFO, data, 4, false);
// Initialize VFO values
vfo[0].freq = 7074000;

29
uSDR.c
Wyświetl plik

@ -22,6 +22,7 @@
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "uSDR.h"
#include "hmi.h"
#include "lcd.h"
#include "dsp.h"
@ -30,15 +31,30 @@
#include "relay.h"
#define I2C0_SDA 16
#define I2C0_SCL 17
#define I2C1_SDA 18
#define I2C1_SCL 19
/*
* Wrappers around i2c_write_blocking() and i2c_read_blocking()
* The SDK functions return too soon, potentially causing overlapping calls
*/
int i2c_put_data(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop)
{
int r = i2c_write_blocking(i2c, addr, src, len, nostop);
sleep_us(I2C_LINGER_US);
return(r);
}
int i2c_get_data(i2c_inst_t *i2c, uint8_t addr, uint8_t *dst, size_t len, bool nostop)
{
int r = i2c_read_blocking(i2c, addr, dst, len, nostop);
sleep_us(I2C_LINGER_US);
return(r);
}
/*
* LED TIMER definition and callback routine
*/
#define LED_MS 1000
struct repeating_timer led_timer;
bool led_callback(struct repeating_timer *t)
{
@ -49,11 +65,11 @@ bool led_callback(struct repeating_timer *t)
return true;
}
/*
* Scheduler callback function.
* This executes every LOOP_MS.
*/
#define LOOP_MS 100
semaphore_t loop_sem;
struct repeating_timer loop_timer;
bool loop_callback(struct repeating_timer *t)
@ -66,6 +82,7 @@ bool loop_callback(struct repeating_timer *t)
int main()
{
/*
* Main loop rnning on Core 0
* Optional: increase core voltage (normally 1.1V)
* Optional: overclock the CPU to 250MHz (normally 125MHz)
* Note that clk_peri (e.g. I2C) is derived from the SYS PLL

74
uSDR.h 100644
Wyświetl plik

@ -0,0 +1,74 @@
#ifndef __USDR_H__
#define __USDR_H__
/*
* uSDR.h
*
* Created: Aug 2022
* Author: Arjan te Marvelde
*
* This file contains the system-wide definitions and platform services.
*
*/
#include "hardware/i2c.h"
/* Set this to 1 when FFT engine must be used */
#define DSP_FFT 1
/* GPIO (pin) assignments */
#define GP_ENC_A 2 // Pin 4: Encoder channel A
#define GP_ENC_B 3 // Pin 5: Encoder channel B
#define GP_AUX_0 6 // Pin 9: Enter, Confirm
#define GP_AUX_1 7 // Pin 10: Escape, Cancel
#define GP_AUX_2 8 // Pin 11: Left move
#define GP_AUX_3 9 // Pin 12: Right move
#define GP_PTT 15 // Pin 20: PTT line (low is active)
#define I2C0_SDA 16 // Pin 21: I2C channel 0 - data
#define I2C0_SCL 17 // Pin 22: I2C channel 0 - clock
#define I2C1_SDA 18 // Pin 24: I2C channel 1 - data
#define I2C1_SCL 19 // Pin 25: I2C channel 1 - clock
#define DAC_Q 20 // Pin 26: PWM DAC Q channel
#define DAC_I 21 // Pin 27: PWM DAC I channel
#define DAC_A 22 // Pin 29: PWM DAC Audio channel
#define ADC_Q 26 // Pin 31: ADC 0
#define ADC_I 27 // Pin 32: ADC 1
#define ADC_A 28 // Pin 34: ADC 2
/* Timer values */
#define LED_MS 1000 // LED flashing, half cycle duration
#define LOOP_MS 100 // Core 0 main loop timer (see also uSDR.c)
/* I2C addresses */
#define I2C_RX 0x21 // Expander on Rx board
#define I2C_BPF 0x20 // Expander on Filter board
#define I2C_VFO 0x60 // Si5351A
#define I2C_LCD 0x3E // Grove: 0x3E, 8574 backpack range: 0x20..0x27
/* I2C wrapper functions (blocking write and read) */
#define I2C_LINGER_US 200 // Linger time added after i2c SDK functions
int i2c_put_data(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop);
int i2c_get_data(i2c_inst_t *i2c, uint8_t addr, uint8_t *dst, size_t len, bool nostop);
/* LCD type selection (see also lcd.c) */
#define LCD_1804 0 // Type 0: Seeed / Grove
#define LCD_8574_ADA 1 // Type 1: Adafruit I2C backpack
#define LCD_8574_GEN 2 // Type 2: Generic I2C backpack
#define LCD_TYPE LCD_1804 // Active selection
#endif