2019年4月20日土曜日

si5351 oled 3BAND VFO

si5351用に作ったPCB使用の、3BAND VFO(7MHz、14MHz、18MHz)である。スケッチは、LZ2WSG, KN34PCベースだが、機能追加などもあり、殆ど原型を留めてない。 VFOの機能は、自動メモリー書込み、自動モード切替、Sメータ、送信時の周波数範囲外保護(但し、受信範囲制限なし)など。oledはI2Cの128x64を使ったが、128X32用も別スケッチを用意した。




128x32を実装した様子である。oled128x64との違いは、コールサイン表示有無である。この表示を見る限り、LZ2WSG, KN34PCと違いは、モード表示のみである。
   














回路図

回路図である。モード切替用に2本、バンド切り替え用に3本のI/O使用。













スケッチ

スケッチは、JA2GQP's Download siteのsi5351 VFOフォルダからダウンロード可能。ベースにしたスケッチは、BITX用のもので逆ヘテロダインになっている。今回作ったものは、アッパーヘテロダインで目標周波数+IF周波数である。

//////////////////////////////////////////////////////////////////////
//  si5351a oled(128x64) VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2019/4/19
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(10k,1k,50)
//    2.Automatic memory
//    3.Protection Operation At The Time Of Transmission
//    4.3 bands
//    5.Automatic mode switching
//    6.S-Meter
//--------------------------------------------------------------------
// Reference sketch
//    24.12.2018, Arduino IDE v1.8.8, LZ2WSG, KN34PC
//    Si5351 VFO CLK0, 20m, OLED 0.91" 128x32 display, S-meter, RX/TX, 50Hz/1000Hz
//////////////////////////////////////////////////////////////////////

#include "src/Rotary.h"               // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
#include "src/si5351.h"               // https://github.com/etherkit/Si5351Arduino, v2.1.0
#include "src/SSD1306AsciiAvrI2c.h"   // https://github.com/greiman/SSD1306Ascii
#include <EEPROM.h>


////////////////////////////////
// Set Device
////////////////////////////////
Rotary r = Rotary(2, 3);
Si5351 si5351(0x60);                 // Si5351 I2C address
SSD1306AsciiAvrI2c oled;

////////////////////////////////
// I/O Port
////////////////////////////////
const byte SW_BAND = A0;              // Band SW
const byte SW_STEP = A1;              // STEP SW
const byte SW_TX = A2;                // TX/RX
const byte AD_SM = A7;                // S-meter AD

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte  Eep_Init = 0x00;          // Eep Init(1byte*1)
const byte  Eep_Band = 0x01;          // Band(1byte*1)
const byte  Eep_Freq = 0x10;          // Frequency(4byte*8)
const byte  Eep_Step = 0x30;          // STEP(4byte*8)

////////////////////////////////
// frquency data
////////////////////////////////
const unsigned long FRQ_TBL[3][4] = {
 // DEF     LOW(cw)  MID(ssb)  HI
  7050000 ,7000000 ,7045000 ,7200000,
  14090000,14000000,14100000,14350000,
  18110000,18068000,18110000,18168000
  };
//---- data offset -----
const byte DEF_F = 0;
const byte LOW_F = 1;
const byte MID_F = 2;
const byte HI_F  = 3;
//---- IF offset -------
const unsigned long RX_IF = 10700000;
const unsigned long TX_IF = 0;

////////////////////////////////
// Encorder STEP
////////////////////////////////
const int STEP_50 = 50;               // STEP 50Hz
const int STEP_1k = 1000;             //      1k
const int STEP_10k = 10000;           //      10k

////////////////////////////////
// etc
////////////////////////////////
const byte  Max_Band = 3;             // Max Channel(8ch)
const byte  Def_Band = 0;             // Default Band(0)
const byte  Int_End = 72;             // Initial end code
const char Call[9] = "JA2GQP";        // Display Call sign

////////////////////////////////
// Memory Assign
////////////////////////////////
unsigned long Vfo_Dat;                // VFO Data
unsigned long Enc_Step;               // STEP
char Enc_Dir = 0;                     // -1 DIR_CCW, 0 DIR_NONE, 1 DIR_CCW
unsigned int Val_Smeter = 0;
unsigned int Val_Smeterb = 1;

long Time_Passd;                      // int to hold the arduino miilis since startup
byte Flg_eepWT = 0;                   // EEP Write Flag

byte Byt_Band = 0;
byte Flg_Mode;
byte Flg_Over;
byte Flg_Vfo = 0;
byte Flg_Tx = 0;
byte Disp_over = 1;
byte Disp_freq = 1;
byte Disp_Tx = 0;

//----------  setup  ----------------------------------------------------

void setup() {
  pinMode(SW_STEP, INPUT_PULLUP);
  pinMode(SW_TX, INPUT_PULLUP);       
  pinMode(SW_BAND, INPUT_PULLUP);       
  pinMode(9, OUTPUT);                 // Band1       
  pinMode(10, OUTPUT);                //     2       
  pinMode(11, OUTPUT);                //     3         
  pinMode(12, OUTPUT);                // LOW=none, HOGH=CW
  pinMode(13, OUTPUT);                // LOW=USB,  HIGH=LSB       

  attachInterrupt(0, rotary_encoder, CHANGE);
  attachInterrupt(1, rotary_encoder, CHANGE);

  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);    // crystal 25.000 MHz, correction 0
  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set
  oled.begin(&Adafruit128x64, 0x3C);

  if(EEPROM.read(Eep_Init) != Int_End){    // Eep initialaz
    delay(10);
    eep_init();
  }
  eep_rdata();

  mode_disp();
  step_disp();
  call_disp();
}

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

