si5351.c and si5351.h

Cleanup and optimize  code
Add comments, fix definitions
Fix rounding errors
Fix band 1 stability

mcuconf.h
Set I2C bus clock to SYSCLK (more fast)
Apply 400kHz bus I2C clock timings for 8MHz and 48Mhz clock

main.c
Remove and reset some variables
Add separate sweep for calibration (allow calibrate if sweep paused)

Increase main thread stack (need for run calibrate, possibly need execute some commands in sweep threads for reduce stack usage)
This commit is contained in:
DiSlord 2020-03-07 14:54:51 +03:00
parent b77e1d6680
commit a43b6e3acc
7 changed files with 289 additions and 341 deletions

View file

@ -64,13 +64,13 @@ endif
# Stack size to be allocated to the Cortex-M process stack. This stack is
# the stack used by the main() thread.
ifeq ($(USE_PROCESS_STACKSIZE),)
USE_PROCESS_STACKSIZE = 0x200
USE_PROCESS_STACKSIZE = 0x300
endif
# Stack size to the allocated to the Cortex-M main/exceptions stack. This
# stack is used for processing interrupts and exceptions.
ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
USE_EXCEPTIONS_STACKSIZE = 0x200
USE_EXCEPTIONS_STACKSIZE = 0x100
endif
#

152
main.c
View file

