2017年7月31日月曜日

K4GC VFO

何時もお世話になっているKさんから、K4GC VFOスケッチについて相談を受けた。エンコーダ操作で周波数調整が困難との事であった。スケッチを見ると割込み処理を行わずに、エンコーダ読取りが行われており、取りこぼしが原因と思われる。今回問題が有ったエンコーダ処理とLCD表示を変更した。

回路図である。
ダイレクトコンバージョン受信機と組合わせた40m CW QRP機で、フルブレイクイン動作する。オリジナルからkey paddleとエンコーダのIO割付のみ変更。









スケッチ

基本的な処理は、オリジナルを継承している。RIT処理がアナログ処理で行われており、参考になる所もある。スケッチは、ダウンロードサイトのsi5351aフォルダ。

/**************************************************************************
   DIRECT CONVERSION CW TRANSCEIVER CONTROLLER WITH IAMBIC KEYER FOR ARDUINO
   Uses SI5351 clock generator shild from Adafruit for VFO
   Uses NT7S SI5351 library for Arduino on github:
   https://github.com/etherkit/Si5351Arduino
   2 x 16 LCD display for frequency, offset, and tuning rate
   Has an iambic keyer with dot/dash memories
   Arduino Pro Mini pin assignments:
   2) Encoder A input
   3) Encoder B input
   4) STEP SW
   5) Iambic paddle DSH input
   6) Iambic paddle DIT input
   7)
   8) Transmit/mute output
   9) Sidetone output
   10) LCD RS output
   11) LCD E output
   12) LCD D4 output
   13) LCD D5 output
   A0) LCD D6 output
   A1) LCD D7 output
   A2)
   A3)
   A4) si5351 SDA
   A5) si5351 SCL
   A6) Offset voltage input
   A7) Keyer speed voltage input
 ***************************************************************************/

#include <si5351.h>
#include "Wire.h"
#include <Rotary.h>
#include "src/LiquidCrystal.h"                // use Arduino LCD library.


//----------   PLL setting  --------------------

Si5351 si5351;

//----------   LCD setting  --------------------

LiquidCrystal lcd(10, 11, 12, 13, A0, A1);  // RS,E,D4,D5,D6,D7

//----------   Encorder setting  ---------------

#define   ENC_A       2                       // Encorder A
#define   ENC_B       3                       // Encoeder B

Rotary r=Rotary(ENC_A,ENC_B);


////////////////////////ENCODER SETUP///////////////////////////////////////
#define SW_STEP       4
#define Pad_Dah       5
#define Pad_Dit       6  
#define XMIT          8
#define TONE          9

#define LW_FRQ        7000000L
#define HI_FRQ        7200000L

long Vfo_Dat = 7030000L;                      //Start up frequency <<<2017/6/27>>>
long Vfo_Datb = 0;                            //                   <<<2017/6/27>>>
int Enc_Stp = 10;                             // STEP=10Hz
int rateindicator = 5;
char Lcd_data[17] = "                ";
int Flg_Set0 = 1;
/////////////////////////////////////////////////////////////////////////////

//////////////////////////////RIT SETUP//////////////////////////////////////
int rit = A6;
int ritvoltage = 0;
int oldritvoltage = 0;
int modulo = 0;
int ritcenter = 512;
int rittune = 0;
int rittuneb = rittune;
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////IAMBIC KEYER////////////////////////////////////
int count = 0;
int dit = 1;
int dah = 1;
int sidetone = 700;
int ditmem = 0;
int dahmem = 0;
int sspeed = 50;
int speedinput = A7;
/////////////////////////////////////////////////////////////////////////////

//----------  Setup  ---------------

void setup(){
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  lcd.begin(16, 2);

  pinMode(Pad_Dah, INPUT_PULLUP);                  // Dah
  pinMode(Pad_Dit, INPUT_PULLUP);                  // Dit

  pinMode(SW_STEP, INPUT_PULLUP);                  // STEP

  pinMode(TONE, OUTPUT);
  pinMode(XMIT, OUTPUT);
  digitalWrite(XMIT, HIGH);

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

  Fnc_Step_Disp();
}

//----------  Main loop  --------

void loop() {
  if(digitalRead(SW_STEP) == LOW)                   // STEP SW On?
    Fnc_Stp();                          

  setclock0();
 
  ritvoltage = analogRead(A6);
  rittune = ritvoltage - ritcenter;
  rittune = rittune * 5;
  if (ritvoltage != oldritvoltage){
    ritune();
  }

  if (ditmem == 1) makedit();
  if (dahmem == 1) makedah();
  sspeed = analogRead(speedinput);
  sspeed = sspeed / 6.5;
  if (sspeed < 24) sspeed = 24;
  count = 0;
  checkpaddle();
}

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

ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if(result) {  
    if(result == DIR_CW)
      Vfo_Dat = Vfo_Dat + Enc_Stp;
    else
      Vfo_Dat = Vfo_Dat - Enc_Stp;
    Vfo_Dat = constrain(Vfo_Dat,LW_FRQ,HI_FRQ);
  }
  Flg_Set0 = 1;
}

//----------  Set Clock0  ---------------

void setclock0() {                            //function to set the SI5351 clock frequency
  if(Flg_Set0 == 1){
    lcd.setCursor(2, 0);
    Fnc_Dot_Edit(Lcd_data,Vfo_Dat);
    lcd.print(Lcd_data);
    lcd.print(" MHz");
    si5351.set_freq((Vfo_Dat + rittune) * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);
  }
  Flg_Set0 = 0;
}

//----------  RIT proc  ---------------

void ritune() {                             //function to determine RIT offset frequency
  modulo = 0;
  oldritvoltage = ritvoltage;
  rittune = ritvoltage - ritcenter;
  rittune = rittune * 5;                    //RIT steps in 5 Hz increments
  if (rittune > 30) {                       //Keep a dead zone near 0 offset
    rittune = rittune;
  }
  else if (rittune < -30)  {
    rittune = rittune;                      //Keep a dead zone near 0 offset
  }
  else {
    rittune = 0;
  }
  lcd.setCursor(8, 1);
  lcd.print("        ");
  lcd.setCursor(8, 1);
  lcd.print("Rit");
  if (rittune > 0) {
    lcd.print("+");
  }
  lcd.print(rittune);
  Flg_Set0 = 1;
  setclock0();
}

//----------  Check Paddle  ---------------

void checkpaddle() {                            //function for checking iambic paddle status
  dit = digitalRead(Pad_Dit);
  dah = digitalRead(Pad_Dah);
  if (!dit) makedit();
  if (!dah) makedah();
}

//----------  Dit make proc  ---------------

void makedit() {                                //function to make dits
  count = 0;
  ditmem = 0;
  si5351.set_freq(Vfo_Dat * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);
  digitalWrite(XMIT, 0);
  tone(TONE, sidetone);
  while (count < sspeed) {
    dah = digitalRead(Pad_Dah);
    if (!dah) dahmem = 1;
    delay(1);
    ++count;
  }
  count = 0;
  noTone(TONE);
  digitalWrite(XMIT, 1);
  si5351.set_freq((Vfo_Dat + rittune) * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);
    while (count < sspeed) {
    dah = digitalRead(Pad_Dah);
    delay(1);
    ++count;
  }
}

//----------  Dah make proc  ---------------

void makedah(){                                 //function to make dahs
  count = 0;
  dahmem = 0;
  si5351.set_freq(Vfo_Dat * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);
  digitalWrite(XMIT, 0);
  tone(TONE, sidetone);

  while (count < (sspeed * 3)){
    dit = digitalRead(Pad_Dit);
    if (!dit) ditmem = 1;
    delay(1);
    ++count;
  }

  count = 0;
  noTone(TONE);
  digitalWrite(XMIT, 1);
  si5351.set_freq((Vfo_Dat + rittune) * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);

  while (count < sspeed) {
    dit = digitalRead(Pad_Dit);
    if (!dit) ditmem = 1;
    delay(1);
    ++count;
  }
}

