Added automatic bandfilter switching
Optimized si5351 driver a bit
pull/13/head
ArjanteMarvelde 2022-07-18 21:10:15 +02:00
rodzic de115a043c
commit 53e7c155f5
5 zmienionych plików z 177 dodań i 85 usunięć

9
dsp.h
Wyświetl plik

@ -7,10 +7,15 @@
* Author: Arjan te Marvelde
*
* See dsp.c for more information
*
* HERE THE SELECTION BETWEEN TIME OR FREQUENCY DOMAIN PROCESSING IS MADE
* DO THIS BY SETTING THE #define DSP_FFT TO 0 OR TO 1 RESPECTIVELY
*
*/
// 1 for FFT, 0 for time domain processing
#define DSP_FFT 0
#define DSP_FFT 1
/*

43
hmi.c
Wyświetl plik

@ -115,11 +115,12 @@ char hmi_o_vox [HMI_NVOX][8] = {"NoVOX","VOX-L","VOX-M","VOX-H"}; // Indexed b
char hmi_o_bpf [HMI_NBPF][8] = {"<2.5","2-6","5-12","10-24","20-40"}; // Indexed by
// Map option to setting
int hmi_mode[4] = {MODE_USB, MODE_LSB, MODE_AM, MODE_CW};
int hmi_agc[3] = {AGC_NONE, AGC_SLOW, AGC_FAST};
int hmi_pre[5] = {REL_ATT_30, REL_ATT_20, REL_ATT_10, REL_ATT_00, REL_PRE_10};
int hmi_vox[4] = {VOX_OFF, VOX_LOW, VOX_MEDIUM, VOX_HIGH};
int hmi_bpf[5] = {REL_LPF2, REL_BPF6, REL_BPF12, REL_BPF24, REL_BPF40};
int hmi_mode[HMI_NMODE] = {MODE_USB, MODE_LSB, MODE_AM, MODE_CW};
int hmi_agc[HMI_NAGC] = {AGC_NONE, AGC_SLOW, AGC_FAST};
int hmi_pre[HMI_NPRE] = {REL_ATT_30, REL_ATT_20, REL_ATT_10, REL_ATT_00, REL_PRE_10};
int hmi_vox[HMI_NVOX] = {VOX_OFF, VOX_LOW, VOX_MEDIUM, VOX_HIGH};
int hmi_bpf[HMI_NBPF] = {REL_LPF2, REL_BPF6, REL_BPF12, REL_BPF24, REL_BPF40};
int hmi_state, hmi_option; // Current state and menu option selection
int hmi_sub[HMI_NSTATES] = {4,0,0,3,0,2}; // Stored option selection per state
@ -159,7 +160,7 @@ void hmi_handler(uint8_t event)
switch (event)
{
case HMI_E_ENTER: // Commit current value
SI_SETFREQ(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET)); // Commit frequency
si_setfreq(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET)); // Commit frequency
break;
case HMI_E_ESCAPE: // Enter submenus
hmi_sub[hmi_state] = hmi_option; // Store selection (i.e. digit)
@ -291,8 +292,8 @@ void hmi_init(void)
hmi_option = 4; // Active kHz digit
hmi_freq = 7074000UL; // Initial frequency
SI_SETFREQ(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET)); // Set freq to 7074 kHz (depends on mixer type)
SI_SETPHASE(0, 1); // Set phase to 90deg (depends on mixer type)
si_setfreq(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET)); // Set freq to 7074 kHz (depends on mixer type)
si_setphase(0, 1); // Set phase to 90deg (depends on mixer type)
ptt_state = PTT_DEBOUNCE;
ptt_active = false;
@ -311,6 +312,7 @@ void hmi_init(void)
*/
void hmi_evaluate(void)
{
int band;
char s[32];
// Print top line of display
@ -373,11 +375,28 @@ void hmi_evaluate(void)
if (ptt_state == 0) // Set PTT when debounced level low
ptt_active = true;
/* Set parameters corresponding to latest entered option value */
SI_SETFREQ(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET)); // Always set frequency
if (hmi_update) // Others only when indicated
{
// Frequency might have been changed in hmi_handler, so set anyway
si_setfreq(0, HMI_MULFREQ*(hmi_freq-FC_OFFSET));
// Check bandfilter setting (thanks Alex)
if (hmi_freq < 2500000UL) band = REL_LPF2;
else if (hmi_freq < 6000000UL) band = REL_BPF6;
else if (hmi_freq < 12000000UL) band = REL_BPF12;
else if (hmi_freq < 24000000UL) band = REL_BPF24;
else band = REL_BPF40;
if (band != hmi_bpf[hmi_sub[HMI_S_BPF]]) // Force update when changed
{
hmi_bpf[hmi_sub[HMI_S_BPF]] = band;
hmi_update = true;
}
// Update peripherals according to menu setting
// For frequency si5351 is set directly, HMI top line follows
if (hmi_update)
{
dsp_setmode(hmi_sub[HMI_S_MODE]);
dsp_setvox(hmi_sub[HMI_S_VOX]);
dsp_setagc(hmi_sub[HMI_S_AGC]);

19
relay.c
Wyświetl plik

@ -6,15 +6,18 @@
*
* Two PCF8574 expanders are on the I2C bus, one on the RX and one on the BPF board.
* The RX (0x42) bit assignments:
* 0: Enable -20dB attenuator
* 1: Enable -10dB attenuator
* 2: Enable +10dB pre-amplifier
* 0x03: Enable -20dB and -10dB attenuators
* 0x01: Enable -20dB attenuator
* 0x02: Enable -10dB attenuator
* 0x04: Enable +10dB pre-amplifier
* 0x00: No attenuator or pre-amp
*
* The BPF (0x40) bit assignments:
* 0: Enable LPF 2.5 MHz
* 1: Enable BPF 2.0 - 6.0 MHz
* 2: Enable BPF 5.0 -12.0 MHz
* 3: Enable BPF 10.0 -24.0 MHz
* 4: Enable BPF 20.0 -40.0 MHz
* 0x01: Enable LPF 2.5 MHz
* 0x02: Enable BPF 2.0 - 6.0 MHz
* 0x04: Enable BPF 5.0 -12.0 MHz
* 0x08: Enable BPF 10.0 -24.0 MHz
* 0x10: Enable BPF 20.0 -40.0 MHz
*
*/
#include <stdio.h>

160
si5351.c
Wyświetl plik

@ -9,13 +9,13 @@
Si5351 principle of operation:
==============================
Crystal frequency Fxtal (usually 25MHz) is multiplied in a PLL by MSN to obtain Fvco.
PLL A and B have independent MSN values, the Fout channel i can be derived from either.
Fvco is between 600MHz and 900MHz, but the spec in reality is more relaxed.
Fvco is divided by MSi and Ri to obtain the output frequency Fout.
MSi and Ri are selected to be in the ballpark of desired frequency range.
MSN is then used for tuning.
Only certain values of MSi and Ri are allowed when quadrature output is needed.
Crystal frequency <Fxtal> (usually 25MHz) is multiplied in a PLL by <MSN> to obtain <Fvco>.
PLL A and B have independent MSN values, the <Fout> on channel <i> can be derived from either.
<Fvco> must be between 600MHz and 900MHz, but the spec is more relaxed in reality.
<Fvco> is divided by <MSi> and <Ri> to obtain the output frequency <Fout>.
<MSi> and <Ri> are selected to be in the ballpark of desired frequency range.
<MSN> is then used for tuning <Fout>.
Only certain values of <MSi> and <Ri> are allowed when quadrature output is required.
+-------+ +-------+ +------+
- Fxtal --> | * MSN | -- Fvco --> | / MSi | --> | / Ri | -- Fout -->
@ -27,28 +27,24 @@ Details:
MSN determines: Fvco = Fxtal * (MSN) , where MSN = a + b/c
MSi and Ri determine: Fout = Fvco / (Ri*MSi) , where MSi = a + b/c (different a, b and c)
---Derivation of register values, for MSN and MSi---
---Derivation of the register values, that determine MSN and MSi---
P1 = 128*a + Floor(128*b/c) - 512
P2 = 128*b - c*Floor(128*b/c) (P2 = 0 for MSi integer mode, or calculated for MSN tuning)
P3 = c (P3 = 1 for MSi integer mode, or P3 = 1000000 for MSN tuning)
This VFO implementation assumes PLLA is used for clk0 and clk1, PLLB is used for clk2
This VFO implementation assumes PLLA is used for VFO0 (clk0 and clk1), and PLLB is used for VFO1 (clk2)
Algorithm to get from frequency to synthesizer settings:
(this assumes that the current settings are consistent, i.e. must be initialized at startup)
| calculate new MSN from the desired Fout, based on current Ri and MSi
The algorithm to get from required Fout to synthesizer settings:
| calculate new <MSN> from the desired <Fout>, based on the current <Ri> and <MSi>
| if MSN is still inside [600/Fxtal, 900/Fxtal]
| then
| just write the MSN parameter registers
| just update the MSN related registers
| else
| re-calculate MSi, Ri and MSN from desired Fout
| write the MSi and Ri parameter registers, including phase offset (MSi equals phase offset for 90 deg, use INV to shift 180deg more)
| write the MSN parameter registers
| re-calculate <MSi>, <Ri> and <MSN> from desired <Fout>
| write the <MSi> and <Ri> parameter registers, including phase offset (MSi equals phase offset for 90 deg, use INV to shift 180deg more)
| write the <MSN> parameter registers
| reset PLL
Ri=128 for Fout <1 MHz
Ri= 32 for Fout 1-6 MHz
Ri= 1 for Fout >6 MHz
(this all assumes that the current settings are consistent, i.e. must be initialized at startup)
Some boundary values:
Ri MSi Lo MHz Hi MHz
@ -70,7 +66,10 @@ Quadrature Phase offsets (i.e. delay):
- Offset for MS0 (reg 165) must be 0 (cosine),
- Offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sine),
- Set INV bit (reg 17) to add 180 deg.
NOTE: Phase offsets only work when Ri = 1, this means minimum Fout is 4.762MHz at Fvco = 600MHz. Additional flip/flop dividers are needed to get 80m band frequencies, or Fvco must be tuned below spec.
NOTE: Phase offsets only work when Ri = 1,
This implies that minimum Fout is 4.762MHz at Fvco = 600MHz.
Additional flip/flop dividers are needed to get down to 80m band frequencies, or Fvco must be tuned below spec.
Control Si5351 (see AN619):
@ -159,10 +158,12 @@ Control Si5351 (see AN619):
#define SI_PLL_RESET 177
#define SI_XTAL_LOAD 183
// CLK_OE register 3 values
// CLK_OE register 3 masks
#define SI_CLK0_ENABLE 0b00000001 // Enable clock 0 output
#define SI_CLK1_ENABLE 0b00000010 // Enable clock 1 output
#define SI_CLK2_ENABLE 0b00000100 // Enable clock 2 output
#define SI_VFO0_DISABLE 0b00000011 // Set bits to disable
#define SI_VFO1_DISABLE 0b00000100 // Set bits to disable
// CLKi_CTL register 16, 17, 18 values
// Normally 0x4f for clk 0 and 1, 0x6f for clk 2
@ -184,8 +185,59 @@ Control Si5351 (see AN619):
#define SI_PLL_C 1000000UL // Parameter c for PLL-A and -B setting
typedef struct
{
uint32_t freq; // type can hold up to 4GHz
uint8_t flag; // flag != 0 when update needed
uint8_t phase; // in quarter waves (0, 1, 2, 3)
uint8_t ri; // Ri (1 .. 128)
uint8_t msi; // MSi parameter a (4, 6, 8 .. 126)
double msn; // MSN (24.0 .. 35.9999)
} vfo_t;
vfo_t vfo[2]; // 0: clk0 / clk1 1: clk2
vfo_t vfo[2]; // 0: clk0 & clk1 1: clk2
void si_setfreq(int i, uint32_t f)
{
if ((i<0)||(i>1)) return; // Check VFO range
if (f>150000000) return; // Check frequency range
if (vfo[i].freq == f) return; // Anything to set at all?
vfo[i].freq = f; // Entry checks pass, so do the actual setting
vfo[i].flag = 1;
}
void si_setphase(int i, uint8_t p)
{
if (i!=0) return; // Check VFO range
if (p>3) return; // Check phase range
if (vfo[i].phase == p) return; // Anything to set at all?
vfo[i].phase = p; // Entry checks pass, so do the actual setting
vfo[i].flag = 1;
}
void si_enable(int i, bool en)
{
uint8_t data[2];
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_read_blocking(i2c0, I2C_VFO, &data[1], 1, false);
data[0] = SI_CLK_OE;
if (i==0)
{
data[1] = en ? data[1]&~SI_VFO0_DISABLE : data[1]|SI_VFO0_DISABLE;
}
else
{
data[1] = en ? data[1]&~SI_VFO1_DISABLE : data[1]|SI_VFO1_DISABLE;
}
i2c_write_blocking(i2c0, I2C_VFO, &data[0], 2, false);
}
/*
* read contents of SI5351 registers, from reg to reg+len-1, output in data array
@ -207,23 +259,26 @@ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len)
* Optimize for speed, this may be called with short intervals
* See also SiLabs AN619 section 3.2
*/
void si_setmsn(uint8_t i)
void si_setmsn(int i)
{
uint8_t data[16]; // I2C trx buffer
uint32_t P1, P2; // MSN parameters
uint32_t A;
uint32_t B;
i=(i>0?1:0);
if ((i<0)||(i>1)) return; // Check VFO range
A = (uint32_t)(floor(vfo[i].msn)); // A is integer part of MSN
B = (uint32_t)((vfo[i].msn - (float)A) * SI_PLL_C); // B is C * fraction part of MSN (C is a constant)
P2 = (uint32_t)(floor((float)(128 * B) / (float)SI_PLL_C));
B = (uint32_t)((vfo[i].msn - (double)A) * SI_PLL_C); // B is C * fraction part of MSN (C is a constant)
P2 = (uint32_t)(floor((double)(128 * B) / (double)SI_PLL_C));
P1 = (uint32_t)(128 * A + P2 - 512);
P2 = (uint32_t)(128 * B - SI_PLL_C * P2);
// transfer registers
data[0] = (i==0?SI_SYNTH_PLLA:SI_SYNTH_PLLB);
// transfer PLL A or PLL B registers
if (i==0)
data[0] = SI_SYNTH_PLLA;
else
data[0] = SI_SYNTH_PLLB;
data[1] = (SI_PLL_C & 0x0000FF00) >> 8;
data[2] = (SI_PLL_C & 0x000000FF);
data[3] = (P1 & 0x00030000) >> 16;
@ -252,7 +307,10 @@ void si_setmsi(uint8_t i)
R = vfo[i].ri;
R = (R&0xf0) ? ((R&0xc0)?((R&0x80)?7:6):(R&0x20)?5:4) : ((R&0x0c)?((R&0x08)?3:2):(R&0x02)?1:0); // quick log2(r)
data[0] = (i==0?SI_SYNTH_MS0:SI_SYNTH_MS2);
if (i==0)
data[0] = SI_SYNTH_MS0;
else
data[0] = SI_SYNTH_MS2;
data[1] = 0x00;
data[2] = 0x01;
data[3] = ((P1 & 0x00030000) >> 16) | (R << 4 );
@ -272,18 +330,18 @@ void si_setmsi(uint8_t i)
if (vfo[0].phase&1) // Phase is either 90 or 270 deg?
{
data[0] = SI_CLK1_PHOFF;
data[1] = vfo[0].msi;
data[1] = vfo[0].msi; // offset == MSi for 90deg
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
}
else // Phase is 0 or 180 deg
{
data[0] = SI_CLK1_PHOFF;
data[1] = 0;
data[1] = 0; // offset == 0 for 0deg
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
}
if (vfo[0].phase&2) // Phase is 180 or 270 deg?
{
data[0] = SI_CLK1_CTL;
data[0] = SI_CLK1_CTL; // Then set the invert flag
data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
}
@ -304,13 +362,13 @@ void si_setmsi(uint8_t i)
*/
void si_evaluate(void)
{
float msn;
double msn;
if (vfo[0].flag)
{
msn = (float)(vfo[0].msi); // Re-calculate MSN
msn = msn * (float)(vfo[0].ri);
msn = msn * (float)(vfo[0].freq) / SI_XTAL_FREQ;
msn = (double)(vfo[0].msi); // Re-calculate MSN
msn = msn * (double)(vfo[0].ri);
msn = msn * (double)(vfo[0].freq) / SI_XTAL_FREQ;
if ((msn>=SI_MSN_LO)&&(msn<SI_MSN_HI))
{
vfo[0].msn = msn;
@ -318,15 +376,25 @@ void si_evaluate(void)
}
else
{
vfo[0].ri = (vfo[0].freq<1000000)?128:((vfo[0].freq<3000000)?32:1); // Pre-scale Ri, stretch down Ri=1 range
if ((vfo[0].freq >= 3000000)&&(vfo[0].freq < 6000000)) // Low end of Ri=1 range
vfo[0].msi = (uint8_t)126; // Maximum MSi on Fvco=(4x126)MHz
// Pre-scale Ri, stretch down Ri=1 range
if (vfo[0].freq<1000000)
vfo[0].ri = 128;
else if (vfo[0].freq<3000000)
vfo[0].ri = 32;
else
vfo[0].msi = (uint8_t)(750000000UL / (vfo[0].freq * vfo[0].ri)) & 0xfe; // Calculate MSi on Fvco=750MHz
msn = (float)(vfo[0].msi); // Re-calculate MSN
msn = msn * (float)(vfo[0].ri);
msn = msn * (float)(vfo[0].freq) / SI_XTAL_FREQ;
vfo[0].ri = 1;
// Set MSi
if ((vfo[0].freq >= 3000000)&&(vfo[0].freq < 6000000)) // Handle Low end of Ri=1 range
vfo[0].msi = (uint8_t)126; // Maximum MSi on Fvco=(4x126)MHz
else // Or calculate MSi on Fvco=750MHz
vfo[0].msi = (uint8_t)(750000000UL / (vfo[0].freq * vfo[0].ri)) & 0x000000fe;
msn = (double)(vfo[0].msi); // Re-calculate MSN
msn = msn * (double)(vfo[0].ri);
msn = msn * (double)(vfo[0].freq) / SI_XTAL_FREQ;
vfo[0].msn = msn;
si_setmsn(0);
si_setmsi(0);
}
@ -424,9 +492,9 @@ void si_init(void)
data[1] = 0xa0;
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
// Enable all outputs
// Enable only VFO0 outputs
data[0] = SI_CLK_OE;
data[1] = 0x00;
data[1] = ~SI_VFO0_DISABLE;
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
}

Wyświetl plik

@ -6,31 +6,28 @@
* Created: March 2021
* Author: Arjan
*
* Driver for Si5351A chip.
* VFO 0 is a quadrature clock on outputs 0 and 1,
* VFO 1 is a regular clock on output 2.
*
* The quadrature clock allows to set shared frequency and phase offsets of 0, 90, 180 and 270 deg
* The regular clock just allows to set frequency, phase is ignored
*
* See si5351.c for more information
*
*/
typedef struct
{
uint32_t freq; // type can hold up to 4GHz
uint8_t flag; // flag != 0 when update needed
uint8_t phase; // in quarter waves (0, 1, 2, 3)
uint8_t ri; // Ri (1 .. 128)
uint8_t msi; // MSi parameter a (4, 6, 8 .. 126)
float msn; // MSN (24.0 .. 35.9999)
} vfo_t;
extern vfo_t vfo[2]; // Table contains all control data for three clk outputs, but 0 and 1 are coupled in vfo[0]
#define PH000 0
#define PH090 1
#define PH180 2
#define PH270 3
int si_getreg(uint8_t *data, uint8_t reg, uint8_t len);
void si_init(void);
void si_evaluate(void);
void si_setfreq(int i, uint32_t f);
void si_setphase(int i, uint8_t p);
void si_enable(int i, bool en);
#define SI_GETFREQ(i) ((((i)>=0)&&((i)<2))?vfo[(i)].freq:0)
#define SI_INCFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)<(150000000-(d)))) { vfo[(i)].freq += (d); vfo[(i)].flag = 1;}
#define SI_DECFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)>(d))) { (vfo[(i)].freq) -= (d); vfo[(i)].flag = 1;}
#define SI_SETFREQ(i, f) if ((((i)>=0)&&((i)<2))&&((f)<150000000)) { vfo[(i)].freq = (f); vfo[(i)].flag = 1;}
#define SI_SETPHASE(i, p) if (((i)>=0)&&((i)<2)) {vfo[(i)].phase = ((uint8_t)p)&3; vfo[(i)].flag = 1;}
#endif /* _SI5351_H */