2017年5月26日金曜日

si5351a phase out

以前から気になっていたのが、si5351aを使った位相出力である。身近にあるArduino IDE位相出力のスケッチは、Etherkitのサンプルがある。その他、Web検索しても一部分の公開が有る物の、参考になるスケッチを見つけられなかった。
そこで、位相出力に特化した考え方の、TJ Labのコードを使用させて頂く事にした。スケッチの動作試験環境は、si5351VFOのH/Wを流用した。

試験に使ったH/Wの写真。無改造で使用している。









回路図。













スケッチ

ch0とch1が位相差90度出力を行う。出力範囲は、7.0MHz~7.2MHzに制限した。
特化した機能なので、通常のVFOとして使用できないであろう。割り込み処理が異なるので、ロータリエンコーダ処理とI/O割付を替えれば、stm32duinoでも動作可能。
ファイルは、JA2GQP's Download siteのsi5351aフォルダ。

//////////////////////////////////////////////////////////////////////
//  si5351 phase out(ch0,ch1) VFO program ver.1.0
//    Copyright(C)2017.JA2GQP.All rights reserved.
//
//                                Arduino IDE 1.8.2 Compiled                                  
//
//                                                2017/5/26
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(1k,100,10)
//    2.Frequency limit(7.00MHz - 7.20MHz)
//////////////////////////////////////////////////////////////////////

#include <LCD5110_Basic.h>
#include <Rotary.h>
#include <Wire.h>


//////////////////////////
// Si5351A
//////////////////////////
#define   Si5351A_ADDR  0x60
#define   CLK0_CTRL   16
#define   CLK1_CTRL   17
#define   CLK2_CTRL   18
#define   MSNA_ADDR   26
#define   MSNB_ADDR   34
#define   MS0_ADDR    42
#define   MS1_ADDR    50
#define   MS2_ADDR    58
#define   CLK0_PHOFF  165
#define   CLK1_PHOFF  166
#define   PLL_RESET   177
#define   XTAL_LOAD_C 183


//////////////////////////
// system clock
//////////////////////////
const unsigned long XtalFreq = 25000000;


//////////////////////////
// I/O assign
//////////////////////////
#define   ENC_A   2                 //Rotary encoder A
#define   ENC_B   3                 //Rotary encoder B
#define   SW_STEP 4                 // Step SW


//////////////////////////
// Register set
//////////////////////////
const long LOW_FREQ = 7000000;    // lower frequency limit
const long HI_FREQ = 7200000;     // upper frequency limit
unsigned long FREQ = 7000000;     // default frequency
unsigned long FREQ_OLD = FREQ;    // old frequency
int STEP = 1000;                  // STEP(default)


//////////////////////////
// LCD assign
//////////////////////////
LCD5110 myGLCD(8,9,10,11,12);    // SCK,MOSI,DC,RST,CS

extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];


//////////////////////////
// Rotal encorder
//////////////////////////
Rotary r = Rotary(ENC_A,ENC_B); //ENC_A=2,ENC_B=3


//------------------ setup --------------------

void setup()
{
  Wire.begin();                
  si5351_init();                
  freq_set(FREQ);            
  myGLCD.InitLCD();                 // nokia5110 initialyze
  pinMode(SW_STEP,INPUT_PULLUP);    // STEP SW pullup

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                            // int enable

  myGLCD.setFont(SmallFont);
  myGLCD.print("STEP",12,32);
  myGLCD.print(".",12,16);
  myGLCD.print(".",54,16);
  freq_disp();
  step_disp();
}

//------------- Main program -------------------

void loop(){
  if(digitalRead(SW_STEP) == LOW){step_set();}

  if(FREQ != FREQ_OLD){
    freq_set(FREQ);
    freq_disp();
    FREQ_OLD = FREQ;          
  }
  delay(10);
}

//---------- rotaly encorder int proc. ------------

ISR(PCINT2_vect){
  unsigned char result = r.process();
  if(result){
    if(result == DIR_CW){
      FREQ = FREQ + STEP;
    }
    else{
      FREQ = FREQ - STEP;
    }
  }
  FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ);  
}

//---------- step set -----------

