From 9dbce30497a7f8c7c0d387470349c9d697a8dfe6 Mon Sep 17 00:00:00 2001 From: erikkaashoek Date: Sat, 1 Aug 2020 10:59:40 +0200 Subject: [PATCH] I2C, I2S, si5351 and tlv320aic3204 added --- Makefile | 2 +- halconf.h | 2 +- main.c | 25 ++- mcuconf.h | 2 +- mcuconf_F303.h | 2 +- nanovna.h | 11 +- si5351.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++++ si5351.h | 85 +++++++++ tlv320aic3204.c | 154 ++++++++++++++++ 9 files changed, 725 insertions(+), 23 deletions(-) create mode 100644 si5351.c create mode 100644 si5351.h create mode 100644 tlv320aic3204.c diff --git a/Makefile b/Makefile index dd4f8f3..9005847 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ CSRC = $(STARTUPSRC) \ FatFs/ff.c \ FatFs/ffunicode.c \ usbcfg.c \ - main.c plot.c ui.c ili9341.c numfont20x22.c Font5x7.c Font10x14.c flash.c adc.c si4432.c Font7x13b.c rtc.c + main.c plot.c ui.c ili9341.c tlv320aic3204.c si5351.c numfont20x22.c Font5x7.c Font10x14.c flash.c adc.c si4432.c Font7x13b.c rtc.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. diff --git a/halconf.h b/halconf.h index be98e75..15f6596 100644 --- a/halconf.h +++ b/halconf.h @@ -213,7 +213,7 @@ * @brief Enables the mutual exclusion APIs on the I2C bus. */ #if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define I2C_USE_MUTUAL_EXCLUSION FALSE +#define I2C_USE_MUTUAL_EXCLUSION TRUE #endif /*===========================================================================*/ diff --git a/main.c b/main.c index 782ef75..5cc7034 100644 --- a/main.c +++ b/main.c @@ -23,9 +23,7 @@ //#include "hal_serial.h" #include "usbcfg.h" -#ifdef __VNA__ #include "si5351.h" -#endif #include "nanovna.h" #ifdef __VNA__ #include "fft.h" @@ -654,29 +652,29 @@ VNA_SHELL_FUNCTION(cmd_clearconfig) "Do reset manually to take effect. Then do touch cal and save.\r\n"); } -#ifdef __VNA__ +#ifdef __AUDIO__ static struct { int16_t rms[2]; int16_t ave[2]; int callback_count; -#if 0 +#if 1 int32_t last_counter_value; int32_t interval_cycles; int32_t busy_cycles; #endif } stat; - int16_t rx_buffer[AUDIO_BUFFER_LEN * 2]; #ifdef ENABLED_DUMP int16_t dump_buffer[AUDIO_BUFFER_LEN]; int16_t dump_selection = 0; #endif - volatile uint8_t wait_count = 0; volatile uint8_t accumerate_count = 0; +#endif +#ifdef __VNA__ const int8_t bandwidth_accumerate_count[] = { 1, // 1kHz 3, // 300Hz @@ -688,7 +686,7 @@ const int8_t bandwidth_accumerate_count[] = { float measured[2][POINTS_COUNT][2]; #endif measurement_t measured; -#ifdef __VNA__ +#ifdef __AUDIO__ #ifdef ENABLED_DUMP static void duplicate_buffer_to_dump(int16_t *p) @@ -715,7 +713,7 @@ void i2s_end_callback(I2SDriver *i2sp, size_t offset, size_t n) --wait_count; } else if (wait_count > 0) { if (accumerate_count > 0) { - dsp_process(p, n); + // dsp_process(p, n); accumerate_count--; } #ifdef ENABLED_DUMP @@ -930,10 +928,11 @@ ensure_edit_config(void) } #include "sa_core.c" -#ifdef __VNA__ +#ifdef __AUDIO__ #define DSP_START(delay) wait_count = delay; #define DSP_WAIT_READY while (wait_count) __WFI(); - +#endif +#ifdef __VNA__ #define DELAY_CHANNEL_CHANGE 2 // main loop for measurement @@ -2502,7 +2501,7 @@ THD_FUNCTION(myshellThread, p) } #endif -#if 0 +#if 1 // I2C clock bus setting: depend from STM32_I2C1SW in mcuconf.h static const I2CConfig i2ccfg = { .timingr = STM32_TIMINGR_PRESC(0U) | /* 72MHz I2CCLK. ~ 600kHz i2c */ @@ -2605,10 +2604,8 @@ int main(void) //palSetPadMode(GPIOB, 8, PAL_MODE_ALTERNATE(1) | PAL_STM32_OTYPE_OPENDRAIN); //palSetPadMode(GPIOB, 9, PAL_MODE_ALTERNATE(1) | PAL_STM32_OTYPE_OPENDRAIN); -#ifdef __VNA__ i2cStart(&I2CD1, &i2ccfg); si5351_init(); -#endif // MCO on PA8 //palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(0)); @@ -2702,7 +2699,7 @@ int main(void) setupSA(); set_sweep_points(POINTS_COUNT); -#ifdef __VNA__ +#ifdef __AUDIO__ /* * I2S Initialize */ diff --git a/mcuconf.h b/mcuconf.h index be724e5..9520c1c 100644 --- a/mcuconf.h +++ b/mcuconf.h @@ -134,7 +134,7 @@ /* * I2C driver system settings. */ -#define STM32_I2C_USE_I2C1 FALSE +#define STM32_I2C_USE_I2C1 TRUE #define STM32_I2C_USE_I2C2 FALSE #define STM32_I2C_BUSY_TIMEOUT 50 #define STM32_I2C_I2C1_IRQ_PRIORITY 3 diff --git a/mcuconf_F303.h b/mcuconf_F303.h index aede087..33195d5 100644 --- a/mcuconf_F303.h +++ b/mcuconf_F303.h @@ -154,7 +154,7 @@ #define STM32_I2C_BUSY_TIMEOUT 50 #define STM32_I2C_I2C1_IRQ_PRIORITY 3 #define STM32_I2C_I2C2_IRQ_PRIORITY 3 -#define STM32_I2C_USE_DMA FALSE +#define STM32_I2C_USE_DMA TRUE #define STM32_I2C_I2C1_DMA_PRIORITY 1 #define STM32_I2C_I2C2_DMA_PRIORITY 1 #define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") diff --git a/nanovna.h b/nanovna.h index 8ef6a93..a83c568 100644 --- a/nanovna.h +++ b/nanovna.h @@ -33,7 +33,7 @@ #define __SELFTEST__ #define __CALIBRATE__ #define __FAST_SWEEP__ // Pre-fill SI4432 RSSI buffer to get fastest sweep in zero span mode - +#define __AUDIO__ //#define __ULTRA__ // Add harmonics mode on low input. //#define __ULTRA_SA__ // Adds ADF4351 control for extra high 1st IF stage #define __SPUR__ // Does spur reduction by shifting IF @@ -234,14 +234,14 @@ void set_measurement(int); // extern int settingSpeed; //extern int setting.step_delay; void sweep_remote(void); -#ifdef __VNA__ +#ifdef __AUDIO__ /* * dsp.c */ // 5ms @ 48kHz #define AUDIO_BUFFER_LEN 96 -extern int16_t rx_buffer[]; +extern int16_t rx_buffer[AUDIO_BUFFER_LEN * 2]; #define STATE_LEN 32 #define SAMPLE_LEN 48 @@ -250,7 +250,8 @@ extern int16_t rx_buffer[]; extern int16_t ref_buf[]; extern int16_t samp_buf[]; #endif - +#endif +#ifdef __VNA__ void dsp_process(int16_t *src, size_t len); void reset_dsp_accumerator(void); void calculate_gamma(float *gamma); @@ -258,7 +259,7 @@ void fetch_amplitude(float *gamma); void fetch_amplitude_ref(float *gamma); #endif -#ifdef __VNA__ +#ifdef __AUDIO__ /* * tlv320aic3204.c */ diff --git a/si5351.c b/si5351.c new file mode 100644 index 0000000..572189d --- /dev/null +++ b/si5351.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2014-2015, TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include "hal.h" +#include "nanovna.h" +#include "si5351.h" + +#define SI5351_I2C_ADDR (0x60<<1) + +static bool 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, ®, 1, buf, len, 1000); + i2cReleaseBus(&I2CD1); + return mr == MSG_OK; +} + +static bool si5351_write(uint8_t reg, uint8_t dat) +{ + int addr = SI5351_I2C_ADDR>>1; + uint8_t buf[] = { reg, dat }; + i2cAcquireBus(&I2CD1); + msg_t mr = i2cMasterTransmitTimeout(&I2CD1, addr, buf, 2, NULL, 0, 1000); + i2cReleaseBus(&I2CD1); + return mr == MSG_OK; +} + +static bool si5351_bulk_write(const uint8_t *buf, int len) +{ + int addr = SI5351_I2C_ADDR>>1; + i2cAcquireBus(&I2CD1); + msg_t mr = i2cMasterTransmitTimeout(&I2CD1, addr, buf, len, NULL, 0, 1000); + i2cReleaseBus(&I2CD1); + return mr == MSG_OK; +} + +// register addr, length, data, ... +static 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, + // setup PLL (26MHz * 32 = 832MHz, 32/2-2=14) + 9, SI5351_REG_26_PLL_A, /*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, + // 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, +#ifdef __ENABLE_CLK2__ + 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, +#else + 2, SI5351_REG_18_CLK2_CONTROL,SI5351_CLK_POWERDOWN, + 2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x04, +#endif + 0 // sentinel +}; + +static bool si5351_wait_ready(void) +{ + uint8_t status = 0xff; + systime_t start = chVTGetSystemTime(); + systime_t end = start + MS2ST(1000); // 1000 ms timeout + while (chVTIsSystemTimeWithin(start, end)) + { + if(!si5351_bulk_read(0, &status, 1)) + status = 0xff; // comm timeout + if ((status & 0x80) == 0) + return true; + } + return false; +} + +#if 0 +static void si5351_wait_pll_lock(void) +{ + systime_t start = chVTGetSystemTime(); + uint8_t status = 0xff; + if(!si5351_bulk_read(0, &status, 1)) + status = 0xff; // comm timeout + if ((status & 0x60) == 0) + return; + systime_t end = start + MS2ST(100); // 100 ms timeout + while (chVTIsSystemTimeWithin(start, end)) + { + if(!si5351_bulk_read(0, &status, 1)) + status = 0xff; // comm timeout + if ((status & 0x60) == 0) + return; + } + pll_lock_failed = true; +} + +#endif + +bool si5351_init(void) +{ + if (!si5351_wait_ready()) + return false; + const uint8_t *p = si5351_configs; + while (*p) { + uint8_t len = *p++; + if (!si5351_bulk_write(p, len)) + return false; + p += len; + } + return true; +} + +static void si5351_disable_output(void) +{ + uint8_t reg[4]; + si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff); + reg[0] = SI5351_REG_16_CLK0_CONTROL; + reg[1] = SI5351_CLK_POWERDOWN; + reg[2] = SI5351_CLK_POWERDOWN; + reg[3] = SI5351_CLK_POWERDOWN; + si5351_bulk_write(reg, 4); +} + +static void si5351_enable_output(void) +{ +#ifdef __ENABLE_CLK2__ + si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x00); +#else + si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x04); +#endif +} + +static void si5351_reset_pll(void) +{ + //si5351_write(SI5351_REG_177_PLL_RESET, SI5351_PLL_RESET_A | SI5351_PLL_RESET_B); + si5351_write(SI5351_REG_177_PLL_RESET, 0xAC); +} + +static void si5351_setupPLL( + uint8_t pll, /* SI5351_PLL_A or SI5351_PLL_B */ + uint8_t mult, + uint32_t num, + uint32_t denom) +{ + /* Get the appropriate starting point for the PLL registers */ + const uint8_t pllreg_base[] = { + SI5351_REG_26_PLL_A, + SI5351_REG_34_PLL_B + }; + 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 + * P2 register is a 20-bit value using the following formula: + * P2[19:0] = 128 * num - denom * floor(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); + P3 = denom; + } + + /* The datasheet is a nightmare of typos and inconsistencies here! */ + uint8_t reg[9]; + reg[0] = pllreg_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); + si5351_bulk_write(reg, 9); +} + +static void si5351_setupMultisynth( + uint8_t output, + 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) +{ + /* Get the appropriate starting point for the PLL registers */ + const uint8_t msreg_base[] = { + SI5351_REG_42_MULTISYNTH0, + SI5351_REG_50_MULTISYNTH1, + SI5351_REG_58_MULTISYNTH2, + }; + const uint8_t clkctrl[] = { + SI5351_REG_16_CLK0_CONTROL, + SI5351_REG_17_CLK1_CONTROL, + SI5351_REG_18_CLK2_CONTROL + }; + 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 + * P2 register is a 20-bit value using the following formula: + * P2[19:0] = 128 * b - c * floor(128*(b/c)) + * P3 register is a 20-bit value using the following formula: + * 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; + } + + /* Set the MSx config registers */ + uint8_t reg[9]; + reg[0] = msreg_base[output]; + 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); + si5351_bulk_write(reg, 9); + + /* Configure the clk control and enable the output */ + dat = drive_strength | SI5351_CLK_INPUT_MULTISYNTH_N; + if (pllSource == SI5351_PLL_B) + dat |= SI5351_CLK_PLL_SELECT_B; + if (num == 0) + dat |= SI5351_CLK_INTEGER_MODE; + si5351_write(clkctrl[output], dat); +} + +static uint32_t gcd(uint32_t x, uint32_t y) +{ + uint32_t z; + while (y != 0) { + z = x % y; + x = y; + y = z; + } + return x; +} + +#define XTALFREQ 26000000L +#define PLL_N 32 +#define PLLFREQ (XTALFREQ * PLL_N) + +static void si5351_set_frequency_fixedpll( + int channel, int pll, int pllfreq, int freq, + uint32_t rdiv, uint8_t drive_strength) +{ + int32_t div = pllfreq / freq; // range: 8 ~ 1800 + int32_t num = pllfreq - freq * div; + int32_t denom = freq; + //int32_t k = freq / (1<<20) + 1; + int32_t k = gcd(num, denom); + num /= k; + denom /= k; + while (denom >= (1<<20)) { + num >>= 1; + denom >>= 1; + } + si5351_setupMultisynth(channel, pll, div, num, denom, rdiv, drive_strength); +} + +static void si5351_set_frequency_fixeddiv( + int channel, int pll, int freq, int div, + uint8_t drive_strength) +{ + int32_t pllfreq = freq * div; + int32_t multi = pllfreq / XTALFREQ; + int32_t num = pllfreq - multi * XTALFREQ; + int32_t denom = XTALFREQ; + int32_t k = gcd(num, denom); + num /= k; + denom /= k; + while (denom >= (1<<20)) { + num >>= 1; + denom >>= 1; + } + si5351_setupPLL(pll, multi, num, denom); + si5351_setupMultisynth(channel, pll, div, 0, 1, SI5351_R_DIV_1, drive_strength); +} + +/* + * 1~100MHz fixed PLL 900MHz, fractional divider + * 100~150MHz fractional PLL 600-900MHz, fixed divider 6 + * 150~200MHz fractional PLL 600-900MHz, fixed divider 4 + */ +void si5351_set_frequency(int channel, int freq, uint8_t drive_strength) +{ + if (freq <= 100000000) { + si5351_setupPLL(SI5351_PLL_B, 32, 0, 1); + si5351_set_frequency_fixedpll(channel, SI5351_PLL_B, PLLFREQ, freq, SI5351_R_DIV_1, drive_strength); + } else if (freq < 150000000) { + si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 6, drive_strength); + } else { + si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 4, drive_strength); + } +} + + +static int current_band = -1; + +/* + * configure output as follows: + * CLK0: frequency + offset + * CLK1: frequency + * CLK2: fixed 8MHz + */ +#define CLK2_FREQUENCY 8000000L +int si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength) +{ + int band; + int delay = 3; + uint32_t ofreq = freq + offset; + uint32_t rdiv = SI5351_R_DIV_1; +#ifdef __VNA__ + /* if (freq > config.harmonic_freq_threshold * 5 ) { + freq /= 7; + ofreq /= 9; + }else */ + if (freq > config.harmonic_freq_threshold * 3) { + freq /= 5; + ofreq /= 7; + } else if (freq > config.harmonic_freq_threshold) { + freq /= 3; + ofreq /= 5; + } +#endif + if (freq <= 100000000) { + band = 0; + } else if (freq < 160000000) { + band = 1; + } else { + band = 2; + } + if (freq <= 500000) { + rdiv = SI5351_R_DIV_64; + } else if (freq <= 4000000) { + rdiv = SI5351_R_DIV_8; + } + +#if 1 + if (current_band != band) + si5351_disable_output(); +#endif + + switch (band) { + case 0: + // fractional divider mode. only PLL A is used. + if (current_band == 1 || current_band == 2){ + si5351_reset_pll(); + si5351_setupPLL(SI5351_PLL_A, 32, 0, 1); + } + + if (rdiv == SI5351_R_DIV_8) { + freq *= 8; + ofreq *= 8; + } else if (rdiv == SI5351_R_DIV_64) { + freq *= 64; + ofreq *= 64; + } + + si5351_set_frequency_fixedpll(0, SI5351_PLL_A, PLLFREQ, ofreq, + rdiv, drive_strength); + si5351_set_frequency_fixedpll(1, SI5351_PLL_A, PLLFREQ, freq, + rdiv, drive_strength); + //if (current_band != 0) +#ifdef __ENABLE_CLK2__ + si5351_set_frequency_fixedpll(2, SI5351_PLL_A, PLLFREQ, CLK2_FREQUENCY, + SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); +#endif + break; + + case 1: + // Set PLL twice on changing from band 2 + if (current_band == 2) { + si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength); + si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength); + } + + // div by 6 mode. both PLL A and B are dedicated for CLK0, CLK1 + si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength); + si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength); +#ifdef __ENABLE_CLK2__ + si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq * 6, CLK2_FREQUENCY, + SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); +#endif + break; + + case 2: + // div by 4 mode. both PLL A and B are dedicated for CLK0, CLK1 + si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 4, drive_strength); + si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 4, drive_strength); +#ifdef __ENABLE_CLK2__ + si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq * 4, CLK2_FREQUENCY, + SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); +#endif + break; + } + + if (current_band != band) { + si5351_reset_pll(); + si5351_wait_pll_lock(); +#if 1 + si5351_enable_output(); +#endif + delay += 10; + } + + current_band = band; + return delay; +} diff --git a/si5351.h b/si5351.h new file mode 100644 index 0000000..6792143 --- /dev/null +++ b/si5351.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014-2015, TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef __SI5351_H__ +#define __SI5351_H__ + +#include +#include + +#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_R_DIV_1 (0<<4) +#define SI5351_R_DIV_2 (1<<4) +#define SI5351_R_DIV_4 (2<<4) +#define SI5351_R_DIV_8 (3<<4) +#define SI5351_R_DIV_16 (4<<4) +#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_REG_16_CLK0_CONTROL 16 +#define SI5351_REG_17_CLK1_CONTROL 17 +#define SI5351_REG_18_CLK2_CONTROL 18 +#define SI5351_REG_26_PLL_A 26 +#define SI5351_REG_34_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) +#define SI5351_PLL_RESET_A (1<<5) + +#define SI5351_REG_183_CRYSTAL_LOAD 183 +#define SI5351_CRYSTAL_LOAD_6PF (1<<6) +#define SI5351_CRYSTAL_LOAD_8PF (2<<6) +#define SI5351_CRYSTAL_LOAD_10PF (3<<6) + +#define SI5351_CRYSTAL_FREQ_25MHZ 25000000 + +bool si5351_init(void); +void si5351_set_frequency(int channel, int freq, uint8_t drive_strength); +int si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength); + +#endif //__SI5351_H__ diff --git a/tlv320aic3204.c b/tlv320aic3204.c new file mode 100644 index 0000000..4ebfb9e --- /dev/null +++ b/tlv320aic3204.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014-2015, TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include "hal.h" +#include "nanovna.h" + +#define REFCLK_8000KHZ +#define AIC3204_ADDR 0x18 + +#define wait_ms(ms) chThdSleepMilliseconds(ms) + +static const uint8_t conf_data_pll[] = { + // len, ( reg, data ), + 2, 0x00, 0x00, /* Initialize to Page 0 */ + 2, 0x01, 0x01, /* Initialize the device through software reset */ + 2, 0x04, 0x43, /* PLL Clock High, MCLK, PLL */ +#ifdef REFCLK_8000KHZ + /* 8.000MHz*10.7520 = 86.016MHz, 86.016MHz/(2*7*128) = 48kHz */ + 2, 0x05, 0x91, /* Power up PLL, P=1,R=1 */ + 2, 0x06, 0x0a, /* J=10 */ + 2, 0x07, 29, /* D=7520 = (29<<8) + 96 */ + 2, 0x08, 96, +#endif + 0 // sentinel +}; + +// default fs=48kHz +static const uint8_t conf_data_clk[] = { + 2, 0x0b, 0x82, /* Power up the NDAC divider with value 2 */ + 2, 0x0c, 0x87, /* Power up the MDAC divider with value 7 */ + 2, 0x0d, 0x00, /* Program the OSR of DAC to 128 */ + 2, 0x0e, 0x80, + 2, 0x3c, 0x08, /* Set the DAC Mode to PRB_P8 */ + //2, 0x3c, 25, /* Set the DAC Mode to PRB_P25 */ + 2, 0x1b, 0x0c, /* Set the BCLK,WCLK as output */ + 2, 0x1e, 0x80 + 28, /* Enable the BCLKN divider with value 28 */ + 2, 0x25, 0xee, /* DAC power up */ + + 2, 0x12, 0x82, /* Power up the NADC divider with value 2 */ + 2, 0x13, 0x87, /* Power up the MADC divider with value 7 */ + 2, 0x14, 0x80, /* Program the OSR of ADC to 128 */ + 2, 0x3d, 0x01, /* Select ADC PRB_R1 */ + 0 // sentinel +}; + +static const uint8_t conf_data_routing[] = { + 2, 0x00, 0x01, /* Select Page 1 */ + 2, 0x01, 0x08, /* Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO*/ + 2, 0x02, 0x01, /* Enable Master Analog Power Control */ + 2, 0x7b, 0x01, /* Set the REF charging time to 40ms */ + 2, 0x14, 0x25, /* HP soft stepping settings for optimal pop performance at power up Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound. */ + 2, 0x0a, 0x33, /* Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to 1.65V */ + + 2, 0x3d, 0x00, /* Select ADC PTM_R4 */ + 2, 0x47, 0x32, /* Set MicPGA startup delay to 3.1ms */ + 2, 0x7b, 0x01, /* Set the REF charging time to 40ms */ + 2, 0x34, 0x10, /* Route IN2L to LEFT_P with 10K */ + 2, 0x36, 0x10, /* Route IN2R to LEFT_N with 10K */ + 2, 0x37, 0x04, /* Route IN3R to RIGHT_P with 10K */ + 2, 0x39, 0x04, /* Route IN3L to RIGHT_N with 10K */ + 2, 0x3b, 0, /* Unmute Left MICPGA, Gain selection of 32dB to make channel gain 0dB */ + 2, 0x3c, 0, /* Unmute Right MICPGA, Gain selection of 32dB to make channel gain 0dB */ + 0 // sentinel +}; + +static const uint8_t conf_data_unmute[] = { + 2, 0x00, 0x00, /* Select Page 0 */ + 2, 0x51, 0xc0, /* Power up Left and Right ADC Channels */ + 2, 0x52, 0x00, /* Unmute Left and Right ADC Digital Volume Control */ + 0 // sentinel +}; + +static void tlv320aic3204_bulk_write(const uint8_t *buf, int len) +{ + int addr = AIC3204_ADDR; + i2cAcquireBus(&I2CD1); + (void)i2cMasterTransmitTimeout(&I2CD1, addr, buf, len, NULL, 0, 1000); + i2cReleaseBus(&I2CD1); +} + +#if 0 +static int tlv320aic3204_read(uint8_t d0) +{ + int addr = AIC3204_ADDR; + uint8_t buf[] = { d0 }; + i2cAcquireBus(&I2CD1); + i2cMasterTransmitTimeout(&I2CD1, addr, buf, 1, buf, 1, 1000); + i2cReleaseBus(&I2CD1); + return buf[0]; +} +#endif + +static void tlv320aic3204_config(const uint8_t *data) +{ + const uint8_t *p = data; + while (*p) { + uint8_t len = *p++; + tlv320aic3204_bulk_write(p, len); + p += len; + } +} + +void tlv320aic3204_init(void) +{ + tlv320aic3204_config(conf_data_pll); + tlv320aic3204_config(conf_data_clk); + tlv320aic3204_config(conf_data_routing); + wait_ms(40); + tlv320aic3204_config(conf_data_unmute); +} + +void tlv320aic3204_select(int channel) +{ + const uint8_t ch3[] = { + 2, 0x00, 0x01, /* Select Page 1 */ + 2, 0x37, 0x04, /* Route IN3R to RIGHT_P with input impedance of 10K */ + 2, 0x39, 0x04, /* Route IN3L to RIGHT_N with input impedance of 10K */ + 0 // sentinel + }; + const uint8_t ch1[] = { + 2, 0x00, 0x01, /* Select Page 1 */ + 2, 0x37, 0x40, /* Route IN1R to RIGHT_P with input impedance of 10K */ + 2, 0x39, 0x10, /* Route IN1L to RIGHT_N with input impedance of 10K */ + 0 // sentinel + }; + tlv320aic3204_config(channel ? ch1 : ch3); +} + +void tlv320aic3204_set_gain(int lgain, int rgain) +{ + uint8_t data[] = { + 2, 0x00, 0x01, /* Select Page 1 */ + 2, 0x3b, lgain, /* Unmute Left MICPGA, set gain */ + 2, 0x3c, rgain, /* Unmute Right MICPGA, set gain */ + 0 // sentinel + }; + tlv320aic3204_config(data); +}