//----------  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 Encorder STEP  ---------

void Fnc_Stp(){
  if(Enc_Stp == 10000)                   // Step = 10kHz ?
    Enc_Stp = 10;                        //   Yes,100khz set
  else
    Enc_Stp = Enc_Stp * 10;              // Step up 1 digit

  Fnc_Step_Disp();
  while(digitalRead(SW_STEP) == LOW)
    ;
  delay(50);
}

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

void Fnc_Step_Disp(){
  lcd.setCursor(0,1);
  switch(Enc_Stp){
    case 10:
      lcd.print("Step10 ");
      break;
    case 100:
      lcd.print("Step100");
      break;
    case 1000:
      lcd.print("Step1k ");
      break;
    case 10000:
      lcd.print("Step10k");
      break;
    default:
      lcd.print("Step10 ");
      Enc_Stp = 10;
      break;
  }
}


     

2017年6月17日土曜日

stm32VFO 6mAM

stm32f103c8t6とsi5351aを使って、50MHzAM用VFOを作った。周波数は50MHzから51MHzまでとし、STEPは1Khz、10kHzとした。電源off時の周波数とSTEPを電源on時に復帰させるメモリ機能を付加したVFOである。資料は、Download siteのstm32フォルダに保存してある。






stm32BaseBordから不要な部分を除いた、回路図である。












スケッチ

si5351a2.hは、本来、スケッチと同じフォルダに入れるが、srcフルダに入れ階層的にした。PLL発振出力は clk0 1chのみである。デフォルト周波数は、50.6MHzとし、STEPは10kHz刻みにした。受信機はダブルスーパ(IF=10.7MHz)を想定したので、条件が異なれば修正が必要。メモリー保存は、AD7C方式を採用した。また、周波数補正は、si5351a2.hの41行目XTAL_FREQ 25000000を実周波数(カウンターなどで測定した値)に書換えれば良い。

///////////////////////////////////////////////////////////////////
//    si5351a Simple 50MHz AM VFO(Memory Version) Ver1.01
//                                        2017/6/17
//                                        JA2GQP
//-----------------------------------------------------------------
//  Bug fixes
//    Encoder processing problem.   Ve.1.01 2017/8/17
///////////////////////////////////////////////////////////////////

#include <LCD5110_Basic.h>
#include <Rotary.h>
#include <EEPROM.h>
#include "src/si5351a2.h"

#define ENC_A     PC13                            // Rotary encoder A
#define ENC_B     PC14                            // Rotary encoder B
#define SW_STEP   PC15                            // Frequency Change Step
#define SW_TX     PB12                            // TX switch

#define LOW_FREQ  50000000                        // Lowwer Frequency (50MHz)
#define HI_FREQ   51000000                        // Upper Frequency  (51MHz)
#define IF_FREQ   10700000                        // IF Frequency

#define EEP_FREQ  0x00                            // EEPROM Frequency Adress
#define EEP_STEP  0x04                            //        STEP
#define EEP_INIT  0x20                            // Initialyze check Adress
#define EEP_END   0x73                            // Initialyze check data

//------ Register define ------

long Vfo_freq =  50600000;                        // Default Frequency(50.6MHz )
long Vfo_freqb = Vfo_freq;                        // Frequency Old
long Enc_step = 10000;                            // Default STEP
byte Flg_Tx;                                      // TX Flag

uint16 Status;
uint16 Data;

int_fast32_t timepassed;                          // int to hold the arduino miilis since startup

int Flg_eepWT = 0;                                // EEP Wite Flag

char Lcd_data[10];

//------  nokia5110 ------

LCD5110 myGLCD(PB3,PB5,PA15,PA12,PA11);        // SCK,MOSI,DC,RST,CS
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];

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

Rotary r = Rotary(ENC_A,ENC_B);               // ENC_A=2,ENC_B=3

//---------- Setup -----------------------------------------------------------------

void setup(){
  timepassed = millis();
  Wire.begin();                  

  attachInterrupt(PC13, Rotaly_enc, CHANGE);    // Encorder A
  attachInterrupt(PC14, Rotaly_enc, CHANGE);    //          B

  pinMode(PC13,INPUT_PULLUP);                   // PC13 pull up
  pinMode(PC14,INPUT_PULLUP);                   // PC14
  pinMode(SW_STEP,INPUT_PULLUP);
  pinMode(SW_TX,INPUT_PULLUP);


  Fnc_eepINIT();
 
  Status = EEPROM.read(EEP_INIT, &Data);
  if(Data != EEP_END){
    Status = EEPROM.write(EEP_INIT, EEP_END);
    Vfo_freq = 50600000;
    Fnc_eepWT(Vfo_freq,EEP_FREQ);
    Fnc_eepWT(Enc_step,EEP_STEP);
  }
  else{
    Vfo_freq = Fnc_eepRD(EEP_FREQ);
    Enc_step = Fnc_eepRD(EEP_STEP);
  }


  Si5351_write(XTAL_LOAD_C,0x80);               // Crystal Load Capasitance=8pF
  si5351aSetFrequency(Vfo_freq);                // CLK0

  myGLCD.InitLCD();                             // nokia5110 Initialyze
  Feq_disp(Vfo_freq);
  Stp_disp();
  myGLCD.setFont(SmallFont);
  myGLCD.print("JA2GQP",CENTER,32);
}

//---------- Main Loop --------------------------------------------------------

void loop(){
  if(Flg_Tx == 0){
    if(digitalRead(SW_STEP) == LOW)             // STEP sw check
      Fnc_Stp();
  }                      

  if(digitalRead(SW_TX) == LOW)                 // Tx On?
    Flg_Tx = 1;                            
  else                                  
    Flg_Tx = 0;                                          

  if(Vfo_freq != Vfo_freqb){                    // Frequency update?
    Vfo_freqb = Vfo_freq;
    Flg_eepWT = 1;
    myGLCD.clrRow(0);
    myGLCD.clrRow(1);
    Feq_disp(Vfo_freq);                         // Frequency display
  }

    if(Flg_Tx  == 0){
      si5351aSetFrequency(Vfo_freq+IF_FREQ);    // CLK0 = RX frequency
      myGLCD.setFont(SmallFont);  
      myGLCD.print("     ", CENTER,16);
    }    
    else{
      si5351aSetFrequency(Vfo_freq);            // CLK0 = TX frequency
      myGLCD.setFont(SmallFont);  
      myGLCD.print("OnAir", CENTER,16);
    }

  if(Flg_eepWT == 1){  
    if(timepassed+2000 < millis()){
      Fnc_eepWT(Vfo_freq,EEP_FREQ);
      Flg_eepWT = 0;
    }
  }  
 
}

//---------- Rotary Encorder Interrupt handling ------------------------------

void Rotaly_enc(){
  noInterrupts();
  unsigned char result = r.process();
  if(Flg_Tx == 0){
    if(result) {  
      if(result == DIR_CW)
        Vfo_freq = Vfo_freq + Enc_step;
      if(result == DIR_CCW)
        Vfo_freq = Vfo_freq - Enc_step;
      Vfo_freq = constrain(Vfo_freq,LOW_FREQ,HI_FREQ);    // Frequency range check
    }
  }
  interrupts();
}

//---------- Step -------------------------------------------------------------

void Fnc_Stp(){
  if(Enc_step == 10000)                           // Step = 10kHz ?
//    Enc_step = 10;                                // 10Hz set
     Enc_step = 1000;                             // 1kHz set
  else
    Enc_step= Enc_step * 10;
  Stp_disp();
  while(digitalRead(SW_STEP) == LOW)
    ;
  delay(20);
}


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