void step_set(){
  if(STEP == 10){
    STEP = 1000;
  }
  else{
    STEP= STEP / 10;
  }
  delay(10);
  step_disp();
  while(digitalRead(SW_STEP) == LOW){
    delay(10);
  }
}

//---------- frequency display -----------

void freq_disp(){
  myGLCD.setFont(MediumNumbers);
  long freqH = FREQ / 1000000;
  myGLCD.printNumI(freqH,0,8);
  long freqL = FREQ - freqH * 1000000;
  long fM = freqL /1000;
  int fL = (freqL - fM * 1000) / 10;
  myGLCD.printNumI(fM,18,8,3,'0');
  myGLCD.printNumI(fL,60,8,2,'0');
}

//---------- step display  ----------

void step_disp(){
  myGLCD.setFont(SmallFont);
  myGLCD.printNumI(STEP,12,40,4,' ');
}

//---------- si5351a register write 1 byte----------

void cmd_si5351(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

//---------- si5351A initialyze ----------

void si5351_init(){
  cmd_si5351(XTAL_LOAD_C,0b10010010);   // CL=8pF
  cmd_si5351(CLK0_CTRL,0x80);           // Disable CLK0
  cmd_si5351(CLK1_CTRL,0x80);           // Disable CLK1
  cmd_si5351(PLL_RESET,0xA0);           // Reset PLL_A
  cmd_si5351(CLK0_CTRL,0x4F);           // Enable CLK0 (MS0=Integer Mode, Source=PLL_A)
  cmd_si5351(CLK1_CTRL,0x4F);           // Enable CLK1 (MS1=Integer Mode, Source=PLL_A)
}

//---------- set frequency ----------

void freq_set(unsigned long freq){
//    freq [Hz]
//
//    fvco= fxtal*(a+b/c)  ( a:15 -- 90,   b:0 -- 1048575, c:1 -- 1048575 )
//    freq= fvco /(a+b/c)  ( a:4, 6--1800, b:0 -- 1048575, c:1 -- 1048575 )
//
//    P1= 128*a +   floor(128*b/c) - 512
//    P2= 128*b - c*floor(128*b/c)
//    P3= c
//

  int k;
  unsigned long M;
  unsigned int R;

  if(freq<1500) freq=1500; else if(freq>280000000) freq=280000000;

  if(     freq> 150000000){M=4; R=0;}
  else if(freq>=63000000){M=6; R=0;}
  else if(freq>=27500000){M=14; R=0;}
  else if(freq>=13000000){M=30; R=0;}
  else if(freq>= 6500000){M=62; R=0;}
  else if(freq>= 3000000){M=126; R=0;}
  else if(freq>= 1500000){M=280; R=0;}
  else if(freq>=  700000){M=600; R=0;}
  else if(freq>=  330000){M=1280; R=0;}
  else if(freq>=  150000){M=1300; R=1;}
  else if(freq>=   67000){M=1500; R=2;}
  else if(freq>=   30300){M=1600; R=3;}
  else if(freq>=   14000){M=1800; R=4;}
  else if(freq>=    7000){M=1800; R=5;}
  else if(freq>=    3500){M=1800; R=6;}
  else{M=1800; R=7;}

  freq*=M;
  freq<<=R;

  unsigned long c=0xFFFFF;
  unsigned long a=freq/XtalFreq;
  unsigned long b=(long)((double)(freq-a*XtalFreq)*(double)c/(double)XtalFreq);
  unsigned long dd=(128*b)/c;
  unsigned long P1=128*a+dd-512;
  unsigned long P2=128*b-c*dd;
  unsigned long P3=c;


  //Set fvco of PLL_A
    cmd_si5351(MSNA_ADDR+0,(P3>>8)&0xFF);        //MSNA_P3[15:8]
    cmd_si5351(MSNA_ADDR+1,P3&0xFF);             //MSNA_P3[7:0]
    cmd_si5351(MSNA_ADDR+2,(P1>>16)&0x03);       //MSNA_P1[17:16]
    cmd_si5351(MSNA_ADDR+3,(P1>>8)&0xFF);        //MSNA_P1[15:8]
    cmd_si5351(MSNA_ADDR+4,P1&0xFF);              //MSNA_P1[7:0]
    cmd_si5351(MSNA_ADDR+5,(P3>>12)&0xF0|(P2>>16)&0x0F);//MSNA_P3[19:16], MSNA_P2[19:16]
    cmd_si5351(MSNA_ADDR+6,(P2>>8)&0xFF);        //MSNA_P2[15:8]
    cmd_si5351(MSNA_ADDR+7,P2&0xFF);             //MSNA_P2[7:0]

  // Set MS0, MS1
  // a=M, b=0, c=1 ---> P1=128*M-512, P2=0, P3=1
  if(M==4){
    P1=0;
    cmd_si5351(MS0_ADDR+0,0);                   //MS0_P3[15:8]
    cmd_si5351(MS0_ADDR+1,1);                   //MS0_P3[7:0]
    cmd_si5351(MS0_ADDR+2,0b00001100);          //0,R0_DIV[2:0],MS0_DIVBY4[1:0],MS0_P1[17:16]
    cmd_si5351(MS0_ADDR+3,0);                   //MS0_P1[15:8]
    cmd_si5351(MS0_ADDR+4,0);                   //MS0_P1[7:0]
    cmd_si5351(MS0_ADDR+5,0);                   //MS0_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS0_ADDR+6,0);                   //MS0_P2[15:8]
    cmd_si5351(MS0_ADDR+7,0);                   //MS0_P2[7:0]
 
    cmd_si5351(MS1_ADDR+0,0);                   //MS1_P3[15:8]
    cmd_si5351(MS1_ADDR+1,1);                   //MS1_P3[7:0]
    cmd_si5351(MS1_ADDR+2,0b00001100);          //0,R1_DIV[2:0],MS1_DIVBY4[1:0],MS1_P1[17:16]
    cmd_si5351(MS1_ADDR+3,0);                   //MS1_P1[15:8]
    cmd_si5351(MS1_ADDR+4,0);                   //MS1_P1[7:0]
    cmd_si5351(MS1_ADDR+5,0);                   //MS1_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS1_ADDR+6,0);                   //MS1_P2[15:8]
    cmd_si5351(MS1_ADDR+7,0);                   //MS1_P2[7:0]
  }else{
    P1=128*M-512;
    cmd_si5351(MS0_ADDR+0,0);                    //MS0_P3[15:8]
    cmd_si5351(MS0_ADDR+1,1);                    //MS0_P3[7:0]
    cmd_si5351(MS0_ADDR+2,(R<<4)&0x70|(P1>>16)&0x03);//0,R0_DIV[2:0],MS0_DIVBY4[1:0],MS0_P1[17:16]
    cmd_si5351(MS0_ADDR+3,(P1>>8)&0xFF);        //MS0_P1[15:8]
    cmd_si5351(MS0_ADDR+4,P1&0xFF);              //MS0_P1[7:0]
    cmd_si5351(MS0_ADDR+5,0);                    //MS0_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS0_ADDR+6,0);                    //MS0_P2[15:8]
    cmd_si5351(MS0_ADDR+7,0);                    //MS0_P2[7:0]
 
    cmd_si5351(MS1_ADDR+0,0);                    //MS1_P3[15:8]
    cmd_si5351(MS1_ADDR+1,1);                    //MS1_P3[7:0]
    cmd_si5351(MS1_ADDR+2,(R<<4)&0x70|(P1>>16)&0x03);//0,R1_DIV[2:0],MS1_DIVBY4[1:0],MS1_P1[17:16]
    cmd_si5351(MS1_ADDR+3,(P1>>8)&0xFF);        //MS1_P1[15:8]
    cmd_si5351(MS1_ADDR+4,P1&0xFF);              //MS1_P1[7:0]
    cmd_si5351(MS1_ADDR+5,0);                    //MS1_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS1_ADDR+6,0);                    //MS1_P2[15:8]
    cmd_si5351(MS1_ADDR+7,0);                    //MS1_P2[7:0]
  }
  cmd_si5351(CLK0_PHOFF,0);
  cmd_si5351(CLK1_PHOFF,M);

  cmd_si5351(PLL_RESET,0xA0);                 // Reset PLL_A.
}





            

0 件のコメント: