2015年8月30日日曜日

PLLクロックジェネレータSi5351Aお試し ~いわゆるLチカ~

今日はハムフェアの反省会(?)と称して秋葉原の某中華料理店で謎の団体とご一緒させていただきました。

会が終了し、一行は秋葉原にあるハムショップに訪れ中を徘徊し夕方解散となりました。


ハムフェアのBasicommブースにも現れた謎の集団(笑)ご一行様が秋葉原に向け進行中。


そういえば秋月電子通商秋葉原店、店内がリニューアルしていました。
しかし相変わらず訪れるお客さんが多くゆっくり滞在できませんでしたが、主要なパーツ置き場などをチェックしてきました。

と、長い前置きはこのくらいにしておいて

FD移動やハムフェアでしばらく中断していた新デバイスの動作テストを行いました。

以前QRP Labs.で頒布されていたUltimate3というWSPRなどデジタルモードビーコン送信機キットを製作しましたが、このキットに使われていた発振器はAD9850を使った安価な中華DDSユニットでした。しかしその後3Sとしてリニューアルされたキットの発振器に使われていたのがSi570でおなじみの(...といってもJAではあまり取り上げられていないようですが)Silicon Labs.で出している多出力クロックジェネレータSi5351Aで、チップ自体はRSコンポーネンツでも単価が200円もしない非常に安価なデバイスです。
そんな中、ArduinoやRaspberryPiの周辺ボードでおなじみの(...といってもやはりJAでは(以下略))Adafruitからこのデバイスを使ったbreakout boardが出ているのをGoogle+で見つけ、早速2枚オーダーしました。

ボード中央に見える10pinMSOPのチップがSi5351A
もともとは電子機器のクロック用に開発されたデバイスですが、中に2つの独立したPLLと3系統のプログラマブル分周器が内蔵され、同時に3つの異なる周波数のクロックが得られるという面白いチップです。

基準発振は外付けの25MHzのXTALを接続して内部で発振し、PLLフィードバック用のプログラマブル分周器で分周することで最高900MHzまで任意の周波数でPLLをロックします。その後PLLの信号をさらに分周することで目的周波数を得るというものです。
Si3531Aのブロックダイアグラム(データシートより引用)
コントロールはI2C経由で行います。設定手順は、PLLのフィードバック分周器の設定、分周器(MultiSynth0(~2)の設定、PLLリセット、 PLLオンという具合でそう難しくはなさそうですが、各分周器のパラメータの設定が最初なかなか理解しにくかったです。

まずは今回DDSのときと同じように固定周波数で発振までこぎつけるといういわゆるLチカ儀式(笑)を行うことにしました。

目的周波数を10MHzとし、最初にPLLの周波数を決めてそのあと10MHzになるように分周比を決めてみました。PLL周波数は800MHzで分周比は80とすれば10MHzで出力されるという計算です。

まずは、PLL周波数を800MHzにする設定を割りだします。

フィードバック用分周器の設定は、まずデータシート(ここここ)から

  i) fvco = fXTAL x (a + b / c)
     aは15~90の整数値、bは0~1048575の整数、cは1~1048575の整数
     fvco = 800MHz, fXTAL = 25MHzとすると、a,b,cはそれぞれ32,0,1です。
   これらの値を設定用パラメータP1,P2,P3に変換し(詳しくはデータシート参照)
     Si3531Aのレジスタ(MSNA_Px or MSNB_Pxレジスタ)にI2C経由で転送します。
  ii) fout = fvco / (a + b / c)
      i)と同様になります(MSx_Pxレジスタ)

そんなこんなでいつものPIC16F1827用にプログラムを書いてコンパイルし初動作。



あっさり10MHzあたりで発振しています。出力は50Ωインピーダンスで20dBアッテネータを挿入しABP-3で測定すると-10dBmと+数dBmは出ていると見えDDSよりも大きくそのままパッシブDBMにつなげられそうな勢いです。



 方形波なので高調波はこんな感じで、5MHzごとの柱は基準発振器との混合波の可能性と見ていますが、近傍の余計な信号が気になります




拡大すると信号周囲50Hz毎に柱が見られており、ハムが乗っかっていると想像したためACアダプターを電池に切り替えて再測定しました。




と50Hzごとの柱は消え、なんとなく小さい柱は残っているもののとりあえずは使えるレベルかもしれません。ただし電源のデカップリングなどしっかりした対策は必要ですね。

参考までに、ソースを公開します。最低限のものなのですが、これを芯にしてVFOとBFOを同時に賄える発振器に出来ればな~と考えてます。

//
//    si5351 handling test program
//    31 Jul 2015 / HARU
//
//    ver.0.1 29 Aug 2015
//    first implementation (so-called "L-Chika") output freqency 10MHz
//
//    ** parameters calculation **
//
//    fvco = fXTAL * (a + b / c) a:15 to 90, b:0 to 1048575, c:1 to 1048575
//    fout = fvco / (a + b / c) a:4 to 900, b:0 to 1048575, c:1 to 1048575
//
//    P1 = 128 * a + floor(128 * b / c) - 512
//    P2 = 128 * b - c * floor(128 * b / c)
//    P3 = c
//

#define EEPROM_SIZE     256
#define _XTAL_FREQ        4000000

#include <xc.h>