void Feq_disp(long f_disp){
  char s[4] ={'\0'};

  Fnc_Dot_Edit(Lcd_data,f_disp/1000L);
  myGLCD.setFont(MediumNumbers);
  myGLCD.print(Lcd_data,RIGHT, 0);

//  myGLCD.printNumI(f_disp/1000L,RIGHT, 0);
//  myGLCD.setFont(SmallFont);
//  sprintf(s,"%03d",f_disp%1000L);
//  myGLCD.print(s,RIGHT,16);

}

//---------- Step Display ------------------------------------------------------

void Stp_disp(){
  myGLCD.setFont(SmallFont);
  switch(Enc_step){
//    case 10:
//      myGLCD.print("10  ", LEFT,16);
//      break;
//    case 100:
//      myGLCD.print("100 ", LEFT,16);
//      break;
    case 1000:
      myGLCD.print("1k  ", LEFT,16);
      break;
    case 10000:
      myGLCD.print("10k ", LEFT,16);
      break;
    default:
      myGLCD.print("10k ", LEFT,16);
      Enc_step = 10000;
      break;
  }
  Fnc_eepWT(Enc_step,EEP_STEP);
}

//----------  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 EEPROM Initialize  ---------

void Fnc_eepINIT(){
  uint16 dummy;
 
  EEPROM.PageBase0 = 0x801F000;
  EEPROM.PageBase1 = 0x801F800;
  EEPROM.PageSize  = 0x400;             // 2kB
  dummy = EEPROM.init();
}

//----------  Function EEPROM Read(4byte)  ---------

long Fnc_eepRD(uint16 adr){
  long val = 0;
  uint16 dat,dummy;

  dummy = EEPROM.read(adr,&dat);
  val = dat << 16;
  dummy = EEPROM.read(adr+1,&dat);
  return val | dat;
}

//----------  Function EEPROM Write(4byte)  ---------

void Fnc_eepWT(long dat,uint16 adr){
  uint16 dummy,val;

  val = dat & 0xffff;
  dummy = EEPROM.write(adr+1,val);
  val = dat >> 16;
  val = val & 0xffff;
  dummy = EEPROM.write(adr,val);
}


2017年6月12日月曜日

stm32f103c8t6 Base Bord

stm32f103c8t6のスケッチを開発するにあたり、プロトタイプのベースボードを作った。同時に、このボードのI/O割付に修正したスケッチサンプルを作った。このサンプルを使えば、VFOを簡単に作る事が出来るであろう。





ベースボードの回路図である。スケッチ書込みポートは、シリアルのコネクタを設けてあり、USBアダプタ(CH340G)を直接繋ぐ事ができる。表示器は、0.96"OLED、1.8"TFT、nokia5110を繋ぐ事ができる。si5351a取付コネクタを設けてあるので、このままVFO開発が可能。






サンプルスケッチ

全てのサンプルは、先に紹介した回路図に対応する様、I/O割付を変更済である。回路図通り接続し、即、スケッチが実行可能。このサンプルスケッチは、JA2GQP's Download siteのstm32フォルダ。

・Blink

LEDのブリンク。

・encorder

stm32f103c8t6の割込み処理は自由度がある。どのポートに割付しても、割込み処理で動作させる事ができる。

・si5351a

PLL出力(clk0、clk2)の出力サンプル。

・noki5110

nokia5110液晶文字表示サンプル。

・oled(ssd1306)

oledグラフィック表示サンプル。

・メモリ記憶

エンコーダと組合わせた、メモリ記憶のサンプル。

・Ucglib(ili9341、st7735)

TFT液晶の表示サンプル。

VFO製作例


              
サンプルスケッチを基にして作ったVFO。

            

2017年6月3日土曜日

si5351a TFT VFO Ver.1.1

 2.2"TFTが入手難の為、1.8"TFTにダウンサイジングを行った。今回の移植作業と合わせ、機能追加を行う事にした。追加変更内容は、Lowerヘテロダイン対応とBFO追加である。理由は、BITX40がLowerヘテロダインである事と、si5351aのマルチ出力が生かされて無かったからである。
回路図、スケッチはダウンロードサイトsi5351a TFTフォルダに2種類有ります。
si5351_TFT18.zip,si5351_TFT181.zip



回路図である。si5351aモジュールのclk2からBFO出力を行っている。













インクルードファイル

si5351aの制御は、ライブラリを使わずに、インクルードファイルで行っている。このファイルをスケッチと同じフォルダーに保存して使う。周波数が変わる毎に発生するクリック低減のため、PLLリセットを行わない事(コメントアウトしてある)とした。但し、位相制御などタイミングを重視する場合、PLLリセットを行う必要が有るであろう。

si5351a2.hの内容。


////////////////////////////////////////////////////////////////////////
// Author: Hans Summers, 2015
// Website: http://www.hanssummers.com
//
// A very very simple Si5351a demonstration
// using the Si5351a module kit http://www.hanssummers.com/synth
// Please also refer to SiLabs AN619 which describes all the registers to use
//----------------------------------------------------------------------
// Modifications: JA2GQP,2017/5/20
//     1)Output is CLK0 and CLK2.
//     2)Arduino and stm32duino Operable.
////////////////////////////////////////////////////////////////////////

#include <Wire.h>

#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 PLL_RESET   177
#define XTAL_LOAD_C 183

#define R_DIV_1      0b00000000     // R-division ratio definitions
#define R_DIV_2      0b00010000
#define R_DIV_4      0b00100000
#define R_DIV_8      0b00110000
#define R_DIV_16     0b01000000
#define R_DIV_32     0b01010000
#define R_DIV_64     0b01100000
#define R_DIV_128    0b01110000

#define Si5351A_ADDR  0x60

#define CLK_SRC_PLL_A 0b00000000
#define CLK_SRC_PLL_B 0b00100000

#define XTAL_FREQ     25000000    // Crystal frequency for Hans' board

////////////////////////////////////////////////////////////////////////
// I2C write
////////////////////////////////////////////////////////////////////////