void loop() {
  if(digitalRead(SW_TX) == HIGH){           // receive?
    Flg_Tx = 0;
    si5351.output_enable(SI5351_CLK0, 1);   // VFO enable

    if (Disp_freq == 1) {
      Disp_freq = 0;
      Vfo_Dat += Enc_Dir * Enc_Step;
      Enc_Dir = 0;
      band_check();                        // range check

      si5351_set_freq(Vfo_Dat + RX_IF);
      freq_disp(Vfo_Dat);                  // frequency display
      Flg_Vfo = 1;
      mode_disp();

      Time_Passd = millis();
      Flg_eepWT = 1;
    }

    if (digitalRead(SW_STEP) == LOW) {      // increment events
      enc_step();
      step_disp();
      while (digitalRead(SW_STEP) == LOW);
    }

    if (digitalRead(SW_BAND) == LOW) {
      band_set();
    }
    Disp_Tx = 1;
  }
  else{                                     // send
    Flg_Tx = 1;
    Disp_over = 1;

    if(Flg_Over == 0){                      // Within transmittable range?
      si5351.output_enable(SI5351_CLK0, 1); // VFO enable
      if(Flg_Vfo == 1){
        si5351_set_freq(Vfo_Dat + TX_IF);
        Flg_Vfo = 0;
      }
    }
    else{                                   // Out of range
      si5351.output_enable(SI5351_CLK0, 0); // VFO disable
      if(Disp_over == 1){
        fover_disp();                       // Display of over range
        Disp_over = 0;
        Disp_freq = 1;
      }
    }
    if(Disp_Tx == 1){
      tx_disp();
      Disp_Tx = 0;     
    }
    Disp_freq = 1;
  }

  Val_Smeter = analogRead(AD_SM);
  if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter
    sm_disp();
    Val_Smeterb = Val_Smeter;
  }

  if(Flg_eepWT == 1){                       // EEPROM auto Write
    if(Time_Passd+2000 < millis()){
      eep_wdata(Byt_Band);
      Flg_eepWT = 0;
    }
  } 
}

//----------  Band set  ------------------------------------------------

void band_set(){
  if(Byt_Band == Max_Band-1)
    Byt_Band = 0;
  else
    Byt_Band++;
  EEPROM.write(Eep_Band,Byt_Band);

  while(digitalRead(SW_BAND) == LOW);
  eep_rdata();
  band_check();                             // range check
  si5351_set_freq(Vfo_Dat + RX_IF);
  freq_disp(Vfo_Dat);                       // frequency display
  step_disp();
  mode_disp();
}

//----------  Write EEPROM 4byte  --------------------------------------

void eep_write4(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Read EEPROM 4byte  ---------------------------------------

long eep_read4(int address){
  long value = 0;

  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
    }
  }
  return value;
}

//----------  EEPROM Dat Read  -----------------------------------------

void eep_rdata(){
  Byt_Band = EEPROM.read(Eep_Band);
  if(Byt_Band == 0){
    digitalWrite(9,HIGH);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);
  }
  else if(Byt_Band == 1){
    digitalWrite(9,LOW);
    digitalWrite(10,HIGH);
    digitalWrite(11,LOW);
  }
  else if(Byt_Band == 2){
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,HIGH);
  }
  Vfo_Dat = eep_read4(Eep_Freq+Byt_Band*4);
  Enc_Step = eep_read4(Eep_Step+Byt_Band*4);
}

//----------  EEPROM Dat Write  ----------------------------------------

void eep_wdata(byte band){
  EEPROM.write(Eep_Band,band);
  eep_write4(Vfo_Dat,Eep_Freq+band*4);
  eep_write4(Enc_Step,Eep_Step+band*4);
}
 
//----------  EEPROM Initialization ------------------------------------

void eep_init(){
  int i;

  for (i=0;i<128;i++)                       // 0 clear(128byte)
    EEPROM.write(i, 0);

  for(i=0;i<Max_Band;i++){
    eep_write4(FRQ_TBL[i][DEF_F],Eep_Freq+i*4);
    eep_write4(STEP_1k,Eep_Step+i*4);       // Step(1kHz)
  }
  EEPROM.write(Eep_Band,Def_Band);          // Default Band(0)
  EEPROM.write(Eep_Init,Int_End);           // Init end set(73)
}

//----------  Rotaly Encorder  -----------------------------------------

void rotary_encoder() {                     // rotary encoder events
  uint8_t result = r.process();

  if(Flg_Tx == 0){
    if (result) {
      Disp_freq = 1;
      if (result == DIR_CW)
        Enc_Dir = 1;
      else
        Enc_Dir = -1;
    }
  }
}

//----------  si5351 PLL Output  ---------------------------------------

void si5351_set_freq(uint32_t frequency) {
  si5351.set_freq(frequency * SI5351_FREQ_MULT, SI5351_CLK0);
}

//----------  Encorede STEP  -------------------------------------------

void enc_step() {
  if (Enc_Step == STEP_50) {
    Enc_Step = STEP_1k;                     // 1000 Hz, round to XX.XXX.000
    float tmp = round (Vfo_Dat / (STEP_1k * 1.000));
    Vfo_Dat = (uint32_t)(tmp * STEP_1k);
    Disp_freq = 1;
  }
  else if(Enc_Step == STEP_1k)
    Enc_Step = STEP_10k;                    // 10k
  else 
    Enc_Step = STEP_50;                     // 50 Hz
}

//----------  STEP Display  --------------------------------------------

void step_disp() {
  oled.setCursor(109, 4);
  oled.setFont(labels);
  if (Enc_Step == STEP_50)
    oled.print("6");                        // 50Hz
  else if(Enc_Step == STEP_1k)
    oled.print("8");                        // 1k
  else
    oled.print("9");                        // 10k
}

//---------- Frequency renge over --------------------------------------

void fover_disp() {
  oled.setFont(lcdnums14x24);
  oled.setCursor(1, 0);
  oled.print("--.---.--");
}

//---------- callsign display ------------------------------------------

void call_disp() {
  oled.setFont(Arial_bold_14);
  oled.setCursor(56, 6);
  oled.print(Call);
}

//----------  Frequency Display  ---------------------------------------

void freq_disp(uint32_t sf_rx) {
  uint16_t fr;

  oled.setFont(lcdnums14x24);
  oled.setCursor(1, 0);
  fr = sf_rx / 1000000;
  if (fr < 10)
    oled.print(':');                        // ':' is changed to ' ' in lcdnums14x24.h
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 1000000) / 1000;
  if (fr < 100)
    oled.print('0');
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 1000) / 10;
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
}

//---------- TX display ------------------------------------------------

void tx_disp() {
  oled.setCursor(2, 4);
  oled.setFont(labels);
  oled.print("1");                          // "1" is "TX" in labels.h
}

//---------- Mode display ----------------------------------------------

