そこで、位相出力に特化した考え方の、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.
}