2018年8月31日金曜日

si5351a Xtal代替発振器

si5351aをpic12f1840でコントロールしたX'tal代替発振器である。S/W I2Cコントロールを行っており、JE1AHW内田OMの「クリスタルもどきの製作」を参考にさせて頂いた。開発用ボードは、試験が容易な構造にした。si5351aを発振させた場合、PLLである為に周波数の微調整が難しい。今回、周波数のキャリブレーションを容易にする為、ポテンションメータで周波数の微調整(±1kHZ)が出来る様にした。



回路図



電源電圧3.3Vとし、レベルコンバータなしでI/F出来る様にした。試験では、si5351aボードにレベルコンバータ回路が搭載されたボードを使った。出力は、1chのみ。多回転半固定ボリュームを使うと小型化できると思う。
SKIP SWは、今回のスケッチでは使ってない。








スケッチ

目標周波数をボリューム中心値となる様、周波数オフセットを行っている。ADC分解能が10Bitなので、1023*2を周波数データに加えている。従って、発振周波数=目標周波数-オフセット値(1023)+AD値(0-2026)となっている。開発は、Mplab X X8Cを使った。
必要なファイルは、JA2GQP's Download sitePICフォルダからダウンロードできる。

////////////////////////////////////////////////////////////
//    si5351a PLL control(PIC12F1840)
//
//                                    2018/9/3
//                                    JA2GQP     
////////////////////////////////////////////////////////////


//---------- Header file include -------------------------//

#include <xc.h>

//----------Configuration setting ------------------------//

////////////////////////////
// config1
////////////////////////////
#pragma config FOSC     = INTOSC            // Internal clock
#pragma config WDTE     = OFF               // Watchdog timer off
#pragma config PWRTE    = ON                // Power on start
#pragma config MCLRE    = OFF               // External reset not used
#pragma config CP       = OFF               // Program memory not protected
#pragma config CPD      = OFF               // Data memory not protected
#pragma config BOREN    = ON                // Power drop monitoring
#pragma config CLKOUTEN = OFF               // Clock out pin is RA4
#pragma config IESO     = OFF               // No activation with clock switching
#pragma config FCMEN    = OFF               // Do not monitor external clock

////////////////////////////
// config2
////////////////////////////
#pragma config WRT      = OFF               // Flash memory not protected
#pragma config PLLEN    = OFF               // It does not work at 32 MHz
#pragma config STVREN   = ON                // Reset with stack overflow(underflow)
#pragma config BORV     = HI                // Voltage drop monitoring
#pragma config LVP      = OFF               // Low voltage programming not used

//---------- Define value setting ------------------------//

#define DEF_FREQ        14000000-AD_OFFSET  // Default frequency
#define EEP_ADR         0x00                // EEPROM address
#define SCL   RA1                           // I2C Clock
#define SDA   RA2                           // I2C Data
#define AD_OFFSET       1023                // Frequency offset
#define _XTAL_FREQ      16000000            // clock 16MHz(Use with delay)

////////////////////////////
// si5351a parameter
////////////////////////////
#define CLK0_CTRL     16                    // Register definitions
#define CLK1_CTRL     17
#define MSNA_ADDR     26
#define MS0_ADDR      42
#define PLL_RESET    177
#define XTAL_LOAD_C  183
#define R_DIV_1      0b00000000             // R-division ratio definitions

#define Si5351A_ADDR  0xC0
#define XTAL_FREQ     25000000              // Crystal frequency for Hans' board

#define _6pF          0b01010010            // 6pF
#define _8pF          0b10010010            // 8pF
#define _10pF         0b11010010            // 10pF
#define XTAL_CL       _8pF                 // XTAL_CL 8pF set

#define _2mA           0x4C                  // 2mA(1dBm))
#define _4mA           0x4D                  // 4mA(5dBm))
#define _6mA           0x4E                  // 6mA(10dBm))
#define _8mA           0x4F                  // 8mA(12dBm)
#define mA            _6mA                   // output lebel 10dBm set

//---------- Memory define -------------------------------//

unsigned long  frequency=DEF_FREQ;          // Frequency data

//------------- Initial proc. ----------------------------//

void PIC12F1840_set(){
     OSCCON     = 0b01111000 ;              // clock set(16MHz=0x78,8MHz=0x70,4MHz=0x68)
     ANSELA     = 0b00010000 ;              // Anarog = AN3,Othe digital
     TRISA      = 0b00011000;               // I/O set(0=output,1=input)
     PORTA      = 0b00000000 ;              // Output pin initial value

     ADCON1     = 0b11010000 ;              // FOSC/16,VDD=Ref
     ADCON0     = 0b00001101 ;
    __delay_us(5) ;                         // 5us(at clock 16MHz))
}

//------------- wait proc. -------------------------------//

void await(unsigned long ct){
  while(ct>0) ct--;
}

//------------- I2C start proc. --------------------------//

void I2C_start(){
    SCL = 1;                                // start condition
    await(3);
    SDA = 1;
    await(3);
    SDA = 0;
    await(3);
    SCL = 0;
    await(3);
 }

//------------- I2C stop proc. ---------------------------//

void I2C_stop(){
    await(3);
    SCL = 1;                                // stop condition
    await(3);
    SDA = 0;
    await(3);
    SDA = 1;
    await(3);
    SCL = 0;
    await(3);
 }

//------------- I2C write byte proc. ---------------------//

void wr_Byte(unsigned char x){
    unsigned int k;
    for(k=0;k<8;k++){
        if(x & 0x80) SDA = 1; else SDA = 0;
        await(3);
        SCL = 1;
        await(3);
        SCL = 0;
        await(3);
        SDA = 0;
        x <<= 1;
    }
    SCL = 1;
    await(3);
    SCL = 0;
}

