Changeset - 0dd5c923fdea
[Not reviewed]
default
0 5 0
Ethan Zonca - 10 years ago 2016-01-19 22:08:19
ez@ethanzonca.com
i2c comms still not working
5 files changed with 34 insertions and 11 deletions:
0 comments (0 inline, 0 general)
inc/gpio.h
Show inline comments
 
#ifndef __gpio_H
 
#define __gpio_H
 
 
#include "stm32f0xx_hal.h"
 
 
 
#define OSC_EN_Pin GPIO_PIN_1
 
#define OSC_EN_GPIO_Port GPIOF
 
#define OSC_NOTEN OSC_EN_GPIO_Port , OSC_EN_Pin
 
 
#define VBATT_SENSE_Pin GPIO_PIN_6
 
#define VBATT_SENSE_GPIO_Port GPIOA
 
 
#define LED_BLUE_Pin GPIO_PIN_0
 
#define LED_BLUE_GPIO_Port GPIOB
 
#define LED_BLUE LED_BLUE_GPIO_Port , LED_BLUE_Pin
 
 
#define TCXO_EN_Pin GPIO_PIN_8
 
#define TCXO_EN_GPIO_Port GPIOA
 
#define TCXO_EN TCXO_EN_GPIO_Port  , TCXO_EN_Pin
 
 
 
void MX_GPIO_Init(void);
 
void gpio_init(void);
 
void led_blink(uint8_t n);
 
 
#endif 
 
lib/si5351/si5351.c
Show inline comments
 
/*
 
 * si5351.cpp - Si5351 library for Arduino
 
 *
 
 * Copyright (C) 2015 Jason Milldrum <milldrum@gmail.com>
 
 *                    Dana H. Myers <k6jq@comcast.net>
 
 *
 
 * Some tuning algorithms derived from clk-si5351.c in the Linux kernel.
 
 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 
 * Rabeeh Khoury <rabeeh@solid-run.com>
 
 *
 
 * This program 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 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#include <stdint.h>
 
#include "stm32f0xx_hal.h"
 
#include "si5351.h"
 
#include "gpio.h"
 

	
 

	
 
// Private prototypes
 
uint64_t si5351_pll_calc(uint64_t, struct Si5351RegSet *, int32_t);
 
uint64_t si5351_multisynth_calc(uint64_t, uint64_t, struct Si5351RegSet *);
 
void si5351_update_sys_status(struct Si5351Status *);
 
void si5351_update_int_status(struct Si5351IntStatus *);
 
void si5351_ms_div(enum si5351_clock, uint8_t, uint8_t);
 
uint8_t si5351_select_r_div(uint64_t *);
 

	
 
// Globals
 
int32_t ref_correction;
 
uint8_t lock_plla, lock_pllb;
 
uint32_t xtal_freq;
 

	
 
uint64_t plla_freq;
 
uint64_t pllb_freq;
 
uint64_t clk0_freq;
 
uint64_t clk1_freq;
 
uint64_t clk2_freq;
 
uint8_t clk0_int_mode, clk1_int_mode, clk2_int_mode;
 

	
 
struct Si5351Status dev_status;
 
struct Si5351IntStatus dev_int_status;
 

	
 
I2C_HandleTypeDef* si5351_i2cport;
 

	
 
/********************/
 
/* Public functions */
 
/********************/
 

	
 
/*
 
 * init(uint8_t xtal_load_c, uint32_t ref_osc_freq)
 
 *
 
 * Setup communications to the Si5351 and set the crystal
 
 * load capacitance.
 
 *
 
 * xtal_load_c - Crystal load capacitance. Use the SI5351_CRYSTAL_LOAD_*PF
 
 * defines in the header file
 
 * ref_osc_freq - Crystal/reference oscillator frequency in 1 Hz increments.
 
 * Defaults to 25000000 if a 0 is used here.
 
 *
 
 */
 
void si5351_init(I2C_HandleTypeDef* i2cport, uint8_t xtal_load_c, uint32_t ref_osc_freq)
 
{
 
    si5351_i2cport = i2cport;
 
	lock_plla = SI5351_CLKNONE;
 
	lock_pllb = SI5351_CLKNONE;
 
	clk0_int_mode = 0;
 
	clk1_int_mode = 0;
 
	clk2_int_mode = 0;
 
	plla_freq = 0ULL;
 
	pllb_freq = 0ULL;
 
	clk0_freq = 0ULL;
 
	clk1_freq = 0ULL;
 
	clk2_freq = 0ULL;
 
	xtal_freq = SI5351_XTAL_FREQ;
 

	
 
	// Set crystal load capacitance
 
	si5351_write(SI5351_CRYSTAL_LOAD, xtal_load_c);
 
	
 
	// Change the ref osc freq if different from default
 
	if (ref_osc_freq != 0)
 
	{
 
		xtal_freq = ref_osc_freq;
 
	}
 
	
 
	// Initialize the CLK outputs according to flowchart in datasheet
 
	// First, turn them off
 
	si5351_write(16, 0x80);
 
	si5351_write(17, 0x80);
 
	si5351_write(18, 0x80);
 
	
 
	// Turn the clocks back on...
 
	si5351_write(16, 0x0c);
 
	si5351_write(17, 0x0c);
 
	si5351_write(18, 0x0c);
 
  
 
	// Then reset the PLLs
 
	si5351_pll_reset(SI5351_PLLA);
 
	si5351_pll_reset(SI5351_PLLB);
 
}
 

	
 
/*
 
 * set_freq(uint64_t freq, uint64_t pll_freq, enum si5351_clock output)
 
 *
 
 * Sets the clock frequency of the specified CLK output
 
 *
 
 * freq - Output frequency in Hz
 
 * pll_freq - Frequency of the PLL driving the Multisynth
 
 *   Use a 0 to have the function choose a PLL frequency
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 */
 
uint8_t si5351_set_freq(uint64_t freq, uint64_t pll_freq, enum si5351_clock clk)
 
