tiistai 30. lokakuuta 2018

Si5351 I/Q VFO 3.2MHz to 58MHz 90 deg phase output


Si5351 Adafruit development board with Arduino Nano + LCD display

Below is I/Q phase accuracy measurements with different frequencies.
I/Q phase shift value is 87...90degree within 3.2 MHz to 58 MHz

Outputs are not terminated so square wave signal is not clean. 








//v.0.1.7 04.11.2018 Si5351 VCO from 3.3MHZ to 58MHz 90 degree I/Q with LCD display and rotary switch with button. OH2BTG
// Thanks to Hans Summers and others who contributed help and code
// https://qrp-labs.com/synth/si5351ademo.html#arduino
/**************************************************************************/

#include <Wire.h>
#include <LiquidCrystal.h>
#include <rotary.h>
#define ENCODER_A    3                      // Encoder pin A
#define ENCODER_B    2                      // Encoder pin B
#define ENCODER_BTN  4 // Encoder c button
Rotary r = Rotary(ENCODER_A, ENCODER_B);
uint32_t vfoSave = 0;
uint32_t pllFreq; // si5351 internal vco frequency
int menuX = 1;
int menuXsaved = 1;
int butPresTime = 1;
int menu_val = 1;
int settings_menu_state = 0;
int Itimer = 0;
byte UsbLsb = 1; // LSB = 0 USB = 1
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); //  LCD: RS,E,D4,D5,D6,D7 Erska
// OH2BTG additions
unsigned long vfo = 14267000;
unsigned long frequency = vfo;
unsigned long vfox2 = 2*vfo;
unsigned long fstep = 100; // frequency step in Hz
unsigned long fstepM = fstep;
boolean changed_f = 1;
// Original Hans Summers:
uint32_t divider = 1;
uint32_t divider_M = 0; // divider stored value
#include <inttypes.h>
void i2cInit();
uint8_t i2cSendRegister(uint8_t reg, uint8_t data);
uint8_t i2cReadRegister(uint8_t reg, uint8_t *data);

#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
#ifndef SI5351A_H
#define SI5351A_H

#define SI_CLK0_CONTROL 16 // Register definitions
#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_CLK0_PHOFF 165 // OH2BTG addition for phase offset
#define SI_CLK1_PHOFF 166 // OH2BTG addition for phase offset
#define SI_CLK2_PHOFF 167 // OH2BTG addition for phase offset
#define SI_PLL_RESET 177

#define SI_R_DIV_1 0b00000000 // R-division ratio definitions
#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

#define SI_CLK_SRC_PLL_A 0b00000000
#define SI_CLK_SRC_PLL_B 0b00100000

#define XTAL_FREQ 25000000 // 25 MHz adafruit Crystal frequency

void si5351aOutputOff(uint8_t clk);
void si5351aSetFrequency(uint32_t frequency);

#endif //SI5351A_H

uint8_t i2cStart()
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

while (!(TWCR & (1<<TWINT))) ;

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 = 92;
TWSR = 0;
TWDR = 0xFF;
PRR = 0;
}
void setup()
{
  i2cInit();  // Hans Summers original
  Serial.begin(115200);
  Serial.println(" Erska Testaa");
  // Initialize and clear the LCD
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("  OH2BTG ");
  lcd.setCursor(0, 1);
  lcd.print("1-150MHz v.0.1.7");
  delay(2000);  //2seconds
  Wire.begin();
  pinMode(ENCODER_BTN, INPUT_PULLUP);
  PCICR |= (1 << PCIE2);           // Enable pin change interrupt for the encoder
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  count_frequency();  // Count f and update the display
}