void mode_disp() {
  oled.setCursor(2, 4);
  oled.setFont(labels);
  if (Flg_Mode == 0){
    oled.print("2");                        // "2" is "CW" in labels.h
    digitalWrite(12,HIGH);
    digitalWrite(13,LOW);
  }
  else if(Flg_Mode == 1){
    oled.print("3");                        // "3" is "LSB"
    digitalWrite(12,LOW);
    digitalWrite(13,HIGH);
  }
  else if(Flg_Mode == 2){
    oled.print("4");                        // "4" is "USB"
    digitalWrite(12,LOW);
    digitalWrite(13,LOW);
  }
}

//----------  S-Meter Display  -----------------------------------------

void sm_disp() {
  uint8_t a = 0;
  uint8_t m = 0;

  a = (Val_Smeter + 3) / 113;  // 1024 / 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
  oled.setFont(pixels);
  oled.setCursor(25, 4);
  for (m = 0; m < a; m++)
    if (m < 6)
      oled.print('7');                      // '5' - hollow rectangle, 6px
    else
      oled.print('8');                      // '6' - filled rectangle, 6px
  for (m = a; m < 9; m++)
    oled.print('.');                        // '.' 1px
}

//----------  band chek  -----------------------------------------------

void band_check(){
  if((Vfo_Dat >= FRQ_TBL[Byt_Band][LOW_F])
      && (Vfo_Dat <= FRQ_TBL[Byt_Band][MID_F])){
    Flg_Mode = 0;                             // CW(0=CW, 1=LSB, 2=USB) 
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= FRQ_TBL[Byt_Band][MID_F])
      && (Vfo_Dat <= FRQ_TBL[Byt_Band][HI_F])){
    if(Vfo_Dat >= 10000000)                   // greate than 10MHz                     
      Flg_Mode = 2;                           // USB(0=CW, 1=LSB, 2=USB)
    else
      Flg_Mode = 1;                           // LSB(0=CW, 1=LSB, 2=USB)
    Flg_Over = 0;
  }
  else
    Flg_Over = 1;
}      



    



2019年3月20日水曜日

6m AM VFO

  
周辺デバイスコントロールにI2C使用のVFO PCBを作った。このVFO PCBは、拡張性を高める為、多くのI/O端子を引出した。PCBパターン引き回し上、アナログ専用ポートのA6を入力ポートに割り付けてしまった。正常動作させる為、アナログ専用ポートA6からアナログ/デジタルのA2に再割り当てを行った。また、PCB動作確認用に6m AMチャンネル切替式VFOスケッチを作り、H/Wチェックを行った。








回路図


回路図である。I/O端子を殆ど使ってないので、拡張用予備端子になっている。
  





PCB部品面

PCB小型化(50x50)の為、Arduinoボードとsi5351aボードで隠れた所迄、部品を実装している。左側のランドは、未使用のI/O端子である。














Arduinoとsi5351aユニット実装

標準高さのピンソケットにArduino nanoとsi5351aユニットを実装した様子である。半固定抵抗は、Sメータレベル調整用である。














A6からA2へ改造


Arduino nanoには、A6,A7端子があるがアナログ専用となっている。その為、PCB裏面(操作面側)のA6パターンをカットし、ジャンパーでA2に改造した。今回は、この方法で対応する事にしたが、アナログ信号を読み取って、しきい値判断によるデジタル化による判断方式などがある。   
    








スケッチ     

PCBチェックの為にスケッチを書いた。簡単で実用的な6m AMチャンネル切替式VFOにした。チャンネルは、50.500、50.550、50.600、50.620の切替式で、現在のチャンネルのメモリーする。使ったOLED128x32と小型だが、Sメータ表示が出来る。必要なファイルは、si5351 VFOフォルダからダウンロードできる。

//////////////////////////////////////////////////////////////////////
//  si5351a 6m AM VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2019/3/19
//                                                  JA2GQP
//////////////////////////////////////////////////////////////////////

#include "src/si5351.h"              // https://github.com/etherkit/Si5351Arduino, v2.1.0
#include "src/SSD1306AsciiAvrI2c.h"  // https://github.com/greiman/SSD1306Ascii
#include <EEPROM.h>

//---------- Set I/O Device ---------------------

Si5351 si5351(0x60);                 // Si5351 I2C address
SSD1306AsciiAvrI2c oled;

//----------  Define Constant Value   ----------
                                                
#define   SW_CH     A0                // CH SW
#define   SW_TX     A2                // TX SW
#define   PIN_SM    A7                // S-meter voltage input

////////////////////////////////
// Channel Frequency
////////////////////////////////
#define  CH1_FRQ    50500000L         // Channel 1 Frequency
#define  CH2_FRQ    50550000L         //         2
#define  CH3_FRQ    50600000L         //         3
#define  CH4_FRQ    50620000L         //         4

////////////////////////////////
// etc
////////////////////////////////
#define  CH1        1                 // Channel 1
#define  CH2        2                 // Channel 2
#define  CH3        3                 // Channel 3
#define  CH4        4                 // Channel 4

#define  MAX_CHN    4                 // Max Channel
#define  INT_END    73                // Initial end code
#define  IF_FRQ     10700000L         // IF Frequency 10.7MHz
#define  OLED_ADR   0x3C              // OLED Address

//----------  EEPROM Memory Address   -----------------------

#define  EEP_INT    0x00              // Eep Init(1byte*1)
#define  EEP_CHN    0x01              // Channel(1byte*1)

//----------  Memory Assign  -------------------

unsigned long Vfo_Dat = 0;            // VFO Data
unsigned long Vfo_Datb = 0;           //          old
unsigned long If_Dat;                 
unsigned long Frq_Dat;                // Frequency Data                 

byte Byt_Chn = CH1;                   // Channel SW
byte Flg_Tx = 0;                      // TX Flag
byte Flg_Txb = 1;                     //         old

unsigned int Val_SM = 0;              // S-Meter Data
unsigned int Val_SMb = 0;             //              old


//----------  Initialization  Program  ---------------

void setup(){
  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);//crystal 25.000 MHz, correction 0
  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set 
  
  oled.begin(&Adafruit128x32, OLED_ADR);

  pinMode(SW_TX,INPUT_PULLUP);
  pinMode(SW_CH,INPUT_PULLUP);

  if(EEPROM.read(EEP_INT) != INT_END){    // Eep initialaz
    delay(10);
    Eep_Init();
  }

  Byt_Chn = EEPROM.read(EEP_CHN);         //     Channel

  CH_Set();
  Disp_Comm1();
}