{
 
	struct Si5351RegSet ms_reg, pll_reg;
 
	enum si5351_pll target_pll;
 
	uint8_t write_pll = 0;
 
	uint8_t reg_val;
 
	uint8_t r_div = SI5351_OUTPUT_CLK_DIV_1;
 
	uint8_t int_mode = 0;
 
	uint8_t div_by_4 = 0;
 
	
 
	// PLL bounds checking
 
	if(pll_freq != 0)
 
	{
 
		if ((pll_freq < SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT) || (pll_freq > SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT))
 
		{
 
			return 1;
 
		}
 
	}
 

	
 
	// Lower bounds check	
 
	if(freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT;
 
	}
 
	
 
	// Upper bounds check
 
	if(freq > SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT;
 
	}
 
	
 
	// Select the proper R div value
 
	r_div = si5351_select_r_div(&freq);
 
	
 
	// Calculate the synth parameters
 
	// If pll_freq is 0 and freq < 150 MHz, let the algorithm pick a PLL frequency
 
	if((pll_freq) && (freq < SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT))
 
	{
 
		si5351_multisynth_calc(freq, pll_freq, &ms_reg);
 
		write_pll = 0;
 
		div_by_4 = 0;
 
		int_mode = 0;
 
		
 
		switch(clk)
 
		{
 
		case SI5351_CLK0:
 
			clk0_freq = freq;
 
			break;
 
		case SI5351_CLK1:
 
			clk1_freq = freq;
 
			break;
 
		case SI5351_CLK2:
 
			clk2_freq = freq;
 
			break;
 
		default:
 
			break;
 
		}
 
	}
 
	else
 
	{
 
		// The PLL must be calculated and set by firmware when 150 MHz <= freq <= 160 MHz
 
		if(freq >= SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT)
 
		{
 
			pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
 
			write_pll = 1;
 
			div_by_4 = 1;
 
			int_mode = 1;
 
		}
 
		
 
		// Determine which PLL to use
 
		// CLK0 gets PLLA, CLK1 gets PLLB
 
		// CLK2 gets PLLB if necessary
 
		// Only good for Si5351A3 variant at the moment
 
		switch(clk)
 
		{
 
		case SI5351_CLK0:
 
			pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
 
			target_pll = SI5351_PLLA;
 
			write_pll = 1;
 
			si5351_set_ms_source(SI5351_CLK0, SI5351_PLLA);
 
		
 
			plla_freq = pll_freq;
 
			clk0_freq = freq;
 
			break;
 
		case SI5351_CLK1:
 
			// Check to see if PLLB is locked due to other output being < 1.024 MHz or >= 112.5 MHz
 
			if(lock_pllb == SI5351_CLK2)
 
			{
 
				// We can't have a 2nd output < 1.024 MHz or >= 112.5 MHz on the same PLL unless exact same freq, so exit
 
				if((freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT 
 
					|| freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128) && freq != clk2_freq)
 
				{
 
					clk1_freq = 0;
 
					return 1;
 
				}
 
				// Else, set multisynth to same PLL freq as CLK2
 
				else
 
				{
 
					pll_freq = pllb_freq;
 
					si5351_multisynth_calc(freq, pll_freq, &ms_reg);
 
					write_pll = 0;
 
					si5351_set_ms_source(SI5351_CLK1, SI5351_PLLB);
 
				}
 
			}
 
			else
 
			{
 
				pllb_freq = pll_freq;
 
				pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
 
				write_pll = 1;
 
				si5351_set_ms_source(SI5351_CLK1, SI5351_PLLB);
 
			}
 
		
 
			if(freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128)
 
			{
 
				lock_pllb = SI5351_CLK1;
 
		
 
				// Recalc and rewrite the multisynth parameters on CLK2
 
				if(clk2_freq != 0)
 
				{
 
					struct Si5351RegSet ms_temp_reg;
 
					r_div = si5351_select_r_div(&clk2_freq);
 
					si5351_multisynth_calc(clk2_freq, pllb_freq, &ms_temp_reg);
 
					si5351_set_ms(SI5351_CLK2, ms_temp_reg, 0, r_div, 0);
 
				}
 
			}
 
			else
 
			{
 
				lock_pllb = SI5351_CLKNONE;
 
			}
 
		
 
			target_pll = SI5351_PLLB;
 
			clk1_freq = freq;
 
			break;
 
		case SI5351_CLK2:
 
			// Check to see if PLLB is locked due to other output being < 1.024 MHz or >= 112.5 MHz
 
			if(lock_pllb == SI5351_CLK1)
 
			{
 
				// We can't have a 2nd output < 1.024 MHz  or >= 112.5 MHz on the same PLL unless exact same freq, so exit
 
				if((freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT 
 
					|| freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128) && freq != clk2_freq)
 
				{
 
					clk2_freq = 0;
 
					return 1;
 
				}
 
				// Else, set multisynth to same PLL freq as CLK1
 
				else
 
				{
 
					pll_freq = pllb_freq;
 
					si5351_multisynth_calc(freq, pll_freq, &ms_reg);
 
					write_pll = 0;
 
					si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
 
				}
 
			}
 
			// need to account for CLK2 set before CLK1
 
			else
 
			{
 
				pllb_freq = pll_freq;
 
				pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
 
				write_pll = 1;
 
				si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
 
			}
 
		
 
			if(freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128)
 
			{
 
				lock_pllb = SI5351_CLK2;
 
		
 
				if(clk1_freq != 0)
 
				{
 
					// Recalc and rewrite the multisynth parameters on CLK1
 
					struct Si5351RegSet ms_temp_reg;
 
					r_div = si5351_select_r_div(&clk1_freq);
 
					si5351_multisynth_calc(clk1_freq, pllb_freq, &ms_temp_reg);
 
					si5351_set_ms(SI5351_CLK1, ms_temp_reg, 0, r_div, 0);
 
				}
 
			}
 
			else
 
			{
 
				lock_pllb = SI5351_CLKNONE;
 
			}
 
		
 
			target_pll = SI5351_PLLB;
 
			clk2_freq = freq;
 
			break;
 
		default:
 
			return 1;
 
		}
 
	}
 

	
 
	// Set multisynth registers (MS must be set before PLL)
 
	si5351_set_ms(clk, ms_reg, int_mode, r_div, div_by_4);
 
	
 
	// Set PLL if necessary
 
	if(write_pll == 1)
 
	{
 
		si5351_set_pll(pll_freq, target_pll);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/*
 
 * set_pll(uint64_t pll_freq, enum si5351_pll target_pll)
 
 *
 
 * Set the specified PLL to a specific oscillation frequency
 
 *
 
 * pll_freq - Desired PLL frequency
 
 * target_pll - Which PLL to set
 
 *     (use the si5351_pll enum)
 
 */
 

	
 

	
 
uint8_t params[20];
 

	
 
void si5351_set_pll(uint64_t pll_freq, enum si5351_pll target_pll)
 
{ struct Si5351RegSet pll_reg;
 

	
 
  si5351_pll_calc(pll_freq, &pll_reg, ref_correction);
 

	
 
  // Derive the register values to write 
 

	
 
  // Prepare an array for parameters to be written to
 
//  uint8_t *params = new uint8_t[20];
 
  uint8_t i = 0;
 
  uint8_t temp;
 

	
 
  // Registers 26-27
 
  temp = ((pll_reg.p3 >> 8) & 0xFF);
 
  params[i++] = temp;
 

	
 
  temp = (uint8_t)(pll_reg.p3  & 0xFF);
 
  params[i++] = temp;
 

	
 
  // Register 28
 
  temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03);
 
  params[i++] = temp;
 

	
 
  // Registers 29-30
 
  temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF);
 
  params[i++] = temp;
 

	
 
  temp = (uint8_t)(pll_reg.p1  & 0xFF);
 
  params[i++] = temp;
 

	
 
  // Register 31
 
  temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0);
 
  temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F);
 
  params[i++] = temp;
 

	
 
  // Registers 32-33
 
  temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF);
 
  params[i++] = temp;
 

	
 
  temp = (uint8_t)(pll_reg.p2  & 0xFF);
 
  params[i++] = temp;
 

	
 
  // Write the parameters
 
  if(target_pll == SI5351_PLLA)
 
  {
 
    si5351_write_bulk(SI5351_PLLA_PARAMETERS, i, params);
 
  }
 
  else if(target_pll == SI5351_PLLB)
 
  {
 
    si5351_write_bulk(SI5351_PLLB_PARAMETERS, i, params);
 
  }
 

	
 