void Si5351_write(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

////////////////////////////////////////////////////////////////////////
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
///////////////////////////////////////////////////////////////////////

void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom){
  uint32_t P1;                            // PLL config register P1
  uint32_t P2;                            // PLL config register P2
  uint32_t P3;                            // PLL config register P3

  P1 = (uint32_t)(128 * ((float)num / (float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(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 with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate
// register, it is a #define in si5351a.h
////////////////////////////////////////////////////////////////////////

void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv){
  uint32_t P1;                          // Synth config register P1
  uint32_t P2;                          // Synth config register P2
  uint32_t 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));
}

////////////////////////////////////////////////////////////////////////
// Switches off Si5351a output
// Example: si5351aOutputOff(CLK0_CTRL);
// will switch off output CLK0
////////////////////////////////////////////////////////////////////////

void si5351aOutputOff(uint8_t clk){
  Si5351_write(clk, 0x80);              // Refer to SiLabs AN619 to see
                                        //bit values - 0x80 turns off the output stage
}

////////////////////////////////////////////////////////////////////////
// Set CLK0 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL A
// and MultiSynth 0
// and produces the output on CLK0
////////////////////////////////////////////////////////////////////////

void si5351aSetFrequency(uint32_t frequency){
  uint32_t pllFreq;
  uint32_t xtalFreq = XTAL_FREQ;
  uint32_t l;
  float f;
  uint8_t mult;
  uint32_t num;
  uint32_t denom;
  uint32_t 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);
                                          // Reset the PLL. This causes a glitch in the output. For small changes to
                                          // the parameters, you don't need to reset the PLL, and there is no glitch
//  Si5351_write(PLL_RESET, 0x20);
                                          // Finally switch on the CLK0 output (0x4F)
                                          // and set the MultiSynth0 input to be PLL A
  Si5351_write(CLK0_CTRL, 0x4F | CLK_SRC_PLL_A);    // Strength 8mA
}

////////////////////////////////////////////////////////////////////////
// Set CLK1 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency2(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL B
// and MultiSynth 1
// and produces the output on CLK1
////////////////////////////////////////////////////////////////////////

void si5351aSetFrequency2(uint32_t frequency){
  uint32_t pllFreq;
  uint32_t xtalFreq = XTAL_FREQ;
  uint32_t l;
  float f;
  uint8_t mult;
  uint32_t num;
  uint32_t denom;
  uint32_t 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 B with the calculated  multiplication ratio
  setupPLL(MSNB_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(MS2_ADDR, divider, R_DIV_1);
                                          // Reset the PLL. This causes a glitch in the output. For small changes to
                                          // the parameters, you don't need to reset the PLL, and there is no glitch
//  Si5351_write(PLL_RESET, 0x80);
                                          // Finally switch on the CLK1 output
                                          // and set the MultiSynth0 input to be PLL B
  Si5351_write(CLK2_CTRL, 0x6F | CLK_SRC_PLL_B);    // Strength 8mA
}

スケッチ

BFO出力とLOwerヘテロダインに対応。BITX40の設定は、仕様周波数で記述してあるので必要であれば、実周波数に変更する程度で良いだろう。ただ、BITX40実機を持ってないので未確認。

//////////////////////////////////////////////////////////////////////////////
//       Copyright©2016.JA2GQP.All rights reserved.
//            si5351a PLL VFO Ver1.101      << JA2NKD sketch is based >>      
//                                                    2017/6/3
//                                                    JA2GQP  
//                      << Aruduino IDE 1.8.2 >>
//
//----------------------------------------------------------------------------  
//  Function
//    1.RIT Operation(-10kHZ to 10kHZ)
//    2.STEP(10,100,1k,10k)
//    3.Memory Operation is Push RIT
//    4.Protection Operation At The Time Of Transmission
//    5.Memory 4ch(LSB,USB.CW,AM)
//    6.IF shift
//----------------------------------------------------------------------------  
//  Library
//          <Rotary.h>    https://github.com/brianlow/Rotary
//          "Ucglib.h"    https://github.com/olikraus/ucglib
//
//////////////////////////////////////////////////////////////////////////////

//---------- include Files ---------------

#include "si5351a2.h"
#include <SPI.h>
#include <Rotary.h>
#include <EEPROM.h>
#include "Ucglib.h"

//----------   TFT setting  ---------------

  Ucglib_ST7735_18x128x160_HWSPI ucg(/*sclk= 13, data= 11, */ /*cd=*/ 9 , /*cs=*/ 10, /*reset=*/ 8);

//----------   Encorder setting  ---------------

#define   ENC_A       2                   // Encorder A
#define   ENC_B       3                   // Encoeder B

Rotary r=Rotary(ENC_A,ENC_B);

//----------   I/O setting  -------------------

#define   modeout1    7                   // DIO7
#define   modeout2    12                  // DIO12
#define   modesw      6                   // DIO6
#define   stepsw      4                   // DIO4
#define   ritsw       5                   // DIO5
#define   txsw        A3                  // A3
#define   s_meter     A0                  // A0
#define   t_meter     A1                  // A1

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

#define   Frq_Eep     0x00                // Frequency(4byte*4)
#define   Stp_Eep     0x10                // STEP(4byte*4)
#define   Chn_Eep     0x20                // Channel(1byte*1)
#define   Mode_Eep    0x22                // Mode(1byte*1)
#define   Eep_Int     0x2e                // Eep Init(1byte*1)

#define   Max_Chn     4                   // Max Channel(4ch)
#define   Int_End     73                  // Initial end code

//----------- Default Frequency Value --------------------

#define   UPPER      0                    // Upoper heterodyne
#define   LOWER      1                    // Lower heterodyne

////////////////////////////////////////////////////////////////
//  BITX40 Setting example(Upper heterodyne)      No.1
////////////////////////////////////////////////////////////////


#define   DEF_FRQ    7050000L             // Init Frequency
#define   DEF_FMAX   7200000L             // Frequency Max
#define   DEF_FMIN   7000000L             //           Min
#define   DEF_RLSB   10698500L            // RX IF Shift LSB
#define   DEF_RUSB   10701500L            //             USB
#define   DEF_RCW    10699200L            //             CW
#define   DEF_RAM    10700000L            //             AM
#define   DEF_TLSB   10698500L            // TX IF Shift LSB
#define   DEF_TUSB   10701500L            //             USB
#define   DEF_TCW    10700000L            //             CW
#define   DEF_TAM    10700000L            //             AM

byte   hetero = UPPER;                         // Lower heterodyne set


////////////////////////////////////////////////////////////////
//  BITX40 Setting example(Lower heterodyne)      No.2
////////////////////////////////////////////////////////////////

/*
#define   DEF_FRQ    7050000L             // Init Frequency
#define   DEF_FMAX   7200000L             // Frequency Max
#define   DEF_FMIN   7000000L             //           Min
#define   DEF_RLSB   11998500L            // RX IF Shift LSB
#define   DEF_RUSB   12001500L            //             USB
#define   DEF_RCW    11999400L            //             CW
#define   DEF_RAM    12000000L            //             AM
#define   DEF_TLSB   11998500L            // TX IF Shift LSB
#define   DEF_TUSB   12001500L            //             USB
#define   DEF_TCW    12000000L            //             CW
#define   DEF_TAM    12000000L            //             AM

byte   hetero = LOWER;                    // Lower heterodyne set
*/

//----------- Default ETC. Value --------------------

#define   DEF_STP    100L                 // Init STEP
#define   DEF_Mode   0                    // 0=LSB 1=USB 2=CW 3=AM

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

long freq    = DEF_FRQ;                   // Frequency data
long freqb;                               //           old data
long freqmax = DEF_FMAX;                  // VFO Upper Limit
long freqmin = DEF_FMIN;                  //     Lower Limit
long freqold = 0;
long freqrit = 0;
String freqt=String(freq);
long ifshift = 0;
long ifshiftb;
long ifshiftLSB = DEF_RLSB;               // RX IF Shift LSB
long ifshiftUSB = DEF_RUSB;               //             USB
long ifshiftCW = DEF_RCW;                 //             CW
long ifshiftAM = DEF_RAM;                 //             AM
long txshiftLSB = DEF_TLSB;               // TX IF Shift LSB
long txshiftUSB = DEF_TUSB;               //             USB
long txshiftCW = DEF_TCW;                 //             CW
long txshiftAM = DEF_TAM;                 //             AM
long vfofreq = 0;                         // VFO data
long vfofreqb;                            //     old
char f100m,f10m,fmega,f100k,f10k,f1k,f100,f10,f1;
int vfostep=2;
int rit=0;
int fstep = DEF_STP;                      // Default Step
int fmode;
int fmodeold=1;
int flagrit=0;
int fritold=0;
int flagmode=0;
int smeterval1=0;
int tmeterval=0;
byte Byt_Chn;                             // Channel SW
byte Byt_Chnb;                            //            Old

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

void setup() {
  Wire.begin();                

  delay(100);
  ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  ucg.clearScreen();
  ucg.setRotate90();

  pinMode (stepsw,INPUT_PULLUP);
  pinMode (ritsw,INPUT_PULLUP);
  pinMode(txsw,INPUT_PULLUP);
  pinMode(modesw,INPUT_PULLUP);
  pinMode(modeout1,OUTPUT);
  pinMode(modeout2,OUTPUT);

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

  screen01();

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

  Byt_Chn = EEPROM.read(Chn_Eep);       // Channel
  Byt_Chnb = Byt_Chn;                    //
  Fnc_Eep_Rd();                          // EEPROM Read

  modeset();                             // modeset * 4 times
  modeset();
  modeset();
  modeset();

  steplcd();
  freqt=String(freq);
  freqlcd();
 }

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

void loop() {
  if (digitalRead(stepsw)==LOW){setstep();}
  if (digitalRead(modesw)==LOW){modeset();}
  if (digitalRead(ritsw)==LOW){setrit();Fnc_Eep_Wt(Byt_Chn);}
  if (digitalRead(txsw)==LOW){txset();}

    if(Byt_Chnb != Byt_Chn){             // CH SW OLD != NEW?
      Fnc_Eep_Wt(Byt_Chnb);
      Byt_Chnb = Byt_Chn;
      Fnc_Eep_Rd();
      steplcd();
    }
     
  if (flagrit==1){
    if (freqrit == fritold){
      smeter();
    }  

    if (freqrit!=fritold){
      Vfo_write();
      ritlcd();
      fritold=freqrit;
    }
  }
  else{
    if (freq == freqold){
        smeter();
    }
    Vfo_write();
    freqt=String(freq);
    freqlcd();
    freqold=freq;
  }
}

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

void Fnc_Eep_Int(){
  int i;

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

  for(i=0;i<Max_Chn;i++){
    Fnc_Eep_Sav4(DEF_FRQ,Frq_Eep+i*4);            // Frequency(7.10MHz)
    Fnc_Eep_Sav4(DEF_STP,Stp_Eep+i*4);            // Step(100Hz)
  }

  EEPROM.write(Chn_Eep,0);
  EEPROM.write(Mode_Eep,DEF_Mode);
  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))
    freq = Fnc_Eep_Lod4(Frq_Eep+Byt_Chn*4);
  else{
    freq = Fnc_Eep_Lod4(Frq_Eep+0);
    Byt_Chn = 0;
  }

  if((0 <= Byt_Chn) && (Byt_Chn < Max_Chn))
    fstep = Fnc_Eep_Lod4(Stp_Eep+Byt_Chn*4);
  else
    fstep = Fnc_Eep_Lod4(Stp_Eep+0);
 
  fmode = EEPROM.read(Mode_Eep);
}

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