//------------- si5351 command processing ----------------//

void Si5351_write(unsigned char reg_No, unsigned char x){
    I2C_start();

    wr_Byte(Si5351A_ADDR);                  // address set
    wr_Byte(reg_No);
    wr_Byte(x);

    I2C_stop();
}

//------------- si5351 Initialization --------------------//

void Si5351_init(void){
    SDA=1;
    SCL=1;
    await(200);
    Si5351_write(XTAL_LOAD_C,XTAL_CL);      // XTAL_CL set
    Si5351_write(CLK0_CTRL,0x80);           // Disable CLK0
    Si5351_write(PLL_RESET,0xA0);           // Reset PLL_A
       
    Si5351_write(CLK0_CTRL,mA);
}

//------------- si5351 PLL data set --------------------------//

void setupPLL(unsigned char pll, unsigned char mult, unsigned long num, unsigned long denom){
  unsigned long P1;                         // PLL config register P1
  unsigned long P2;                         // PLL config register P2
  unsigned long P3;                         // PLL config register P3

  P1 = (unsigned long)(128 * ((float)num / (float)denom));
  P1 = (unsigned long)(128 * (unsigned long)(mult) + P1 - 512);
  P2 = (unsigned long)(128 * ((float)num / (float)denom));
  P2 = (unsigned long)(128 * num - denom * P2);
  P3 = denom;

  Si5351_write(pll + 0, (P3 & 0x0000FF00) >> 8);
  Si5351_write(pll + 1, (P3 & 0x000000FF));
  Si5351_write(pll + 2, (P1 & 0x00030000) >> 16);
  Si5351_write(pll + 3, (P1 & 0x0000FF00) >> 8);
  Si5351_write(pll + 4, (P1 & 0x000000FF));
  Si5351_write(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  Si5351_write(pll + 6, (P2 & 0x0000FF00) >> 8);
  Si5351_write(pll + 7, (P2 & 0x000000FF));
}

//------------- Set up MultiSynth --------------------------//

void setupMultisynth(unsigned char synth, unsigned long divider, unsigned char rDiv){
  unsigned long P1;                         // Synth config register P1
  unsigned long P2;                         // Synth config register P2
  unsigned long P3;                         // Synth config register P3

  P1 = 128 * divider - 512;
  P2 = 0;                                   // P2 = 0, P3 = 1 forces an integer value for the divider
  P3 = 1;

  Si5351_write(synth + 0, (P3 & 0x0000FF00) >> 8);
  Si5351_write(synth + 1, (P3 & 0x000000FF));
  Si5351_write(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
  Si5351_write(synth + 3, (P1 & 0x0000FF00) >> 8);
  Si5351_write(synth + 4, (P1 & 0x000000FF));
  Si5351_write(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  Si5351_write(synth + 6, (P2 & 0x0000FF00) >> 8);
  Si5351_write(synth + 7, (P2 & 0x000000FF));
}

//------------- si5351 data set --------------------------//

void si5351aSetFrequency(unsigned long frequency){
  unsigned long pllFreq;
  unsigned long xtalFreq = XTAL_FREQ;
  unsigned long l;
  float f;
  unsigned char mult;
  unsigned long num;
  unsigned long denom;
  unsigned long divider;

  divider = 900000000 / frequency;          // Calculate the division ratio. 900,000,000 is the maximum internal
                                            // PLL frequency: 900MHz
  if (divider % 2) divider--;               // Ensure an even integer
                                            //division ratio

  pllFreq = divider * frequency;            // Calculate the pllFrequency:
                                            //the divider * desired output frequency

  mult = pllFreq / xtalFreq;                // Determine the multiplier to
                                            //get to the required pllFrequency
  l = pllFreq % xtalFreq;                   // It has three parts:
  f = l;                                    // mult is an integer that must be in the range 15..90
  f *= 1048575;                             // num and denom are the fractional parts, the numerator and denominator
  f /= xtalFreq;                            // each is 20 bits (range 0..1048575)
  num = f;                                  // the actual multiplier is mult + num / denom
  denom = 1048575;                          // For simplicity we set the denominator to the maximum 1048575

                                            // Set up PLL A with the calculated  multiplication ratio
  setupPLL(MSNA_ADDR, mult, num, denom);
                                            // Set up MultiSynth divider 0, with the calculated divider.
                                            // The final R division stage can divide by a power of two, from 1..128.
                                            // reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
                                            // If you want to output frequencies below 1MHz, you have to use the
                                            // final R division stage
  setupMultisynth(MS0_ADDR, divider, R_DIV_1);
}

//------------- ADconverter ------------------------------//

unsigned int adconv(){
    unsigned int temp;

    GO_nDONE = 1 ;                          // Anarog read start
    while(GO_nDONE) ;                       // PIC wait
    temp = ADRESH ;                         // Data high set
    temp = ( temp << 8 ) | ADRESL ;         //      low set

    return temp * 2 ;                       // (0-1023) * 2 = 0-2026
}

//------------- main -------------------------------------//

void main(){
    int wk,wk1;
   
    PIC12F1840_set();                       // Cip Initialization
    Si5351_init();                          // si5351a Initialization

    while(1){
        wk = adconv();
        if(wk != wk1){
            si5351aSetFrequency(frequency + wk);  // Frequency data set
            wk1 = wk;
        }
        __delay_ms(30);
    }
}


1 件のコメント:

JA2GQP さんのコメント...

X'tal負荷容量設定に誤りが有ったので、修正した。