//  delete params;
 
}
 

	
 
/*
 
 * set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4)
 
 *
 
 * Set the specified multisynth parameters. Not normally needed, but public for advanced users.
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * int_mode - Set integer mode
 
 *  Set to 1 to enable, 0 to disable
 
 * r_div - Desired r_div ratio
 
 * div_by_4 - Set Divide By 4 mode
 
 *   Set to 1 to enable, 0 to disable
 
 */
 
void si5351_set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4)
 
{
 
//	uint8_t *params = new uint8_t[20];
 
	uint8_t i = 0;
 
 	uint8_t temp;
 
 	uint8_t reg_val;
 

	
 
	// Registers 42-43 for CLK0
 
	temp = (uint8_t)((ms_reg.p3 >> 8) & 0xFF);
 
	params[i++] = temp;
 

	
 
	temp = (uint8_t)(ms_reg.p3  & 0xFF);
 
	params[i++] = temp;
 

	
 
	// Register 44 for CLK0
 
	reg_val = si5351_read((SI5351_CLK0_PARAMETERS + 2) + (clk * 8));
 
	reg_val &= ~(0x03);
 
	temp = reg_val | ((uint8_t)((ms_reg.p1 >> 16) & 0x03));
 
	params[i++] = temp;
 

	
 
	// Registers 45-46 for CLK0
 
	temp = (uint8_t)((ms_reg.p1 >> 8) & 0xFF);
 
	params[i++] = temp;
 

	
 
	temp = (uint8_t)(ms_reg.p1  & 0xFF);
 
	params[i++] = temp;
 

	
 
	// Register 47 for CLK0
 
	temp = (uint8_t)((ms_reg.p3 >> 12) & 0xF0);
 
	temp += (uint8_t)((ms_reg.p2 >> 16) & 0x0F);
 
	params[i++] = temp;
 

	
 
	// Registers 48-49 for CLK0
 
	temp = (uint8_t)((ms_reg.p2 >> 8) & 0xFF);
 
	params[i++] = temp;
 

	
 
	temp = (uint8_t)(ms_reg.p2  & 0xFF);
 
	params[i++] = temp;
 

	
 
	// Write the parameters
 
	switch(clk)
 
	{
 
		case SI5351_CLK0:
 
			si5351_write_bulk(SI5351_CLK0_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK1:
 
			si5351_write_bulk(SI5351_CLK1_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK2:
 
			si5351_write_bulk(SI5351_CLK2_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK3:
 
			si5351_write_bulk(SI5351_CLK3_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK4:
 
			si5351_write_bulk(SI5351_CLK4_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK5:
 
			si5351_write_bulk(SI5351_CLK5_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK6:
 
			si5351_write_bulk(SI5351_CLK6_PARAMETERS, i, params);
 
			break;
 
		case SI5351_CLK7:
 
			si5351_write_bulk(SI5351_CLK7_PARAMETERS, i, params);
 
			break;
 
	}
 
	
 
	si5351_set_int(clk, int_mode);
 
	si5351_ms_div(clk, r_div, div_by_4);
 

	
 
	//delete params;
 
}
 

	
 
/*
 
 * output_enable(enum si5351_clock clk, uint8_t enable)
 
 *
 
 * Enable or disable a chosen output
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * enable - Set to 1 to enable, 0 to disable
 
 */
 
void si5351_output_enable(enum si5351_clock clk, uint8_t enable)
 
{
 
  uint8_t reg_val;
 

	
 
  reg_val = si5351_read(SI5351_OUTPUT_ENABLE_CTRL);
 

	
 
  if(enable == 1)
 
  {
 
    reg_val &= ~(1<<(uint8_t)clk);
 
  }
 
  else
 
  {
 
    reg_val |= (1<<(uint8_t)clk);
 
  }
 

	
 
  si5351_write(SI5351_OUTPUT_ENABLE_CTRL, reg_val);
 
}
 

	
 
/*
 
 * drive_strength(enum si5351_clock clk, enum si5351_drive drive)
 
 *
 
 * Sets the drive strength of the specified clock output
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * drive - Desired drive level
 
 *   (use the si5351_drive enum)
 
 */
 
void si5351_drive_strength(enum si5351_clock clk, enum si5351_drive drive)
 
{
 
  uint8_t reg_val;
 
  const uint8_t mask = 0x03;
 

	
 
  reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 
  reg_val &= ~(mask);
 

	
 
  switch(drive)
 
  {
 
  case SI5351_DRIVE_2MA:
 
    reg_val |= 0x00;
 
    break;
 
  case SI5351_DRIVE_4MA:
 
   reg_val |= 0x01;
 
    break;
 
  case SI5351_DRIVE_6MA:
 
    reg_val |= 0x02;
 
    break;
 
  case SI5351_DRIVE_8MA:
 
    reg_val |= 0x03;
 
    break;
 
  default:
 
    break;
 
  }
 

	
 
  si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
}
 

	
 
/*
 
 * update_status(void)
 
 *
 
 * Call this to update the status structs, then access them
 
 * via the dev_status and dev_int_status global variables.
 
 *
 
 * See the header file for the struct definitions. These
 
 * correspond to the flag names for registers 0 and 1 in
 
 * the Si5351 datasheet.
 
 */
 
void si5351_update_status(void)
 
{
 
	si5351_update_sys_status(&dev_status);
 
	si5351_update_int_status(&dev_int_status);
 
}
 

	
 
/*
 
 * set_correction(int32_t corr)
 
 *
 
 * Use this to set the oscillator correction factor to
 
 * EEPROM. This value is a signed 32-bit integer of the
 
 * parts-per-billion value that the actual oscillation
 
 * frequency deviates from the specified frequency.
 
 *
 
 * The frequency calibration is done as a one-time procedure.
 
 * Any desired test frequency within the normal range of the
 
 * Si5351 should be set, then the actual output frequency
 
 * should be measured as accurately as possible. The
 
 * difference between the measured and specified frequencies
 
 * should be calculated in Hertz, then multiplied by 10 in
 
 * order to get the parts-per-billion value.
 
 *
 
 * Since the Si5351 itself has an intrinsic 0 PPM error, this
 
 * correction factor is good across the entire tuning range of
 
 * the Si5351. Once this calibration is done accurately, it
 
 * should not have to be done again for the same Si5351 and
 
 * crystal. The library will read the correction factor from
 
 * EEPROM during initialization for use by the tuning
 
 * algorithms.
 
 */
 
void si5351_set_correction(int32_t corr)
 
{
 
	ref_correction = corr;
 
}
 

	
 
/*
 
 * set_phase(enum si5351_clock clk, uint8_t phase)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * phase - 7-bit phase word
 
 *   (in units of VCO/4 period)
 
 *
 
 * Write the 7-bit phase register. This must be used
 
 * with a user-set PLL frequency so that the user can
 
 * calculate the proper tuning word based on the PLL period.
 
 */
 
void si5351_set_phase(enum si5351_clock clk, uint8_t phase)
 
{
 
	// Mask off the upper bit since it is reserved
 
	phase = phase & 0b01111111;
 
	
 
	si5351_write(SI5351_CLK0_PHASE_OFFSET + (uint8_t)clk, phase);
 
}
 

	
 
/*
 
 * get_correction(void)
 
 *
 
 * Returns the oscillator correction factor stored
 
 * in RAM.
 
 */
 
int32_t si5351_get_correction(void)
 
{
 
	return ref_correction;
 
}
 

	
 
/*
 
 * pll_reset(enum si5351_pll target_pll)
 
 *
 
 * target_pll - Which PLL to reset
 
 *     (use the si5351_pll enum)
 
 *
 
 * Apply a reset to the indicated PLL.
 
 */
 
void si5351_pll_reset(enum si5351_pll target_pll)
 
{
 
	if(target_pll == SI5351_PLLA)
 
 	{
 
    	si5351_write(SI5351_PLL_RESET, SI5351_PLL_RESET_A);
 
	}
 
	else if(target_pll == SI5351_PLLB)
 
	{
 
	    si5351_write(SI5351_PLL_RESET, SI5351_PLL_RESET_B);
 
	}
 
}
 

	
 
/*
 
 * set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * pll - Which PLL to use as the source
 
 *     (use the si5351_pll enum)
 
 *
 
 * Set the desired PLL source for a multisynth.
 
 */
 
void si5351_set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
 
{
 
	uint8_t reg_val;
 

	
 
	reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 

	
 
	if(pll == SI5351_PLLA)
 
	{
 
		reg_val &= ~(SI5351_CLK_PLL_SELECT);
 
	}
 
	else if(pll == SI5351_PLLB)
 
	{
 
		reg_val |= SI5351_CLK_PLL_SELECT;
 
	}
 
	
 
	si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
}
 

	
 
/*
 
 * set_int(enum si5351_clock clk, uint8_t int_mode)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * enable - Set to 1 to enable, 0 to disable
 
 *
 
 * Set the indicated multisynth into integer mode.
 
 */
 
void si5351_set_int(enum si5351_clock clk, uint8_t enable)
 
{
 
	uint8_t reg_val;
 
	reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 
	
 
	if(enable == 1)
 
	{
 
		reg_val |= (SI5351_CLK_INTEGER_MODE);
 
	}
 
	else
 
	{
 
		reg_val &= ~(SI5351_CLK_INTEGER_MODE);
 
	}
 
	
 
	si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
	
 
	// Integer mode indication
 
	switch(clk)
 
	{
 
	case SI5351_CLK0:
 
		clk0_int_mode = enable;
 
		break;
 
	case SI5351_CLK1:
 
		clk1_int_mode = enable;
 
		break;
 
	case SI5351_CLK2:
 
		clk2_int_mode = enable;
 
		break;
 
	default:
 
		break;
 
	}
 
}
 

	
 
/*
 
 * set_clock_pwr(enum si5351_clock clk, uint8_t pwr)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * pwr - Set to 1 to enable, 0 to disable
 
 *
 
 * Enable or disable power to a clock output (a power
 
 * saving feature).
 
 */
 
void si5351_set_clock_pwr(enum si5351_clock clk, uint8_t pwr)
 
{
 
	uint8_t reg_val, reg;
 
	reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 
	
 
	if(pwr == 1)
 
	{
 
		reg_val &= 0b01111111;
 
	}
 
	else
 
	{
 
		reg_val |= 0b10000000;
 
	}
 
	
 
	si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
}
 

	
 
/*
 
 * set_clock_invert(enum si5351_clock clk, uint8_t inv)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * inv - Set to 1 to enable, 0 to disable
 
 *
 
 * Enable to invert the clock output waveform.
 
 */
 
void si5351_set_clock_invert(enum si5351_clock clk, uint8_t inv)
 
{
 
	uint8_t reg_val;
 
	reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 
	
 
	if(inv == 1)
 
	{
 
		reg_val |= (SI5351_CLK_INVERT);
 
	}
 
	else
 
	{
 
		reg_val &= ~(SI5351_CLK_INVERT);
 
	}
 
	
 
	si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
}
 

	
 
/*
 
 * set_clock_source(enum si5351_clock clk, enum si5351_clock_source src)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * src - Which clock source to use for the multisynth
 
 *   (use the si5351_clock_source enum)
 
 *
 
 * Set the clock source for a multisynth (based on the options
 
 * presented for Registers 16-23 in the Silicon Labs AN619 document).
 
 * Choices are XTAL, CLKIN, MS0, or the multisynth associated with
 
 * the clock output.
 
 */
 
void si5351_set_clock_source(enum si5351_clock clk, enum si5351_clock_source src)
 
{
 
	uint8_t reg_val;
 
	reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
 
	
 
	// Clear the bits first
 
	reg_val &= ~(SI5351_CLK_INPUT_MASK);
 
	
 
	switch(src)
 
	{
 
	case SI5351_CLK_SRC_XTAL:
 
		reg_val |= (SI5351_CLK_INPUT_XTAL);
 
		break;
 
	case SI5351_CLK_SRC_CLKIN:
 
		reg_val |= (SI5351_CLK_INPUT_CLKIN);
 
		break;
 
	case SI5351_CLK_SRC_MS0:
 
		if(clk == SI5351_CLK0)
 
		{
 
			return;
 
		}
 
		
 
		reg_val |= (SI5351_CLK_INPUT_MULTISYNTH_0_4);
 
		break;
 
	case SI5351_CLK_SRC_MS:
 
		reg_val |= (SI5351_CLK_INPUT_MULTISYNTH_N);
 
		break;
 
	default:
 
		return;
 
	}
 
	
 
	si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
 
}
 

	
 
/*
 
 * set_clock_disable(enum si5351_clock clk, enum si5351_clock_disable dis_state)
 
 *
 
 * clk - Clock output
 
 *   (use the si5351_clock enum)
 
 * dis_state - Desired state of the output upon disable
 
 *   (use the si5351_clock_disable enum)
 
 *
 
 * Set the state of the clock output when it is disabled. Per page 27
 
 * of AN619 (Registers 24 and 25), there are four possible values: low,
 
 * high, high impedance, and never disabled.
 
 */
 
void si5351_set_clock_disable(enum si5351_clock clk, enum si5351_clock_disable dis_state)
 
{
 
	uint8_t reg_val, reg;
 
	
 
	if (clk >= SI5351_CLK0 && clk <= SI5351_CLK3)
 
	{
 
		reg = SI5351_CLK3_0_DISABLE_STATE;
 
	}
 
	else if (clk >= SI5351_CLK0 && clk <= SI5351_CLK3)
 
	{
 
		reg = SI5351_CLK7_4_DISABLE_STATE;
 
	}
 
	
 
	reg_val = si5351_read(reg);
 
	
 
	if (clk >= SI5351_CLK0 && clk <= SI5351_CLK3)
 
	{
 
		reg_val &= ~(0b11 << (clk * 2));
 
		reg_val |= dis_state << (clk * 2);
 
	}
 
	else if (clk >= SI5351_CLK0 && clk <= SI5351_CLK3)
 
	{
 
		reg_val &= ~(0b11 << ((clk - 4) * 2));
 
		reg_val |= dis_state << ((clk - 4) * 2);
 
	}
 
	
 
	si5351_write(reg, reg_val);
 
}
 

	
 
/*
 
 * set_clock_fanout(enum si5351_clock_fanout fanout, uint8_t enable)
 
 *
 
 * fanout - Desired clock fanout
 
 *   (use the si5351_clock_fanout enum)
 
 * enable - Set to 1 to enable, 0 to disable
 
 *
 
 * Use this function to enable or disable the clock fanout options
 
 * for individual clock outputs. If you intend to output the XO or
 
 * CLKIN on the clock outputs, enable this first.
 
 *
 
 * By default, only the Multisynth fanout is enabled at startup.
 
 */
 
void si5351_set_clock_fanout(enum si5351_clock_fanout fanout, uint8_t enable)
 
{
 
	uint8_t reg_val;
 
	reg_val = si5351_read(SI5351_FANOUT_ENABLE);
 
	
 
	switch(fanout)
 
	{
 
	case SI5351_FANOUT_CLKIN:
 
		if(enable)
 
		{
 
			reg_val |= SI5351_CLKIN_ENABLE;
 
		}
 
		else
 
		{
 
			reg_val &= ~(SI5351_CLKIN_ENABLE);
 
		}
 
		break;
 
	case SI5351_FANOUT_XO:
 
		if(enable)
 
		{
 
			reg_val |= SI5351_XTAL_ENABLE;
 
		}
 
		else
 
		{
 
			reg_val &= ~(SI5351_XTAL_ENABLE);
 
		}
 
		break;
 
	case SI5351_FANOUT_MS:
 
		if(enable)
 
		{
 
			reg_val |= SI5351_MULTISYNTH_ENABLE;
 
		}
 
		else
 
		{
 
			reg_val &= ~(SI5351_MULTISYNTH_ENABLE);
 
		}
 
		break;
 
	}
 
	
 
	si5351_write(SI5351_FANOUT_ENABLE, reg_val);
 
}
 

	
 

	
 

	
 

	
 
/* AAAH REFREAKINGIMPLEMENT WITH COMMON STANDARD I2C THINGS:
 
~/Projects/featherhab-fw/libopencm3/lib/stm32/common/i2c_common_all.c
 
*/
 

	
 
uint8_t si5351_write_bulk(uint8_t addr, uint8_t bytes, uint8_t *data)
 
{
 
    HAL_I2C_Mem_Write(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data, bytes, 100);
 
    HAL_Delay(300);
 
    uint32_t res = HAL_I2C_Mem_Write(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data, bytes, 100);
 
    led_blink(res);
 
    HAL_Delay(300);
 

	
 

	
 

	
 
/*
 
	Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
 
	Wire.write(addr);
 
	for(int i = 0; i < bytes; i++)
 
	{
 
		Wire.write(data[i]);
 
	}
 
	Wire.endTransmission();
 
*/
 
}
 

	
 
uint8_t si5351_write(uint8_t addr, uint8_t data)
 
{
 
    HAL_Delay(300);
 
    uint8_t data_arr[1] = {data};
 
    HAL_I2C_Mem_Write(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data, 1, 100);
 
    uint8_t res = HAL_I2C_Mem_Write(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data, 1, 100);
 

	
 
    led_blink(res);
 
    HAL_Delay(300);
 
/*
 
	Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
 
	Wire.write(addr);
 
	Wire.write(data);
 
	Wire.endTransmission();
 
*/
 
}
 

	
 
uint8_t si5351_read(uint8_t addr)
 
{
 
	uint8_t data_arr[1] = {0};
 

	
 
    HAL_I2C_Mem_Read(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data_arr, 1, 100);
 
    HAL_Delay(300);
 
    uint8_t res = HAL_I2C_Mem_Read(si5351_i2cport, SI5351_BUS_BASE_ADDR, addr, 1, data_arr, 1, 100);
 

	
 
    led_blink(res);
 
    HAL_Delay(300);
 
/*
 
	Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
 
	Wire.write(addr);
 
	Wire.endTransmission();
 

	
 
	Wire.requestFrom(SI5351_BUS_BASE_ADDR, 1, false);
 

	
 
	while(Wire.available())
 
	{
 
		reg_val = Wire.read();
 
	}
 
*/	
 
	return data_arr[1];
 
}
 

	
 
/*********************/
 
/* Private functions */
 
/*********************/
 

	
 
uint64_t si5351_pll_calc(uint64_t freq, struct Si5351RegSet *reg, int32_t correction)
 
{
 
	uint64_t ref_freq = xtal_freq * SI5351_FREQ_MULT;
 
	uint32_t a, b, c, p1, p2, p3;
 
	uint64_t lltmp, rfrac, denom;
 
	int64_t ref_temp;
 

	
 
	// Factor calibration value into nominal crystal frequency
 
	// Measured in parts-per-billion
 

	
 
	ref_freq = ref_freq + (int32_t)((((((int64_t)correction) << 31) / 1000000000LL) * ref_freq) >> 31);
 

	
 
	// PLL bounds checking
 
	if (freq < SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT;
 
	}
 
	if (freq > SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT;
 
	}
 

	
 
	// Determine integer part of feedback equation
 
	a = freq / ref_freq;
 

	
 
	if (a < SI5351_PLL_A_MIN)
 
	{
 
		freq = ref_freq * SI5351_PLL_A_MIN;
 
	}
 
	if (a > SI5351_PLL_A_MAX)
 
	{
 
		freq = ref_freq * SI5351_PLL_A_MAX;
 
	}
 

	
 
	// Find best approximation for b/c = fVCO mod fIN
 
	denom = 1000ULL * 1000ULL;
 
	lltmp = freq % ref_freq;
 
	lltmp *= denom;
 
	si5351_do_div(lltmp, ref_freq);
 
	rfrac = lltmp;
 
	
 
	b = (((uint64_t)(freq % ref_freq)) * RFRAC_DENOM) / ref_freq;
 
	c = b ? RFRAC_DENOM : 1;
 

	
 
	// Calculate parameters
 
    p1 = 128 * a + ((128 * b) / c) - 512;
 
    p2 = 128 * b - c * ((128 * b) / c);
 
    p3 = c;
 

	
 
	// Recalculate frequency as fIN * (a + b/c)
 
	lltmp  = ref_freq;
 
	lltmp *= b;
 
	si5351_do_div(lltmp, c);
 
	freq = lltmp;
 
	freq += ref_freq * a;
 

	
 
	reg->p1 = p1;
 
	reg->p2 = p2;
 
	reg->p3 = p3;
 

	
 
	return freq;
 
}
 

	
 
uint64_t si5351_multisynth_calc(uint64_t freq, uint64_t pll_freq, struct Si5351RegSet *reg)
 
{
 
	uint64_t lltmp;
 
	uint32_t a, b, c, p1, p2, p3;
 
	uint8_t divby4;
 
	uint8_t ret_val = 0;
 

	
 
	// Multisynth bounds checking
 
	if (freq > SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT;
 
	}
 
	if (freq < SI5351_MULTISYNTH_MIN_FREQ * SI5351_FREQ_MULT)
 
	{
 
		freq = SI5351_MULTISYNTH_MIN_FREQ * SI5351_FREQ_MULT;
 
	}
 

	
 
	divby4 = 0;
 
	if (freq >= SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT)
 
	{
 
		divby4 = 1;
 
	}
 

	
 
	if(pll_freq == 0)
 
	{
 
		// Find largest integer divider for max
 
		// VCO frequency and given target frequency
 
		if(divby4 == 0)
 
		{
 
			lltmp = SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT;
 
			si5351_do_div(lltmp, freq);
 
			a = (uint32_t)lltmp;
 
		}
 
		else
 
		{
 
			a = 4;
 
		}
 

	
 
		b = 0;
 
		c = 1;
 
		pll_freq = a * freq;
 
	}
 
	else
 
	{
 
		// Preset PLL, so return the actual freq for these params instead of PLL freq
 
		ret_val = 1;
 
		
 
		// Determine integer part of feedback equation
 
		a = pll_freq / freq;
 
	
 
		if (a < SI5351_MULTISYNTH_A_MIN)
 
		{
 
			freq = pll_freq / SI5351_MULTISYNTH_A_MIN;
 
		}
 
		if (a > SI5351_MULTISYNTH_A_MAX)
 
		{
 
			freq = pll_freq / SI5351_MULTISYNTH_A_MAX;
 
		}
 
	
 
		b = (pll_freq % freq * RFRAC_DENOM) / freq;
 
		c = b ? RFRAC_DENOM : 1;
 
	}
 

	
 
	// Calculate parameters
 
	if (divby4 == 1)
 
	{
 
		p3 = 1;
 
		p2 = 0;
 
		p1 = 0;
 
	}
 
	else
 
	{
 
        p1 = 128 * a + ((128 * b) / c) - 512;
 
        p2 = 128 * b - c * ((128 * b) / c);
 
        p3 = c;
 
	}
 

	
 
	reg->p1 = p1;
 
	reg->p2 = p2;
 
	reg->p3 = p3;
 

	
 
	if(ret_val == 0)
 
	{
 
		return pll_freq;
 
	}
 
	else
 
	{
 
		return freq;
 
	}
 
}
 

	
 
void si5351_update_sys_status(struct Si5351Status *status)
 
{
 
  uint8_t reg_val = 0;
 

	
 
  reg_val = si5351_read(SI5351_DEVICE_STATUS);
 

	
 
  // Parse the register
 
  status->SYS_INIT = (reg_val >> 7) & 0x01;
 
  status->LOL_B = (reg_val >> 6) & 0x01;
 
  status->LOL_A = (reg_val >> 5) & 0x01;
 
  status->LOS = (reg_val >> 4) & 0x01;
 
  status->REVID = reg_val & 0x03;
 
}
 

	
 
void si5351_update_int_status(struct Si5351IntStatus *int_status)
 
{
 
  uint8_t reg_val = 0;
 

	
 
  reg_val = si5351_read(SI5351_DEVICE_STATUS);
 

	
 
  // Parse the register
 
  int_status->SYS_INIT_STKY = (reg_val >> 7) & 0x01;
 
  int_status->LOL_B_STKY = (reg_val >> 6) & 0x01;
 
  int_status->LOL_A_STKY = (reg_val >> 5) & 0x01;
 
  int_status->LOS_STKY = (reg_val >> 4) & 0x01;
 
}
 

	
 
void si5351_ms_div(enum si5351_clock clk, uint8_t r_div, uint8_t div_by_4)
 
{
 
	uint8_t reg_val, reg_addr;
 
	
 
	switch(clk)
 
	{
 
		case SI5351_CLK0:
 
			reg_addr = SI5351_CLK0_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK1:
 
			reg_addr = SI5351_CLK1_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK2:
 
			reg_addr = SI5351_CLK2_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK3:
 
			reg_addr = SI5351_CLK3_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK4:
 
			reg_addr = SI5351_CLK4_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK5:
 
			reg_addr = SI5351_CLK5_PARAMETERS + 2;
 
			break;
 
		case SI5351_CLK6:
 
			return;
 
		case SI5351_CLK7:
 
			return;
 
	}
 
	
 
	reg_val = si5351_read(reg_addr);
 
	
 
	// Clear the relevant bits
 
	reg_val &= ~(0x7c);
 
	
 
	if(div_by_4 == 0)
 
	{
 
		reg_val &= ~(SI5351_OUTPUT_CLK_DIVBY4);
 
	}
 
	else
 
	{
 
		reg_val |= (SI5351_OUTPUT_CLK_DIVBY4);
 
	}
 
	
 
	reg_val |= (r_div << SI5351_OUTPUT_CLK_DIV_SHIFT);
 
	
 
	si5351_write(reg_addr, reg_val);
 
}
 

	
 
uint8_t si5351_select_r_div(uint64_t *freq)
 
{
 
	uint8_t r_div = SI5351_OUTPUT_CLK_DIV_1;
 
	
 
	// Choose the correct R divider
 
	if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 2))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_128;
 
		*freq *= 128ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 2) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 4))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_64;
 
		*freq *= 64ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 4) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 8))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_32;
 
		*freq *= 32ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 8) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 16))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_16;
 
		*freq *= 16ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 16) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 32))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_8;
 
		*freq *= 8ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 32) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 64))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_4;
 
		*freq *= 4ULL;
 
	}
 
	else if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 64) && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128))
 
	{
 
		r_div = SI5351_OUTPUT_CLK_DIV_2;
 
		*freq *= 2ULL;
 
	}
 
	
 
	return r_div;
 
}
 

	
src/gpio.c
Show inline comments
 