void Fnc_Eep_Wt(byte chn){
  if((0 <= chn) && (chn < Max_Chn)){
    Fnc_Eep_Sav4(freq,Frq_Eep+chn*4);
    Fnc_Eep_Sav4(fstep,Stp_Eep+chn*4);
  }

  EEPROM.write(Chn_Eep,Byt_Chn);
  EEPROM.write(Mode_Eep,fmode);
}

//----------  Function Save EEPROM 4byte  --------  

void Fnc_Eep_Sav4(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 Load EEPROM 4byte  ---------  

long Fnc_Eep_Lod4(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;
}

//----------  VFO out  ---------------      

void Vfo_out(long frequency){
  if(vfofreq != vfofreqb){
    si5351aSetFrequency(frequency);
    vfofreqb = vfofreq;
  }
}

//----------  BFO out  ---------------      

void Bfo_out(long frequency){
  if(ifshift != ifshiftb){
    si5351aSetFrequency2(frequency);
    ifshiftb = ifshift;
  }
}

//---------- S-meter --------------------------

void smeter(){
 smeterval1=analogRead(s_meter);
 smeterval1=smeterval1/50;
 if (smeterval1>14){smeterval1=14;}     //15
  int sx1=sx1+(smeterval1*9);           //16
  sx1=sx1+21;                           //41
  int sx2=0;
  sx2=sx2+(40+((15-smeterval1)*9));     //ucg_font_6x10_mf
  ucg.setFont(ucg_font_fub20_tn);       //ucg_font_fub35_tn
  ucg.setColor(0,0,0);                  // BLACK
  ucg.drawBox(sx1,99,sx2,8);
  ucg.setPrintPos(20,109);              //40,200
  for(int i=1;i<=smeterval1;i++){
    if (i<=8){
      ucg.setColor(0,255,255);
      ucg.print("-");
    }
    else{
      ucg.setColor(255,0,0);
      ucg.print("-");
    }
  }
}

//---------- Transmission Power meter ------------------

void tmeter(){
 ucg.setColor(0,0,0);                   // BLACK
 ucg.drawBox(20,99,135,8);              //41,180,270,16
 tmeterval=analogRead(t_meter);
 tmeterval=tmeterval/50;
 if (tmeterval>14){tmeterval=14;}
  int sx1=sx1+(tmeterval*9);
  sx1=sx1+21;
  int sx2=0;
  sx2=sx2+(40+((15-tmeterval)*9));
  ucg.setFont(ucg_font_fub20_tn);       //35ucg_font_fub20_tn
  ucg.setColor(0,0,0);                  //BLACK
  ucg.drawBox(sx1,78,sx2,8);
  ucg.setPrintPos(20,90);               //85(40,165)
  for(int i=1;i<=tmeterval;i++){
    if (i<=9){
      ucg.setColor(250,80,0);
      ucg.print("-");
    }
    else{
      ucg.setColor(250,0,0);
      ucg.print("-");
    }
  }
}

//---------- Encoder Interrupt -----------------------

ISR(PCINT2_vect) {
  if (flagrit==1){
  unsigned char result = r.process();
    if(result) {
      if(result == DIR_CW){
        freqrit=freqrit+fstep;
        if (freqrit>=10000){
          freqrit=10000;
        }
      }
     else{
        freqrit=freqrit-fstep;
        if (freqrit<=-10000){
          freqrit=-10000;
        }
     }
    }
  }

  else{
    unsigned char result = r.process();
      if(result) {
        if(result == DIR_CW){
          freq=freq+fstep;
          if (freq>=freqmax){freq=freqmax;}
        }
        else{
          freq=freq-fstep;
          if (freq<=freqmin){freq=freqmin;}
        }
     }
  }

}

//------------ On Air -----------------------------

void txset(){
  long vfofreq_RX = vfofreq;;
  long ifshift_RX = ifshift;;
//  noInterrupts();
  if(hetero == UPPER){                          // Upper heterodyne
    if (flagmode==0){ifshift=txshiftLSB;}
    if (flagmode==1){ifshift=txshiftUSB;}
    if (flagmode==2){ifshift=txshiftCW;}
    if (flagmode==3){ifshift=txshiftAM;}
    vfofreq=freq+ifshift;
  }
  else{                                                  // Lower heterodyne
    if (flagmode==0){ifshift=txshiftLSB;}
    if (flagmode==1){ifshift=txshiftUSB;}
    if (flagmode==2){ifshift=txshiftCW;}
    if (flagmode==3){ifshift=txshiftAM;}
    vfofreq=ifshift-freq;
  }

  Vfo_out(vfofreq);                               // vfo out
  Bfo_out(ifshift);                                 // BFO out
 
  ucg.setPrintPos(70,75);                      //140,140
  ucg.setFont(ucg_font_6x10_mf);        //17ucg_font_fub11_tn
  ucg.setColor(255,0,0);
  ucg.print("ON AIR");
  while(digitalRead(txsw) == LOW){
    tmeter();
  }
  vfofreq = vfofreq_RX;
  ifshift = ifshift_RX;

  ucg.setColor(0,0,0);
  ucg.drawBox(15,63,95,15);                   //30,120,250,30 // ON AIR Erase
  Vfo_write();
  ucg.drawBox(20,80,135,8);                   //41,145,270,16  // Level Erase
//    interrupts();
}

//------------- Mode change(LSB-USB-CW-AM) ------------

void modeset(){
  ucg.setFont(ucg_font_6x10_mf);  //17ucg_font_fub11_tn
//  if (fmode==0){                          
  if (fmode==1){                              // 2016/8/3
    ifshift=ifshiftUSB;
    flagmode = 1;                             // 2016/8/3
    ucg.setColor(255,255,0);                  //Black?
    ucg.setPrintPos(44,43);                   //(82,82
    ucg.print("USB");
    ucg.setPrintPos(9,43);                    //12,82
    ucg.setColor(0,0,0);                      //Black Yellow
    ucg.print("LSB");  
    digitalWrite(modeout1,HIGH);
    digitalWrite(modeout2,LOW);  
  }

//  if(fmode==1){                          
  if(fmode==2){                                // 2016/8/3
    ifshift=ifshiftCW;
    flagmode = 2;                             // 2016/8/3
    ucg.setPrintPos(9,60);                    //12,112
    ucg.setColor(255,255,0);                  //Yellow
    ucg.print("C W");
    ucg.setPrintPos(44,43);                   //82,82
    ucg.setColor(0,0,0);                      //Black
    ucg.print("USB");
    digitalWrite(modeout1,LOW);
    digitalWrite(modeout2,HIGH);
  }

//  if (fmode==2){                          
  if (fmode==3){                              // 2016/8/3
    ifshift=ifshiftAM;
    flagmode = 3;                             // 2016/8/3
    ucg.setPrintPos(44,60);                   //82,112
    ucg.setColor(255,255,0);                  //Yellow
    ucg.print("A M");
    ucg.setColor(0,0,0);                      //Black  
    ucg.setPrintPos(9,60);                    //12,112
    ucg.print("C W");
    digitalWrite(modeout1,HIGH);
    digitalWrite(modeout2,HIGH);
    }

//  if (fmode==3){                          
  if (fmode==0){                              // 2016/8/3
    ifshift=ifshiftLSB;
    flagmode = 0;                             // 2016/8/3
    ucg.setPrintPos(9,43);                    //12,82
    ucg.setColor(255,255,0);                  //Yellow
    ucg.print("LSB");
    ucg.setPrintPos(44,60);                   //82,112
    ucg.setColor(0,0,0);                      //Black
    ucg.print("A M");
    digitalWrite(modeout1,LOW);
    digitalWrite(modeout2,LOW);
  }

  fmode=fmode+1;

  Byt_Chn++;                              
  if(Byt_Chn > 3)            
    Byt_Chn = 0;

  if (fmode==4){fmode=0;}
  Vfo_write();
  while(digitalRead(modesw) == LOW);
}

//------------ Rit SET ------------------------------

void setrit(){
  if(flagrit==0){
    flagrit=1;
    ucg.setFont(ucg_font_6x10_mf);            //11ucg_font_fub11_tn
    ucg.setPrintPos(95,55);                   //190,110
    ucg.setColor(255,0,0);
//    freqrit=0;
    ritlcd();
  }
  else {
    flagrit=0;

    if(hetero == UPPER)
      vfofreq=freq+ifshift;
    else
      vfofreq=ifshift-freq;
    Vfo_out(vfofreq);                       // vfo Out

    freqt=String(freq);
    ucg.setFont(ucg_font_6x10_mf);          //11ucg_font_fub11_tn
    ucg.setPrintPos(95,55);                 //190,110
    ucg.setColor(255,255,255);
   // ucg.print("RIT");
    ucg.setColor(0,0,0);
    ucg.drawRBox(112,52,44,10,1);           //222,92,91,21,3
    freqrit=0;
  }
  while(digitalRead(ritsw) == LOW);
}

//----------- Rit screen ----------------------

void ritlcd(){
  noInterrupts();
  ucg.setColor(0,0,0);
  ucg.drawBox(111,51,45,13);              //222,92,91,21 110,50,47,15,
  ucg.setFont(ucg_font_6x10_mf);          //17 ucg_font_fub11_tn
  ucg.setColor(0,255,255);
  ucg.setPrintPos(115,61);                //230,110
  ucg.print(freqrit);
  interrupts();
}

//-------------- encorder frequency step set -----------

void setstep(){
  noInterrupts();
  if (fstep==10000){
    fstep=10;
  }
  else{
    fstep=fstep * 10;
  }

 steplcd();
 while(digitalRead(stepsw) == LOW);
 interrupts();
}

//------------- Step Screen ---------------------------

void steplcd(){
  ucg.setColor(0,0,0);                    //0,0,0
  ucg.drawRBox(111,33,45,11,1);           //221,61,93,23,3
  ucg.setFont(ucg_font_6x10_mf);          //17ucg_font_fub11_tn
  ucg.setColor(255,255,255);
  ucg.setPrintPos(110,43);                //220,80
  if (fstep==10000){ucg.print("  10KHz");}
  if (fstep==1000){ucg.print("   1KHz");}
  if (fstep==100){ucg.print("  100Hz");}
  if (fstep==10){ucg.print("  10Hz");}
}

//----------- Main frequency screen -------------------

void freqlcd(){
  ucg.setFont(ucg_font_osr18_tn);         //35 ucg_font_fub20_tn
  int mojisuu=(freqt.length());

  if(freq<1000000){
    ucg.setColor(0,0,0);
    ucg.drawBox(52,7,9,20);               //103,9,15,36
    }
  if(f100k !=(freqt.charAt(mojisuu-6))){
    ucg.setColor(0,0,0);
    ucg.drawBox(59,7,15,20);              //118,9,28,36
    ucg.setPrintPos(59,24);               //118,45
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-6));
    f100k = (freqt.charAt(mojisuu-6));
  }
 
  if(freq<100000){
    ucg.setColor(0,0,0);
    ucg.drawBox(59,7,15,20);              //118,9,28,36
  }
  if(f10k !=(freqt.charAt(mojisuu-5))){
    ucg.setColor(0,0,0);
    ucg.drawBox(73,6,15,20);              //146,9,28,36
    ucg.setPrintPos(73,24);               //146,45
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-5));
    f10k = (freqt.charAt(mojisuu-5));
  }
 
  if(freq<10000){
    ucg.setColor(0,0,0);
    ucg.drawBox(73,6,14,19);              //146,9,28,36
    }
  if(f1k !=(freqt.charAt(mojisuu-4))){
    ucg.setColor(0,0,0);
    ucg.drawBox(87,6,14,19);              //174,9,28,36
    ucg.setPrintPos(87,24);               //(174,45
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-4));    
    f1k  = (freqt.charAt(mojisuu-4));
  }

  if(freq>=1000){
    ucg.setPrintPos(101,24);              //202,45
    ucg.setColor(0,255,0);
    ucg.print(".");
  }

  if(freq<1000){
    ucg.setColor(0,0,0);
    ucg.drawBox(101,6,8,19);              //   (202,9,15,36
    }
  if(f100 !=(freqt.charAt(mojisuu-3))){
    ucg.setColor(0,0,0);
    ucg.drawBox(109,6,14,19);             //217,9,28,36
    ucg.setPrintPos(108,24);              //(215,45
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-3));
    f100 = (freqt.charAt(mojisuu-3));
  }

  if(freq<100){
    ucg.setColor(0,0,0);
    ucg.drawBox(109,6,14,19);             //217,9,28,36
  }
  if(f10 !=(freqt.charAt(mojisuu-2))){
    ucg.setColor(0,0,0);
    ucg.drawBox(123,6,14,19);           //245,9,28,36
    ucg.setPrintPos(123,24);            //(245,45
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-2));
    f10 = (freqt.charAt(mojisuu-2));
  }