//----------  Main program  ---------------

void loop() {
  if(digitalRead(SW_TX) == HIGH){          // RX ?
    if((digitalRead(SW_CH) == LOW)){       // CH SW On?
      CH_Switch();
      CH_Set();                                   
    }
    Flg_Tx = 0;
    If_Dat = IF_FRQ;
  }
  else{                                   // TX 
    Flg_Tx = 1;                           // Yes,TX Flag set
    If_Dat = 0;                           //     If_Dat 0 set
  }

  Vfo_Dat = Frq_Dat + If_Dat;
  
  if(Vfo_Dat != Vfo_Datb){                  // Frequency update?
    si5351.set_freq(Vfo_Dat * SI5351_FREQ_MULT,SI5351_CLK0);
    Vfo_Datb = Vfo_Dat;
  }

  if(Flg_Tx != Flg_Txb){
    Disp_Comm1();
    Flg_Txb = Flg_Tx;
  }

  Val_SM = analogRead(PIN_SM);
  if ((abs(Val_SM - Val_SMb)) > 3) {  // if needed draw S-meter
    Disp_SM();
    Val_SMb = Val_SM;
  }

}  


//----------  Channel Data Set & Display Frequency  ---------

void CH_Set(){
  oled.setFont(lcdnums14x24);
  oled.setCursor(1, 0);

  switch(Byt_Chn){
    case CH1:
      Frq_Dat = CH1_FRQ;
      oled.print("01:50.500");      
      break;
    case CH2:
      Frq_Dat = CH2_FRQ;
      oled.print("02:50.550");      
      break;
    case CH3:
      Frq_Dat = CH3_FRQ;
      oled.print("03:50.600");      
      break;
    case CH4:
      Frq_Dat = CH4_FRQ;
      oled.print("04:50.620");      
      break;
    default:
      break;
  }
  EEPROM.write(EEP_CHN,Byt_Chn);
}

//----------  Display Common1(TX/RX)  ---------

void Disp_Comm1(){
  oled.setCursor(2, 3);
  oled.setFont(labels);

  if(Flg_Tx == 1)
    oled.print("2");                // "2" is "TX" in labels.h
  else
    oled.print("1");                // "1" is "RX" in labels.h
}

//----------  CH SW Check  ----------------------

void CH_Switch(){
  Byt_Chn++;
  while(digitalRead(SW_CH) == LOW)
    ;
  if(Byt_Chn > MAX_CHN)
    Byt_Chn = CH1;
}

//----------  Display S-Meter  -------------------

void Disp_SM() {
  uint8_t a = 0;
  uint8_t m = 0;

  a = (Val_SM + 3) / 113;            // 1024 / 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
  oled.setFont(pixels);
  oled.setCursor(25, 3);
  for (m = 0; m < a; m++)
    if (m < 6)
      oled.print('7');               // '5' - hollow rectangle, 6px
    else
      oled.print('8');               // '6' - filled rectangle, 6px
  for (m = a; m < 9; m++)
    oled.print('.');                 // '.' 1px
}

//---------- EEProm Initialization -----------------

void Eep_Init(){
  EEPROM.write(EEP_INT,INT_END);                  // Init end set(73)  
  EEPROM.write(EEP_CHN,CH1);                      // Init end set(73)  
}
























     

2019年2月26日火曜日

si5351a CB VFO

 si5351aの8ch CB VFOである。このVFOは、周辺デバイスとのI/Fは全てI2Cで行っている。合法CBは、1-8CHであるが、9-10CHに受信のみ出来るチャンネルを設けた。CBの場合、チャンネル有りき周波数なので、周波数表示は不要である。ただ、普段アマチュア無線を行っていると、周波数表示が欲しくなる。そこで今回、周波数表示はテキストデータをそのまま表示させる事にした。適当なテキストを表示させるのも面白いと思う。操作は、CHスイッチを押すとチャンネルが順送りし、メモリー保存する。超シンプルである。



回路図

回路図は、AFPSN VFO回路の不要な所を除いたのみである。













スケッチ

ライブラリは、バージョンが異なると正しく動作しない場合がある為、スケッチと同じフォルダにsrcフォルダを作って、si5351.hとLiquidCrystal_I2C.hが入れてある。必要なファイルはsi5351 VFOフォルダのcb_vfo.zip。

//////////////////////////////////////////////////////////////////////
//  si5351a CB VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2019/2/26
//                                                  JA2GQP
//////////////////////////////////////////////////////////////////////
#include "src/si5351.h"                      // Ver2,1,0
#include "src/LiquidCrystal_I2C.h"           //    1.1.2
#include <EEPROM.h>

//---------- Set I/O Device ---------------------

LiquidCrystal_I2C lcd(0x27,16,2);       // address 0x27, display 16x2
Si5351 si5351;

//----------  Define Constant Value   ----------
                                               
#define   SW_CH     5                     // CH SW
#define   SW_TX     9                     // TX SW

////////////////////////////////
// Channel Frequency
////////////////////////////////
#define  CH1_FRQ    26968000L           // Channel 1 Frequency
#define  CH2_FRQ    26976000L           //         2
#define  CH3_FRQ    27040000L           //         3
#define  CH4_FRQ    27080000L           //         4
#define  CH5_FRQ    27088000L           //         5
#define  CH6_FRQ    27112000L           //         6
#define  CH7_FRQ    27120000L           //         7
#define  CH8_FRQ    27144000L           //         8
#define  CH9_FRQ    27005000L           //         9
#define  CH10_FRQ   27524000L           //        10

////////////////////////////////
// etc
////////////////////////////////
#define  CH1        1                   // Channel 1
#define  CH2        2                   // Channel 2
#define  CH3        3                   // Channel 3
#define  CH4        4                   // Channel 4
#define  CH5        5                   // Channel 5
#define  CH6        6                   // Channel 6
#define  CH7        7                   // Channel 7
#define  CH8        8                   // Channel 8
#define  CH9        9                   // Channel 9
#define  CH10       10                  // Channel 10

#define  Max_Chn    10                  // Max Channel(10ch)
#define  Int_End    73                  // Initial end code
#define  CALL      "JA2GQP"             // Display Call sign
#define  IF_FRQ     455000L             // IF Frequency 455kHz

//----------  EEPROM Memory Address   -----------------------

