ここまで、Arduino Pro Miniを使って、ロータリーエンコーダとOLEDのテストをしました。それらに、3チャンネルクロックジェネレーターSi5351Aを加えて、7MHz VFOを作ってみます。
回路図です。
ロータリーエンコーダは、スイッチ付きの中華製を使いました。スイッチは、STEP切り替え用に使っています。ロータリーエンコーダ用のライブラリは、Ben Buxton のものを使い、D2とD2に接続して、外部割込みを利用しています。なお、詳細は、当ブログの記事「ロータリーエンコーダ その2」を参照してください。
OLEDの表示には、SSD1306Ascii ライブラリを使いました。使い方は前の記事を見てください。
Si5351Aのコントロール用のライブラリもたくさんありますが、今回は私がこれまで使っていたスケッチを使っています。
スケッチです。
----------------------------------------------------------------------------------------------------
/*
* Arduino Si5351A 7MHz VFO OLED Display
* 2020.12.29
* JH7UBC Keiji Hata
*/
#include <Wire.h>
#include <SSD1306AsciiAvrI2c.h>
#include <Rotary.h>
SSD1306AsciiAvrI2c oled;
Rotary r = Rotary(2, 3);
//Si5351A関係の定義
#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177
uint32_t XtalFreq = 25000000;
uint32_t divider;
uint32_t PllFreq;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t l;
float f;
uint32_t P1;
uint32_t P2;
uint32_t P3;
char PLL;
uint8_t PLL_ADDR;
uint8_t MS_ADDR;
//周波数STEP
#define SW_STEP 4 //周波数STEPボタン
//周波数設定
const long LOW_FREQ = 7000000; //下限周波数
const long HI_FREQ = 7200000; //上限周波数
unsigned long FREQ = 7000000; //VFO周波数初期値
unsigned long FREQ_OLD = FREQ; //周波数の前の値
int dF = 120; //周波数補正値
int STEP = 1000; //STEP 初期値
//Rotary Encoder 外部割込み処理ルーチン
void rotary_encoder(){
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); //VFOの下限と上限を超えないように
}
//STEP SWが押された時の処理
void Fnc_Stp()
{
if(STEP == 10){
STEP = 1000;
}
else{
STEP /= 10;
}
delay(10);
Step_Disp(STEP);
while(digitalRead(SW_STEP) == LOW){
delay(10);
}
}
void setup() {
r.begin();//ロータリーエンコーダ初期化
attachInterrupt(0,rotary_encoder,CHANGE); //外部割込み設定
attachInterrupt(1,rotary_encoder,CHANGE);
pinMode(SW_STEP,INPUT_PULLUP); //STEP SW 入力に設定しプルアップ
oled.begin(&Adafruit128x64, 0x3C); //OLED初期化
Si5351_init(); //Si5351Aの初期化
PLL_Set('A',FREQ + dF); //VFO周波数初期値セット
MS_Set(0);
//初期画面表示
oled.setFont(font5x7);
oled.setCursor(5,0);
oled.print("Arduino Si5351A VFO");
oled.setFont(font8x8);
oled.setCursor(5,6);
oled.print("STEP");
Freq_Disp(FREQ); //周波数表示
Step_Disp(STEP); //STEP表示
}
//Main program
void loop() {
if(digitalRead(SW_STEP) == LOW){Fnc_Stp();} //STEP SWが押されたら、周波数STEPを変更
if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、Si5351Aの周波数を変更
PLL_Set('A',FREQ + dF);
MS_Set(0);
Freq_Disp(FREQ); //周波数を表示する。
FREQ_OLD = FREQ; //変更された周波数を保存
}
delay(10);
}
//周波数表示
void Freq_Disp(unsigned long Fre){
String freqt = String(Fre);
oled.setFont(lcdnums14x24);
oled.setCursor(0,2);
oled.print(freqt.substring(0,1) + "." + freqt.substring(1,4) + "." + freqt.substring(4));
}
//STEP表示
void Step_Disp(int Stp){
oled.setFont(font8x8);
oled.setCursor(40,6);
if(Stp == 1000){
oled.print(" 1K");
}else if(Stp == 100){
oled.print("100");
}else{
oled.print(" 10");
}
}
//レジスタに1バイトデータを書き込む。
void Si5351_write(byte Reg , byte Data){
Wire.beginTransmission(Si5351A_ADDR);
Wire.write(Reg);
Wire.write(Data);
Wire.endTransmission();
}
//Si5351Aの初期化
void Si5351_init(){
Si5351_write(OUTPUT_CTRL,0xFF); //Disable Output
Si5351_write(CLK0_CTRL,0x80); //CLOCK0 Power down
Si5351_write(CLK1_CTRL,0x80); //CLOCK1 Power down
Si5351_write(CLK2_CTRL,0x80); //CLOCK2 Power down
Si5351_write(XTAL_LOAD_C,0x80); //Crystal Load Capasitance=8pF
Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0x4F); //CLOCK0 Power up 8mA
Si5351_write(CLK1_CTRL,0x4F); //CLOCK1 Power up
Si5351_write(CLK2_CTRL,0x4F); //CLOCK2 Power up
Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}
//PLLの設定
void PLL_Set(char Pll,uint32_t Frequency){
divider = 900000000 / Frequency;
if (divider % 2) divider--;
PllFreq = divider * Frequency;
mult = PllFreq / XtalFreq;
l = PllFreq % XtalFreq;
f = l;
f *= 1048575;
f /= XtalFreq;
num = f;
denom = 1048575;
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;
if (Pll == 'A'){
PLL_ADDR = MSNA_ADDR;
}else{
PLL_ADDR = MSNB_ADDR;
}
Parameter_write(PLL_ADDR,P1,P2,P3);
}
//MultiSynth(分周器)のセット
void MS_Set(uint8_t MS_No){
P1 = 128 * divider - 512;
P2 = 0;
P3 = 1;
switch(MS_No){
case 0:
MS_ADDR = MS0_ADDR;
break;
case 1:
MS_ADDR = MS1_ADDR;
break;
case 2:
MS_ADDR = MS2_ADDR;
break;
default:
MS_ADDR = MS0_ADDR;
}
Parameter_write(MS_ADDR,P1,P2,P3);
}
//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(uint8_t REG_ADDR,uint32_t Pa1,uint32_t Pa2,uint32_t Pa3)
{
Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF));
}
----------------------------------------------------------------------------------------------------
* Arduino Si5351A 7MHz VFO OLED Display
* 2020.12.29
* JH7UBC Keiji Hata
*/
#include <Wire.h>
#include <SSD1306AsciiAvrI2c.h>
#include <Rotary.h>
SSD1306AsciiAvrI2c oled;
Rotary r = Rotary(2, 3);
//Si5351A関係の定義
#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177
uint32_t XtalFreq = 25000000;
uint32_t divider;
uint32_t PllFreq;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t l;
float f;
uint32_t P1;
uint32_t P2;
uint32_t P3;
char PLL;
uint8_t PLL_ADDR;
uint8_t MS_ADDR;
//周波数STEP
#define SW_STEP 4 //周波数STEPボタン
//周波数設定
const long LOW_FREQ = 7000000; //下限周波数
const long HI_FREQ = 7200000; //上限周波数
unsigned long FREQ = 7000000; //VFO周波数初期値
unsigned long FREQ_OLD = FREQ; //周波数の前の値
int dF = 120; //周波数補正値
int STEP = 1000; //STEP 初期値
//Rotary Encoder 外部割込み処理ルーチン
void rotary_encoder(){
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); //VFOの下限と上限を超えないように
}
//STEP SWが押された時の処理
void Fnc_Stp()
{
if(STEP == 10){
STEP = 1000;
}
else{
STEP /= 10;
}
delay(10);
Step_Disp(STEP);
while(digitalRead(SW_STEP) == LOW){
delay(10);
}
}
void setup() {
r.begin();//ロータリーエンコーダ初期化
attachInterrupt(0,rotary_encoder,CHANGE); //外部割込み設定
attachInterrupt(1,rotary_encoder,CHANGE);
pinMode(SW_STEP,INPUT_PULLUP); //STEP SW 入力に設定しプルアップ
oled.begin(&Adafruit128x64, 0x3C); //OLED初期化
Si5351_init(); //Si5351Aの初期化
PLL_Set('A',FREQ + dF); //VFO周波数初期値セット
MS_Set(0);
//初期画面表示
oled.setFont(font5x7);
oled.setCursor(5,0);
oled.print("Arduino Si5351A VFO");
oled.setFont(font8x8);
oled.setCursor(5,6);
oled.print("STEP");
Freq_Disp(FREQ); //周波数表示
Step_Disp(STEP); //STEP表示
}
//Main program
void loop() {
if(digitalRead(SW_STEP) == LOW){Fnc_Stp();} //STEP SWが押されたら、周波数STEPを変更
if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、Si5351Aの周波数を変更
PLL_Set('A',FREQ + dF);
MS_Set(0);
Freq_Disp(FREQ); //周波数を表示する。
FREQ_OLD = FREQ; //変更された周波数を保存
}
delay(10);
}
//周波数表示
void Freq_Disp(unsigned long Fre){
String freqt = String(Fre);
oled.setFont(lcdnums14x24);
oled.setCursor(0,2);
oled.print(freqt.substring(0,1) + "." + freqt.substring(1,4) + "." + freqt.substring(4));
}
//STEP表示
void Step_Disp(int Stp){
oled.setFont(font8x8);
oled.setCursor(40,6);
if(Stp == 1000){
oled.print(" 1K");
}else if(Stp == 100){
oled.print("100");
}else{
oled.print(" 10");
}
}
//レジスタに1バイトデータを書き込む。
void Si5351_write(byte Reg , byte Data){
Wire.beginTransmission(Si5351A_ADDR);
Wire.write(Reg);
Wire.write(Data);
Wire.endTransmission();
}
//Si5351Aの初期化
void Si5351_init(){
Si5351_write(OUTPUT_CTRL,0xFF); //Disable Output
Si5351_write(CLK0_CTRL,0x80); //CLOCK0 Power down
Si5351_write(CLK1_CTRL,0x80); //CLOCK1 Power down
Si5351_write(CLK2_CTRL,0x80); //CLOCK2 Power down
Si5351_write(XTAL_LOAD_C,0x80); //Crystal Load Capasitance=8pF
Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0x4F); //CLOCK0 Power up 8mA
Si5351_write(CLK1_CTRL,0x4F); //CLOCK1 Power up
Si5351_write(CLK2_CTRL,0x4F); //CLOCK2 Power up
Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}
//PLLの設定
void PLL_Set(char Pll,uint32_t Frequency){
divider = 900000000 / Frequency;
if (divider % 2) divider--;
PllFreq = divider * Frequency;
mult = PllFreq / XtalFreq;
l = PllFreq % XtalFreq;
f = l;
f *= 1048575;
f /= XtalFreq;
num = f;
denom = 1048575;
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;
if (Pll == 'A'){
PLL_ADDR = MSNA_ADDR;
}else{
PLL_ADDR = MSNB_ADDR;
}
Parameter_write(PLL_ADDR,P1,P2,P3);
}
//MultiSynth(分周器)のセット
void MS_Set(uint8_t MS_No){
P1 = 128 * divider - 512;
P2 = 0;
P3 = 1;
switch(MS_No){
case 0:
MS_ADDR = MS0_ADDR;
break;
case 1:
MS_ADDR = MS1_ADDR;
break;
case 2:
MS_ADDR = MS2_ADDR;
break;
default:
MS_ADDR = MS0_ADDR;
}
Parameter_write(MS_ADDR,P1,P2,P3);
}
//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(uint8_t REG_ADDR,uint32_t Pa1,uint32_t Pa2,uint32_t Pa3)
{
Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF));
}
----------------------------------------------------------------------------------------------------
CLK0の出力を周波数カウンタで測定しています。
Si5351Aの発振周波数は、設定と若干ずれますので、周波数補正値dFの値で調整します。
ブレッドボードです。コンパクトです。