Arduino Pro Mini Si5351A OLED表示 7MHz VFO
2020.12.30 de JH7UBC
Arduino Pro Mini とSi5351AとOLEDの組み合わせで7MHzのVFOを作ってみました。
回路図です。
ロータリーエンコーダは、スイッチ付きの中華製を使いました。(Amazonで購入しました)
ロータリーエンコーダは、D2とD3に接続して、外部割込みで動作させます。
ロータリーエンコーダ用のライブラリは、Ben Buxtonのものを利用しています。
ライブラリは、GitHubからダウンロードできます。
こちらからRotary-master.zipをダウンロードして、Arduino IDEでライブラリーに登録します。
OLEDはAmazonから購入したHeiLetgoの0.96インチ128×64ドットのI2C接続のものを使います。
(秋月電子でも同様のOLEDを販売しています。)
このOLEDには、SD1306といコントローラが使われていて、いくつかのライブラリを利用することができます。
今回は、文字表示だけですので、SSD1306Asciiというライブラリを使ってみます。
ライブラリーマネージャまたは、GitHubからライブラリをインストールします。GitHubは、こちら。
最後に3チャンネルクロックジネレータ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)); } |
周波数カウンタで出力を測定しています。