Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <avr/wdt.h> // Watchdog Timer functions
- #include <SoftwareSerialMC_KW.h>
- SoftwareSerial Serial1(7, 6); // RX, TX
- #include <MemoryUsage.h> // Check memory avaliable for debuging
- //Constants
- #define I2C_START 0x08
- #define I2C_START_RPT 0x10
- #define I2C_SLA_W_ACK 0x18
- #define I2C_SLA_R_ACK 0x40
- #define I2C_DATA_ACK 0x28
- #define I2C_WRITE 0b11000000
- #define I2C_READ 0b11000001
- // Register definitions
- #define SI_CLK_OEB 3
- #define SI_OEB_MASK 9
- #define SI_CLK0_CONTROL 16
- #define SI_CLK1_CONTROL 17
- #define SI_CLK2_CONTROL 18
- #define SI_SYNTH_PLL_A 26
- #define SI_SYNTH_PLL_B 34
- #define SI_SYNTH_MS_0 42
- #define SI_SYNTH_MS_1 50
- #define SI_SYNTH_MS_2 58
- #define SI_PLL_RESET 177
- // R-division ratio definitions
- #define SI_R_DIV_1 0b00000000
- #define SI_R_DIV_2 0b00010000
- #define SI_R_DIV_4 0b00100000
- #define SI_R_DIV_8 0b00110000
- #define SI_R_DIV_16 0b01000000
- #define SI_R_DIV_32 0b01010000
- #define SI_R_DIV_64 0b01100000
- #define SI_R_DIV_128 0b01110000
- // Register 3. Output Enable Control definitions
- #define SI_CLK0_OEB 0b00000001
- #define SI_CLK1_OEB 0b00000010
- #define SI_CLK2_OEB 0b00000100
- #define SI_CLK_SRC_PLL_A 0b00000000
- #define SI_CLK_SRC_PLL_B 0b00100000
- #define SI5_POWER A2
- #define WSPR_FREQ 1409712500ULL // = 1,525Hz audio on 14,095,600 Mhz
- #define WSPR_1_FREQ 1409707500ULL // = 1,475Hz audio on 14,095,600 Mhz
- #define WSPR_2_FREQ 1409712500ULL // = 1,525Hz audio on 14,095,600 Mhz
- uint8_t data;
- typedef struct {
- uint32_t FB_a, FB_b, FB_c, MS_a, MS_b, MS_c;
- bool FB_int, MS_int;
- } dividerVals_t;
- uint8_t i2cStart()
- {
- // Enable internal pullup resistors
- digitalWrite(SDA, 1);
- digitalWrite(SCL, 1);
- TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Set TWINT, TWSTA (Start) and TWEN (TwoWireEnable) Bits in the TwoWireControlRegister
- while (!(TWCR & (1 << TWINT))) ; // Wait until TWINT is set
- return (TWSR & 0xF8);
- }
- void i2cStop()
- {
- TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
- while ((TWCR & (1 << TWSTO))) ;
- }
- uint8_t i2cByteSend(uint8_t data)
- {
- TWDR = data;
- TWCR = (1 << TWINT) | (1 << TWEN);
- while (!(TWCR & (1 << TWINT))) ;
- return (TWSR & 0xF8);
- }
- uint8_t i2cByteRead()
- {
- TWCR = (1 << TWINT) | (1 << TWEN);
- while (!(TWCR & (1 << TWINT))) ;
- return (TWDR);
- }
- uint8_t i2cSendRegister(uint8_t reg, uint8_t data)
- {
- uint8_t stts;
- stts = i2cStart();
- if (stts != I2C_START) return 1;
- stts = i2cByteSend(I2C_WRITE);
- if (stts != I2C_SLA_W_ACK) return 2;
- stts = i2cByteSend(reg);
- if (stts != I2C_DATA_ACK) return 3;
- stts = i2cByteSend(data);
- if (stts != I2C_DATA_ACK) return 4;
- i2cStop();
- return 0;
- }
- uint8_t i2cReadRegister(uint8_t reg, uint8_t *data)
- {
- uint8_t stts;
- stts = i2cStart();
- if (stts != I2C_START) return 1;
- stts = i2cByteSend(I2C_WRITE);
- if (stts != I2C_SLA_W_ACK) return 2;
- stts = i2cByteSend(reg);
- if (stts != I2C_DATA_ACK) return 3;
- stts = i2cStart();
- if (stts != I2C_START_RPT) return 4;
- stts = i2cByteSend(I2C_READ);
- if (stts != I2C_SLA_R_ACK) return 5;
- *data = i2cByteRead();
- i2cStop();
- return 0;
- }
- // Init TWI (I2C)
- void i2cInit()
- {
- TWBR = 0xB8; // TwoWireBitRate register (0xB8 = 184 default) 92 = 40khz I think?
- TWSR = 0; // TWI Status Register, Clear status codes and set prescaler to 0 which equals /1
- TWDR = 0xFF; // TWI Data Register
- //PRR = 0; // Power Reduction Register, 0 = start all modules,
- }
- // Reboot
- void(* resetFunc) (void) = 0; //declare reset function @ address 0
- uint32_t ceilDivUL(uint32_t a, uint32_t b) {
- // Performs ceil(a/b) when a and b are unsigned long ints
- return (a - 1) / b + 1;
- }
- //uint32_t lcm(uint32_t a, uint32_t b) {
- // return a/gcd(a, b)*b;
- //}
- uint32_t gcd(uint32_t u, uint32_t v) {
- int shift;
- if (u == 0) return v;
- if (v == 0) return u;
- shift = __builtin_ctzl(u | v);
- while ( !(u & 0b1L)) u >>= 1; // manual u >>= __buitin_ctzl(u)
- while (v ) {
- while ( !(v & 0b1L)) v >>= 1;
- if (u > v) {
- uint32_t t = v;
- v = u;
- u = t;
- }
- v = v - u;
- }
- return u << shift;
- }
- dividerVals_t divCalcFast(uint32_t desired_freq, uint32_t osc_freq, uint8_t dCurr) {
- const uint32_t FVCO_MIN = 600000000, FVCO_MAX = 900000000;
- dividerVals_t divider_vals = {0, 0, 0, 0, 0, 0, 0, 0};
- uint32_t fvco = desired_freq * dCurr; // zero when d = 0, current fvco
- if (fvco > FVCO_MAX || fvco < FVCO_MIN) {
- dCurr = (FVCO_MIN + FVCO_MAX) / 2 / desired_freq; // set fvco to the mid point, for max tuning
- //d = FVCO_MAX / desired_freq;
- dCurr &= 0xFE; // this is the projected d
- }
- uint8_t d = dCurr; // this is our working value of d, either the current or projected d
- // make sure the d works. First try with even integer d, then odd.
- // If doesn't work, change d and try again.
- do {
- fvco = desired_freq * d;
- ldiv_t xDivY = ldiv(fvco, osc_freq);
- uint32_t gcdTemp = gcd(xDivY.rem, osc_freq);
- divider_vals.FB_a = xDivY.quot;
- divider_vals.FB_b = xDivY.rem / gcdTemp;
- divider_vals.FB_c = osc_freq / gcdTemp;
- divider_vals.FB_int = 0;
- d = (dCurr - d < 0) ? d + 2 * (dCurr - d) : d + 2 * (dCurr - d) + 2; // find even d centered near mid point
- } while (divider_vals.FB_c > 1048757UL && fvco > FVCO_MIN && fvco < FVCO_MAX );
- // lazy catch all for now
- if (divider_vals.FB_c > 1048757UL) {
- fvco = desired_freq * dCurr;
- ldiv_t xDivY = ldiv(fvco, osc_freq);
- divider_vals.FB_a = xDivY.quot;
- divider_vals.FB_b = xDivY.rem;
- divider_vals.FB_c = osc_freq;
- while (divider_vals.FB_c > 1048757UL) {
- divider_vals.FB_b >>= 1;
- divider_vals.FB_c >>= 1; // just keep halving until you hit a good value
- }
- divider_vals.FB_int = 0;
- }
- divider_vals.MS_a = d;
- divider_vals.MS_b = 0;
- divider_vals.MS_c = 1;
- divider_vals.MS_int = !(0b1L & divider_vals.MS_a); // set true if even
- return divider_vals;
- }
- void SetFrequency (dividerVals_t results)
- {
- Serial1.println(F("SetFrequency(): "));
- Serial1.print(" FB_a "); Serial1.println(results.FB_a);
- Serial1.print(" FB_b "); Serial1.println(results.FB_b);
- Serial1.print(" FB_c "); Serial1.println(results.FB_c);
- Serial1.print(" FB_int "); Serial1.println(results.FB_int);
- Serial1.print(" MS_a "); Serial1.println(results.MS_a);
- Serial1.print(" MS_b "); Serial1.println(results.MS_b);
- Serial1.print(" MS_c "); Serial1.println(results.MS_c);
- Serial1.print(" MS_int "); Serial1.println(results.MS_int);
- unsigned long MSNA_P1; // Si5351a Feedback Multisynth register MSNA_P1
- unsigned long MSNA_P2; // Si5351a Feedback Multisynth register MSNA_P2
- unsigned long MSNA_P3; // Si5351a Feedback Multisynth register MSNA_P3
- unsigned long MS0_P1; // Si5351a Output Divider register MS0_P1
- unsigned long MS0_P2; // Si5351a Output Divider register MS0_P1
- unsigned long MS0_P3; // Si5351a Output Divider register MS0_P1
- // Assume only use PLL A
- // MSNA_P1[17:0] = 128 * FB_a + Floor(128*(FB_b/FB_c))-512
- // MSNA_P2[19:0] = 128 * FB_b - FB_c * Floor(128*(FB_b/FB_c))
- // MSNA_P3[19:0] = FB_c
- // FBA_INT = FB_int // Integer mode?
- //MSNA_P1 = 128 * results.FB_a + floor(128 * (results.FB_b/results.FB_c)) - 512;
- //MSNA_P2 = 128 * results.FB_b - results.FB_c * floor(128 * (results.FB_b/results.FB_c));
- //MSNA_P3 = results.FB_c;
- MSNA_P1 = (uint32_t)(128 * ((float)results.FB_b / (float)results.FB_c));
- MSNA_P1 = (uint32_t)(128 * (uint32_t)(results.FB_a) + MSNA_P1 - 512);
- MSNA_P2 = (uint32_t)(128 * ((float)results.FB_b / (float)results.FB_c));
- MSNA_P2 = (uint32_t)(128 * results.FB_b - results.FB_c * MSNA_P2);
- MSNA_P3 = results.FB_c;
- i2cSendRegister (26, (MSNA_P3 & 0x0000FF00) >> 8); // Bits [15:8] of MSNA_P3 in register 26
- i2cSendRegister (27, MSNA_P3 & 0x000000FF); // Bits [7:0] of MSNA_P3 in register 27
- i2cSendRegister (28, (MSNA_P1 & 0x00030000) >> 16); // Bits [17:16] of MSNA_P1 in bits [1:0] of register 28
- i2cSendRegister (29, (MSNA_P1 & 0x0000FF00) >> 8); // Bits [15:8] of MSNA_P1 in register 29
- i2cSendRegister (30, MSNA_P1 & 0x000000FF); // Bits [7:0] of MSNA_P1 in register 30
- i2cSendRegister (31, ((MSNA_P3 & 0x000F0000) >> 12) | ((MSNA_P2 & 0x000F0000) >> 16)); // Parts of MSNA_P3 and MSNA_P1 in 31
- i2cSendRegister (32, (MSNA_P2 & 0x0000FF00) >> 8); // Bits [15:8] of MSNA_P2 in register 32
- i2cSendRegister (33, MSNA_P2 & 0x000000FF); // Bits [7:0] of MSNA_P2 in register 33
- // Assume only use MS 0
- // MS0_SRC = 0 // Connect PLL A output to MultiSynth 0 input
- // MS0_P1[17:0] = 128 * MS_a + Floor(128*(MS_b/MS_c))-512
- // MS0_P2[19:0] = 128 * MS_b - MS_c * Floor(128*(MS_b/MS_c))
- // MS0_P3[19:0] = MS_c
- // MS0_INT = MS_int // Even Integer mode?
- //MS0_P1 = 128 * results.MS_a + floor(128 * (results.MS_b/results.MS_c)) - 512;
- //MS0_P2 = 128 * results.MS_b - results.MS_c * floor(128 * (results.MS_b/results.MS_c));
- //MS0_P3 = results.MS_c;
- MS0_P1 = (uint32_t)(128 * ((float)results.MS_b / (float)results.MS_c));
- MS0_P1 = (uint32_t)(128 * (uint32_t)(results.MS_a) + MS0_P1 - 512);
- MS0_P2 = (uint32_t)(128 * ((float)results.MS_b / (float)results.MS_c));
- MS0_P2 = (uint32_t)(128 * results.MS_b - results.MS_c * MS0_P2);
- MS0_P3 = results.MS_c;
- i2cSendRegister(42, (MS0_P3 & 0x0000FF00) >> 8); // Bits [15:8] of MS0_P3 in register 42
- i2cSendRegister(43, (MS0_P3 & 0x000000FF)); // Bits [7:0] of MS0_P3 in register 43
- // TODO Reg 44 MS0_DIVBY4 in [3:2] ?
- i2cSendRegister(44, (MS0_P1 & 0x00030000) >> 16);// Bits [17:16] of MS0_P1 in bits [1:0] MS0_DIVBY4 in [3:2] and R0_DIV in [7:4]
- i2cSendRegister(45, (MS0_P1 & 0x0000FF00) >> 8); // Bits [15:8] of MS0_P1 in register 45
- i2cSendRegister(46, (MS0_P1 & 0x000000FF)); // Bits [7:0] of MS0_P1 in register 46
- i2cSendRegister(47, ((MS0_P3 & 0x000F0000) >> 12) | ((MS0_P2 & 0x000F0000) >> 16)); // Bits [19:16] of MS0_P2 in bits [7:4] and [19:16] of MS0_P3 in [3:0]
- i2cSendRegister(48, (MS0_P2 & 0x0000FF00) >> 8); // Bits [15:8] of MS0_P2
- i2cSendRegister(49, (MS0_P2 & 0x000000FF)); // Bits [7:0] of MS0_P2
- // Register 16. CLK0 Control
- // ToDo Set MultiSynth 0 Integer mode when we can,
- // 0b00001111, 0x0F, Powered up:Yes:0, Integer mode:No:0, PLL:A:0, Inverted:Not:0, Source:MS0:11, Drive:8ma:11
- i2cSendRegister(SI_CLK0_CONTROL, 0x4F);
- }
- // Set up specified PLL with mult, num and denom
- // mult is 15..90
- // num is 0..1,048,575 (0xFFFFF)
- // denom is 0..1,048,575 (0xFFFFF)
- void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom)
- {
- uint32_t P1; // PLL config register P1
- uint32_t P2; // PLL config register P2
- uint32_t P3; // PLL config register P3
- Serial1.println(F("setupPLL(): "));
- Serial1.print(" FB_a "); Serial1.println(mult);
- Serial1.print(" FB_b "); Serial1.println(num);
- Serial1.print(" FB_c "); Serial1.println(denom);
- P1 = (uint32_t)(128 * ((float)num / (float)denom));
- P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
- P2 = (uint32_t)(128 * ((float)num / (float)denom));
- P2 = (uint32_t)(128 * num - denom * P2);
- P3 = denom;
- i2cSendRegister(pll + 0, (P3 & 0x0000FF00) >> 8);
- i2cSendRegister(pll + 1, (P3 & 0x000000FF));
- i2cSendRegister(pll + 2, (P1 & 0x00030000) >> 16);
- i2cSendRegister(pll + 3, (P1 & 0x0000FF00) >> 8);
- i2cSendRegister(pll + 4, (P1 & 0x000000FF));
- i2cSendRegister(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
- i2cSendRegister(pll + 6, (P2 & 0x0000FF00) >> 8);
- i2cSendRegister(pll + 7, (P2 & 0x000000FF));
- }
- // Set up MultiSynth with integer Divider and R Divider
- void setupMultisynth(uint8_t synth, uint32_t Divider, uint8_t rDiv)
- {
- uint32_t P1; // Synth config register P1
- uint32_t P2; // Synth config register P2
- uint32_t P3; // Synth config register P3
- Serial1.println(F("setupMultisynth(): "));
- Serial1.print(" MS_a ?"); Serial1.println(Divider);
- Serial1.print(" MS_b ?"); Serial1.println("0");
- Serial1.print(" MS_c "); Serial1.println("1");
- P1 = 128 * Divider - 512;
- P2 = 0; // P2 = 0, P3 = 1 forces an integer value for the Divider
- P3 = 1;
- i2cSendRegister(synth + 0, (P3 & 0x0000FF00) >> 8);
- i2cSendRegister(synth + 1, (P3 & 0x000000FF));
- i2cSendRegister(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
- i2cSendRegister(synth + 3, (P1 & 0x0000FF00) >> 8);
- i2cSendRegister(synth + 4, (P1 & 0x000000FF));
- i2cSendRegister(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 &
- 0x000F0000) >> 16));
- i2cSendRegister(synth + 6, (P2 & 0x0000FF00) >> 8);
- i2cSendRegister(synth + 7, (P2 & 0x000000FF));
- }
- void SetFrequencyKW(uint64_t frequency) // Frequency is in centiHz
- {
- uint64_t pllFreq;
- uint32_t l;
- float f;
- uint8_t mult;
- uint32_t num;
- uint32_t denom;
- uint32_t Divider;
- Serial1.println(F("SetFrequencyKW(): "));
- Divider = 90000000000ULL / frequency; // Calculate the division ratio. 600 to 900MHz is the maximum internal (deciHz)
- pllFreq = Divider * frequency; // Calculate the pllFrequency:
- mult = pllFreq / ((27000000UL) * 100UL); // Determine the multiplier
- l = pllFreq % ((27000000UL) * 100UL); // It has three parts:
- f = l; // mult is an integer that must be in the range 15..90
- f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
- f /= (27000000UL); // each is 20 bits (range 0..1048575)
- num = f; // the actual multiplier is mult + num / denom
- denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
- num = num / 100;
- setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
- // Set up MultiSynth Divider 0, with the calculated Divider.
- setupMultisynth(SI_SYNTH_MS_0, Divider, SI_R_DIV_1);
- i2cSendRegister(SI_CLK0_CONTROL, 0x4F);
- }
- void setup()
- {
- //dividerVals_t results = {0, 0, 0, 0, 0, 0, 0, 0};
- dividerVals_t results = {33, 194180, 1048575, 64, 0, 1, 0 , 0};
- wdt_disable();
- Serial1.begin(57600);
- Serial1.println(F("START"));
- delay(3000);
- pinMode(SI5_POWER, OUTPUT);
- digitalWrite(SI5_POWER, HIGH);
- delay(20); // Max 10ms power up time
- i2cInit(); // Initialise i2c for the SI5351a
- Serial1.println(F("si5351a powered on and I2C Initialised."));
- delay(5000);
- // Reg 17, 18. CLK1 & 2 Control - power off
- // 0b10001111, 0x8F, Powered up:No:1, Integer mode:No:0, PLL:A:0, Inverted:Not:0, Source:MS0:11, Drive:8ma:11
- i2cSendRegister(SI_CLK1_CONTROL, 0x8F);
- i2cSendRegister(SI_CLK2_CONTROL, 0x8F);
- // Read status registers
- i2cSendRegister(1, 0); // reset sticky bits
- Serial1.print(F(" Reg 0: ")); Serial1.print(i2cReadRegister(0, &data)); Serial1.print(F(" ")); Serial1.print(data);
- Serial1.print(F(" Reg 1: ")); Serial1.print(i2cReadRegister(1, &data)); Serial1.print(F(" ")); Serial1.print(data);
- Serial1.print(F(" Reg 2: ")); Serial1.print(i2cReadRegister(2, &data)); Serial1.print(F(" ")); Serial1.println(data);
- Serial1.println(F("Set Initial frequency and reset PLL's"));
- //SetFrequency(results);
- SetFrequency(results);
- i2cSendRegister(SI_PLL_RESET, 0xA0); // 0xA0 resets both PLLs
- delay(5000);
- Serial1.println(F("Set Initial frequency and reset PLL's"));
- results = divCalcFast(14000000UL, 27000000UL, results.MS_a); // (desired, osc_clock, current MS divider value a)
- SetFrequency(results);
- i2cSendRegister(SI_PLL_RESET, 0xA0); // 0xA0 resets both PLLs
- // Read status registers
- i2cSendRegister(1, 0); // reset sticky bits
- Serial1.print(F(" Reg 0: ")); Serial1.print(i2cReadRegister(0, &data)); Serial1.print(F(" ")); Serial1.print(data);
- Serial1.print(F(" Reg 1: ")); Serial1.print(i2cReadRegister(1, &data)); Serial1.print(F(" ")); Serial1.print(data);
- Serial1.print(F(" Reg 2: ")); Serial1.print(i2cReadRegister(2, &data)); Serial1.print(F(" ")); Serial1.println(data);
- delay(5000);
- resetFunc(); //reboot
- Serial1.println(F("First Tone 14097075UL"));
- results = divCalcFast(14097075UL, 27000000UL, 0); // (desired, osc_clock, current MS divider value a)
- SetFrequency(results);
- delay(5000);
- Serial1.println(F("Second Tone 14097076UL"));
- results = divCalcFast(14097076UL, 27000000UL, results.MS_a); // (desired, osc_clock, current MS divider value a)
- SetFrequency(results);
- delay(5000);
- Serial1.println(F("Third Tone 14097077UL"));
- results = divCalcFast(14097077UL, 27000000UL, results.MS_a); // (desired, osc_clock, current MS divider value a)
- SetFrequency(results);
- delay(5000);
- Serial1.println(F("Fourth Tone 14097078UL"));
- results = divCalcFast(14097078UL, 27000000UL, results.MS_a); // (desired, osc_clock, current MS divider value a)
- SetFrequency(results);
- delay(5000);
- Serial1.println(F("END"));
- digitalWrite(SI5_POWER, LOW);
- delay(5000);
- resetFunc(); //reboot
- }
- void loop()
- {
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement