お気に入りの無線機の製作「Si5351Aを使う」


 写真はPICマイコンに、Si5351モジュールを組み合わせた、実験ボード。少しずつ周辺回路を追加しながらソフトのデバックをすすめています。

2018.3.15
2022.8.06 Si5351を実機に盛り込むを追加
はじめに
 「お気に入りの無線機」を自作しようとすると、いくつかの要素技術の習得が必要になります。中でもVFOやDDS制御に必要なマイコンのソフト開発は 必須です。
 無線機に組み込むソフトとしては、周波数カウンター、シリアル通信、DDS制御、Si5351制御、LCD表示などの機能があれば大体間に合いそうで、  今回は、Si5351の制御方法について、まとめておきます。

マイコンの選定
 使用するマイコンとしては、上記機能をすべて盛り込むとしたら、プログラム容量8kWではやや少ないかもしれませんが、PIC16Fシリーズでは8k以上の機種は無いので、 今回はPIC16F886を選定しました。
 当局の場合、書き込みには秋月ライターを使うので、対応機種が限定され、16F886を選定しましたがPicKit等を使う場合は選択肢は拡がります、開発には無償で提供されている MPLAB X IDE、コンパイラにXC8を使います。

Si5351Aを試してみる
 無線機にSi5351を組み込む用途として、ローカル水晶発振の代替えが適しています。他にVFOへの応用も出来なくはありませんが、周波数可変のたびに発振が 途切れ、「プチプチ」ノイズが出るので、不向きかもしれません。
 本デバイスの周波数設定は、マイコンで制御、Webによればなかなか面倒な設定の様で、マイコンはArduino での制御例が多いのですが、今回は開発環境上使い慣れたPICでの制御を試してみます。
 Si5351を周波数設定するには、内部レジスターに設定値をI2Cで書き込むことで制御できます。設定レジスター数が多いのと、XC8コンパイラには I2Cの組み込み関数が無いので、スタートしずらく感じますが、慣れてしまえば問題ありません。
 Si5351Aをモジュール化した製品が、秋月電子から出ています。詳細データーアプリケーションノートはこちらを参照してください。

プログラムの構想
 今回はSi5351Aをとりあえず使ってみるということで、周波数は一波(36MHz)のみとし、レジスターの設定方法やI2Cのプログラムをマスターするという 目標で作成します。
 一波の制御が出来れば、後はレジスターに書き込む値を変えれば周波数可変も出来るはずです。

周波数設定方法
 

 周波数設定はPLL VCO周波数の設定と、そのPLL周波数を何分周するかのマルチシンセ部の設定で行います。
 周波数設定例として、出力36MHzで計算してみます。
 Si5351モジュールに実装されているXtalは25MHzなので、Fvco=25MHz x X1と36MHz=Fvco/X2の2式からX1=36、X2=25になります。 ここでbを0と仮設定するとFvcoを求めるaは36に、Foutを求めるaは25になります。
 bを0としたのでcは似意の値で良いことになるのでここでは1とします。
 次に、今求めたa〜cをパラメーターP1〜P3を求める式に代入すると、MSNA_P1は4096、P2は0、P3は1になります。MS0_P1は2688、P2は0、P3は1になり、それぞれ の値を2進数で各レジスターに書き込みます。
 これらの値を求める計算ソフト、クロックビルダーも提供されていますが、使い方を理解するのも面倒なので手計算でやりました。

  ソフトの説明
ソースリストを参考まで、プログラムについての簡単な説明をします。
 プログラムはメイン関数の他、PIC初期設定、I2Cコマンド送信、I2Cアイドル待ち確認、I2C 1Byte送信関数から構成しています。

Config設定 以前のMPLAB IDEでは設定内容を記述していましたが、X IDEではエディター画面下のリストから選択すれば自動で作成してくれるソースをコピーすれば OKで、MPLAB IDEに比較して簡単になりました。