#define  Eep_Int    0x00                // Eep Init(1byte*1)
#define  Eep_Chn    0x01                // Channel(1byte*1)

//----------  Memory Assign  -------------------

unsigned long Vfo_Dat = 0;                // VFO Data
unsigned long Vfo_Datb = 0;               //          old
unsigned long If_Dat;               
unsigned long Frq_Dat;                    // Frequency Data               

byte Byt_Chn = CH1;                       // Channel SW
byte Flg_Tx = 0;                          // TX Flag

//----------  Initialization  Program  ---------------

void setup(){
  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);//crystal 25.000 MHz, correction 0
  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set
 
  lcd.begin();
  lcd.backlight();
  lcd.clear();

  pinMode(SW_TX,INPUT_PULLUP);
  pinMode(SW_CH,INPUT_PULLUP);

  if(EEPROM.read(Eep_Int) != Int_End){    // Eep initialaz
    delay(10);
    Fnc_Eep_Int();
  }

  Byt_Chn = EEPROM.read(Eep_Chn);         //     Channel

  Channel_Set();
  LCD_Line1();
}

//----------  Main program  ---------------

void loop() {

  if(digitalRead(SW_TX) == HIGH){          // TX off?
    if((digitalRead(SW_CH) == LOW)){       // CH SW On?
      Fnc_Chsw();
      Channel_Set();                                 
    }
    Flg_Tx = 0;
    If_Dat = IF_FRQ;
    si5351.output_enable(SI5351_CLK0,1);  // PLL Out enable 
  }
  else{                                   // TX on
    if(Byt_Chn <= CH8){                   // CH1-CH8?
      Flg_Tx = 1;                         // Yes,TX Flag set
      If_Dat = 0;                         //     If_Dat 0 set
      si5351.output_enable(SI5351_CLK0,1);// PLL Out enable 
    }
    else{                                 // CH9-CH10
      Flg_Tx = 0;                         // TX Flag reset                         
      If_Dat = IF_FRQ;                    // If_Dat set
      si5351.output_enable(SI5351_CLK0,0);// PLL Out disable 
    }
  }

  Vfo_Dat = Frq_Dat + If_Dat;
 
  if(Vfo_Dat != Vfo_Datb){                  // Frequency update?
    si5351.set_freq(Vfo_Dat * SI5351_FREQ_MULT,SI5351_CLK0);
    Vfo_Datb = Vfo_Dat;
  }
  LCD_Line1();



//----------  Channel Data Set & LCD Line0 Display  ---------

void Channel_Set(){
  lcd.setCursor(0,0);
  lcd.print("CH");

  switch(Byt_Chn){
    case CH1:
      Frq_Dat = CH1_FRQ;
      lcd.print("01  26.968");     
      break;
    case CH2:
      Frq_Dat = CH2_FRQ;
      lcd.print("02  26.976");     
      break;
    case CH3:
      Frq_Dat = CH3_FRQ;
      lcd.print("03  27.040");     
      break;
    case CH4:
      Frq_Dat = CH4_FRQ;
      lcd.print("04  27.080");     
      break;
    case CH5:
      Frq_Dat = CH5_FRQ;
      lcd.print("05  27.088");     
      break;
    case CH6:
      Frq_Dat = CH6_FRQ;
      lcd.print("06  27.112");     
      break;
    case CH7:
      Frq_Dat = CH7_FRQ;
      lcd.print("07  27.120");     
      break;
    case CH8:
      Frq_Dat = CH8_FRQ;
      lcd.print("08  27.144");     
      break;
    case CH9:
      Frq_Dat = CH9_FRQ;
      lcd.print("09  27.005");     
      break;
    case CH10:
      Frq_Dat = CH10_FRQ;
      lcd.print("10  27.524");     
      break;
    default:
      break;
  }
  EEPROM.write(Eep_Chn,Byt_Chn);
  lcd.setCursor(13,0);
  lcd.print("MHz");
}

//----------  LCD Line1 Display  ---------

void LCD_Line1(){
  lcd.setCursor(0,1);

  if(Flg_Tx == 1)
    lcd.print("TX");
  else
    lcd.print("RX");

  lcd.setCursor(10,1);
  lcd.print(CALL);
}

//----------  Function CH SW Check  ---------

void Fnc_Chsw(){
  Byt_Chn++;
  while(digitalRead(SW_CH) == LOW)
    ;
  if(Byt_Chn > Max_Chn)
    Byt_Chn = CH1;
}

//---------- Function Eeprom Initialization -----------------

void Fnc_Eep_Int(){
  EEPROM.write(Eep_Int,Int_End);                  // Init end set(73) 
  EEPROM.write(Eep_Chn,CH1);                      // Init end set(73) 
}




     


2019年1月22日火曜日

AFPSN用VFO

dsPICとMAX2452を組合わせたAFPSN用のVFOである。MAX2452のローカル発振は、目的周波数の2倍が必要な為、送信用VFOとした。設計に当たり、拡張性のあるH/Wを目指す事にし、I2Cを多用した。今迄のsi5351VFOは、カスタムライブラリを使っていたが、今回はgithubのものを使っている。

VFOの主な仕様
1)8チャンネルメモリー
2)メモリー自動書き込み
3)BPF、LPF切替
4)周波数範囲チェック
5)自動モード切替
6)STEP 100Hz、1k、10k、100k、
      1M




回路図


回路図である。I2Cデバイスのアドレスが判る様に記入した。EXTスイッチは、拡張用に割り付けたが未使用。TD62083のOut1からOut8は、リレー駆動用。










スケッチ

Arduino IDE1.8.8でコンパイルした。ライブラリの相性善し悪しで、I2Cデバイス動作が左右される。その為、スケッチのフォルダーにsrcフォルダーを作り、ライブラリを入れた。必要なファイルは、JA2GQP's Download siteのafpsnフォルダーからダウンロード可能。
バンドの概念が無いので、どのチャンネルにどんなバンド帯でも設定が出来るが、設定したチャンネル番号でBPF、LPFを切替えているので注意。

//////////////////////////////////////////////////////////////////////
//  si5351a PLL AFPSN VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2019/1/22
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(1M,100k,10k,1k,100)
//    2.Memory Channel ch0 - ch7(8ch)
//    3.Protection Operation At The Time Of Transmission
//////////////////////////////////////////////////////////////////////
//       PCF8574  https://github.com/xreef/PCF8574_library