void loop() {
  // Update the display if the frequency has been changed
  if (changed_f)
  {
    count_frequency();
    si5351aSetFrequency_IQ(frequency);
  }
     ReadButton(); //Read coder button
}
void readMenu()
{
    switch (menuX)
  {
    case 1:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("LSB");
      Serial.println("LSB");
      break;
    case 2:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("USB");
      Serial.println("USB");
      break;
    case 3:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("100Hz");
      Serial.println("100Hz");
      break;
    case 4:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("1kHz");
      Serial.println("1kHz");
      break;
    case 5:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("100kHz");
      Serial.println("100kHz");
      break;
    case 6:
      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(0, 1);
      lcd.print("1MHz");
      Serial.println("1MHz");
      break;
    case 7:
      lcd.setCursor(0, 1);
      lcd.print("            ");
      lcd.setCursor(0, 1);
      lcd.print("3,699MHz LSB");
      Serial.println("3,699MHz LSB");
      break;
    case 8:
      lcd.setCursor(0, 1);
      lcd.print("             ");
      lcd.setCursor(0, 1);
      lcd.print("14,000MHz USB");
      Serial.println("14,000MHz USB");
      break;   
  }
}
/**************************************/
/* Interrupt service routine for      */
/* encoder frequency change           */
/**************************************/
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_CW)
    {set_frequency(1);
    set_menu_val(1);
    }
  else if (result == DIR_CCW)
{
  set_frequency(-1);
    set_menu_val(-1);
    }
}
/**************************************/
/* Change the frequency               */
/* dir = 1    Increment               */
/* dir = -1   Decrement               */
/**************************************/
void set_frequency(short dir)
{
  if (dir == 1)
    vfo += fstep;
  if (dir == -1)
    vfo -= fstep;
  if (vfo > 150000000)
    vfo = 100000;
  if (vfo < 100000)
    vfo = 150000000;
  if (vfo < 0)
    vfo = 100000;
  changed_f = 1;
}