メイン関数 プログラムのメイン関数部です。PIC初期化を呼び出した後、Si5351Aの各レジスターのアドレスと、設定値を引数としてi2c_send関数の呼び出し をレジスター分繰り返しています。
 今回のソフトでは、出力周波数36MHzのみで、Si5351のPLL AとマルチシンセAのみ使用するので、それらのレジスターに設定値を書きこみます。PLL Bとマルチシンセ Bや分周器を使うとそれらのレジスターにも設定値を書きこむ必要があります。
 レジスターへの書き込み順には、指定があり詳細はアプリケーションノートAN-619に書かれています。
 プログラムの下部に500ms周期のLED点滅は、デバック確認用です。叉、設定をWhile文でループしていますが、動作確認のためで、実際には一回のみ設定すれば OKです。

PIC初期化関数 PICマイコンの初期設定部で、ポートの設定やI2C制御のレジスタ設定をしています。I2CのSDA/SDLで使用するRC3/RC4はそれぞれ 入力ポートに設定します。

I2Cコマンド送信関数 「PIC16F1ファミリー活用ガイドブック」の例を、ほぼそのまま使いました。CCS Cコンパイラでは組み込み関数が充実しているので PIC内部レジスタのビット設定まで記述することはありませんが、XC8コンパイラでは組み込み関数が無い分、PICレジスターを詳しく確認必要です。

プログラムのトラブル X IDEでは日本語のコメントが使えるようになりましたので、プログラム行頭やインデント(字下げ)に全角のスペースが入っている箇所 があり、ビルドエラーになってしまいます。このエラーを見つけるのに随分時間がかかってしまいました。

試験回路
 

 テスト回路を左図に示します。回路はとりあえずSi5351Aの制御部のみ実装していますが今後順次周辺回路を組み込んで動作確認していく予定です。、
 回路図のSi5351は秋月モジュールで、単体のICのピン配置と異なっています。Si5351は3.3V動作なのでマイコンも3.3V動作させています。

  周波数設定方法2
 

 今までは切りの良い周波数設定例でしたが、実際の周波数設定例について紹介します。例ではCLK0で7.960MHzを出力する場合
step1でVCO周波数を仮設定します。仮設定ではdの値が設定許容範囲内になるようにします。
step2,3でdの値を求め、設定許容範囲内になっていることを確認、なっていなければVCO周波数の設定変更
step4でstep3で得られた値より小さいdの偶数値を求める。
step5でVCO周波数を決定する。
step6,7でVCO設定のa,b,cを求めるが、cの値は65536で固定する。VCO可変ステップはb/65536になる。
以上で求めた各値をパラメータ計算に代入してVCO設定パラメータP1-P3,マルチシンセ設定パラメーターP1-P3を求める。

   プログラムへの盛り込み

void pic_init();             //PIC初期化
long get_freq();  
void i2c_send(unsigned char, unsigned char); //Reg address, Data
void i2c_send2(unsigned char); //MCP4018 Data
void send_byte(unsigned char);
void rsc_send();                //USART
void idlei2c();
void si5351_init();             //si5351初期化
void si5351_getpara(long out_freq, long vco_freq);
void si5351_setregster(long p1, long p2, long p3, char addres);
    

 プログラム例を左記に示します。プログラムは「お気に入りSDRトランシーバー3号機」で製作中の物からの抜粋です。
 プロクラムで使用する関数の宣言部で、PICの初期化関数に続いて、3行目からSi5351のi2c制御、パラメーター計算、レジスター書き込み等の 関数を使用します。


//===========I2C コマンド送信======================
void i2c_send( char reg_addres, char data)
{
    const char slv_addres = 0xC0; //Si5351 0b1100 000_
    idlei2c();                     //アイドル待ち
    SSPCON2bits.SEN = 1;           //スタート出力
    while (SSPCON2bits.SEN);       //スタート終了待ち
    send_byte(slv_addres);         //Si5351 addres+マスタ送信
    send_byte(reg_addres);         //Si5351 Regster送信
    send_byte(data);               //Si5351 set data送信
    SSPCON2bits.PEN = 1;           //stop 出力
    while (SSPCON2bits.PEN);        //ストップ終了待ち    
}