@ -69,19 +69,17 @@ static void transform_domain(void);
static MUTEX_DECL(mutex);
int32_t frequency_offset = 5000;
uint32_t frequency = 10000000;
int8_t drive_strength = DRIVE_STRENGTH_AUTO;
int8_t sweep_enabled = TRUE;
volatile int8_t sweep_once = FALSE;
int8_t cal_auto_interpolate = TRUE;
uint16_t redraw_request = 0; // contains REDRAW_XXX flags
// Obsolete enable/disable calibration interpolation (always on)
#define cal_auto_interpolate TRUE
static int32_t frequency_offset = 5000;
static uint32_t frequency = 10000000;
static int8_t drive_strength = DRIVE_STRENGTH_AUTO;
volatile int8_t sweep_mode = SWEEP_MODE_ENABLED;
volatile uint8_t redraw_request = 0; // contains REDRAW_XXX flags
int16_t vbat = 0;
//
// Profile stack usage (enable threads command by def ENABLE_THREADS_COMMAND) show:
// Stack maximum usage = 576 bytes, free stack = 64 bytes
//
static THD_WORKING_AREA(waThread1, 640);
static THD_FUNCTION(Thread1, arg)
{
@ -90,11 +88,10 @@ static THD_FUNCTION(Thread1, arg)
while (1) {
bool completed = false;
if (sweep_enabled || sweep_once) {
if (sweep_mode&(SWEEP_MODE_ENABLED|SWEEP_MODE_RUN_ONCE)) {
chMtxLock(&mutex);
// Sweep require 8367 system tick
completed = sweep(true);
sweep_once = FALSE;
sweep_mode&=~SWEEP_MODE_RUN_ONCE;
chMtxUnlock(&mutex);
} else {
si5351_disable_output();
@ -102,10 +99,8 @@ static THD_FUNCTION(Thread1, arg)
}
chMtxLock(&mutex);
// Ui and render require 800 system tick
ui_process();
if (sweep_enabled) {
if (sweep_mode&SWEEP_MODE_ENABLED) {
if (vbat != -1) {
adc_stop(ADC1);
vbat = adc_vbat_read(ADC1);
@ -135,23 +130,10 @@ static THD_FUNCTION(Thread1, arg)
}
}
static inline void
pause_sweep(void)
{
sweep_enabled = FALSE;
}
static inline void
resume_sweep(void)
{
sweep_enabled = TRUE;
}
void
toggle_sweep(void)
{
sweep_enabled = !sweep_enabled;
}
static inline void run_once_sweep(void) {sweep_mode|=SWEEP_MODE_RUN_ONCE;}
static inline void pause_sweep(void) {sweep_mode&=~SWEEP_MODE_ENABLED;}
static inline void resume_sweep(void){sweep_mode|= SWEEP_MODE_ENABLED;}
void toggle_sweep(void){sweep_mode^= SWEEP_MODE_ENABLED;}
static float
bessel0(float x) {
@ -316,16 +298,15 @@ const int8_t gain_table[] = {
#define DELAY_GAIN_CHANGE 2
static int
adjust_gain(int newfreq)
adjust_gain(uint32_t newfreq)
{
int delay = 0;
int new_order = newfreq / FREQ_HARMONICS;
int old_order = frequency / FREQ_HARMONICS;
if (new_order != old_order) {
tlv320aic3204_set_gain(gain_table[new_order], gain_table[new_order]);
delay += DELAY_GAIN_CHANGE;
return DELAY_GAIN_CHANGE;
}
return delay;
return 0;
}
int set_frequency(uint32_t freq)
@ -778,27 +759,23 @@ ensure_edit_config(void)
#define DELAY_CHANNEL_CHANGE 2
// main loop for measurement
bool sweep(bool break_on_operation)
static bool sweep(bool break_on_operation)
{
int i;
int i, delay;
// blink LED while scanning
palClearPad(GPIOC, GPIOC_LED);
systime_t time = chVTGetSystemTimeX();
systime_t sweep_t = 0;
si5351_enable_output();
// On CW set freq once, and run
for (i = 0; i < sweep_points; i++) { // 8365
sweep_t-= chVTGetSystemTimeX();
int delay = set_frequency(frequencies[i]); // 1560
sweep_t+= chVTGetSystemTimeX();
wait_dsp(1); // Wait for get optimal timings
for (i = 0; i < sweep_points; i++) { // 5300
delay = set_frequency(frequencies[i]); // 700
tlv320aic3204_select(0); // 60 CH0:REFLECT
wait_dsp(delay+(i==0 ? 1 :0)); // 3270
wait_dsp(delay); // 1900
// calculate reflection coefficient
(*sample_func)(measured[0][i]); // 60
tlv320aic3204_select(1); // 60 CH1:TRANSMISSION
wait_dsp(DELAY_CHANNEL_CHANGE); // 2700
wait_dsp(DELAY_CHANNEL_CHANGE); // 1800
// calculate transmission coefficient
(*sample_func)(measured[1][i]); // 60
// ======== 170 ===========
@ -813,7 +790,6 @@ bool sweep(bool break_on_operation)
return false;
}
}
{char string_buf[18];plot_printf(string_buf, sizeof string_buf, "T:%06d:%06d", chVTGetSystemTimeX() - time, sweep_t);ili9341_drawstringV(string_buf, 1, 90);}
// blink LED while scanning
palSetPad(GPIOC, GPIOC_LED);
return true;
@ -849,11 +825,11 @@ VNA_SHELL_FUNCTION(cmd_scan)
if (cal_auto_interpolate && (cal_status & CALSTAT_APPLY))
cal_interpolate(lastsaveid);
sweep_once = TRUE;
run_once_sweep();
chMtxUnlock(&mutex);
// wait finishing sweep
while (sweep_once)
while (sweep_mode&SWEEP_MODE_RUN_ONCE)
chThdSleepMilliseconds(10);
}
@ -1294,36 +1270,21 @@ void
cal_collect(int type)
{
ensure_edit_config();
int dst, src;
switch (type) {
case CAL_LOAD:
cal_status |= CALSTAT_LOAD;
memcpy(cal_data[CAL_LOAD], measured[0], sizeof measured[0]);
break;
case CAL_OPEN:
cal_status |= CALSTAT_OPEN;
cal_status &= ~(CALSTAT_ES|CALSTAT_APPLY);
memcpy(cal_data[CAL_OPEN], measured[0], sizeof measured[0]);
break;
case CAL_SHORT:
cal_status |= CALSTAT_SHORT;
cal_status &= ~(CALSTAT_ER|CALSTAT_APPLY);
memcpy(cal_data[CAL_SHORT], measured[0], sizeof measured[0]);
break;
case CAL_THRU:
cal_status |= CALSTAT_THRU;
memcpy(cal_data[CAL_THRU], measured[1], sizeof measured[0]);
break;
case CAL_ISOLN:
cal_status |= CALSTAT_ISOLN;
memcpy(cal_data[CAL_ISOLN], measured[1], sizeof measured[0]);
break;
case CAL_LOAD: cal_status|= CALSTAT_LOAD; dst = CAL_LOAD; src = 0; break;
case CAL_OPEN: cal_status|= CALSTAT_OPEN; dst = CAL_OPEN; src = 0; cal_status&= ~(CALSTAT_ES|CALSTAT_APPLY); break;
case CAL_SHORT: cal_status|= CALSTAT_SHORT; dst = CAL_SHORT; src = 0; cal_status&= ~(CALSTAT_ER|CALSTAT_APPLY); break;
case CAL_THRU: cal_status|= CALSTAT_THRU; dst = CAL_THRU; src = 1; break;
case CAL_ISOLN: cal_status|= CALSTAT_ISOLN; dst = CAL_ISOLN; src = 1; break;
default:
return;
}
redraw_request |= REDRAW_CAL_STATUS;
// Made sweep operation for collect calibration data
sweep(false);
// Copy calibration data
memcpy(cal_data[dst], measured[src], sizeof measured[0]);
redraw_request|= REDRAW_CAL_STATUS;
}
void
@ -2165,24 +2126,27 @@ THD_FUNCTION(myshellThread, p) {
#endif
// I2C clock bus setting: depend from STM32_I2C1SW in mcuconf.h
// STM32_I2C1SW = STM32_I2C1SW_HSI (HSI=8MHz)
// STM32_I2C1SW = STM32_I2C1SW_SYSCLK (SYSCLK = 48MHz)
static const I2CConfig i2ccfg = {
// TIMINGR register initialization. (use I2C timing configuration tool for STM32F3xx and STM32F0xx microcontrollers (AN4235))
// 400kHz @ SYSCLK 48MHz (Use 26.4.10 I2C_TIMINGR register configuration examples from STM32 RM0091 Reference manual)
// STM32_TIMINGR_PRESC(5U) |
// STM32_TIMINGR_SCLDEL(3U) | STM32_TIMINGR_SDADEL(3U) |
// STM32_TIMINGR_SCLH(3U) | STM32_TIMINGR_SCLL(9U),
// 400kHz @ HSI 8MHz (Use 26.4.10 I2C_TIMINGR register configuration examples from STM32 RM0091 Reference manual)
.timingr = // TIMINGR register initialization. (use I2C timing configuration tool for STM32F3xx and STM32F0xx microcontrollers (AN4235))
#if STM32_I2C1SW == STM32_I2C1SW_HSI
// STM32_I2C1SW == STM32_I2C1SW_HSI (HSI=8MHz)
// 400kHz @ HSI 8MHz (Use 26.4.10 I2C_TIMINGR register configuration examples from STM32 RM0091 Reference manual)
STM32_TIMINGR_PRESC(0U) |
STM32_TIMINGR_SCLDEL(3U) | STM32_TIMINGR_SDADEL(1U) |
STM32_TIMINGR_SCLH(3U) | STM32_TIMINGR_SCLL(9U),
// Old values voodoo magic 400kHz @ HSI 8MHz
// STM32_TIMINGR_PRESC(0U) |
// STM32_TIMINGR_SCLDEL(3U) | STM32_TIMINGR_SDADEL(0U) |
// STM32_TIMINGR_SCLH(5U) | STM32_TIMINGR_SCLL(6U),
0, // CR1 register initialization.
0 // CR2 register initialization.
// Old values voodoo magic 400kHz @ HSI 8MHz
//0x00300506,
#elif STM32_I2C1SW == STM32_I2C1SW_SYSCLK
// STM32_I2C1SW == STM32_I2C1SW_SYSCLK (SYSCLK = 48MHz)
// 400kHz @ SYSCLK 48MHz (Use 26.4.10 I2C_TIMINGR register configuration examples from STM32 RM0091 Reference manual)
STM32_TIMINGR_PRESC(5U) |
STM32_TIMINGR_SCLDEL(3U) | STM32_TIMINGR_SDADEL(3U) |
STM32_TIMINGR_SCLH(3U) | STM32_TIMINGR_SCLL(9U),
#else
#error "Need Define STM32_I2C1SW and set correct TIMINGR settings"
#endif
.cr1 = 0, // CR1 register initialization.
.cr2 = 0 // CR2 register initialization.
};
static DACConfig dac1cfg1 = {

View file

@ -58,7 +58,8 @@
#define STM32_ADCSW STM32_ADCSW_HSI14
#define STM32_USBSW STM32_USBSW_HSI48
#define STM32_CECSW STM32_CECSW_HSI
#define STM32_I2C1SW STM32_I2C1SW_HSI
//#define STM32_I2C1SW STM32_I2C1SW_HSI
#define STM32_I2C1SW STM32_I2C1SW_SYSCLK
#define STM32_USART1SW STM32_USART1SW_PCLK
#define STM32_RTCSEL STM32_RTCSEL_LSI

View file

@ -86,7 +86,9 @@ double my_atof(const char *p);
void toggle_sweep(void);
void loadDefaultProps(void);
extern int8_t sweep_enabled;
#define SWEEP_MODE_ENABLED 0x01
#define SWEEP_MODE_RUN_ONCE 0x02
extern volatile int8_t sweep_mode;
/*
* dsp.c
@ -279,12 +281,12 @@ int marker_search(void);
int marker_search_left(int from);
int marker_search_right(int from);
extern uint16_t redraw_request;
// _request flag for update screen
#define REDRAW_CELLS (1<<0)
#define REDRAW_FREQUENCY (1<<1)
#define REDRAW_CAL_STATUS (1<<2)
#define REDRAW_MARKER (1<<3)
extern volatile uint8_t redraw_request;
extern int16_t vbat;

384
si5351.c
View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2015, TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com
* Modified by DiSlord dislordlive@gmail.com
* All rights reserved.
*
* This is free software; you can redistribute it and/or modify
@ -21,15 +22,19 @@
#include "nanovna.h"
#include "si5351.h"
#define XTALFREQ 26000000L
// MCLK (processor clock, audio codec) frequency clock
#define CLK2_FREQUENCY 8000000L
// Enable cache for SI5351 CLKX_CONTROL register, little speedup exchange
#define USE_CLK_CONTROL_CACHE TRUE
// Fixed PLL mode multiplier
// XTAL frequency on si5351
#define XTALFREQ 26000000U
// MCLK (processor clock if set, audio codec) frequency clock
#define CLK2_FREQUENCY 8000000U
// Fixed PLL mode multiplier (used in band 1)
#define PLL_N 32
//
#define SI5351_I2C_ADDR (0x60<<1)
// I2C address on bus (only 0x60 for Si5351A in 10-Pin MSOP)
#define SI5351_I2C_ADDR 0x60
static uint8_t current_band = 0;
static uint32_t current_freq = 0;
@ -37,18 +42,17 @@ static uint32_t current_freq = 0;
static void
si5351_bulk_write(const uint8_t *buf, int len)
{
int addr = SI5351_I2C_ADDR>>1;
i2cAcquireBus(&I2CD1);
(void)i2cMasterTransmitTimeout(&I2CD1, addr, buf, len, NULL, 0, 1000);
(void)i2cMasterTransmitTimeout(&I2CD1, SI5351_I2C_ADDR, buf, len, NULL, 0, 1000);
i2cReleaseBus(&I2CD1);
}
#if 0
static void si5351_bulk_read(uint8_t reg, uint8_t* buf, int len)
{
int addr = SI5351_I2C_ADDR>>1;
i2cAcquireBus(&I2CD1);
msg_t mr = i2cMasterTransmitTimeout(&I2CD1, addr, &reg, 1, buf, len, 1000);
i2cReleaseBus(&I2CD1);
int addr = SI5351_I2C_ADDR>>1;
i2cAcquireBus(&I2CD1);
msg_t mr = i2cMasterTransmitTimeout(&I2CD1, addr, &reg, 1, buf, len, 1000);
i2cReleaseBus(&I2CD1);
}
#endif
@ -64,15 +68,18 @@ const uint8_t si5351_configs[] = {
2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff,
4, SI5351_REG_16_CLK0_CONTROL, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN,
2, SI5351_REG_183_CRYSTAL_LOAD, SI5351_CRYSTAL_LOAD_8PF,
// All of this init code run late on sweep
#if 0
// setup PLL (26MHz * 32 = 832MHz, 32/2-2=14)
// 9, SI5351_REG_PLL_A, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
// 9, SI5351_REG_PLL_B, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
9, SI5351_REG_PLL_A, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
9, SI5351_REG_PLL_B, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
// RESET PLL
2, SI5351_REG_177_PLL_RESET, SI5351_PLL_RESET_A | SI5351_PLL_RESET_B | 0x0C, //
// setup multisynth (832MHz / 104 = 8MHz, 104/2-2=50)
// 9, SI5351_REG_58_MULTISYNTH2, /*P3*/0, 1, /*P1*/0, 50, 0, /*P2|P3*/0, 0, 0,
// 2, SI5351_REG_18_CLK2_CONTROL, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_INPUT_MULTISYNTH_N | SI5351_CLK_INTEGER_MODE,
// 2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0,
9, SI5351_REG_58_MULTISYNTH2, /*P3*/0, 1, /*P1*/0, 50, 0, /*P2|P3*/0, 0, 0,
2, SI5351_REG_18_CLK2_CONTROL, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_INPUT_MULTISYNTH_N | SI5351_CLK_INTEGER_MODE,
2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0,
#endif
0 // sentinel
};
@ -106,217 +113,185 @@ static const uint8_t clkctrl[] = {
SI5351_REG_18_CLK2_CONTROL
};
// Reset PLL need then band changes
static void si5351_reset_pll(uint8_t mask)
{
// Writing a 1<<5 will reset PLLA, 1<<7 reset PLLB, this is a self clearing bits.
// !!! Need delay before reset PLL for apply PLL freq changes before
chThdSleepMicroseconds(200);
si5351_write(SI5351_REG_177_PLL_RESET, mask | 0x0C);
// chThdSleepMicroseconds(250);
}
void si5351_disable_output(void)
{
// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, (SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN));
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xFF);
si5351_bulk_write(disable_output, sizeof(disable_output));
}
void si5351_enable_output(void)
{
si5351_reset_pll( SI5351_PLL_RESET_A | SI5351_PLL_RESET_B );
// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, ~(SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN));
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x00);
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, ~(SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN));
//si5351_reset_pll(SI5351_PLL_RESET_A | SI5351_PLL_RESET_B);
current_freq = 0;
current_band = 0;
}
static void si5351_setupPLL(uint8_t pll, /* SI5351_PLL_A or SI5351_PLL_B */
uint8_t mult,
uint32_t num,
uint32_t denom)
// Set PLL freq = XTALFREQ * (mult + num/denom)
static void si5351_setupPLL(uint8_t pllSource, /* SI5351_REG_PLL_A or SI5351_REG_PLL_B */
uint32_t mult,
uint32_t num,
uint32_t denom)
{
uint32_t P1;
uint32_t P2;
uint32_t P3;
/* Feedback Multisynth Divider Equation
* where: a = mult, b = num and c = denom
* P1 register is an 18-bit value using following formula:
* P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512
* P1[17:0] = 128 * mult + int((128*num)/denom) - 512
* P2 register is a 20-bit value using the following formula:
* P2[19:0] = 128 * num - denom * floor(128*(num/denom))
* P2[19:0] = (128 * num) % denom
* P3 register is a 20-bit value using the following formula:
* P3[19:0] = denom
*/
/* Set the main PLL config registers */
if (num == 0)
{
/* Integer mode */
P1 = 128 * mult - 512;
P2 = 0;
P3 = 1;
}
else
{
/* Fractional mode */
//P1 = (uint32_t)(128 * mult + floor(128 * ((float)num/(float)denom)) - 512);
P1 = 128 * mult + ((128 * num) / denom) - 512;
//P2 = (uint32_t)(128 * num - denom * floor(128 * ((float)num/(float)denom)));
P2 = 128 * num - denom * ((128 * num) / denom);
mult<<=7;
num<<=7;
uint32_t P1 = mult - 512; // Integer mode
uint32_t P2 = 0;
uint32_t P3 = 1;
if (num){ // Fractional mode
P1+= num / denom;
P2 = num % denom;
P3 = denom;
}
// Pll MSN(A|B) registers Datasheet
// MSN_P3[15:8]
// MSN_P3[ 7:0]
// Reserved | MSN_P1[17:16]
// MSN_P1[15:8]
// MSN_P1[ 7:0]
// MSN_P3[19:16] | MSN_P2[19:16]
// MSN_P2[15:8]
// MSN_P2[ 7:0]
// Pll MSN(A|B) registers Datasheet
uint8_t reg[9];
reg[0] = pll;//reg_base[pll];
reg[1] = (P3 & 0x0000FF00) >> 8;
reg[2] = (P3 & 0x000000FF);
reg[3] = (P1 & 0x00030000) >> 16;
reg[4] = (P1 & 0x0000FF00) >> 8;
reg[5] = (P1 & 0x000000FF);
reg[6] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
reg[7] = (P2 & 0x0000FF00) >> 8;
reg[8] = (P2 & 0x000000FF);
reg[0]= pllSource; // SI5351_REG_PLL_A or SI5351_REG_PLL_B
reg[1]=( P3 & 0x0FF00)>> 8; // MSN_P3[15: 8]
reg[2]=( P3 & 0x000FF); // MSN_P3[ 7: 0]
reg[3]=( P1 & 0x30000)>>16; // MSN_P1[17:16]
reg[4]=( P1 & 0x0FF00)>> 8; // MSN_P1[15: 8]
reg[5]=( P1 & 0x000FF); // MSN_P1[ 7: 0]
reg[6]=((P3 & 0xF0000)>>12)|((P2 & 0xF0000)>>16); // MSN_P3[19:16] | MSN_P2[19:16]
reg[7]=( P2 & 0x0FF00)>> 8; // MSN_P2[15: 8]
reg[8]=( P2 & 0x000FF); // MSN_P2[ 7: 0]
si5351_bulk_write(reg, 9);
}
// Set Multisynth divider = (div + num/denom) * rdiv
static void
si5351_setupMultisynth(uint8_t channel,
uint8_t pllSource,
uint32_t div, // 4,6,8, 8+ ~ 900
uint32_t num,
uint32_t denom,
uint32_t rdiv, // SI5351_R_DIV_1~128
uint8_t drive_strength)
si5351_setupMultisynth(uint8_t channel,
uint32_t div, // 4,6,8, 8+ ~ 900
uint32_t num,
uint32_t denom,
uint32_t rdiv, // SI5351_R_DIV_1~128
uint8_t chctrl) // SI5351_REG_16_CLKX_CONTROL settings
{
uint8_t dat;
uint32_t P1;
uint32_t P2;
uint32_t P3;
uint32_t div4 = 0;
/* Output Multisynth Divider Equations
* where: a = div, b = num and c = denom
* P1 register is an 18-bit value using following formula:
* P1[17:0] = 128 * a + floor(128*(b/c)) - 512
* P1[17:0] = 128 * a + int((128*b)/c) - 512
* P2 register is a 20-bit value using the following formula:
* P2[19:0] = 128 * b - c * floor(128*(b/c))
* P2[19:0] = (128 * b) % c
* P3 register is a 20-bit value using the following formula:
* P3[19:0] = c
* P3[19:0] = c
*/
/* Set the main PLL config registers */
if (div == 4) {
div4 = SI5351_DIVBY4;
P1 = P2 = 0;
P3 = 1;
} else if (num == 0) {
/* Integer mode */
P1 = 128 * div - 512;
P2 = 0;
P3 = 1;
} else {
/* Fractional mode */
P1 = 128 * div + ((128 * num) / denom) - 512;
P2 = 128 * num - denom * ((128 * num) / denom);
P3 = denom;
uint32_t P1 = 0;
uint32_t P2 = 0;
uint32_t P3 = 1;
if (div == 4)
rdiv|= SI5351_DIVBY4;
else {
num<<=7;
div<<=7;
P1 = div - 512; // Integer mode
if (num){ // Fractional mode
P1+= num / denom;
P2 = num % denom;
P3 = denom;
}
}
/* Set the MSx config registers */
uint8_t reg[9];
reg[0] = msreg_base[channel];
reg[1] = (P3 & 0x0000FF00) >> 8;
reg[2] = (P3 & 0x000000FF);
reg[3] = ((P1 & 0x00030000) >> 16) | div4 | rdiv;
reg[4] = (P1 & 0x0000FF00) >> 8;
reg[5] = (P1 & 0x000000FF);
reg[6] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
reg[7] = (P2 & 0x0000FF00) >> 8;
reg[8] = (P2 & 0x000000FF);
reg[0]= msreg_base[channel]; // SI5351_REG_42_MULTISYNTH0, SI5351_REG_50_MULTISYNTH1, SI5351_REG_58_MULTISYNTH2
reg[1]=( P3 & 0x0FF00)>>8; // MSx_P3[15: 8]
reg[2]=( P3 & 0x000FF); // MSx_P3[ 7: 0]
reg[3]=((P1 & 0x30000)>>16)| rdiv; // Rx_DIV[2:0] | MSx_DIVBY4[1:0] | MSx_P1[17:16]
reg[4]=( P1 & 0x0FF00)>> 8; // MSx_P1[15: 8]
reg[5]=( P1 & 0x000FF); // MSx_P1[ 7: 0]
reg[6]=((P3 & 0xF0000)>>12)|((P2 & 0xF0000)>>16); // MSx_P3[19:16] | MSx_P2[19:16]
reg[7]=( P2 & 0x0FF00)>>8; // MSx_P2[15: 8]
reg[8]=( P2 & 0x000FF); // MSx_P2[ 7: 0]
si5351_bulk_write(reg, 9);
/* Configure the clk control and enable the output */
dat = drive_strength | SI5351_CLK_INPUT_MULTISYNTH_N;
if (pllSource == SI5351_REG_PLL_B)
dat |= SI5351_CLK_PLL_SELECT_B;
uint8_t dat = chctrl | SI5351_CLK_INPUT_MULTISYNTH_N;
if (num == 0)
dat |= SI5351_CLK_INTEGER_MODE;
#if USE_CLK_CONTROL_CACHE == TRUE
// Use cache for this reg, not update if not change
static uint8_t clk_cache[3];
if (clk_cache[channel]!=dat){
si5351_write(clkctrl[channel], dat);
clk_cache[channel]=dat;
}
#else
si5351_write(clkctrl[channel], dat);
#endif
}
static void
si5351_set_frequency_fixedpll(uint8_t channel, uint32_t pllSource, uint64_t pllfreq, uint32_t freq, uint32_t rdiv, uint8_t drive_strength)
{
uint32_t denom = freq;
uint32_t div = pllfreq / denom; // range: 8 ~ 1800
uint32_t num = pllfreq % denom;
// cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
uint32_t max_denominator = (1 << 20) - 1;
if (denom > max_denominator) {
uint32_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
while (denom != 0) {
uint32_t a = num / denom;
uint32_t b = num % denom;
uint32_t q2 = q0 + a*q1;
if (q2 > max_denominator)
break;
uint32_t p2 = p0 + a*p1;
p0 = p1;
q0 = q1;
p1 = p2;
q1 = q2;
num = denom; denom = b;
}
num = p1;
denom = q1;
}
si5351_setupMultisynth(channel, pllSource, div, num, denom, rdiv, drive_strength);
}
void si5351_setupPLL_freq(uint32_t pll, uint32_t freq, uint32_t div, uint32_t mul){
uint32_t denom = XTALFREQ * mul;
uint64_t pllfreq = (uint64_t)freq * div;
uint32_t multi = pllfreq / denom;
uint32_t num = pllfreq % denom;
// Find better approximate values for n/d
#define MAX_DENOMINATOR ((1 << 20) - 1)
static inline void fractionalSolve(uint32_t *n, uint32_t *d){
// cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
uint32_t max_denominator = (1 << 20) - 1;
if (denom > max_denominator) {
uint32_t denom = *d;
if (denom > MAX_DENOMINATOR) {
uint32_t num = *n;
uint32_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
while (denom != 0) {
uint32_t a = num / denom;
uint32_t b = num % denom;
uint32_t q2 = q0 + a*q1;
if (q2 > max_denominator)
uint32_t a = num / denom;
uint32_t b = num % denom;
uint32_t q2 = q0 + a*q1;
if (q2 > MAX_DENOMINATOR)
break;
uint32_t p2 = p0 + a*p1;
p0 = p1; q0 = q1; p1 = p2; q1 = q2;
num = denom; denom = b;
}
num = p1;
denom = q1;
*n = p1;
*d = q1;
}
si5351_setupPLL(pll, multi, num, denom);
}
// Setup Multisynth divider for get correct output freq if fixed PLL = pllfreq
static void
si5351_set_frequency_fixedpll(uint8_t channel, uint64_t pllfreq, uint32_t freq, uint32_t rdiv, uint8_t chctrl)
{
uint32_t denom = freq;
uint32_t div = pllfreq / denom; // range: 8 ~ 1800
uint32_t num = pllfreq % denom;
fractionalSolve(&num, &denom);
si5351_setupMultisynth(channel, div, num, denom, rdiv, chctrl);
}
// Setup PLL freq if Multisynth divider fixed = div (need get output = freq/mul)
static void
si5351_setupPLL_freq(uint32_t pllSource, uint32_t freq, uint32_t div, uint32_t mul){
uint32_t denom = XTALFREQ * mul;
uint64_t pllfreq = (uint64_t)freq * div;
uint32_t multi = pllfreq / denom;
uint32_t num = pllfreq % denom;
fractionalSolve(&num, &denom);
si5351_setupPLL(pllSource, multi, num, denom);
}
#if 0
static void
si5351_set_frequency_fixeddiv(uint8_t channel, uint32_t pll, uint32_t freq, uint32_t div,
uint8_t drive_strength, uint32_t mul)
uint8_t chctrl, uint32_t mul)
{
si5351_setupPLL_freq(pll, freq, div, mul);
si5351_setupMultisynth(channel, pll, div, 0, 1, SI5351_R_DIV_1, drive_strength);
si5351_setupMultisynth(channel, div, 0, 1, SI5351_R_DIV_1, chctrl);
}
void
@ -332,91 +307,106 @@ si5351_set_frequency(int channel, uint32_t freq, uint8_t drive_strength){
}
#endif
/*
* Frequency generation divide on 3 band
* Band 1
* 1~100MHz fixed PLL = XTALFREQ * PLL_N, fractional divider
* Band 2
* 100~150MHz fractional PLL = 600- 900MHz, fixed divider 'fdiv = 6'
* Band 3
* 150~300MHz fractional PLL = 600-1200MHz, fixed divider 'fdiv = 4'
*
* For FREQ_HARMONICS = 300MHz - band range is:
* +-----------------------------------------------------------------------------------------------------------------------+
* | Band 1 | Band 2 | Band 3 | Band 2 | Band 3 |
* +-----------------------------------------------------------------------------------------------------------------------+
* | x1 : x1 | x1 : x1 | x1 : x1 | x3 : x5 | x3 : x5 | x5-x7 | x7-x9 | x9-x11 |
* | 50kHz - 100MHz | 100 - 150MHz | 150 - 300MHz | 300-450MHz | 450-900MHz | 900-1500MHz | 1500-2100MHz | 2100-2700MHz |
* +-----------------------------------------------------------------------------------------------------------------------+
* | f = 50kHz-100MHz | f=100-150 | f=150-300 | f=150-300 | f=214-300 | f=233-300 |
* | of = 50kHz-100MHz |of= 60- 90 |of= 90-180 |of=128-215 |of=166-234 |of=190-246 |
* +-----------------------------------------------------------------------------------------------------------------------+
*/
static inline uint8_t si5351_getBand(uint32_t freq){
if (freq < 100000000U) return 1;
if (freq < 150000000U) return 2;
return 3;
}
// Minimum value is 2, freq change apply at next dsp measure, and need skip it
#define DELAY_NORMAL 2
// Additional delay for band 1 (remove unstable generation at begin)
#define DELAY_BAND_1 1
// Band changes need additional delay after reset PLL
#define DELAY_BANDCHANGE 2
/*
* Maximum supported frequency = FREQ_HARMONICS * 9U
* configure output as follows:
* CLK0: frequency + offset
* CLK1: frequency
* CLK2: fixed 8MHz
*/
int
si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength)
{
si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength){
uint8_t band;
int delay = DELAY_NORMAL;
if (freq == current_freq)
return delay;
return delay;
uint32_t ofreq = freq + offset;
uint32_t mul = 1, omul = 1;
uint32_t rdiv = SI5351_R_DIV_1;
uint32_t fdiv;
current_freq = freq;
if (freq >= FREQ_HARMONICS * 7U) {
mul = 9;
mul = 9;
omul = 11;
} else if (freq >= FREQ_HARMONICS * 5U) {
mul = 7;
mul = 7;
omul = 9;
} else if (freq >= FREQ_HARMONICS * 3U) {
mul = 5;
mul = 5;
omul = 7;
} else if (freq >= FREQ_HARMONICS) {
mul = 3;
mul = 3;
omul = 5;
}
else if (freq <= 500000U) {
rdiv = SI5351_R_DIV_64;
freq *= 64;
ofreq *= 64;
freq<<= 6;
ofreq<<= 6;
} else if (freq <= 4000000U) {
rdiv = SI5351_R_DIV_8;
freq *= 8;
ofreq *= 8;
}
/*
* Band 0
* 1~100MHz fixed PLL = XTALFREQ * PLL_N, fractional divider
* Band 1
* 100~150MHz fractional PLL = 600- 900MHz, fixed divider 6
* Band 2
* 150~300MHz fractional PLL = 600-1200MHz, fixed divider 4
*/
if ((freq / mul) < 100000000U) {
band = 1;
} else if ((freq / mul) < 150000000U) {
band = 2;
fdiv = 6;
} else {
band = 3;
fdiv = 4;
freq<<= 3;
ofreq<<= 3;
}
band = si5351_getBand(freq/mul);
switch (band) {
case 1:
// Setup CH0 and CH1 constant PLL freq at band change, and set CH2 freq = CLK2_FREQUENCY
// Setup CH0 and CH1 constant PLLA freq at band change, and set CH2 freq = CLK2_FREQUENCY
if (current_band != 1){
si5351_setupPLL(SI5351_REG_PLL_A, PLL_N, 0, 1);
si5351_setupPLL(SI5351_REG_PLL_B, PLL_N, 0, 1);
si5351_set_frequency_fixedpll(2, SI5351_REG_PLL_B, XTALFREQ * PLL_N, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA);
si5351_set_frequency_fixedpll(2, XTALFREQ * PLL_N, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA|SI5351_CLK_PLL_SELECT_A);
}
// Calculate and set CH0 and CH1 divider
si5351_set_frequency_fixedpll(0, SI5351_REG_PLL_A, XTALFREQ * PLL_N * omul, ofreq, rdiv, drive_strength);
si5351_set_frequency_fixedpll(1, SI5351_REG_PLL_A, XTALFREQ * PLL_N * mul, freq, rdiv, drive_strength);
si5351_set_frequency_fixedpll(0, (uint64_t)omul * XTALFREQ * PLL_N, ofreq, rdiv, drive_strength|SI5351_CLK_PLL_SELECT_A);
si5351_set_frequency_fixedpll(1, (uint64_t) mul * XTALFREQ * PLL_N, freq, rdiv, drive_strength|SI5351_CLK_PLL_SELECT_A);
delay+=DELAY_BAND_1;
break;
case 2:
case 3:
case 2:// fdiv = 6
case 3:// fdiv = 4;
fdiv = (band == 2) ? 6 : 4;
// Setup CH0 and CH1 constant fdiv divider at change
if (current_band != band){
si5351_setupMultisynth(0, SI5351_REG_PLL_A, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength);
si5351_setupMultisynth(1, SI5351_REG_PLL_B, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength);
si5351_setupMultisynth(0, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength|SI5351_CLK_PLL_SELECT_A);
si5351_setupMultisynth(1, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength|SI5351_CLK_PLL_SELECT_B);
}
// Calculate and set CH0 and CH1 PLL freq
si5351_setupPLL_freq(SI5351_REG_PLL_A, ofreq, fdiv, omul);// set PLLA freq = (ofreq/omul)*fdiv
si5351_setupPLL_freq(SI5351_REG_PLL_B, freq, fdiv, mul);// set PLLB freq = ( freq/ mul)*fdiv
// Calculate CH2 freq = CLK2_FREQUENCY, depend from calculated before CH1 PLLB = (freq/mul)*fdiv
si5351_set_frequency_fixedpll(2, SI5351_REG_PLL_B, (freq/mul)*fdiv, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA);
si5351_set_frequency_fixedpll(2, (uint64_t)freq*fdiv, CLK2_FREQUENCY*mul, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA|SI5351_CLK_PLL_SELECT_B);
break;
}

View file

@ -17,12 +17,39 @@
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//#define SI5351_PLL_A 0
//#define SI5351_PLL_B 1
#define SI5351_MULTISYNTH_DIV_4 4
#define SI5351_MULTISYNTH_DIV_6 6
#define SI5351_MULTISYNTH_DIV_8 8
#define SI5351_REG_3_OUTPUT_ENABLE_CONTROL 3
#define SI5351_CLK0_EN (1<<0)
#define SI5351_CLK1_EN (1<<1)
#define SI5351_CLK2_EN (1<<2)
// Reg 16-18 CLKX_CONTROL
#define SI5351_REG_16_CLK0_CONTROL 16
#define SI5351_REG_17_CLK1_CONTROL 17
#define SI5351_REG_18_CLK2_CONTROL 18
#define SI5351_CLK_POWERDOWN (1<<7)
#define SI5351_CLK_INTEGER_MODE (1<<6)
#define SI5351_CLK_PLL_SELECT_A (0<<5)
#define SI5351_CLK_PLL_SELECT_B (1<<5)
#define SI5351_CLK_INVERT (1<<4)
#define SI5351_CLK_INPUT_MASK (3<<2)
#define SI5351_CLK_INPUT_XTAL (0<<2)
#define SI5351_CLK_INPUT_CLKIN (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
#define SI5351_REG_PLL_A 26
#define SI5351_REG_PLL_B 34
#define SI5351_REG_42_MULTISYNTH0 42
#define SI5351_REG_50_MULTISYNTH1 50
#define SI5351_REG_58_MULTISYNTH2 58
#define SI5351_DIVBY4 (3<<2)
#define SI5351_R_DIV_1 (0<<4)
#define SI5351_R_DIV_2 (1<<4)
#define SI5351_R_DIV_4 (2<<4)
@ -31,39 +58,6 @@
#define SI5351_R_DIV_32 (5<<4)
#define SI5351_R_DIV_64 (6<<4)
#define SI5351_R_DIV_128 (7<<4)
#define SI5351_DIVBY4 (3<<2)
#define SI5351_REG_3_OUTPUT_ENABLE_CONTROL 3
#define SI5351_CLK0_EN (1<<0)
#define SI5351_CLK1_EN (1<<2)
#define SI5351_CLK2_EN (1<<3)
#define SI5351_REG_16_CLK0_CONTROL 16
#define SI5351_REG_17_CLK1_CONTROL 17
#define SI5351_REG_18_CLK2_CONTROL 18
#define SI5351_REG_PLL_A 26
#define SI5351_REG_PLL_B 34
#define SI5351_REG_42_MULTISYNTH0 42
#define SI5351_REG_50_MULTISYNTH1 50
#define SI5351_REG_58_MULTISYNTH2 58
#define SI5351_CLK_POWERDOWN (1<<7)
#define SI5351_CLK_INTEGER_MODE (1<<6)
#define SI5351_CLK_PLL_SELECT_B (1<<5)
#define SI5351_CLK_INVERT (1<<4)
#define SI5351_CLK_INPUT_MASK (3<<2)
#define SI5351_CLK_INPUT_XTAL (0<<2)
#define SI5351_CLK_INPUT_CLKIN (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
#define SI5351_REG_177_PLL_RESET 177
#define SI5351_PLL_RESET_B (1<<7)
@ -74,11 +68,8 @@
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
#define SI5351_CRYSTAL_FREQ_25MHZ 25000000
void si5351_init(void);
void si5351_disable_output(void);
void si5351_enable_output(void);
void si5351_set_frequency(int channel, uint32_t freq, uint8_t drive_strength);
//void si5351_set_frequency(int channel, uint32_t freq, uint8_t drive_strength);
int si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength);

4
ui.c
View file

@ -1358,7 +1358,7 @@ menu_item_modify_attribute(const menuitem_t *menu, int item,
*fg = config.menu_normal_color;
}
} else if (menu == menu_stimulus) {
if (item == 5 /* PAUSE */ && !sweep_enabled) {
if (item == 5 /* PAUSE */ && !(sweep_mode&SWEEP_MODE_ENABLED)) {
*bg = DEFAULT_MENU_TEXT_COLOR;
*fg = config.menu_normal_color;
}
@ -2131,7 +2131,7 @@ touch_lever_mode_select(void)
select_lever_mode(touch_x < FREQUENCIES_XPOS2 ? LM_CENTER : LM_SPAN);
return TRUE;
}
if (touch_y < 15) {
if (touch_y < 25) {
if (touch_x < FREQUENCIES_XPOS2 && get_electrical_delay() != 0.0) {
select_lever_mode(LM_EDELAY);
} else