/*
  if(freq<10){
    ucg.setColor(0,0,0);
    ucg.drawBox(245,9,28,36);  
     }
  if(f1 !=(freqt.charAt(mojisuu-1))){
    ucg.setColor(0,0,0);
    ucg.drawBox(273,9,28,36);
    ucg.setPrintPos(273,45);
    ucg.setColor(0,255,0);
    ucg.print(freqt.charAt(mojisuu-1));  
    f1  = (freqt.charAt(mojisuu-1));
  }
*/
}

//----------- Basic Screen -------------------------

void screen01(){
  ucg.setColor(255,255,255);            //
  ucg.drawRFrame(0,0,160,28,3);         //0,0,320,55,5 OUTside
  //ucg.drawRFrame(0,0,159,27,2);       //1,1,318,53,5  INside
  ucg.setColor(0,128,255);              //FILL COLOR for MODE back
  ucg.drawRBox(2,33,30,12,1);           //5,60,60,25,3  LSB
  ucg.drawRBox(37,33,30,12,1);          //75,60,60,25,3 USB
  ucg.drawRBox(2,50,30,12,1);           //5,90,60,25,3  CW
  ucg.drawRBox(37,50,30,12,1);          //(75,90,60,25,3  AM
  ucg.setFont(ucg_font_6x10_mf);        //17
  ucg.setPrintPos(9,43);                //12,82
  ucg.setColor(0,0,0);
  ucg.print("LSB");
  ucg.setPrintPos(44,43);               //82,82
  ucg.print("USB");
  ucg.setPrintPos(9,60);                //12,112
  ucg.print("C W");
  ucg.setPrintPos(44,60);               //(82,112
  ucg.print("A M");
  ucg.setColor(255,255,255);
  ucg.drawRFrame(110,32,47,15,1);       //220,60,95,25,3 STEP frame
  ucg.drawRFrame(110,50,47,15,1);       //220,90,95,25,3 RIT frame
  ucg.setColor(255,255,100);            //100,100,100
  ucg.setPrintPos(7,105);               //15,200
  ucg.print("S:");
  ucg.setPrintPos(7,85);                //15,165
  ucg.print("P:");
  ucg.setFont(ucg_font_6x10_mf);        //11ucg_font_fub11_tn
  ucg.setColor(250,255,0);
  ucg.setPrintPos(80,43);               //175,80
  ucg.print("STEP");
  ucg.setPrintPos(80,60);               //190,110
  ucg.setColor(255,255,0);
  ucg.print("RIT");
  ucg.setColor(255,255,255);            //100,100,100
  ucg.setPrintPos(20,115);              //40,210
  ucg.print("1--3---6---9--Over---");
  ucg.setPrintPos(20,95);               //40,175
  ucg.print("1--3---5-----10------");
  ucg.setPrintPos(5,127);               //10,230
  ucg.setColor(235,0,200);
  ucg.print("si5351a VFO Ver1.1 JA2GQP" );

  ucg.setFont(ucg_font_osr18_tn);       //35 ucg_font_fub20_tf???
    ucg .setPrintPos(37,24);            //75,45
    ucg.setColor(0,255,0);
    ucg.print("7");                     //Frequency
    ucg.setPrintPos(51,24);             //103,45
    ucg.print(".");
    ucg.setPrintPos(138,24);            //273,45
    ucg.print("0");                     //Frequency  end
}

