diff options
author | Dimitri Stolnikov <horiz0n@gmx.net> | 2019-02-18 23:55:47 +0100 |
---|---|---|
committer | Dimitri Stolnikov <horiz0n@gmx.net> | 2019-02-18 23:55:47 +0100 |
commit | 0e18df5ba646420178c43aca2bccb7023d682fb5 (patch) | |
tree | 3ecdbaaee99e66e87f7329af33a43189b3d13817 /firmware | |
parent | 2a1659e0b927ca472fea270612858d6186a1d0ad (diff) |
Enable 10MHz on each output with automatic switchover for CLKIN and XTAL
clock inputs
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/clock_generator.c | 50 | ||||
-rw-r--r-- | firmware/main.c | 154 | ||||
-rw-r--r-- | firmware/make/Makefile | 4 | ||||
-rw-r--r-- | firmware/si5351c.c | 368 | ||||
-rw-r--r-- | firmware/si5351c.h | 54 |
5 files changed, 389 insertions, 241 deletions
diff --git a/firmware/clock_generator.c b/firmware/clock_generator.c new file mode 100644 index 0000000..cee75e7 --- /dev/null +++ b/firmware/clock_generator.c @@ -0,0 +1,50 @@ +#include <stdbool.h> +#include "si5351c.h" + +void si5351c_init(void) +{ + si5351c_enable_clock_outputs(0x00); /* Disable Outputs */ + + si5351c_disable_oeb_pin_control(); + si5351c_power_down_all_clocks(); /* Powerdown all output drivers */ + si5351c_set_crystal_configuration(); + si5351c_enable_xo_and_ms_fanout(); + si5351c_configure_pll_sources(); + si5351c_configure_pll_multisynth(); + + /* MS0/CLK0 */ + si5351c_configure_multisynth(0, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS1/CLK1 */ + si5351c_configure_multisynth(1, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS2/CLK2 */ + si5351c_configure_multisynth(2, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS3/CLK3 */ + si5351c_configure_multisynth(3, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS4/CLK4 */ + si5351c_configure_multisynth(4, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS5/CLK5 */ + si5351c_configure_multisynth(5, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS6/CLK6 */ + si5351c_configure_multisynth(6, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + /* MS7/CLK7 */ + si5351c_configure_multisynth(7, 80*128-512, 0, 1, 0); /* 800/80 = 10MHz */ + + si5351c_set_clock_source(PLL_SOURCE_XTAL); + si5351c_activate_best_clock_source(); + + si5351c_reset_pll(); /* Apply PLLA and PLLB soft reset */ + + si5351c_enable_clock_outputs(0xff); /* Enable desired outputs */ +} + +bool si5351c_run(void) +{ + bool externally_locked = false; + + enum pll_sources clk_src = si5351c_activate_best_clock_source(); + + if ( clk_src == PLL_SOURCE_CLKIN ) + externally_locked = true; + + return externally_locked; +} diff --git a/firmware/main.c b/firmware/main.c index df1d38b..14a08e0 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -41,6 +41,11 @@ /*- Definitions -------------------------------------------------------------*/ HAL_GPIO_PIN(LED, A, 16) +HAL_GPIO_PIN(I2C_SDA, A, 22) +HAL_GPIO_PIN(I2C_SCL, A, 23) + +#define T_RISE 215e-9 // Depends on the board, actually + #define APP_EP_SEND 1 #define APP_EP_RECV 2 @@ -63,7 +68,7 @@ void irq_handler_tc1(void) { if (TC1->COUNT16.INTFLAG.reg & TC_INTFLAG_MC(1)) { - HAL_GPIO_LED_toggle(); +// HAL_GPIO_LED_toggle(); TC1->COUNT16.INTFLAG.reg = TC_INTFLAG_MC(1); } } @@ -120,6 +125,132 @@ static void sys_init(void) GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN; while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); } + +//----------------------------------------------------------------------------- +static int i2c_init(int freq) +{ + int baud = ((float)F_CPU / freq - (float)F_CPU * T_RISE - 10.0) / 2.0; + + if (baud < 0) + baud = 0; + else if (baud > 255) + baud = 255; + + freq = (float)F_CPU / (2.0 * (5.0 + baud) + (float)F_CPU * T_RISE); + + PM->APBCMASK.reg |= PM_APBCMASK_SERCOM1; + + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM1_GCLK_ID_CORE) | + GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0); + + SERCOM1->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; + while (SERCOM1->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST); + + SERCOM1->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; + while (SERCOM1->I2CM.SYNCBUSY.reg); + + SERCOM1->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD((uint32_t)baud); + while (SERCOM1->I2CM.SYNCBUSY.reg); + + SERCOM1->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_ENABLE | + SERCOM_I2CM_CTRLA_MODE_I2C_MASTER | + SERCOM_I2CM_CTRLA_SDAHOLD(3); + while (SERCOM1->I2CM.SYNCBUSY.reg); + + SERCOM1->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1); + + HAL_GPIO_I2C_SDA_in(); + HAL_GPIO_I2C_SDA_clr(); + HAL_GPIO_I2C_SDA_pmuxen(PORT_PMUX_PMUXE_C_Val); + + HAL_GPIO_I2C_SCL_in(); + HAL_GPIO_I2C_SCL_clr(); + HAL_GPIO_I2C_SCL_pmuxen(PORT_PMUX_PMUXE_C_Val); + + return freq; +} + +//----------------------------------------------------------------------------- +bool i2c_tx_start(uint32_t addr) +{ + SERCOM1->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; + + SERCOM1->I2CM.ADDR.reg = addr; + + while (0 == (SERCOM1->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && + 0 == (SERCOM1->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)); + + if (SERCOM1->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK || + SERCOM1->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR) + { + SERCOM1->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool i2c_tx_byte(uint8_t byte) +{ + SERCOM1->I2CM.DATA.reg = byte; + + while (1) + { + uint8_t flags = SERCOM1->I2CM.INTFLAG.reg; + + if (flags & SERCOM_I2CM_INTFLAG_MB) + break; + + if (flags & (SERCOM_I2CM_INTFLAG_SB | SERCOM_I2CM_INTFLAG_ERROR)) + return false; + } + + if (SERCOM1->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) + { + SERCOM1->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool i2c_rx_byte(uint8_t *byte, bool last) +{ + while (1) + { + uint8_t flags = SERCOM1->I2CM.INTFLAG.reg; + + if (flags & SERCOM_I2CM_INTFLAG_SB) + break; + + if (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_ERROR)) + return false; + } + + if (last) + SERCOM1->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3); + else + SERCOM1->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + *byte = SERCOM1->I2CM.DATA.reg; + + return true; +} + +//----------------------------------------------------------------------------- +bool i2c_stop(void) +{ + if ((SERCOM1->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) || + (SERCOM1->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) + { + SERCOM1->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + + return true; +} + #if 0 //----------------------------------------------------------------------------- static uint32_t get_uint32(uint8_t *data) @@ -187,6 +318,9 @@ void usb_configuration_callback(int config) (void)config; } +void si5351c_init(void); +bool si5351c_run(void); + //----------------------------------------------------------------------------- int main(void) { @@ -198,11 +332,27 @@ int main(void) usb_init(); + i2c_init(100000); + si5351c_init(); + HAL_GPIO_LED_out(); - HAL_GPIO_LED_clr(); + HAL_GPIO_LED_set(); while (1) { + static unsigned delay = 0; + + if ( delay++ == 1000000 ) + { + delay = 0; + + bool externally_locked = si5351c_run(); + if (externally_locked) { + HAL_GPIO_LED_clr(); + } else { + HAL_GPIO_LED_set(); + } + } } return 0; diff --git a/firmware/make/Makefile b/firmware/make/Makefile index 73c7da7..6d25dc5 100644 --- a/firmware/make/Makefile +++ b/firmware/make/Makefile @@ -30,7 +30,9 @@ SRCS += \ ../udc.c \
../usb.c \
../usb_descriptors.c \
- ../startup_samd11.c
+ ../startup_samd11.c \
+ ../clock_generator.c \
+ ../si5351c.c
DEFINES += \
-D__SAMD11D14AM__ \
diff --git a/firmware/si5351c.c b/firmware/si5351c.c index 64a3dda..a2d18a4 100644 --- a/firmware/si5351c.c +++ b/firmware/si5351c.c @@ -20,63 +20,79 @@ * Boston, MA 02110-1301, USA. */ +#include <stdbool.h> #include "si5351c.h" +extern bool i2c_tx_start(uint32_t addr); +extern bool i2c_tx_byte(uint8_t byte); +extern bool i2c_rx_byte(uint8_t *byte, bool last); +extern bool i2c_stop(void); + enum pll_sources active_clock_source; /* write to single register */ -void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) +void si5351c_write_single(uint8_t reg, uint8_t val) { - const uint8_t data_tx[] = { reg, val }; - si5351c_write(drv, data_tx, 2); + i2c_tx_start(SI5351C_I2C_ADDR); + i2c_tx_byte(reg); + i2c_tx_byte(val); + i2c_stop(); } /* read single register */ -uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg) +uint8_t si5351c_read_single(uint8_t reg) { - const uint8_t data_tx[] = { reg }; - uint8_t data_rx[] = { 0x00 }; - i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1); - return data_rx[0]; + uint8_t val; + + /* set register address with write */ + i2c_tx_start(SI5351C_I2C_ADDR); + i2c_tx_byte(reg); + + /* read the value */ + i2c_tx_start(SI5351C_I2C_ADDR | 1); + i2c_rx_byte(&val, true); + i2c_stop(); + + return val; } /* * Write to one or more contiguous registers. data[0] should be the first * register number, one or more values follow. */ -void si5351c_write(si5351c_driver_t* const drv, const uint8_t* const data, const size_t data_count) +void si5351c_write(uint8_t* const data, const uint_fast8_t data_count) { - i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0); -} + uint_fast8_t i; -/* Disable all CLKx outputs. */ -void si5351c_disable_all_outputs(si5351c_driver_t* const drv) -{ - uint8_t data[] = { 3, 0xFF }; - si5351c_write(drv, data, sizeof(data)); + i2c_tx_start(SI5351C_I2C_ADDR); + + for (i = 0; i < data_count; i++) + i2c_tx_byte(data[i]); + i2c_stop(); } /* Turn off OEB pin control for all CLKx */ -void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv) +void si5351c_disable_oeb_pin_control() { - uint8_t data[] = { 9, 0xFF }; - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { 9, 0xFF }; + si5351c_write(data, sizeof(data)); } /* Power down all CLKx */ -void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) -{ - uint8_t data[] = { 16 - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN - , SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE - , SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE - }; - si5351c_write(drv, data, sizeof(data)); +void si5351c_power_down_all_clocks() +{ + uint8_t data[] = { + 16 + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + , SI5351C_CLK_POWERDOWN + }; + si5351c_write(data, sizeof(data)); } /* @@ -84,20 +100,20 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) * Reads as 0xE4 on power-up * Set to 8pF based on crystal specs and HackRF One testing */ -void si5351c_set_crystal_configuration(si5351c_driver_t* const drv) +void si5351c_set_crystal_configuration() { - uint8_t data[] = { 183, 0x80 }; - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { 183, 0x80 }; + si5351c_write(data, sizeof(data)); } /* * Register 187: Fanout Enable * Turn on XO and MultiSynth fanout only. */ -void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) +void si5351c_enable_xo_and_ms_fanout() { - uint8_t data[] = { 187, 0xD0 }; - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { 187, 0xD0 }; + si5351c_write(data, sizeof(data)); } /* @@ -106,196 +122,134 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) * PLLA_SRC=0 (XTAL) * PLLB_SRC=1 (CLKIN) */ -void si5351c_configure_pll_sources(si5351c_driver_t* const drv) +void si5351c_configure_pll_sources(void) { - uint8_t data[] = { 15, 0x08 }; - - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { 15, 0x08 }; + si5351c_write(data, sizeof(data)); } /* MultiSynth NA (PLLA) and NB (PLLB) */ -void si5351c_configure_pll_multisynth(si5351c_driver_t* const drv) -{ - /*PLLA: 25MHz XTAL * (0x0e00+512)/128 = 800mhz -> int mode */ - uint8_t data[] = { 26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00 }; - si5351c_write(drv, data, sizeof(data)); - - /*PLLB: 10MHz CLKIN * (0x2600+512)/128 = 800mhz */ - data[0] = 34; - data[4] = 0x26; - si5351c_write(drv, data, sizeof(data)); -} +void si5351c_configure_pll_multisynth(void) +{ + /* init plla to (0x0e00+512)/128*25MHz xtal = 800MHz -> int mode */ + uint8_t data[] = { 26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00 }; + si5351c_write(data, sizeof(data)); + + /* 10 MHz input on CLKIN for PLLB */ + data[0] = 34; + data[4] = 0x26; + si5351c_write(data, sizeof(data)); +} + +void si5351c_reset_pll(void) +{ + /* reset PLLA and PLLB */ + uint8_t data[] = { 177, 0xAC }; + si5351c_write(data, sizeof(data)); +} + +void si5351c_configure_multisynth(const uint_fast8_t ms_number, + const uint32_t p1, const uint32_t p2, const uint32_t p3, + const uint_fast8_t r_div) +{ + /* + * TODO: Check for p3 > 0? 0 has no meaning in fractional mode? + * And it makes for more jitter in integer mode. + */ + /* + * r_div is the R divider value encoded: + * 0 means divide by 1 + * 1 means divide by 2 + * 2 means divide by 4 + * ... + * 7 means divide by 128 + */ + const uint_fast8_t register_number = 42 + (ms_number * 8); + uint8_t data[] = { + register_number, + (p3 >> 8) & 0xFF, + (p3 >> 0) & 0xFF, + (r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3), + (p1 >> 8) & 0xFF, + (p1 >> 0) & 0xFF, + (((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0), + (p2 >> 8) & 0xFF, + (p2 >> 0) & 0xFF + }; + + si5351c_write(data, sizeof(data)); +} + +void si5351c_configure_clock_control(const enum pll_sources source) +{ + uint8_t pll; + + if (source == PLL_SOURCE_CLKIN) { + /* PLLB on CLKIN */ + pll = SI5351C_CLK_PLL_SRC_B; + } else { + /* PLLA on XTAL */ + pll = SI5351C_CLK_PLL_SRC_A; + } -void si5351c_reset_pll(si5351c_driver_t* const drv) -{ - /* reset PLLA and PLLB */ - uint8_t data[] = { 177, 0xA0 }; - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { + 16 + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + , SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) + }; + si5351c_write(data, sizeof(data)); } -void si5351c_configure_multisynth(si5351c_driver_t* const drv, - const uint_fast8_t ms_number, - const uint32_t p1, const uint32_t p2, const uint32_t p3, - const uint_fast8_t r_div) +void si5351c_enable_clock_outputs(const uint_fast8_t mask) { - /* - * TODO: Check for p3 > 0? 0 has no meaning in fractional mode? - * And it makes for more jitter in integer mode. - */ - /* - * r is the r divider value encoded: - * 0 means divide by 1 - * 1 means divide by 2 - * 2 means divide by 4 - * ... - * 7 means divide by 128 - */ - const uint_fast8_t register_number = 42 + (ms_number * 8); - uint8_t data[] = { - register_number, - (p3 >> 8) & 0xFF, - (p3 >> 0) & 0xFF, - (r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3), - (p1 >> 8) & 0xFF, - (p1 >> 0) & 0xFF, - (((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0), - (p2 >> 8) & 0xFF, - (p2 >> 0) & 0xFF }; - si5351c_write(drv, data, sizeof(data)); + uint8_t data[] = { 3, ~mask }; + si5351c_write(data, sizeof(data)); } - -void si5351c_configure_clock_control(si5351c_driver_t* const drv, const enum pll_sources source) +#if 0 +void si5351c_set_int_mode(const uint_fast8_t ms_number, const uint_fast8_t on) { - uint8_t pll; -#ifdef RAD1O - (void) source; - /* PLLA on XTAL */ - pll = SI5351C_CLK_PLL_SRC_A; -#endif - -#if (defined JAWBREAKER || defined HACKRF_ONE) - if (source == PLL_SOURCE_CLKIN) { - /* PLLB on CLKIN */ - pll = SI5351C_CLK_PLL_SRC_B; - } else { - /* PLLA on XTAL */ - pll = SI5351C_CLK_PLL_SRC_A; - } -#endif - /* Clock to CPU is deactivated as it is not used and creates noise */ - /* External clock output is deactivated as it is not used and creates noise */ - uint8_t data[] = {16 - ,SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA) - ,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV - ,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) - ,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/ - ,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV - ,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) - ,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/ - ,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/ - }; - si5351c_write(drv, data, sizeof(data)); -} - -#define SI5351C_CLK_ENABLE(x) (0<<x) -#define SI5351C_CLK_DISABLE(x) (1<<x) -#define SI5351C_REG_OUTPUT_EN (3) -#define SI5351C_REG_CLK3_CTRL (19) - -void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) -{ - /* Enable CLK outputs 0, 1, 2, 4, 5 only. */ - /* 7: Clock to CPU is deactivated as it is not used and creates noise */ - /* 3: External clock output is deactivated by default */ - // uint8_t data[] = { 3, ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 5))}; - uint8_t data[] = { SI5351C_REG_OUTPUT_EN, - SI5351C_CLK_ENABLE(0) | - SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(2) | - SI5351C_CLK_DISABLE(3) | - SI5351C_CLK_ENABLE(4) | - SI5351C_CLK_ENABLE(5) | - SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7) - }; - si5351c_write(drv, data, sizeof(data)); -} - -void si5351c_set_int_mode(si5351c_driver_t* const drv, const uint_fast8_t ms_number, const uint_fast8_t on){ uint8_t data[] = {16, 0}; if(ms_number < 8){ - data[0] = 16 + ms_number; - data[1] = si5351c_read_single(drv, data[0]); + data[0] = 16 + ms_number; + data[1] = si5351c_read_single(data[0]); - if(on) - data[1] |= SI5351C_CLK_INT_MODE; - else - data[1] &= ~(SI5351C_CLK_INT_MODE); + if(on) + data[1] |= SI5351C_CLK_INT_MODE; + else + data[1] &= ~(SI5351C_CLK_INT_MODE); - si5351c_write(drv, data, 2); + si5351c_write(data, 2); } } - -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source) -{ - si5351c_configure_clock_control(drv, source); - active_clock_source = source; -} - -void si5351c_activate_best_clock_source(si5351c_driver_t* const drv) +#endif +void si5351c_set_clock_source(const enum pll_sources source) { - uint8_t device_status = si5351c_read_single(drv, 0); - - if (device_status & SI5351C_LOS) { - /* CLKIN not detected */ - if (active_clock_source == PLL_SOURCE_CLKIN) { - si5351c_set_clock_source(drv, PLL_SOURCE_XTAL); - } - } else { - /* CLKIN detected */ - if (active_clock_source == PLL_SOURCE_XTAL) { - si5351c_set_clock_source(drv, PLL_SOURCE_CLKIN); - } - } + si5351c_configure_clock_control(source); + active_clock_source = source; } -void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable) +enum pll_sources si5351c_activate_best_clock_source(void) { - /* Set optput in output enable register */ - uint8_t output_enable = si5351c_read_single(drv, 3); - output_enable = output_enable & !SI5351C_CLK_DISABLE(3); - if(enable) - output_enable = output_enable | SI5351C_CLK_ENABLE(3); - else - output_enable = output_enable | SI5351C_CLK_DISABLE(3); - uint8_t oe_data[] = {SI5351C_REG_OUTPUT_EN, output_enable}; - si5351c_write(drv, oe_data, 2); + uint8_t device_status = si5351c_read_single(0); - /* Configure clock to 10MHz (TODO customisable?) */ - si5351c_configure_multisynth(drv, 3, 80*128-512, 0, 1, 0); + if (device_status & SI5351C_LOS) { + /* CLKIN not detected */ + if (active_clock_source == PLL_SOURCE_CLKIN) { + si5351c_set_clock_source(PLL_SOURCE_XTAL); + } + } else { + /* CLKIN detected */ + if (active_clock_source == PLL_SOURCE_XTAL) { + si5351c_set_clock_source(PLL_SOURCE_CLKIN); + } + } - /* Set power up/doen in CLK3 control register*/ - uint8_t pll; - #ifdef RAD1O - /* PLLA on XTAL */ - pll = SI5351C_CLK_PLL_SRC_A; - #endif - - #if (defined JAWBREAKER || defined HACKRF_ONE) - if (active_clock_source == PLL_SOURCE_CLKIN) { - /* PLLB on CLKIN */ - pll = SI5351C_CLK_PLL_SRC_B; - } else { - /* PLLA on XTAL */ - pll = SI5351C_CLK_PLL_SRC_A; - } - #endif - uint8_t clk3_ctrl; - if(enable) - clk3_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); - else - clk3_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE; - uint8_t clk3_data[] = {SI5351C_REG_CLK3_CTRL, clk3_ctrl}; - si5351c_write(drv, clk3_data, 2); + return active_clock_source; } diff --git a/firmware/si5351c.h b/firmware/si5351c.h index 533c583..b3eae03 100644 --- a/firmware/si5351c.h +++ b/firmware/si5351c.h @@ -30,9 +30,8 @@ extern "C" #include <stdint.h> -#include "i2c_bus.h" - #define SI_INTDIV(x) (x*128-512) +#define SI5351C_I2C_ADDR (0x60 << 1) #define SI5351C_CLK_POWERDOWN (1<<7) #define SI5351C_CLK_INT_MODE (1<<6) @@ -59,37 +58,30 @@ extern "C" #define SI5351C_LOS (1<<4) enum pll_sources { - PLL_SOURCE_XTAL = 0, - PLL_SOURCE_CLKIN = 1, + PLL_SOURCE_XTAL = 0, + PLL_SOURCE_CLKIN = 1, }; -typedef struct { - i2c_bus_t* const bus; - uint8_t i2c_address; -} si5351c_driver_t; - -void si5351c_disable_all_outputs(si5351c_driver_t* const drv); -void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); -void si5351c_power_down_all_clocks(si5351c_driver_t* const drv); -void si5351c_set_crystal_configuration(si5351c_driver_t* const drv); -void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv); -void si5351c_configure_pll_sources(si5351c_driver_t* const drv); -void si5351c_configure_pll_multisynth(si5351c_driver_t* const drv); -void si5351c_reset_pll(si5351c_driver_t* const drv); -void si5351c_configure_multisynth(si5351c_driver_t* const drv, - const uint_fast8_t ms_number, - const uint32_t p1, const uint32_t p2, const uint32_t p3, - const uint_fast8_t r_div); -void si5351c_configure_clock_control(si5351c_driver_t* const drv, const enum pll_sources source); -void si5351c_enable_clock_outputs(si5351c_driver_t* const drv); -void si5351c_set_int_mode(si5351c_driver_t* const drv, const uint_fast8_t ms_number, const uint_fast8_t on); -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source); -void si5351c_activate_best_clock_source(si5351c_driver_t* const drv); - -void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); -uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg); -void si5351c_write(si5351c_driver_t* const drv, const uint8_t* const data, const size_t data_count); -void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable); +void si5351c_write_single(uint8_t reg, uint8_t val); +uint8_t si5351c_read_single(uint8_t reg); +void si5351c_write(uint8_t* const data, const uint_fast8_t data_count); + +void si5351c_disable_oeb_pin_control(void); +void si5351c_power_down_all_clocks(void); +void si5351c_set_crystal_configuration(void); +void si5351c_enable_xo_and_ms_fanout(void); +void si5351c_configure_pll_sources(void); +void si5351c_configure_pll_multisynth(void); +void si5351c_reset_pll(void); +void si5351c_configure_multisynth(const uint_fast8_t ms_number, + const uint32_t p1, const uint32_t p2, const uint32_t p3, + const uint_fast8_t r_div); +void si5351c_configure_clock_control(const enum pll_sources source); +void si5351c_enable_clock_outputs(const uint_fast8_t mask); +void si5351c_set_int_mode(const uint_fast8_t ms_number, const uint_fast8_t on); + +void si5351c_set_clock_source(const enum pll_sources source); +enum pll_sources si5351c_activate_best_clock_source(void); #ifdef __cplusplus } |