2023年5月1日月曜日

Pico Si5351A 90度位相シフトVFO 試作

Raspberry Pi Pico  Arduino IDEによる  Si5351A 90度位相シフトVFO 試作

同ブログ関連記事: 









参考サイト
●Tj Lab サイト
●JA2GQP局ブログ
●I2C 通信について
●1602 LCDについて

90度相シフト














回路図


プログラム Arduino IDE【ボード:Raspberry Pi Pico】
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f, 16, 2);//LCDアドレス0x3f
//LiquidCrystal_I2C lcd(0x27, 16, 2);
//////////////////////////
// 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   13                 //Rotary encoder A
#define   ENC_B   14                 //Rotary encoder B
#define   SW_STEP 15                 // Step SW
volatile uint8_t old_value = 0x11;
volatile uint8_t value = 0;
volatile uint8_t D;
volatile int8_t count = 0;

//////////////////////////
// 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)
String freqt = String(FREQ);
String fH_old = "";
String fM_old = "";
String fL_old = "";


//////////////////////////
// Rotal encorder
//////////////////////////

void rotary(){
  value = (digitalRead(ENC_B)<<1) | digitalRead(ENC_A);
  if(old_value != value){
    D = ((old_value << 1) ^ value) & 3;
    if(D < 2){
      count += 1;
    }else{
      count -= 1;
    }
  if(count >= 4){
    FREQ += STEP;
    count = 0;
  }else if(count <= -4){
    FREQ -= STEP;
    count = 0;
  }
  FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ); //VFOの下限と上限を超えないように
  old_value = value;
  }
}

void setup()
{
  Wire.setSDA(16);  //SDA 
  Wire.setSCL(17);  //SCL   
  Wire.begin();                
  si5351_init();                
  freq_set(FREQ);
  pinMode(SW_STEP,INPUT_PULLUP);    // STEP SW pullup
  attachInterrupt(ENC_A, rotary, CHANGE);
  attachInterrupt(ENC_B, rotary, CHANGE); 
  lcd.init();
  lcd.backlight();
  lcd.clear();  
  Freq_Disp(FREQ);
  Step_Disp();
}

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

void loop(){
  if(digitalRead(SW_STEP) == LOW){Set_Step();}
  if(FREQ != FREQ_OLD){
    freq_set(FREQ);
    Freq_Disp(FREQ);
    FREQ_OLD = FREQ;          
  }
  delay(10);
}

//周波数表示
void Freq_Disp(long frequency){
  freqt = String(frequency);
  String fH = freqt.substring(0,1);
  String fM = freqt.substring(1,4);
  String fL = freqt.substring(4,6);  
  lcd.setCursor(3,0);  
  if (STEP == 10)lcd.print("10Hz");
  if (STEP == 100)lcd.print("100Hz");
  if (STEP == 1000)lcd.print("1KHz");
  if (STEP == 10000)lcd.print("10KHz");
//MHzの表示 
  lcd.setCursor(3,1); 
  lcd.print(fH);     
  fH_old = fH;
//KHzの表示    
  lcd.setCursor(4,1); 
  lcd.print(fM);    
  fM_old = fM;
//Hzの表示   
  lcd.setCursor(7,1); 
  lcd.print(fL);   
  fL_old = fL;  
}

//STEP表示
void Step_Disp(){ 
  lcd.setCursor(3, 0);     
  if (STEP == 10)lcd.print("10Hz");
  if (STEP == 100)lcd.print("100Hz");
  if (STEP == 1000)lcd.print("1KHz");
  if (STEP == 10000)lcd.print("10KHz"); 
}
//STEP切り替え
void Set_Step(){
  if (STEP == 10){
    STEP = 10000;
  }else{
    STEP /= 10;
  }
  delay(10);
  Step_Disp();
  while(digitalRead(SW_STEP) == LOW){
    delay(10);
  }
}

//---------- 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 件のコメント:

コメントを投稿

Raspberry Pi Donkey Car スマートカー

  2020年に製作したDonkey Car スマートカー について記事にしました。 Donkey CarはRaspberry Pi のカメラからコースを ディープラーニングさせ自動走行を行います。(動画は白線上を自動走行)   動画