#include "src/si5351.h"                      // Ver2,1,0
#include "src/LiquidCrystal_I2C.h"           //    1.1.2
#include "src/Rotary.h"
#include <EEPROM.h>
#include "src/PCF8574.h"

//---------- Set I/O Device ---------------------

PCF8574 pcf8574(0x20);                    // Set i2c address
LiquidCrystal_I2C lcd(0x27,16,2);       // address 0x27, display 16x2
Si5351 si5351;
Rotary r = Rotary(2,3);                  // 2 = Encorder A, 3 = Encorder B

//----------  Define Constant Value   ----------
                                               
const byte  SW_STEP = 4;                   // STEP SW
const byte  SW_CH = 5;                     // CH SW
const byte  SW_TX = 9;                     // TX SW
const byte  OUT_MODE = 13;                 // OUTPUT Mode

////////////////////////////////
// Limited range
////////////////////////////////
const long  LW_VFO80 = 3500000L;           // 3.5MHz Lower Limit
const long  HI_VFO80 = 3575000L;           //        Upper Limit
const long  LW_VFO40 = 7000000L;           // 7MHz   Lower Limit
const long  HI_VFO40 = 7200000L;           //        Upper Limit
const long  LW_VFO20 = 14000000L;          // 14MHz  Lower Limit
const long  HI_VFO20 = 14350000L;          //        Upper Limit
const long  LW_VFO15 = 21000000L;          // 21MHz  Lower Limit
const long  HI_VFO15 = 21450000L;          //        Upper Limit
const long  LW_VFO10 = 28000000L;          // 28MHz  Lower Limit
const long  HI_VFO10 = 29700000L;          //        Upper Limit
const long  LW_VFO6 = 50000000L;           // 50MHz  Lower Limit
const long  HI_VFO6 = 54000000L;           //        Upper Limit

////////////////////////////////
// default value
////////////////////////////////
const long  DEF_FRQ = 7050000L;            // Default Vfo(7.05MHz)
const long  DEF_STP = 1000L;               // Init STEP(1kHz)

////////////////////////////////
// etc
////////////////////////////////
const byte  Max_Chn = 3;                   // Max Channel(8ch)
const byte  Int_End = 73;                  // Initial end code
const char *CALL = "JA2GQP";               // Display Call sign

//----------  EEPROM Memory Address   -----------------------

const byte  Frq_Eep = 0x00;                // Frequency(4byte*10)
const byte  Stp_Eep = 0x30;                // STEP(4byte*10)
const byte  Chn_Eep = 0x60;                // Channel(1byte*1)
const byte  Vfo_Eep = 0x62;                // Vfo mode(1byte*1)
const byte  Eep_Int = 0x6e;                // Eep Init(1byte*1)

//----------  Memory Assign  -------------------

unsigned long Vfo_Dat = 0;                 // VFO Data
unsigned long Vfo_Datb = 0;                //          old
unsigned long Enc_Stp = 0;                 // STEP
long timepassed;                           // int to hold the arduino miilis since startup

char *Lcd_Dat = "           ";             // Lcd Display Buffer
char *Mode = "    ";                       // Mode display
byte Byt_Chn = 0;                          // Channel SW
byte Byt_Chnb = 0;                         // Channel SW Old
byte Flg_Tx = 0;                           // TX Flag
byte Flg_eepWT = 0;                        // EEP Write Flag
byte Flg_Mode = 0;                         // Mode Flag
char Flg_Over;                             // Over Flag
byte Flg_Vfo = 0;                          // DDS Flag
byte Byt_Vfo = 0;                          // VFO Mode

//----------  Initialization  Program  ---------------

void setup(){
  pcf8574.pinMode(P0,OUTPUT);              // PCF8574 Set pinMode to OUTPUT
  pcf8574.pinMode(P1,OUTPUT);
  pcf8574.pinMode(P2,OUTPUT);
  pcf8574.pinMode(P3,OUTPUT);
  pcf8574.pinMode(P4,OUTPUT);
  pcf8574.pinMode(P5,OUTPUT);
  pcf8574.pinMode(P6,OUTPUT);
  pcf8574.pinMode(P7,OUTPUT);

  pcf8574.begin();

  pcf8574.digitalWrite(P0,LOW);
  pcf8574.digitalWrite(P1,LOW);
  pcf8574.digitalWrite(P2,LOW);
  pcf8574.digitalWrite(P3,LOW);
  pcf8574.digitalWrite(P4,LOW);
  pcf8574.digitalWrite(P5,LOW);
  pcf8574.digitalWrite(P6,LOW);
  pcf8574.digitalWrite(P7,LOW);

  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);//initialize the Si5351
  si5351.set_freq(0,SI5351_CLK0);          // Dummy Frequency data
  si5351.output_enable(SI5351_CLK0,0);     // CLK0 disable

  lcd.begin();
  lcd.backlight();

  pinMode(SW_STEP,INPUT_PULLUP);
  pinMode(SW_TX,INPUT_PULLUP);
  pinMode(SW_CH,INPUT_PULLUP);
  pinMode(OUT_MODE,OUTPUT);

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                                   // INT Enable

  Flg_Tx = 0;                              // Flag Initialization
  Flg_Vfo = 0;                           
  lcd.clear();

  if(EEPROM.read(Eep_Int) != Int_End){    // Eep initialaz
    delay(10);
    Fnc_Eep_Int();
  }

  Byt_Chn = EEPROM.read(Chn_Eep);         //     Channel
  pcf8574.digitalWrite(Byt_Chn,HIGH);
  Byt_Chnb = Byt_Chn;
  Fnc_Eep_Rd();                            //     VFO & STEP
}

//----------  Main program  ---------------

