From 776d4224ac92427533b8b57af523a4d279d9c765 Mon Sep 17 00:00:00 2001 From: Marshal Horn Date: Wed, 8 Jul 2020 09:13:48 -0700 Subject: [PATCH] Made driver for si5351 Also included some of Guido's trig approximations (magn, arctan3) --- NIL-STM32F051-DISCOVERY/.cproject | 4 +- NIL-STM32F051-DISCOVERY/Makefile | 2 +- NIL-STM32F051-DISCOVERY/drivers/si5351.c | 87 ++++++++++++++++++++++++ NIL-STM32F051-DISCOVERY/drivers/si5351.h | 33 +++++++++ NIL-STM32F051-DISCOVERY/trig.c | 20 ++++++ NIL-STM32F051-DISCOVERY/trig.h | 25 +++++++ 6 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 NIL-STM32F051-DISCOVERY/drivers/si5351.c create mode 100644 NIL-STM32F051-DISCOVERY/drivers/si5351.h create mode 100644 NIL-STM32F051-DISCOVERY/trig.c create mode 100644 NIL-STM32F051-DISCOVERY/trig.h diff --git a/NIL-STM32F051-DISCOVERY/.cproject b/NIL-STM32F051-DISCOVERY/.cproject index c826a93..21584b0 100644 --- a/NIL-STM32F051-DISCOVERY/.cproject +++ b/NIL-STM32F051-DISCOVERY/.cproject @@ -67,9 +67,7 @@ - - - + diff --git a/NIL-STM32F051-DISCOVERY/Makefile b/NIL-STM32F051-DISCOVERY/Makefile index ef1fef2..f172d00 100644 --- a/NIL-STM32F051-DISCOVERY/Makefile +++ b/NIL-STM32F051-DISCOVERY/Makefile @@ -120,7 +120,7 @@ LDSCRIPT= $(STARTUPLD)/STM32F051x8.ld # setting. CSRC = $(ALLCSRC) \ $(TESTSRC) \ - main.c radio/rx.c + main.c radio/rx.c drivers/si5351.c trig.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. diff --git a/NIL-STM32F051-DISCOVERY/drivers/si5351.c b/NIL-STM32F051-DISCOVERY/drivers/si5351.c new file mode 100644 index 0000000..9ad3fab --- /dev/null +++ b/NIL-STM32F051-DISCOVERY/drivers/si5351.c @@ -0,0 +1,87 @@ +/* + * si5351.c + * + * Created on: Jul 7, 2020 + * Author: marshal + */ +#include "si5351.h" +#include "../trig.h" + +void synth_en(struct synth * cfg){ + uint8_t buf[2]; + buf[0]=cfg->channel+16; + buf[1]=cfg->PLLx ? 0x2F : 0x1F; + const uint8_t si5351 = 0x60; + i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, + NULL, 0, TIME_MS2I(100)); +} + +void synth_write_params(uint8_t reg, uint64_t val, uint8_t div){ + uint8_t buf[9]; + buf[0]=reg*8+26; + buf[1]=0xFF; // denominator is always 0xFFFFF + buf[2]=0xFF; + buf[3]=val>>36 & 0x03; + buf[3]|=div<<4; + buf[4]=val>>28; + buf[5]=val>>20; + buf[6]=val>>16 | 0xF0; + buf[7]=val>>8; + buf[8]=val; + + const uint8_t si5351 = 0x60; + i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 9, NULL, 0, TIME_MS2I(100)); +} + +void synth_set_carrier(struct synth * cfg, float carrier){ + // Calculate PLL and Multisynth values + const double xtal = 27e6; + double pll = 27; // Good starting value to put vxco at 750MHz + double div = xtal * pll / carrier; + double delta = div - (xtal*pll)/(carrier+1); // Linearized change per Hz + int8_t delta_shift = round(log2(delta)); // round to nearest power of 2 + double delta2 = (0x40000000>>delta_shift)*(1.0/0x40000000U); // (Computing a negative power of 2) + div *= delta2/delta; + div = round(div/2)*2+1; // Minimize chance of rollover + pll = carrier * div / xtal; // Adjust pll to match + + // If desired frequency is very low, we need to use the additional divider register + uint8_t div2; + for(div2=0; div>2048; ++div2){ + div *= 0.5; + --delta_shift; + } + + // Store values into config + const uint32_t fixed_shift = 1<<27; //For shifting values to fixed-point + cfg->reg.pll = pll * fixed_shift; + cfg->reg.synth = div * fixed_shift; + cfg->reg.shift = delta_shift; + cfg->reg.divide = div2; + + // Write changes to chip + synth_write_params(cfg->PLLx, cfg->reg.pll, 0); + synth_write_params(cfg->channel+2, cfg->reg.synth, cfg->reg.divide); + synth_en(cfg); +} + +void synth_set_baseband(struct synth * cfg, int32_t baseband){ + int32_t diff = baseband - cfg->baseband; + diff >>= cfg->reg.shift; + cfg->reg.synth -= diff; + uint32_t val = cfg->reg.synth; + uint8_t buf[5]; + /* This could potentially be sped up by dropping the last byte */ + buf[0]=8*(cfg->channel)+46; + buf[1]=val>>20; + buf[2]=(val>>16) | 0xF0; + buf[3]=val>>8; + buf[4]=val; + const uint8_t si5351 = 0x60; + i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 5,NULL, 0, TIME_MS2I(100)); +} + +void synth_init(struct synth * cfg, uint8_t channel, uint8_t pll){ + cfg->channel = channel; + cfg->PLLx = pll; +} diff --git a/NIL-STM32F051-DISCOVERY/drivers/si5351.h b/NIL-STM32F051-DISCOVERY/drivers/si5351.h new file mode 100644 index 0000000..f1c1aa0 --- /dev/null +++ b/NIL-STM32F051-DISCOVERY/drivers/si5351.h @@ -0,0 +1,33 @@ +/* + * si5351.h + * + * Created on: Jul 8, 2020 + * Author: marshal + */ + +#ifndef SI5351_H_ +#define SI5351_H_ + +#include "hal.h" + +struct synth{ + float carrier; //< in Hz + int32_t baseband; //< 15.16 fixed-point, in Hz + struct { //< Calculated values for setting registers + uint64_t pll:38; //< 11.27 fixed-point for PLL multipler + uint64_t synth:38; //< 11.27 fixed-point for Synth Divider + uint8_t divide:2; //< 4-bit field for extra divider settings + int8_t shift; //< Shift value for scaling the baseband frequency + }reg; + uint8_t channel:2; //Supports four channels - increase bit width if more are desired + uint8_t PLLx:1; //< 0 is PLLA, 1 is PLLB +}; + +void synth_init(struct synth * cfg, uint8_t channel, uint8_t pll); +void synth_en(struct synth * cfg); +void synth_set_carrier(struct synth * cfg, float carrier); +void synth_set_baseband(struct synth * cfg, int32_t baseband); +void synth_write_params(uint8_t reg, uint64_t val, uint8_t div); + + +#endif /* SI5351_H_ */ diff --git a/NIL-STM32F051-DISCOVERY/trig.c b/NIL-STM32F051-DISCOVERY/trig.c new file mode 100644 index 0000000..de53479 --- /dev/null +++ b/NIL-STM32F051-DISCOVERY/trig.c @@ -0,0 +1,20 @@ +/* + * trig.c + * + * Created on: Jul 6, 2020 + * Author: marshal + */ +#include "trig.h" + +inline int16_t arctan3(int16_t q, int16_t i) // error ~ 0.8 degree +{ // source: [1] http://www-labs.iro.umontreal.ca/~mignotte/IFT2425/Documents/EfficientApproximationArctgFunction.pdf +#define _atan2(z) (_UA/8 - _UA/22 * z + _UA/22) * z //derived from (5) [1] + //#define _atan2(z) (_UA/8 - _UA/24 * z + _UA/24) * z //derived from (7) [1] + int16_t r; + if(abs(q) > abs(i)) + r = _UA / 4 - _atan2(abs(i) / abs(q)); // arctan(z) = 90-arctan(1/z) + else + r = (i == 0) ? 0 : _atan2(abs(q) / abs(i)); // arctan(z) + r = (i < 0) ? _UA / 2 - r : r; // arctan(-z) = -arctan(z) + return (q < 0) ? -r : r; // arctan(-z) = -arctan(z) +} diff --git a/NIL-STM32F051-DISCOVERY/trig.h b/NIL-STM32F051-DISCOVERY/trig.h new file mode 100644 index 0000000..d64dd5b --- /dev/null +++ b/NIL-STM32F051-DISCOVERY/trig.h @@ -0,0 +1,25 @@ +/* + * trig.h + * + * Created on: Jul 8, 2020 + * Author: marshal + */ + +#ifndef TRIG_H_ +#define TRIG_H_ + +#include +#include + +#define F_SAMP_TX 4810 //4810 // ADC sample-rate; is best a multiple of _UA and fits exactly in OCR0A = ((F_CPU / 64) / F_SAMP_TX) - 1 , should not exceed CPU utilization +#define _UA (F_SAMP_TX) //360 // unit angle; integer representation of one full circle turn or 2pi radials or 360 degrees, should be a integer divider of F_SAMP_TX and maximized to have higest precision + +// Stdlib includes abs, but I'm not sure we want it. This works for all numerical types. +#define abs(x) (x>=0?x:-x) + +int16_t arctan3(int16_t q, int16_t i); + +#define magn(i, q) (abs(i) > abs(q) ? abs(i) + abs(q) / 4 : abs(q) + abs(i) / 4) // approximation of: magnitude = sqrt(i*i + q*q); error 0.95dB + + +#endif /* TRIG_H_ */