/**アイドル待ちサブ関数*/
static void idlei2c(void)
{
    while (SSPCON2bits.SEN || SSPCON2bits.PEN || SSPCON2bits.RCEN ||
          SSPCON2bits.ACKEN || SSPSTATbits.R_nW);
}

//===========I2C 1Byte送信=======================
static void send_byte(char tx_data)         
{
   SSPBUF = tx_data;             //データーセット送信開始
   while (SSPSTATbits.BF);       //送信終了待ち 
   while (SSPCON2bits.ACKSTAT);  //ACK返信待ち
   idlei2c();                   //アイドル待ち
}

左はi2cコマンド送信関数で、レジスターアドレスとデーターを引き数として受け、各レジスターに書き込みから、データー送信までの処理で、 データーはPICのSSPBUFにデーターをセットすれば後はPICのハードで処理しているようです。。


 //===========Si5351初期設定=======================
void si5351_init()
{
    i2c_send(3,0xFF);           //OUT CONT Reg3,全出力OFF
    i2c_send(16,0x80);          //CLK0 CONT Reg16,PWR DWN
    i2c_send(17,0x80);          //CLK1 CONT Reg17,PWR DWN
    i2c_send(18,0x80);          //CLK2 CONT Reg18,PWR DWN
}

左はSi5351の初期化で、レジスターアドレスとデーターを引き数としてi2c_send関数に渡しています。

//===========Si5351 パラメーター計算=======================
void si5351_getpara(long out_freq, long vco_freq)
{  
   int div;        //分周数
    int a;          //PLL freq =(a+b/c)の係数
    int d;
    long pllfreq;   //PLL周波数
    long b;
    long c, x;
    long e;         //(128 * (float)b / 0xFFFF )を計算
    long si5351_div1000; //si5351xtal周波数を桁落とし
  
    
    div = vco_freq / out_freq;       //VCOを仮設定して分周比を計算
    div = div >> 1;                 //右へシフトして偶数化
    div = div << 1;
    
    pllfreq = div * out_freq;       //PLL周波数は出力周波数の偶数倍にする
    a = pllfreq / si5351_xtal;      //
   // pllfreq = pllfreq / 1000;     // long計算に収める為3桁丸め
                     
    c = 65536;
    si5351_div1000 = si5351_xtal / 1000;     // long計算に収める為3桁丸め
    x = pllfreq % si5351_xtal / 1000;     // long計算に収める為3桁丸め
    b = x * c  / si5351_div1000;
    
     vco_div = div;
   
     e = (128 * b) / c;  //128*(float)b/0xFFFF
    
      
/*Si5351 PLL用パラメーター計算*/
  //  P1 = 128 * a + (long)(128 * (float)b / 0xFFFF ) - 512;
    p1 = 128 * a + e - 512;
   // P2 = 128 * b - c * (long)(128 * (float) b / 0xFFFF);
    p2 = 128 * b - c * e;
    p3 = c;
   
}

 左はsi5351のパラメーターを求める関数で、出力周波数と、仮設定したVCO周波数を引き数として受け取りパラメーターを計算します。
周波数が固定の場合、パラメーターを手計算で求め定数として使用すれば、この関数は不要になります。
 si5351_x'talは25MHzで、最初に定義しておく。実際には
#define si5351_xtal 24999242 //Si5351 X'tal Freq で25MHzぴったりでは無いのでそれぞれの発振周波数にあわせると誤差が少なくなる。