void loop() {

  if(Flg_Tx == 0){                        // TX off?
    if(digitalRead(SW_STEP) == LOW)       // STEP SW On?
      Fnc_Stp();                           

    if((digitalRead(SW_CH) == LOW))       // SEL SW On?
      Fnc_Chsw();                           

    if(Byt_Chnb != Byt_Chn){              // CH SW OLD != NEW?
      Fnc_Eep_Wt(Byt_Chnb);
      pcf8574.digitalWrite(Byt_Chnb,LOW);
      Byt_Chnb = Byt_Chn;
      Fnc_Eep_Rd();
    }
  }

  if(digitalRead(SW_TX) == LOW){             // Tx On?
    Flg_Tx = 1;                              //    Yes,Flg_Tx Set
    si5351.output_enable(SI5351_CLK0, 1);    // VFO enable
  }
  else{                                   
    Flg_Tx = 0;                             //     No,Flg_Tx Reset
    si5351.output_enable(SI5351_CLK0, 0);   // VFO disable
  }

  Fnc_Band();                               // Band check

  if(Vfo_Dat != Vfo_Datb){                  // Frequency update?
    si5351.set_freq((Vfo_Dat * SI5351_FREQ_MULT) * 2,SI5351_CLK0);
    Fnc_Fdsp(Vfo_Dat);
    Vfo_Datb = Vfo_Dat;
    timepassed = millis();
    Flg_eepWT = 1;
  }
 
  Fnc_Lcd();                                // LCD Display

  if(Flg_eepWT == 1){                       // EEPROM auto Write
    if(timepassed+2000 < millis()){
      Fnc_Eep_Wt(Byt_Chn);
      Flg_eepWT = 0;
    }
  } 


//----------  Encorder procedure(INT)  ---------------

ISR(PCINT2_vect) {
  unsigned char result = r.process();

  if(Flg_Tx == 0){
    if(result){   
      if(result == DIR_CW)
          Vfo_Dat = Vfo_Dat + Enc_Stp;
      else
          Vfo_Dat = Vfo_Dat - Enc_Stp;
    }
  }
}

//----------  Function Mode  -------------------

void Fnc_Mode(){
  lcd.setCursor(5,1);

  if(Flg_Mode == 0){                      // LSB ?
    digitalWrite(OUT_MODE, HIGH);       
    lcd.print("LSB ");
  }
  else if(Flg_Mode == 1){                 // USB ?
    digitalWrite(OUT_MODE, LOW);       
    lcd.print("USB ");
  }
}

//----------  Function Encorder STEP  ---------

void Fnc_Stp(){
  if(Enc_Stp == 100)                       // Step = 100Hz ?
    Enc_Stp = 1000000;                    //   Yes,1MHz set
  else
    Enc_Stp = Enc_Stp / 10;               // Step down 1 digit

  Fnc_Step_Disp();
  Fnc_Lcd();
  while(digitalRead(SW_STEP) == LOW)
    ;
}

//----------  Function STEP Display  ----------

void Fnc_Step_Disp(){
  lcd.setCursor(0,1);
  switch(Enc_Stp){
    case 100:
      lcd.print("100 ");
      break;
    case 1000:
      lcd.print("1k  ");
      break;
    case 10000:
      lcd.print("10k ");
      break;
    case 100000:
      lcd.print("100k");
      break;
    case 1000000:
      lcd.print("1M  ");
      break;
    default:
      lcd.print("1k  ");
      Enc_Stp = 1000;
      break;
  }
}

//----------  Function String Dot Edit  --------
   
char *Fnc_Dot_Edit(char *str,long n){
  int  i = 0;                           // Write the number
  char *p = str;
  unsigned long  u = abs(n);

  do{
    *p++ = "0123456789"[u % 10];
    u = u / 10;
    i++;
    if((0 != u) && (0 == (i % 3)))
      *p++ = '.';
  }
  while( 0 != u )
    ;
  if ( n < 0 )
     *p++ = '-';
   *p = '\0';
   Fnc_Revr( str );
   return str;
}

//----------  Function String Reverse  ---------

void Fnc_Revr(char *str){
  int i,n;
  char c;

  n=strlen(str);
  for(i = 0;i < n / 2;i++){
    c=str[i];
    str[i]=str[n - i - 1];
    str[n - i - 1]=c;
  }
}

//----------  Function Write EEPROM 4byte  ---------

void Fnc_Eep_Write(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Function Read EEPROM 4byte  ---------

long Fnc_Eep_Read(int address){
  long value = 0;

  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
    }
  }
  return value;
}

//----------  Function LCD Display  ---------

void Fnc_Lcd(){
  lcd.setCursor(0,0);
  if(Flg_Tx == 1)
    lcd.print("T");
  else{
    lcd.print(Byt_Chn);
    pcf8574.digitalWrite(Byt_Chn,HIGH);
  }
 
  Fnc_Step_Disp();

    if(Flg_Over == -1){
      Fnc_Fdsp(Vfo_Dat);
      lcd.setCursor(5,1);
      lcd.print("Over");
    }
    else
      Fnc_Fdsp(Vfo_Dat);

  if(Flg_Over == 0){ 
    Fnc_Mode();
    lcd.setCursor(10,1);
    lcd.print(CALL);
  }
}

//----------  Function Frequency Display  ---------

void Fnc_Fdsp(long f_disp){ 
  Fnc_Dot_Edit(Lcd_Dat,f_disp);
  lcd.setCursor(1,0);
  lcd.print(": ");
  lcd.print(Lcd_Dat);
  lcd.print("Hz");
}

//----------  Function CH SW Check  ---------

void Fnc_Chsw(){
  byte cnt = 0;
 
  Byt_Chn++;
  while(digitalRead(SW_CH) == LOW){
    delay(500);
    cnt++;
    if(6 <= cnt){                               // Eep Initial start(3sec)?
      lcd.setCursor(5,1);
      lcd.print("Init");
      Fnc_Eep_Int();                            // Initialization
      Byt_Chn = EEPROM.read(Chn_Eep);          // Channel Read
      Byt_Chnb = Byt_Chn;
      Fnc_Eep_Rd();                             // EEPROM Read
    }
  }
}

//---------- Function Eeprom Initialization -----------------

void Fnc_Eep_Int(){
  int i;

  for (i=0;i<160;i++)                            // 0 clear(160byte)
    EEPROM.write(i, 0);

  for(i=0;i<Max_Chn;i++){
    Fnc_Eep_Write(DEF_FRQ,Frq_Eep+i*4);            // Frequency(7.05MHz)
    Fnc_Eep_Write(DEF_STP,Stp_Eep+i*4);            // Step(1kHz)
  }

  EEPROM.write(Eep_Int,Int_End);                  // Init end set(73) 
}

//----------  Function EEPROM Read  ---------

