Made driver for si5351

Also included some of Guido's trig approximations (magn, arctan3)
chibios
Marshal Horn 2020-07-08 09:13:48 -07:00
rodzic a1298df605
commit 776d4224ac
6 zmienionych plików z 167 dodań i 4 usunięć

Wyświetl plik

@ -67,9 +67,7 @@
<sourceEntries>
<entry excluding="radio" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="radio"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>

Wyświetl plik

@ -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.

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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)
}

Wyświetl plik

@ -0,0 +1,25 @@
/*
* trig.h
*
* Created on: Jul 8, 2020
* Author: marshal
*/
#ifndef TRIG_H_
#define TRIG_H_
#include <math.h>
#include <stdint.h>
#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_ */