#include "gpio.h"
 
 
void MX_GPIO_Init(void)
 
void gpio_init(void)
 
{
 
 
  GPIO_InitTypeDef GPIO_InitStruct;
 
 
  /* GPIO Ports Clock Enable */
 
  __GPIOF_CLK_ENABLE();
 
  __GPIOA_CLK_ENABLE();
 
  __GPIOB_CLK_ENABLE();
 
 
  /*Configure GPIO pins : PFPin PFPin */
 
  GPIO_InitStruct.Pin = OSC_EN_Pin;
 
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
 
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
 
 
  /*Configure GPIO pins : PA0 PA2 PA3 PA4 
 
                           PA5 PA7 PA15 */
 
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4 
 
                          |GPIO_PIN_5|GPIO_PIN_7|GPIO_PIN_15;
 
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
  /*Configure GPIO pin : PA1 */
 
  GPIO_InitStruct.Pin = GPIO_PIN_1;
 
  GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
  /*Configure GPIO pin : PtPin */
 
  GPIO_InitStruct.Pin = LED_BLUE_Pin;
 
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
 
  HAL_GPIO_Init(LED_BLUE_GPIO_Port, &GPIO_InitStruct);
 
 
  /*Configure GPIO pins : PB1 PB3 PB4 PB5 */
 
  GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
 
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
 
  /*Configure GPIO pin : PtPin */
 
  GPIO_InitStruct.Pin = TCXO_EN_Pin;
 
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
 
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
 
  HAL_GPIO_Init(TCXO_EN_GPIO_Port, &GPIO_InitStruct);
 
 
}
 
 
void led_blink(uint8_t n)
 