void Fnc_Eep_Rd(){
  if((0 <= Byt_Chn) && (Byt_Chn < Max_Chn))
    Vfo_Dat = Fnc_Eep_Read(Frq_Eep+Byt_Chn*4);
  else{
    Vfo_Dat = Fnc_Eep_Read(Frq_Eep+0*4);
    Byt_Chn = 0;
  }

  if((0 <= Byt_Chn) && (Byt_Chn < Max_Chn))
    Enc_Stp = Fnc_Eep_Read(Stp_Eep+Byt_Chn*4);
  else
    Enc_Stp = Fnc_Eep_Read(Stp_Eep+0*4);

  Byt_Vfo = EEPROM.read(Vfo_Eep);
}

//----------  Function EEPROM Write  ---------

void Fnc_Eep_Wt(byte chn){
  if((0 <= chn) && (chn < Max_Chn)){
    Fnc_Eep_Write(Vfo_Dat,Frq_Eep+chn*4);
    Fnc_Eep_Write(Enc_Stp,Stp_Eep+chn*4);
  }

  EEPROM.write(Chn_Eep,chn);
}

//----------  Function Band  ---------

void Fnc_Band(){
  if((Vfo_Dat >= LW_VFO80) && (Vfo_Dat <= HI_VFO80)){           // 3.5MHz
    Flg_Mode = 0;                                               //    LSB(0=LSB, 1=USB)   
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= LW_VFO40) && (Vfo_Dat <= HI_VFO40)){      // 7MHz
    Flg_Mode = 0;                                               //    LSB(0=LSB, 1=USB)
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= LW_VFO20) && (Vfo_Dat <= HI_VFO20)){      // 14MHz
    Flg_Mode = 1;                                               //    USB(0=LSB, 1=USB)
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= LW_VFO15) && (Vfo_Dat <= HI_VFO15)){      // 21MHz
    Flg_Mode = 1;                                               //    USB(0=LSB, 1=USB)
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= LW_VFO10) && (Vfo_Dat <= HI_VFO10)){      // 28MHz
    Flg_Mode = 1;                                               //    USB(0=LSB, 1=USB)
    Flg_Over = 0;
  }
  else if((Vfo_Dat >= LW_VFO6) && (Vfo_Dat <= HI_VFO6)){        // 50MHz
    Flg_Mode = 1;                                               //    USB(0=LSB, 1=USB)
    Flg_Over = 0;
  }
  else
    Flg_Over = -1;
}
    
    

2018年12月30日日曜日

AFPSN PCB

ユニバーサル基板で作った 回路を見直し、PCB化した。主要部品は、マイクユニット、dsPIC33、MAX2452である。MAX2452の出力は-35dB位なので、十分ゲインを稼ぐため2SC3357を使用した。2SC3357は、広帯域増幅にして多バンド化への可能性を含ませた。キーパーツのMAX2452、マイクユニット、リレーは、Aliexpressで入手可能。






2SC3357ランド抜け、パターン切れ

 赤丸部に2SC3357取付ランドミスがある。小さなスルーホールは、コレクタに繋がる筈であった。更に、ベースに繋がる筈のパターンも途中で切れている。














2SC3357取付

グランドパターンの空きスペースにランドを作り、2SC3357を取付けた。コレクタは、小さなスルーホールに直接半田付けを行った。















回路図

回路図である。マイクユニットは、AGC付きのMAX9814または、AGC無しのMAX4466が使える様にした。更に、ライン入力も選べる。ジェネレータ出力は、1mW~1.5mWである。   

     


 



   

2018年11月20日火曜日

Prescaler 1/1,1/2

MAX2452 PSNにPIC12F1840 VXOを使っているが、PIC12F1840のメモリーが少ない為、表示器レスとした。しかし、試験を行っていると、周波数表示が欲しくなった。
そこで、MAX2452の周波数をsi5351aのclk0から直接拾って表示する事にした。その為、2倍となったMAX2452周波数データを1/2にするプリスケーラを作った。


MAX2452へ7.10MHzを指定する場合の周波数データ。si5351aのclk0を直接拾うと、14.20MHzが表示される。








1/1と1/2のプリスケーラ回路図である。









 
MAX2452へ7.10MHzを指定した周波数設定データの1/2を表示。小容量のPICも使い方次第で、上手いシステムが出来る。 
 

2018年11月11日日曜日

PSN SSB Generator

MAX2452を使ったPSN SSB Generatorである。これは、JA2NKD/1松浦OM公開のPSN SSB主要部分を1ボードにしたジェネレータ。ユニット化したキーパーツを使う事で 、簡素化した。マイクアンプにAGC付きMAX9814を使用。また、LOにPICクリスタルもどきのPCB版(GQP VXO)使用。サンプルは、7.100MHzである。







回路図である。マイクユニットMAX9814、AF PSN dsPIC33そしてRF PSN MAX2452でPSN SSBが発生する。マイクアンプユニットのGain・A/Rは、ジャンパーチップで条件を変える事が出来る。MAX9814及びMAX2452はAliexpressで入手可能。











スケッチ

必要なファイルは、JA2GQP's Download siteのpicフォルダからダウンロード可能。

////////////////////////////////////////////////////////////
//    si5351a PLL control(PIC12F1840) psn
//
//                                    2018/11/11
//                                    JA2GQP     
////////////////////////////////////////////////////////////
//
// Bug FIX  LSB/USB Frequency         2011.11.16
//

//---------- 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        7100000*MULTI-AD_OFFSET  // Default frequency(7.100MHz)
#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)
#define MULTI           2                       // Multiplication factor

////////////////////////////
// si5351a parameter
////////////////////////////
#define CLK0_CTRL     16                    // Register definitions
#define CLK1_CTRL     17
#define CLK2_CTRL     18
#define MSNA_ADDR     26
#define MSNB_ADDR     34
#define MS0_ADDR      42
#define MS1_ADDR      50
#define MS2_ADDR      58
#define CLK0_PHOFF   165
#define CLK1_PHOFF   166
#define PLL_RESET    177
#define XTAL_LOAD_C  183
#define R_DIV_1      0b00000000             // R-division ratio definitions

#define Si5351A_ADDR  0xC0                  // address(cip address<<1)
#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            _2mA                  // output lebel 1dBm set

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

static 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);             // Enable CLK0 (MS0=Integer Mode, Source=PLL_A)
}

//------------- 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-2046
}

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

void main(){
    int wk;

    PIC12F1840_set();                       // Cip Initialization
    Si5351_init();                          // si5351a Initialization

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