
マイコンの選定
使用するマイコンとしては、上記機能をすべて盛り込むとしたら、プログラム容量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周波数を何分周するかのマルチシンセ部の設定で行います。
|
ソフトの説明
ソースリストを参考まで、プログラムについての簡単な説明をします。
プログラムはメイン関数の他、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の制御部のみ実装していますが今後順次周辺回路を組み込んで動作確認していく予定です。、
|
![]() |
今までは切りの良い周波数設定例でしたが、実際の周波数設定例について紹介します。例ではCLK0で7.960MHzを出力する場合
|
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号機」で製作中の物からの抜粋です。
|
//===========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レジスター設定=======================
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のみ分周数で決定されます。
|