//for 16F1847/1827 config
#pragma config CPD = OFF, BOREN = NSLEEP, IESO = OFF, FOSC = INTOSC
#pragma    config FCMEN = OFF, MCLRE = OFF, WDTE = OFF, CP = OFF, PWRTE = ON, CLKOUTEN = OFF
#pragma config PLLEN = OFF, WRT = OFF, STVREN = OFF, BORV = LO, LVP = OFF

#define SCL         LATAbits.LATA2
#define SDA            LATAbits.LATA3

void msec_delay(unsigned short time);
void I2C_init(void);
void I2C_send(unsigned char data);
void si5351_cmd(unsigned char reg, unsigned char data);
void pll_set(void);
void ms0_set(void);

void main(void){
   
    OSCCON = 0x6A;                        // 4MHz internal OSC no PLL
   
    ANSELA = 0x00;
    ANSELB = 0x00;
    TRISA = 0x00;
    TRISB = 0x00;
//    WPUB = 0xFF;                        // PORTB weak pull-up
   
    OPTION_REG = 0x02;                    // weak pull_up, TMR0 internal clock(1us/cycle), prescaler 1:8
   
    I2C_init();
   
    si5351_cmd(16,0x80);                // output off
    pll_set();                            // PLL_A fvco setting by feedback multisynth divider
    ms0_set();                            // Multisynth0 divider setting
    si5351_cmd(177,0xA0);                // PLL_A reset
    si5351_cmd(16,0x4F);                // CLK0 ON (Multisynth0;Integer mode, PLL_A source)

    while(1){
       
    }
}

void msec_delay(unsigned short time){
    unsigned short i;
    for(i=0;i<time;i++){
        __delay_ms(1);
    }
}

void I2C_init(void){
    SDA = 1;
    SCL = 1;
}

void I2C_send(unsigned char data){
    unsigned char i;
    for(i=0;i<8;i++){
        if(data & 0x80) SDA = 1;
        else SDA = 0;

        SCL = 1;
        SCL = 0;
        SDA = 0;
        data <<= 1;
    }
    SCL = 1;                            // for ack
    SCL = 0;
}

void si5351_cmd(unsigned char reg, unsigned char data){
    SDA = 0;                            // start condition
    SCL = 0;                            //
    I2C_send(0xC0);
    I2C_send(reg);
    I2C_send(data);
    SCL = 1;                            // stop condition
    SDA = 1;                            //
    __delay_us(30);
}

void pll_set(void){                        // fXTAL=25MHz, fvco=800MHz, a=32, b=0, c=1(integer) -> P1=3584, P2=0, P3=1
    si5351_cmd(26,0);                    // resister26 <MSNA_P3[15:8]>
    si5351_cmd(27,1);                    // resister27 <MSNA_P3[7:0]>
    si5351_cmd(28,0);                    // resister28 <MSNA_P1[17:16]>
    si5351_cmd(29,0b00001110);            // resister29 <MSNA_P1[15:8]>
    si5351_cmd(30,0);                    // resister30 <MSNA_P1[7:0]>
    si5351_cmd(31,0);                    // resister31 <MSNA_P3[19:16], MSNA_P2[19:16]>
    si5351_cmd(32,0);                    // resister32 <MSNA_P2[15:8]>
    si5351_cmd(33,0);                    // resister33 <MSNA_P2[7:0]>
}

void ms0_set(void){                        // fout=10MHz, 1/80 division, a=80, b=0, c=1(integer) -> P1=9728, P2=0, P3=1
    si5351_cmd(42,0);                    // resister42 <MS0_P3[15:8]>
    si5351_cmd(43,1);                    // resister43 <MS0_P3[7:0]>
    si5351_cmd(44,0);                    // resister44 <0, R0_DIV[2:0], MS0_DIVBY4[1:0], MS0\P1[17:16]>
    si5351_cmd(45,0b00100110);            // resister45 <MS0_P1[15:8]>
    si5351_cmd(46,0);                    // resister46 <MS0_P1[7:0]>
    si5351_cmd(47,0);                    // resister47 <MS0_P3[19:16], MS0_P2[19:16]>
    si5351_cmd(48,0);                    // resister48 <MS0_P2[15:8]>
    si5351_cmd(49,0);                    // resister49 <MS0_P2[7:0]>
}

5 件のコメント:

  1. 秋月で安く販売されているのをネットで知って触っていましたが、初期化がうまくいかずに悩んでおりました。こちらのプログラムを手本に組み直したとこと無事クロックが出力できました。
    ありがとうございました。

    返信削除
    返信
    1. 7L1WRKさん、コメントありがとうございます。

      設定は面倒ですが安価で面白いデバイスですよね。
      ここのところいじっておりませんが、そろそろまた応用を考えてみたいと思います。

      削除
  2. 亀レスですみませんが、最近APB-3を使い始めました。キットのものでスタンドアロンではありません。
    貴ページの画像では、スペアナ画面がstartとstop:X軸が実周波数表示になっていますが、標準APB-3ソフトでは、centerとspan表示で使いづらいです。
    どのようにしたら、貴ページの設定になりますか?

    返信削除
    返信
    1. 星野さん、こんにちは。コメントありがとうございます。

      画面の周波数軸付近を右クリックすると周波数軸に関するコンテキストメニューが現れますので、その中の『周波数軸(開始・終了⇔センター・スパン)』を選ぶと実周波数表示に切り替わると思います。

      おじさん工房のサイトにAPB-3操作マニュアルへのリンクがありますのでそちらも参考になさってみてください。

      削除
    2. 星野です。
      出来ました。有難うございました。
      使うのが楽になります。

      削除