/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button()
{
  if (!digitalRead(ENCODER_BTN))
  {
    delay(5);
    if (!digitalRead(ENCODER_BTN))
    {
      while (!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
  }

  void count_frequency()
  {
  uint16_t f, g;
  f = vfo / 1000000;   //variable is now vfo instead of 'frequency' vfo esim 145787500
  lcd.clear();
  lcd.setCursor(0, 0);
  if (f < 10)
    lcd.print("");
  lcd.print(f);
  lcd.print(",");
  f = (vfo % 1000000) / 1000; // printtaa taajuuden 3 viim numeroa XXX.550.XXX
  // Serial.println (f = (vfo % 1000000) / 1000);
  if (f < 100)
    lcd.print("0");
  if (f < 10)
    lcd.print("0");
  lcd.print(f);
  lcd.print(".");
  // f = vfo % 1000;
  f = (vfo % 1000) / 100; // removing 2 last digit from frequency reading
  lcd.print(f); //
  lcd.print("MHz ");
  if (UsbLsb == 0)
  {
    lcd.setCursor(12,0);
    lcd.print("LSB ");
  }
  if (UsbLsb == 1)
  {
    lcd.setCursor(12,0);
    lcd.print("USB ");
  }
  }
    void ReadButton()
    { 
    if (digitalRead(ENCODER_BTN) == 0)
    {
    butPresTime = ++butPresTime;
    if (butPresTime >= 6)
    {
    vfoSave = vfo;

    settings_menu();
    butPresTime = 1;
    }
    else
    {
    delay(200);
     }
     }
    }
void settings_menu()
{
   for ( Itimer=10; Itimer >= 0; Itimer--){
   lcd.setCursor(10, 1);
   lcd.print("     ");
   lcd.setCursor(10, 1);
   lcd.print("Menu");
     lcd.setCursor(14, 1);
     lcd.print("   ");
     lcd.setCursor(14, 1);
     lcd.print(Itimer);
     settings_menu_state = 1;
     delay(1500);
   //    settings_menu_state = 0;
    saveMenuSel();
    // saveUsbLsb();
  }
}
void set_menu_val(int suunta )
{
  if (Itimer >= 1 )
  {
  if (suunta == 1&& settings_menu_state ==1)
  {
    menu_val += 1;
    Itimer = 9;
    }
  if (suunta == -1&& settings_menu_state ==1)
  {
    menu_val -= 1;
    Itimer = 9;
    } 
  if ( menu_val <= 0)  menu_val = 8;
  if ( menu_val >= 9) menu_val = 1;
   menuX = menu_val;
   readMenu();
   saveMenuSel();
  }
  }
  void saveMenuSel()
    {
     while (digitalRead(ENCODER_BTN) == 0){
     menuXsaved = menuX;
     lcd.setCursor(12,1);
     Itimer = 1; 
   delay(50);
       lcd.print("Save");
   if (menuXsaved == 1)
   { UsbLsb = 2;
     Serial.println("LSB saved");
   }
   if (menuXsaved == 2)
   {
    UsbLsb = 3;
     Serial.println("USB saved");
    }
   if (menuXsaved == 3)
   {
     fstep = 100;
     Serial.println("100Hz saved");
    }
   if (menuXsaved == 4)
   {
     fstep = 1000;
     Serial.println("1kHz saved");
    } 
   if (menuXsaved == 5)
   {
     fstep = 100000;
     Serial.println("100kHz saved");
    }
       if (menuXsaved == 6)
   {
     fstep = 1000000;
     Serial.println("1MHz saved");
    }
       if (menuXsaved == 7)
   {
     vfoSave = 3699000;
     UsbLsb = 0;
     Serial.println("3699kHz LSB");
    }
           if (menuXsaved == 8)
   {
     vfoSave = 14000000;
     UsbLsb = 1;
     Serial.println("14,000MHz USB");
    }
  }
     vfo = vfoSave;
  }
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

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
// R divider is the bit value which is OR'ed onto the appropriate
// register, it is a #define in si5351a.h
//
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

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));
}
//
// Switches off Si5351a output
// Example: si5351aOutputOff(SI_CLK0_CONTROL);
// will switch off output CLK0
//
void si5351aOutputOff(uint8_t clk)
{
i2cSendRegister(clk, 0x80); // Refer to SiLabs AN619 to see
//bit values - 0x80 turns off the output stage
}
void si5351aSetFrequency_IQ(uint32_t frequency)
{
uint32_t pllFreq;
uint32_t xtalFreq = XTAL_FREQ;
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
//uint32_t divider;
frequency = vfo; // vfo is frequency
Serial.println(frequency);
if (frequency >=8000000) divider = 900000000 / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal
if (frequency <=8000000) divider = 400000000 / frequency;// Calculate the division ratio. 400,000,000 is the min internal
if (divider % 2) divider--; // Ensure an even integer
//division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency:
 Serial.print("PLL freq ");
 Serial.println(pllFreq);
//the divider * desired output frequency
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
 Serial.print("PLL mult ");
 Serial.println(mult);
l = pllFreq % xtalFreq; // 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 /= xtalFreq; // 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
// Set up PLL A with the calculated  multiplication ratio
setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
if ( divider_M != divider){
setupMultisynth(SI_SYNTH_MS_0, divider, SI_R_DIV_1);
setupMultisynth(SI_SYNTH_MS_1, divider, SI_R_DIV_1);
if (UsbLsb == 0){ // LSB mode select
i2cSendRegister(SI_CLK1_PHOFF,0x00); // set phase offset for I/Q
i2cSendRegister(SI_CLK0_PHOFF,(divider)); // set phase offset for I/Q
}
if (UsbLsb == 1){ // USB mode select
i2cSendRegister(SI_CLK0_PHOFF,0x00);  // set phase offset for I/Q
i2cSendRegister(SI_CLK1_PHOFF,(divider)); // set phase offset for I/Q
}
i2cSendRegister(SI_PLL_RESET, 0xA0);
i2cSendRegister(SI_CLK0_CONTROL, 0x4F | SI_CLK_SRC_PLL_A);
i2cSendRegister(SI_CLK1_CONTROL, 0x4F | SI_CLK_SRC_PLL_A);
 divider_M = divider;
 Serial.print("M divider ");
 Serial.println(divider);
}
changed_f = 0;           










Ei kommentteja:

Lähetä kommentti