Files
bassofono/codice/Core/Src/si5351.c
2022-01-12 01:09:32 +01:00

176 lines
6.9 KiB
C

#include "main.h"
#include "stm32g4xx_hal.h"
#include <math.h>
#include "si5351.h"
void si5351_write8 (uint8_t reg, uint8_t value){
while (HAL_I2C_IsDeviceReady(&hi2c1, (uint16_t)(SI5351_ADDRESS<<1), 3, 100) != HAL_OK) { }
HAL_I2C_Mem_Write(&hi2c1, (uint8_t)(SI5351_ADDRESS<<1), (uint8_t)reg, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(&value), 1, 100);
}
uint8_t si5351_read8(uint8_t reg, uint8_t *value){
HAL_StatusTypeDef status = HAL_OK;
while (HAL_I2C_IsDeviceReady(&hi2c1, (uint16_t)(SI5351_ADDRESS<<1), 3, 100) != HAL_OK) { }
status = HAL_I2C_Mem_Read(&hi2c1, // i2c handle
(uint8_t)(SI5351_ADDRESS<<1), // i2c address, left aligned
(uint8_t)reg, // register address
I2C_MEMADD_SIZE_8BIT, // si5351 uses 8bit register addresses
(uint8_t*)(&value), // write returned data to this variable
1, // how many bytes to expect returned
100); // timeout
return status;
}
void CalcRegisters(uint32_t fout, uint8_t *regs){
uint32_t fref = SI5351_CRYSTAL_FREQ; // The reference frequency
// Calc Output Multisynth Divider and R with e = 0 and f = 1 => msx_p2 = 0 and msx_p3 = 1
uint32_t d = 4;
uint32_t msx_p1 = 0; // If fout > 150 MHz then MSx_P1 = 0 and MSx_DIVBY4 = 0xC0, see datasheet 4.1.3
int msx_divby4 = 0;
int rx_div = 0;
int r = 1;
if (fout > 150e6)
msx_divby4 = 0x0C; // MSx_DIVBY4[1:0] = 0b11, see datasheet 4.1.3
else if (fout < 292969UL) // If fout < 500 kHz then use R divider, see datasheet 4.2.2. In reality this means > 292 968,75 Hz when d = 2048
{
int rd = 0;
while ((r < 128) && (r * fout < 292969UL))
{
r <<= 1;
rd++;
}
rx_div = rd << 4;
d = 600e6 / (r * fout); // Use lowest VCO frequency but handle d minimum
if (d % 2) // Make d even to reduce spurious and phase noise/jitter, see datasheet 4.1.2.1.
d++;
if (d * r * fout < 600e6) // VCO frequency to low check and maintain an even d value
d += 2;
}
else // 292968 Hz <= fout <= 150 MHz
{
d = 600e6 / fout; // Use lowest VCO frequency but handle d minimum
if (d < 6)
d = 6;
else if (d % 2) // Make d even to reduce phase noise/jitter, see datasheet 4.1.2.1.
d++;
if (d * fout < 600e6) // VCO frequency to low check and maintain an even d value
d += 2;
}
msx_p1 = 128 * d - 512;
uint32_t fvco = (uint32_t) d * r * fout;
// Calc Feedback Multisynth Divider
double fmd = (double)fvco / fref; // The FMD value has been found
int a = fmd; // a is the integer part of the FMD value
double b_c = (double)fmd - a; // Get b/c
uint32_t c = 1048575UL;
uint32_t b = (double)b_c * c;
if (b > 0)
{
c = (double)b / b_c + 0.5; // Improves frequency precision in some cases
if (c > 1048575UL)
c = 1048575UL;
}
uint32_t msnx_p1 = 128 * a + 128 * b / c - 512; // See datasheet 3.2
uint32_t msnx_p2 = 128 * b - c * (128 * b / c);
uint32_t msnx_p3 = c;
// Feedback Multisynth Divider registers
regs[0] = (msnx_p3 >> 8) & 0xFF;
regs[1] = msnx_p3 & 0xFF;
regs[2] = (msnx_p1 >> 16) & 0x03;
regs[3] = (msnx_p1 >> 8) & 0xFF;
regs[4] = msnx_p1 & 0xFF;
regs[5] = ((msnx_p3 >> 12) & 0xF0) + ((msnx_p2 >> 16) & 0x0F);
regs[6] = (msnx_p2 >> 8) & 0xFF;
regs[7] = msnx_p2 & 0xFF;
// Output Multisynth Divider registers
regs[8] = 0; // (msx_p3 >> 8) & 0xFF
regs[9] = 1; // msx_p3 & 0xFF
regs[10] = rx_div + msx_divby4 + ((msx_p1 >> 16) & 0x03);
regs[11] = (msx_p1 >> 8) & 0xFF;
regs[12] = msx_p1 & 0xFF;
regs[13] = 0; // ((msx_p3 >> 12) & 0xF0) + (msx_p2 >> 16) & 0x0F
regs[14] = 0; // (msx_p2 >> 8) & 0xFF
regs[15] = 0; // msx_p2 & 0xFF
// HAL_I2C_Master_Transmit(&hi2c2, Si5351_ConfigStruct->HW_I2C_Address, reg_data, sizeof(reg_data), HAL_MAX_DELAY);
return;
}
void si53531_initialize(){
uint8_t dummy;
// Initialize Si5351A
while (si5351_read8(0,dummy) & 0x80); // Wait for Si5351A to initialize
si5351_write8(SI5351_OUT_ENABLE, 0xFF); // Output Enable Control, disable all
// for (int i = 16; i < 24; i++) si5351_write8 (i, 0x80); // CLKi Control, power down CLKi
si5351_write8(SI5351_INPUT_SOURCE, 0x00); // PLL Input Source, select the XTAL input as the reference clock for PLLA and PLLB
si5351_write8(SI5351_OUT_DIS_STATE, 0x00);
// Output Multisynth0, e = 0, f = 1, MS0_P2 and MSO_P3
si5351_write8(SI5351_MULTISYNTH0, 0x00);
si5351_write8(SI5351_MULTISYNTH0+1, 0x01);
si5351_write8(SI5351_MULTISYNTH0+5, 0x00);
si5351_write8(SI5351_MULTISYNTH0+6, 0x00);
si5351_write8(SI5351_MULTISYNTH0+7, 0x00);
si5351_write8(SI5351_MULTISYNTH1, 0x00);
si5351_write8(SI5351_MULTISYNTH1+1, 0x01);
si5351_write8(SI5351_MULTISYNTH1+5, 0x00);
si5351_write8(SI5351_MULTISYNTH1+6, 0x00);
si5351_write8(SI5351_MULTISYNTH1+7, 0x00);
si5351_write8(SI5351_CLK0_CONTROL, 0x4F); // Power up CLKx, PLLA, MS0 operates in integer mode, Output Clock 0 is not inverted, Select MultiSynth 0 as the source for CLK0 and 8 mA
si5351_write8(SI5351_CLK1_CONTROL, 0x5F); // Power up CLKx, PLLA, MS0 operates in integer mode, Output Clock 0 is not inverted, Select MultiSynth 0 as the source for CLK0 and 8 mA
si5351_write8(SI5351_CLK2_CONTROL, 0x80); // Power down CLKx
// Reference load configuration
si5351_write8(SI5351_CRYSTAL_LOAD, 0x12); // Set reference load C: 6 pF = 0x12, 8 pF = 0x92, 10 pF = 0xD2
// Turn CLK0 output on
// si5351_write8(SI5351_OUT_ENABLE, 0xFC); // Output Enable Control. Active low
}
void si53531_set_frequency(uint32_t freq, uint8_t synth){
uint8_t regs[16];
CalcRegisters(freq, regs);
// Load PLLA Feedback Multisynth NA
for (int i = 0; i < 8; i++)
si5351_write8(SI5351_PLLA + i, regs[i]);
// Load Output Multisynth0 with d (e and f already set during init. and never changed)
if(synth = 0){
for (int i = 10; i < 13; i++)
si5351_write8(34 + i, regs[i]);
} else if(synth = 1){
for (int i = 10; i < 13; i++)
si5351_write8(42 + i, regs[i]);
}
// Reset PLLA
// delayMicroseconds(500); // Allow registers to settle before resetting the PLL
si5351_write8(SI5351_RESET, 0x20);
}
void si5351_off(void){
si5351_write8(SI5351_OUT_ENABLE, 0xFF);
}
void si5351_on(void){
si5351_write8(SI5351_OUT_ENABLE, 0xFC);
}