//===========Si5351レジスター設定=======================
void si5351_setregster(long p1, long p2, long p3, char addres)    
{
    i2c_send(addres, (p3 & 0x0FF00) >> 8);          //MSNA_P3[15:8],P3書き込み
    i2c_send(addres + 1, (p3 & 0x000FF));          //MSNA_P3[7:0],P3
    i2c_send(addres + 2, (p1 & 0x30000) >> 16);    //MSNA_P1[17:16],P1
    i2c_send(addres + 3, (p1 & 0x0FF00) >> 8);    //MSNA_P1[15:8],P1
    i2c_send(addres + 4, (p1 & 0x000FF));          //MSNA_P1[7:0],P1
    i2c_send(addres + 5, ((p3 & 0xF0000) >> 12) | ((p2 & 0xF0000) >> 16)); 
                                ////上位4bit分 下位4bit分MSNA_P2[19:16],P2
    
    i2c_send(addres + 6, (p2 & 0x0FF00) >> 8);         //MSNA_P2[15:8],P2
    i2c_send(addres + 7, (p2 & 0x000FF));         //MSNA_P2[7:0],P2
    
/* Si5351Aマルチシンセ分周設定レジスター P2=0 P3=1 */ 
    /*P1-P3計算*/
    
 
/*ここまでSi5351A周波数設定完了*/    
    i2c_send(177,0xA0);         //PLL A b reset
    i2c_send(16,0x4F);          //CLK0 POWER UP & 8mA
    i2c_send(17,0x6F);          //CLK1 POWER UP & PLLB Select
    i2c_send(18,0x0F);          //CLK2 Power up 8mA PLLA selct   
    //i2c_send(3,0xFE);          //OUT CONT reg,CLK0 Enable
    i2c_send(3,vfo_selsw);          //OUT CONT reg,CLK0 Enable
}

 左はsi5351のパラメーターを引数として受け取り、各レジスターに書き込みます。アドレスは連続しているので受け取ったアドレスに 加算していき、データ―は8bit単位なので8bitずつシフトしてi2c_send関数に渡します。

//==============MAIN======================
void main()
{
    long T0_freq = 0;
    long T1_freq;
    long delta_freq;
    int meas_loop;
    vfo_selsw = 0xFE;   //Si5351reg3設定 CLK0 Enable
    clk2_on = 0xFA;
    clk2_off = 0xFE;
    pic_init();                 //PIC初期設定
    si5351_init();              //Si5351初期設定
    out_freq = 7960000;        //VFO A OUT 7.960MHz
    vco_freq = 650000000;       //VCO A Freq 650MHz仮設定
    addres = 26;                //si5351 Start_Addres 26
    si5351_getpara(out_freq, vco_freq);   //Si5351_PLLAパラメーター計算 
    si5351_setregster(p1, p2, p3, addres);   //Si5351_VFOA周波数設定
    addres = 42;        //シンセ0設定
    p1 = 128 * vco_div - 512;
    p2 = 0;
    p3 = 1;
    si5351_setregster(p1, p2, p3, addres);   //Si5351_シンセ0設定
    
 /*   out_freq = 5400000;         //VFO B OUT 5.4MHz
    vco_freq = 800000000;       //VCO B Freq 800MHz仮設定
    addres = 34;                //VFO B Start_Addres 34
    si5351_getpara(out_freq, vco_freq);   //Si5351_PLLBパラメーター計算
    // p1 = 3524;
   // p2 = 50304;
   // p3 = 65536;
    si5351_setregster(p1, p2, p3, addres);   //Si5351_VFOB周波数設定
    addres = 50;        //シンセ1設定
    p1 = 128 * vco_div - 512;
    p2 = 0;
    p3 = 1;
    si5351_setregster(p1, p2, p3, addres);   //Si5351_シンセ1設定  */
    
    
    addres = 58;        //シンセ2設定 Fout 9.000000MHz
    p1 = 8691;
    p2 = 46592;
    p3 = 65536;
    si5351_setregster(p1, p2, p3, addres);   //Si5351_シンセ2設定

 左はメインプログラム部です、si5351初期化の後、出力周波数7.96MHz、VCO周波数650MHzに設定してPLL Aのパラメーターを計算、シンセ部 のパラメーターは(e/f)を使わないので簡単にP1のみ分周数で決定されます。
 

 DDSに比較して連続可変には不向きですが、出力周波数が3出力あり、叉SDRのPC画面からダイレクトに周波数設定する場合などは使いやすいデバイス だと思います。   Top Menu