{
 
    for(int i = 0; i < n; i++)
 
    {
 
        HAL_GPIO_WritePin(LED_BLUE, 1);
 
        HAL_Delay(100);
 
        HAL_GPIO_WritePin(LED_BLUE, 0);
 
        HAL_Delay(100);
 
    }
 
}
src/i2c.c
Show inline comments
 
//
 
// I2C: Initialize I2C peripheral and return a reference to the port
 
//
 
 
#include "i2c.h"
 
#include "gpio.h"
 
 
I2C_HandleTypeDef hi2c1;
 
 
void i2c_init(void)
 
{
 
    __GPIOA_CLK_ENABLE();
 
 
    GPIO_InitTypeDef GPIO_InitStruct;
 
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
 
    GPIO_InitStruct.Pull = GPIO_PULLUP;
 
    GPIO_InitStruct.Pull = GPIO_NOPULL;
 
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
 
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
 
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
    __I2C1_CLK_ENABLE();
 
 
    hi2c1.Instance = I2C1;
 
    hi2c1.Init.Timing = 0x2000090E;
 
    hi2c1.Init.OwnAddress1 = 0;
 
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
 
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
 
    hi2c1.Init.OwnAddress2 = 0;
 
    hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
 
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
 
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
 
    HAL_I2C_Init(&hi2c1);
 
 
    HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);
 
}
 
 
 