//--------------- VFO Write -------------------------------

void Vfo_write(){
  if (flagrit==0){
    if(hetero == UPPER)
      vfofreq=freq+ifshift;
    else
      vfofreq=ifshift-freq;
    Vfo_out(vfofreq);                               // DDS out
  }

  if(flagrit==1){
    if(hetero == UPPER)
      vfofreq=freq+ifshift+freqrit;
    else
      vfofreq=ifshift-freqrit-freq;
    Vfo_out(vfofreq);                              // DDS out
  }
  Bfo_out(ifshift);                                // BFO
}



      

2017年5月26日金曜日

si5351a phase out

以前から気になっていたのが、si5351aを使った位相出力である。身近にあるArduino IDE位相出力のスケッチは、Etherkitのサンプルがある。その他、Web検索しても一部分の公開が有る物の、参考になるスケッチを見つけられなかった。
そこで、位相出力に特化した考え方の、TJ Labのコードを使用させて頂く事にした。スケッチの動作試験環境は、si5351VFOのH/Wを流用した。

試験に使ったH/Wの写真。無改造で使用している。









回路図。













スケッチ

ch0とch1が位相差90度出力を行う。出力範囲は、7.0MHz~7.2MHzに制限した。
特化した機能なので、通常のVFOとして使用できないであろう。割り込み処理が異なるので、ロータリエンコーダ処理とI/O割付を替えれば、stm32duinoでも動作可能。
ファイルは、JA2GQP's Download siteのsi5351aフォルダ。

//////////////////////////////////////////////////////////////////////
//  si5351 phase out(ch0,ch1) VFO program ver.1.0
//    Copyright(C)2017.JA2GQP.All rights reserved.
//
//                                Arduino IDE 1.8.2 Compiled                                  
//
//                                                2017/5/26
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(1k,100,10)
//    2.Frequency limit(7.00MHz - 7.20MHz)
//////////////////////////////////////////////////////////////////////

#include <LCD5110_Basic.h>
#include <Rotary.h>
#include <Wire.h>


//////////////////////////
// Si5351A
//////////////////////////
#define   Si5351A_ADDR  0x60
#define   CLK0_CTRL   16
#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


//////////////////////////
// system clock
//////////////////////////
const unsigned long XtalFreq = 25000000;


//////////////////////////
// I/O assign
//////////////////////////
#define   ENC_A   2                 //Rotary encoder A
#define   ENC_B   3                 //Rotary encoder B
#define   SW_STEP 4                 // Step SW


//////////////////////////
// Register set
//////////////////////////
const long LOW_FREQ = 7000000;    // lower frequency limit
const long HI_FREQ = 7200000;     // upper frequency limit
unsigned long FREQ = 7000000;     // default frequency
unsigned long FREQ_OLD = FREQ;    // old frequency
int STEP = 1000;                  // STEP(default)


//////////////////////////
// LCD assign
//////////////////////////
LCD5110 myGLCD(8,9,10,11,12);    // SCK,MOSI,DC,RST,CS

extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];


//////////////////////////
// Rotal encorder
//////////////////////////
Rotary r = Rotary(ENC_A,ENC_B); //ENC_A=2,ENC_B=3


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

void setup()
{
  Wire.begin();                
  si5351_init();                
  freq_set(FREQ);            
  myGLCD.InitLCD();                 // nokia5110 initialyze
  pinMode(SW_STEP,INPUT_PULLUP);    // STEP SW pullup

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                            // int enable

  myGLCD.setFont(SmallFont);
  myGLCD.print("STEP",12,32);
  myGLCD.print(".",12,16);
  myGLCD.print(".",54,16);
  freq_disp();
  step_disp();
}

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

void loop(){
  if(digitalRead(SW_STEP) == LOW){step_set();}

  if(FREQ != FREQ_OLD){
    freq_set(FREQ);
    freq_disp();
    FREQ_OLD = FREQ;          
  }
  delay(10);
}

//---------- rotaly encorder int proc. ------------

ISR(PCINT2_vect){
  unsigned char result = r.process();
  if(result){
    if(result == DIR_CW){
      FREQ = FREQ + STEP;
    }
    else{
      FREQ = FREQ - STEP;
    }
  }
  FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ);  
}

//---------- step set -----------

void step_set(){
  if(STEP == 10){
    STEP = 1000;
  }
  else{
    STEP= STEP / 10;
  }
  delay(10);
  step_disp();
  while(digitalRead(SW_STEP) == LOW){
    delay(10);
  }
}

//---------- frequency display -----------

void freq_disp(){
  myGLCD.setFont(MediumNumbers);
  long freqH = FREQ / 1000000;
  myGLCD.printNumI(freqH,0,8);
  long freqL = FREQ - freqH * 1000000;
  long fM = freqL /1000;
  int fL = (freqL - fM * 1000) / 10;
  myGLCD.printNumI(fM,18,8,3,'0');
  myGLCD.printNumI(fL,60,8,2,'0');
}

