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
}