// Get pointer to I2C port
 
inline I2C_HandleTypeDef* i2c_get(void)
 
I2C_HandleTypeDef* i2c_get(void)
 
{
 
    return &hi2c1;
 
}
src/main.c
Show inline comments
 
#include "stm32f0xx_hal.h"
 
#include "si5351.h"
 
#include "jtencode.h"
 
#include "adc.h"
 
#include "dma.h"
 
#include "i2c.h"
 
#include "usart.h"
 
#include "gpio.h"
 
#include "gps.h"
 
 
 
void sysclk_init(void);
 
 
 
char call[7] = "N0CALL";
 
char loc[5] = "AA00";
 
uint8_t dbm = 27;
 
uint8_t tx_buffer[255];
 
 
int main(void)
 
{
 
 
    HAL_Init();
 
 
    sysclk_init();
 
    MX_GPIO_Init();
 
    gpio_init();
 
    led_blink(5);
 
    MX_DMA_Init();
 
    MX_ADC_Init();
 
    i2c_init();
 
 
    HAL_GPIO_WritePin(OSC_NOTEN, 0);
 
    HAL_GPIO_WritePin(TCXO_EN, 1);
 
    HAL_Delay(100);
 
 
//    MX_USART1_UART_Init();
 
 
 
    //jtencode_init();
 
    //gps_init();
 
    si5351_init(i2c_get(), SI5351_CRYSTAL_LOAD_8PF, 0);
 
    si5351_set_correction(0);
 
 
 
    si5351_set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
 
    si5351_set_ms_source(SI5351_CLK0, SI5351_PLLA);
 
 
    si5351_set_freq(100000UL * 100, 0, SI5351_CLK0);
 
 
    si5351_drive_strength(SI5351_CLK0, SI5351_DRIVE_2MA); // Set for max power if desired (8ma max)
 
    si5351_output_enable(SI5351_CLK0, 1); // Disable the clock initially
 
    //wspr_encode(call, loc, dbm, tx_buffer);
 
 
    si5351_pll_reset(SI5351_PLLA);
 
 
    uint32_t led_timer = HAL_GetTick();
 
    uint32_t last_gps  = HAL_GetTick();
 
 
    while (1)
 
    {
 
        if(HAL_GetTick() - led_timer > 100)
 
        {
 
            HAL_GPIO_TogglePin(LED_BLUE);
 
            led_timer = HAL_GetTick();
 
        }
 
        if(HAL_GetTick() - last_gps > 100)
 
        {
 
//            gps_process();
 
            last_gps = HAL_GetTick();
 
        }
 
    }
 
}
 
 
 
// Initialize system clocks
 
void sysclk_init(void)
 
{
 
    RCC_OscInitTypeDef RCC_OscInitStruct;
 
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    RCC_PeriphCLKInitTypeDef PeriphClkInit;
 
 
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
 
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 
    RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
 
    RCC_OscInitStruct.HSICalibrationValue = 16;
 
    RCC_OscInitStruct.HSI14CalibrationValue = 16;
 
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
 
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
 
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
 
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
 
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
 
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
 
 
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
 
    PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
 
    PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;
 
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
 
 
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
 
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
 
    // SysTick_IRQn interrupt configuration 
 
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
 
 
}
 
0 comments (0 inline, 0 general)