//---------- step display  ----------

void step_disp(){
  myGLCD.setFont(SmallFont);
  myGLCD.printNumI(STEP,12,40,4,' ');
}

//---------- si5351a register write 1 byte----------

void cmd_si5351(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

//---------- si5351A initialyze ----------

void si5351_init(){
  cmd_si5351(XTAL_LOAD_C,0b10010010);   // CL=8pF
  cmd_si5351(CLK0_CTRL,0x80);           // Disable CLK0
  cmd_si5351(CLK1_CTRL,0x80);           // Disable CLK1
  cmd_si5351(PLL_RESET,0xA0);           // Reset PLL_A
  cmd_si5351(CLK0_CTRL,0x4F);           // Enable CLK0 (MS0=Integer Mode, Source=PLL_A)
  cmd_si5351(CLK1_CTRL,0x4F);           // Enable CLK1 (MS1=Integer Mode, Source=PLL_A)
}

//---------- set frequency ----------

void freq_set(unsigned long freq){
//    freq [Hz]
//
//    fvco= fxtal*(a+b/c)  ( a:15 -- 90,   b:0 -- 1048575, c:1 -- 1048575 )
//    freq= fvco /(a+b/c)  ( a:4, 6--1800, b:0 -- 1048575, c:1 -- 1048575 )
//
//    P1= 128*a +   floor(128*b/c) - 512
//    P2= 128*b - c*floor(128*b/c)
//    P3= c
//

  int k;
  unsigned long M;
  unsigned int R;

  if(freq<1500) freq=1500; else if(freq>280000000) freq=280000000;

  if(     freq> 150000000){M=4; R=0;}
  else if(freq>=63000000){M=6; R=0;}
  else if(freq>=27500000){M=14; R=0;}
  else if(freq>=13000000){M=30; R=0;}
  else if(freq>= 6500000){M=62; R=0;}
  else if(freq>= 3000000){M=126; R=0;}
  else if(freq>= 1500000){M=280; R=0;}
  else if(freq>=  700000){M=600; R=0;}
  else if(freq>=  330000){M=1280; R=0;}
  else if(freq>=  150000){M=1300; R=1;}
  else if(freq>=   67000){M=1500; R=2;}
  else if(freq>=   30300){M=1600; R=3;}
  else if(freq>=   14000){M=1800; R=4;}
  else if(freq>=    7000){M=1800; R=5;}
  else if(freq>=    3500){M=1800; R=6;}
  else{M=1800; R=7;}

  freq*=M;
  freq<<=R;

  unsigned long c=0xFFFFF;
  unsigned long a=freq/XtalFreq;
  unsigned long b=(long)((double)(freq-a*XtalFreq)*(double)c/(double)XtalFreq);
  unsigned long dd=(128*b)/c;
  unsigned long P1=128*a+dd-512;
  unsigned long P2=128*b-c*dd;
  unsigned long P3=c;


  //Set fvco of PLL_A
    cmd_si5351(MSNA_ADDR+0,(P3>>8)&0xFF);        //MSNA_P3[15:8]
    cmd_si5351(MSNA_ADDR+1,P3&0xFF);             //MSNA_P3[7:0]
    cmd_si5351(MSNA_ADDR+2,(P1>>16)&0x03);       //MSNA_P1[17:16]
    cmd_si5351(MSNA_ADDR+3,(P1>>8)&0xFF);        //MSNA_P1[15:8]
    cmd_si5351(MSNA_ADDR+4,P1&0xFF);              //MSNA_P1[7:0]
    cmd_si5351(MSNA_ADDR+5,(P3>>12)&0xF0|(P2>>16)&0x0F);//MSNA_P3[19:16], MSNA_P2[19:16]
    cmd_si5351(MSNA_ADDR+6,(P2>>8)&0xFF);        //MSNA_P2[15:8]
    cmd_si5351(MSNA_ADDR+7,P2&0xFF);             //MSNA_P2[7:0]

  // Set MS0, MS1
  // a=M, b=0, c=1 ---> P1=128*M-512, P2=0, P3=1
  if(M==4){
    P1=0;
    cmd_si5351(MS0_ADDR+0,0);                   //MS0_P3[15:8]
    cmd_si5351(MS0_ADDR+1,1);                   //MS0_P3[7:0]
    cmd_si5351(MS0_ADDR+2,0b00001100);          //0,R0_DIV[2:0],MS0_DIVBY4[1:0],MS0_P1[17:16]
    cmd_si5351(MS0_ADDR+3,0);                   //MS0_P1[15:8]
    cmd_si5351(MS0_ADDR+4,0);                   //MS0_P1[7:0]
    cmd_si5351(MS0_ADDR+5,0);                   //MS0_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS0_ADDR+6,0);                   //MS0_P2[15:8]
    cmd_si5351(MS0_ADDR+7,0);                   //MS0_P2[7:0]
 
    cmd_si5351(MS1_ADDR+0,0);                   //MS1_P3[15:8]
    cmd_si5351(MS1_ADDR+1,1);                   //MS1_P3[7:0]
    cmd_si5351(MS1_ADDR+2,0b00001100);          //0,R1_DIV[2:0],MS1_DIVBY4[1:0],MS1_P1[17:16]
    cmd_si5351(MS1_ADDR+3,0);                   //MS1_P1[15:8]
    cmd_si5351(MS1_ADDR+4,0);                   //MS1_P1[7:0]
    cmd_si5351(MS1_ADDR+5,0);                   //MS1_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS1_ADDR+6,0);                   //MS1_P2[15:8]
    cmd_si5351(MS1_ADDR+7,0);                   //MS1_P2[7:0]
  }else{
    P1=128*M-512;
    cmd_si5351(MS0_ADDR+0,0);                    //MS0_P3[15:8]
    cmd_si5351(MS0_ADDR+1,1);                    //MS0_P3[7:0]
    cmd_si5351(MS0_ADDR+2,(R<<4)&0x70|(P1>>16)&0x03);//0,R0_DIV[2:0],MS0_DIVBY4[1:0],MS0_P1[17:16]
    cmd_si5351(MS0_ADDR+3,(P1>>8)&0xFF);        //MS0_P1[15:8]
    cmd_si5351(MS0_ADDR+4,P1&0xFF);              //MS0_P1[7:0]
    cmd_si5351(MS0_ADDR+5,0);                    //MS0_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS0_ADDR+6,0);                    //MS0_P2[15:8]
    cmd_si5351(MS0_ADDR+7,0);                    //MS0_P2[7:0]
 
    cmd_si5351(MS1_ADDR+0,0);                    //MS1_P3[15:8]
    cmd_si5351(MS1_ADDR+1,1);                    //MS1_P3[7:0]
    cmd_si5351(MS1_ADDR+2,(R<<4)&0x70|(P1>>16)&0x03);//0,R1_DIV[2:0],MS1_DIVBY4[1:0],MS1_P1[17:16]
    cmd_si5351(MS1_ADDR+3,(P1>>8)&0xFF);        //MS1_P1[15:8]
    cmd_si5351(MS1_ADDR+4,P1&0xFF);              //MS1_P1[7:0]
    cmd_si5351(MS1_ADDR+5,0);                    //MS1_P3[19:16], MS0_P2[19:16]
    cmd_si5351(MS1_ADDR+6,0);                    //MS1_P2[15:8]
    cmd_si5351(MS1_ADDR+7,0);                    //MS1_P2[7:0]
  }
  cmd_si5351(CLK0_PHOFF,0);
  cmd_si5351(CLK1_PHOFF,M);

  cmd_si5351(PLL_RESET,0xA0);                 // Reset PLL_A.
}