summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDimitri Stolnikov <horiz0n@gmx.net>2019-02-18 23:55:47 +0100
committerDimitri Stolnikov <horiz0n@gmx.net>2019-02-18 23:55:47 +0100
commit0e18df5ba646420178c43aca2bccb7023d682fb5 (patch)
tree3ecdbaaee99e66e87f7329af33a43189b3d13817
parent2a1659e0b927ca472fea270612858d6186a1d0ad (diff)
Enable 10MHz on each output with automatic switchover for CLKIN and XTAL
clock inputs
-rw-r--r--firmware/clock_generator.c50
-rw-r--r--firmware/main.c154
-rw-r--r--firmware/make/Makefile4
-rw-r--r--firmware/si5351c.c368
-rw-r--r--firmware/si5351c.h54
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
}