2021年8月1日日曜日

oled CB VFO

oled 50MHz AM VFOのCB版。VFO/チャンネルの2モード方式は、50MHz AM版と全く同じである。違いは、CBの場合、指定された周波数のみでの運用である。そのため、VFO周波数が指定された周波数であるか否かをチェックしなければならない。50MHzバンド外と同様に、送信時に--.---.--と表示される。EEPROMにデータ保存しているので、電源off後、直前の状態に復帰する。




操作

・EEPROM初期化

  STEP SWを押しながら、電源onして”ーー.---.--” が表示されるまでSTEP SWを押していると、EEPROMの初期化が行われる。

・VFOモード(上の写真は、チャンネルモード)

 STEP SWを長押しすると、VFOモードからチャンネルモード、または、チャンネルモードからVFOモードに変わる。

チャンネル書き換え

 チャンネルモードの時、RIT SWを長押しし、VFOモードを表示させ希望の周波数に変更する。再び、RIT SWを長押しするとチャンネルモードに表示が変わり、変更されたチャンネルデータで表示される。書き換え可能なチャンネルは、”0”、”9”のみである。チャンネル”1”から”8”は指定周波数なので書き換え不可。


回路図


   










スケッチ

コンパイルする時、必要に応じマーカ部分の変更をする。

・RX_IF = 455000 受信時のオフセット。初期値 455000(+455kHz)

・TX_IF = 0 送信時のオフセット。初期値 0

・Int_End = 49 プログラムからの初期化を行う時の、初期化終了判断コード。

残っている前回データをクリアして、新しい初期値をセットしたいときに使う。前回の判断コードと同じ場合は、初期化されないので注意。


必要なファイルは、JA2GQP Download siteのsi5351 VFOフォルダからダウンロード出来る。


//////////////////////////////////////////////////////////////////////

//  si5351a oled(128x32) VFO program ver.1.0

//    Copyright(C)2019.JA2GQP.All rights reserved.

//

//                                                2021/7/29

//                                                  JA2GQP

//--------------------------------------------------------------------

//  Function

//    1.VFO/Channel mode

//    2.STEP(10k,1k)

//    3.Automatic memory

//    4.Protection Operation At The Time Of Transmission

//    5.S-Meter

//////////////////////////////////////////////////////////////////////


#include "src/Rotary.h"               // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html

#include "src/si5351.h"               // https://github.com/etherkit/Si5351Arduino, v2.1.0

#include "src/SSD1306AsciiAvrI2c.h"   // https://github.com/greiman/SSD1306Ascii

#include <EEPROM.h>



////////////////////////////////

// Set Device 

////////////////////////////////

Rotary r = Rotary(2, 3);              

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

SSD1306AsciiAvrI2c oled;


////////////////////////////////

// I/O Port

////////////////////////////////

const byte SW_RIT = A0;               // RIT SW

const byte SW_STEP = A1;              // STEP SW

const byte SW_TX = A2;                // TX/RX

const byte AD_SM = A7;                // S-meter AD


////////////////////////////////

// EEPROM Memory Address

////////////////////////////////

const byte  Eep_Init = 0x00;          // Eep Init(1byte*1)

const byte  Eep_Band = 0x01;          // Band(1byte*1)

const byte  Eep_Ch = 0x02;            // Channel(1byte*1)

const byte  Eep_Chan = 0x03;          // VFO-Channel Mode(1byte*1)

const byte  Eep_Freq = 0x10;          // Frequency(4byte*5)

const byte  Eep_Step = 0x30;          // STEP(4byte*5)

const long  Eep_Chfreq = 0x50;        // Channel(4byte*5)


////////////////////////////////

// frquency data

////////////////////////////////

const unsigned long FRQ_TBL[5] = {

 // DEF_F    CW_F      SSB_F     AM_F      HI_F

  26968000 ,26515000 ,26515000 ,26515000 ,27855000

  };


const unsigned long CH_TBL[] = {          // Initial frequency value

  27524000 ,26968000 ,26976000 ,27040000 ,27080000 ,

  27088000 ,27112000 ,27120000 ,27144000 ,27005000

  };

const byte  Max_Chan = sizeof(CH_TBL) / sizeof(CH_TBL[0]);  // Max Channel


//---- data offset -----

const byte DEF_F = 0;

const byte CW_F = 1;

const byte SSB_F = 2;

const byte AM_F = 3;

const byte HI_F  = 4; 

//---- IF offset -------

const unsigned long RX_IF = 455000  // RX_IF offset = +455kHz 

const unsigned long TX_IF = 0;        // TX_IF offset 


////////////////////////////////

// Encorder STEP

////////////////////////////////

const int STEP_50 = 50;               // STEP 50Hz

const int STEP_1k = 1000;             //      1k

const int STEP_10k = 10000;           //      10k


////////////////////////////////

// etc

////////////////////////////////

const byte  Int_End = 49;             // Initial end code

const char Call[9] = "JA2GQP";        // Display Call sign


////////////////////////////////

// Memory Assign

////////////////////////////////

unsigned long Vfo_Dat;                // VFO Data

unsigned long Ch_Dat;                 // Channel Data

int Rit_Dat;                          // RIT Data

unsigned long Enc_Step;               // STEP

char Enc_Dir = 0;                     // -1 DIR_CCW, 0 DIR_NONE, 1 DIR_CCW

byte Ch_Index = 0;                    // Channel TBL index

unsigned int Val_Smeter = 0;

unsigned int Val_Smeterb = 1;


long Time_Passd;                      // int to hold the arduino miilis since startup

long Time_Mode;                       // Mode change Time

byte Flg_eepWT = 0;                   // EEP Write Flag


byte Flg_Mode = 3;

byte Flg_Over = 0;

byte Flg_Vfo = 0; 

byte Flg_Tx = 0;

byte Flg_Chan = 0;                    // Channel Flag 0:VFO, 1:Cannel

byte Disp_Over = 1;

byte Disp_Freq = 1;

byte Disp_Tx = 0;

byte Disp_Rit = 0;

byte Flg_Rit = 0;                     // RIT Flag


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


void setup() {

  pinMode(SW_STEP, INPUT_PULLUP);

  pinMode(SW_TX, INPUT_PULLUP);          

  pinMode(SW_RIT, INPUT_PULLUP);          

  pinMode(12, OUTPUT);                // pin12,pin13

  pinMode(13, OUTPUT);                // 0,0=CW 1,0=LSB 0,1=USB 1,1=AM           


  attachInterrupt(0, rotary_encoder, CHANGE);

  attachInterrupt(1, rotary_encoder, CHANGE);


  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);    // crystal 25.000 MHz, correction 0

  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set 

  oled.begin(&Adafruit128x32, 0x3C);


  if(EEPROM.read(Eep_Init) != Int_End){        // Eep initialaz

    delay(10);

    eep_init();

  }

  else{

    if(digitalRead(SW_STEP) == LOW){

      delay(10);

      eep_init();

      fover_disp();

      while (digitalRead(SW_STEP) == LOW);

    }

  }

  eep_rdata();

  mode_disp();

  step_disp();

}


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


void loop() {

  if(Flg_Chan == 0){

    vfo_mode();

  }

  if(Flg_Chan == 1){

    ch_mode();

  }

}


//----------  Channel mode  ------------------------------------------------


void ch_mode(){


////////////////////////

// receive

////////////////////////

  if(digitalRead(SW_TX) == HIGH){           

    Flg_Tx = 0;

    si5351.output_enable(SI5351_CLK0, 1);   // VFO enable 


    if (Flg_Rit == 0){

      if(Disp_Freq == 1){

        if((Ch_Index == 0) && (Enc_Dir == -1))

          Ch_Index = Max_Chan-1;

        else if((Ch_Index == Max_Chan-1) && (Enc_Dir == 1))  

          Ch_Index = 0;

        else

          Ch_Index += Enc_Dir;


        Enc_Dir = 0;


        Ch_Dat = eep_read4(Eep_Chfreq+Ch_Index*4);  // Channel data read

        band_check(Ch_Dat);                         // range check

        si5351_set_freq(Ch_Dat + RX_IF);            // frequency out

        ch_disp(Ch_Dat);                            // Channel display

        Disp_Freq = 0;


        Flg_Vfo = 1;

        mode_disp();

        Rit_Dat = 0;


        Time_Passd = millis();

        Flg_eepWT = 1;

      }

    }      

    else{

      if (Disp_Rit == 1){

        Rit_Dat += Enc_Dir * Enc_Step;

        Enc_Dir = 0;

        Rit_Dat = constrain(Rit_Dat, -5000, 5000);

        si5351_set_freq(Ch_Dat + RX_IF + Rit_Dat);

        rit_disp(Rit_Dat);

        mode_disp();

        Disp_Rit = 0;

        Flg_Vfo = 1;

      }

    }   


////////////////////////

// STEP

////////////////////////

    if (digitalRead(SW_STEP) == LOW) {      // increment events

      Time_Mode = millis();        


      while (digitalRead(SW_STEP) == LOW){

        if(Time_Mode+1000 < millis()){

          if(Flg_Chan == 0){                  // VFO mode?

            Flg_Chan = 1;                     //  yes,Channel mode set

            break;

          }  

          else{                               //  no,VFO mode

            Flg_Chan = 0;                   

            freq_disp(Vfo_Dat);               //  frequency display

            si5351_set_freq(Vfo_Dat + RX_IF); //  fequency data output

            Flg_Rit = 0;                      //  RIT reset

            Disp_Freq = 0;

            break;

          }

        }

      }


      while (digitalRead(SW_STEP) == LOW);

      if(Flg_Chan == 1){

        enc_step();

        step_disp();

      }

    }


////////////////////////

// RIT

////////////////////////

    if (digitalRead(SW_RIT) == LOW) {

      Time_Mode = millis();

      while (digitalRead(SW_RIT) == LOW){


        if((Ch_Index == 0) || (Ch_Index == 9)){

          if(Time_Mode+1000 < millis()){

            if(Flg_Chan == 1){

              Flg_Chan = 0;

 

              Vfo_Dat = Ch_Dat;

              freq_disp(Vfo_Dat);

              si5351_set_freq(Vfo_Dat + RX_IF);

              Flg_Rit = 0;

              break;

            }

          }

        }

        

      }


      while (digitalRead(SW_RIT) == LOW);

      if(Flg_Chan == 1){

        rit_disp(Rit_Dat);

        Fnc_Rit(); 

      }

    }

    Disp_Tx = 1;

  }


////////////////////////

// send

////////////////////////

  else{                                     

    Flg_Tx = 1;

    Disp_Over = 1;


    if(Flg_Over == 0){                      // Within transmittable range?

        freq_disp(Ch_Dat);                  // frequency display

      si5351.output_enable(SI5351_CLK0, 1); // VFO enable

      if(Flg_Vfo == 1){

        si5351_set_freq(Ch_Dat + TX_IF);

        Flg_Vfo = 0;

      }

      Disp_Freq = 1;

      Disp_Rit = 1;

    }

    else{                                   // Out of range

      si5351.output_enable(SI5351_CLK0, 0); // VFO disable 

      if(Disp_Over == 1){

        fover_disp();                       // Display of over range

        Disp_Over = 0;

      }

    }

    if(Disp_Tx == 1){

      tx_disp();

      Disp_Tx = 0;        

    }

    Disp_Freq = 1;  

  }


////////////////////////

// common

////////////////////////

  Val_Smeter = analogRead(AD_SM);

  if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter

    sm_disp();

    Val_Smeterb = Val_Smeter;

  }


  if(Flg_eepWT == 1){                       // EEPROM auto Write

    if(Time_Passd+2000 < millis()){

      eep_wdata();

      Flg_eepWT = 0; 

    }

  }

}   


//----------  VFO mode  ------------------------------------------------


void vfo_mode(){


////////////////////////

// receive

////////////////////////

  if(digitalRead(SW_TX) == HIGH){           

    Flg_Tx = 0;

    si5351.output_enable(SI5351_CLK0, 1);   // VFO enable 


    if (Flg_Rit == 0){

      if(Disp_Freq == 1){

        Vfo_Dat += Enc_Dir * Enc_Step;

        Enc_Dir = 0;

        

        si5351_set_freq(Vfo_Dat + RX_IF);

        freq_disp(Vfo_Dat);                  // frequency display

        Disp_Freq = 0;


        Flg_Vfo = 1;

        mode_disp();

        Rit_Dat = 0;


        Time_Passd = millis();

        Flg_eepWT = 1;

      }

    }      

    else{

      if (Disp_Rit == 1){

        Rit_Dat += Enc_Dir * Enc_Step;

        Enc_Dir = 0;

        Rit_Dat = constrain(Rit_Dat, -5000, 5000);

        si5351_set_freq(Vfo_Dat + RX_IF + Rit_Dat);

        rit_disp(Rit_Dat);

        mode_disp();

        Disp_Rit = 0;

        Flg_Vfo = 1;

      }

    }   


////////////////////////

// STEP

////////////////////////

    if (digitalRead(SW_STEP) == LOW) {      // increment events

      Time_Mode = millis();


      while (digitalRead(SW_STEP) == LOW){

        if(Time_Mode+1000 < millis()){

          if(Flg_Chan == 0){

            Flg_Chan = 1;

            ch_disp(Ch_Dat);

            si5351_set_freq(Ch_Dat + RX_IF);

            eep_wdata();

            Flg_Rit = 0;

            break;

          }  

          else{

            Flg_Chan = 0;

            break;

          }

        }

      }

      

      while (digitalRead(SW_STEP) == LOW);

      if(Flg_Chan == 0){

        enc_step();

        step_disp();

      }

    }


////////////////////////

// RIT

////////////////////////

    if (digitalRead(SW_RIT) == LOW) {

      Time_Mode = millis();

      while (digitalRead(SW_RIT) == LOW){

        if(Time_Mode+1000 < millis()){

          if(Flg_Chan == 0){

              eep_write4(Vfo_Dat,Eep_Freq);

              Ch_Dat = Vfo_Dat;

              ch_disp(Ch_Dat);

              eep_write4(Ch_Dat,Eep_Chfreq+Ch_Index*4);

              si5351_set_freq(Ch_Dat + RX_IF);

              Flg_Rit = 0;

              Flg_Chan = 1;

              break;

          }

        }

      }


      while (digitalRead(SW_RIT) == LOW);

      if(Flg_Chan == 0){

        rit_disp(Rit_Dat);

        Fnc_Rit(); 

      }

    }

    Disp_Tx = 1;

  }


////////////////////////

// send

////////////////////////

  else{                                     

    Flg_Tx = 1;

    Disp_Over = 1;

    band_check(Vfo_Dat);                        // range check


    

    if(Flg_Over == 0){                      // Within transmittable range?

        freq_disp(Vfo_Dat);                 // frequency display

      si5351.output_enable(SI5351_CLK0, 1); // VFO enable

      if(Flg_Vfo == 1){

        si5351_set_freq(Vfo_Dat + TX_IF);

        Flg_Vfo = 0;

      }

      Disp_Freq = 1;

      Disp_Rit = 1;

    }

    else{                                   // Out of range

      si5351.output_enable(SI5351_CLK0, 0); // VFO disable 

      if(Disp_Over == 1){

        fover_disp();                       // Display of over range

        Disp_Over = 0;

      }

    }

    if(Disp_Tx == 1){

      tx_disp();

      Disp_Tx = 0;        

    }

    Disp_Freq = 1;  

  }


////////////////////////

// common

////////////////////////

  Val_Smeter = analogRead(AD_SM);

  if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter

    sm_disp();

    Val_Smeterb = Val_Smeter;

  }


  if(Flg_eepWT == 1){                       // EEPROM auto Write

    if(Time_Passd+2000 < millis()){

      eep_wdata();

      Flg_eepWT = 0; 

    }

  }

}   


//----------  Function Rit  --------------------------------------------


void Fnc_Rit(){

  if(Flg_Rit == 0){

    Rit_Dat = 0;

    Flg_Rit = 1;

  }

  else{

    Flg_Rit = 0;

    Disp_Freq = 1;

  }


//----------  RIT Display  ---------------------------------------------


void rit_disp(int rit_data) {

  unsigned int ri,rit;


  oled.setFont(lcdnums14x24);

  oled.setCursor(1, 0);

  oled.print("::::");


  if (rit_data < 0)

    oled.print('-');

  else

    oled.print('+');


  rit = abs(rit_data);

   ri = rit / 1000;  

  oled.print(ri);

  oled.print('.');

  ri = (rit % 1000) / 10;

  if (ri < 10)

    oled.print('0');

  oled.print(ri);

}


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


void eep_write4(long value,int address){

  address += 3;

  for(int i = 0;i < 4;i++){

    byte toSave = value & 0xFF;

    if(EEPROM.read(address) != toSave){

      EEPROM.write(address,toSave);

      }

    value = value >> 8;

    address--;

  }

}


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


long eep_read4(int address){

  long value = 0;


  for(int i = 0;i < 4;i++){

    value = value | EEPROM.read(address);

    if( i < 3){

      value = value << 8;

      address++;

    }

  }

  return value;

}


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


void eep_rdata(){

  Vfo_Dat = eep_read4(Eep_Freq);

  Enc_Step = eep_read4(Eep_Step);

  Ch_Index = EEPROM.read(Eep_Ch);

  Flg_Chan = EEPROM.read(Eep_Chan);

  Ch_Dat = eep_read4(Eep_Chfreq+Ch_Index*4);

}


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


void eep_wdata(){

  eep_write4(Vfo_Dat,Eep_Freq);

  eep_write4(Enc_Step,Eep_Step);

  EEPROM.write(Eep_Ch,Ch_Index);            // Ch_Index set  

  EEPROM.write(Eep_Chan,Flg_Chan);          // VFO-Channel 0:VFO 1:Channel  

  eep_write4(Ch_Dat,Eep_Chfreq+Ch_Index*4);

}  

    

//----------  EEPROM Initialization ------------------------------------


void eep_init(){

  int i;


  for (i=0;i<128;i++)                       // 0 clear(128byte)

    EEPROM.write(i, 0);


  eep_write4(FRQ_TBL[DEF_F],Eep_Freq);

  eep_write4(STEP_1k,Eep_Step);            // Step(1kHz)

  EEPROM.write(Eep_Ch,Ch_Index);           // Ch_Index set  

  EEPROM.write(Eep_Chan,Flg_Chan);         // Ch_Index set  


  for(i=0;i<Max_Chan;i++)

    eep_write4(CH_TBL[i],Eep_Chfreq+i*4);  


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

}


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


void rotary_encoder() {                     // rotary encoder events

  uint8_t result = r.process();


  if(Flg_Tx == 0){

    if (result) {

      Disp_Freq = 1;

      Disp_Rit = 1;

      if (result == DIR_CW)

        Enc_Dir = 1;

      else

        Enc_Dir = -1;

    }

  }

}


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


void si5351_set_freq(uint32_t frequency) {

  si5351.set_freq(frequency * SI5351_FREQ_MULT, SI5351_CLK0);

}


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


void enc_step() {

  if(Enc_Step == STEP_1k)

    Enc_Step = STEP_10k;                    // 10k

  else    

    Enc_Step = STEP_1k;                     // 1k

}


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


void step_disp() {

  oled.setCursor(109, 3);

  oled.setFont(labels);

  if (Enc_Step == STEP_50)

    oled.print("6");                        // 50Hz

  else if(Enc_Step == STEP_1k)

    oled.print("8");                        // 1k

  else

    oled.print("9");                        // 10k

}


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


void fover_disp() {

  oled.setFont(lcdnums14x24);

  oled.setCursor(1, 0);

  oled.print("--.---.--");

}


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


void call_disp() {

  oled.setFont(Arial_bold_14);

  oled.setCursor(56, 6);

  oled.print(Call);

}


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


void freq_disp(uint32_t sf_rx) {

  uint16_t fr;


  oled.setFont(lcdnums14x24);

  oled.setCursor(1, 0);

  fr = sf_rx / 1000000;

  if (fr < 10)

    oled.print(':');                        // ':' is changed to ' ' in lcdnums14x24.h

  oled.print(fr);

  oled.print('.');

  fr = (sf_rx % 1000000) / 1000;

  if (fr < 100)

    oled.print('0');

  if (fr < 10)

    oled.print('0');

  oled.print(fr);

  oled.print('.');

  fr = (sf_rx % 1000) / 10;

  if (fr < 10)

    oled.print('0');

  oled.print(fr);

}


//----------  Channel Display  -----------------------------------------


void ch_disp(uint32_t sf_rx) {

  uint16_t fr;


  oled.setFont(lcdnums14x24);

  oled.setCursor(1, 0);

  oled.print(Ch_Index);


  oled.print("::");                        // ':' is changed to ' ' in lcdnums14x24.h


  fr = sf_rx / 1000000;

  if (fr < 10)                            

    oled.print(':');                      // ':' is changed to ' ' in lcdnums14x24.h

  oled.print(fr);


  oled.print('.');

  fr = (sf_rx % 1000000) / 1000;

  if (fr < 100)

    oled.print('0');

  if (fr < 10)

    oled.print('0');

  oled.print(fr);


}


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


void tx_disp() {

  oled.setCursor(2, 3);

  oled.setFont(labels);

  oled.print("1");                          // "1" is "TX" in labels.h

}


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


void mode_disp() {

  oled.setCursor(2, 3);

  oled.setFont(labels);

  if (Flg_Mode == 0){

    oled.print("2");                        // "2" is "CW" in labels.h

//    oled.print("/");                        // "/" is "AM" in labels.h

    digitalWrite(12,LOW);

    digitalWrite(13,LOW);

  }

  else if(Flg_Mode == 1){

    oled.print("3");                        // "3" is "LSB"

    digitalWrite(12,HIGH);

    digitalWrite(13,LOW);

  }

  else if(Flg_Mode == 2){

    oled.print("4");                        // "4" is "USB"

    digitalWrite(12,LOW);

    digitalWrite(13,HIGH);

  }

  else if(Flg_Mode == 3){

    oled.print("/");                        // "/" is "AM"

    digitalWrite(12,HIGH);

    digitalWrite(13,HIGH);

  }

}


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


void sm_disp() {

  uint8_t a = 0;

  uint8_t m = 0;


  a = (Val_Smeter + 3) / 113;  // 1024 / 9 characters for S = 1,3,5,7,8,9,+10,+20,+30

  oled.setFont(pixels);

  oled.setCursor(25, 3);

  for (m = 0; m < a; m++)

    if (m < 6)

      oled.print('7');                      // '5' - hollow rectangle, 6px

    else

      oled.print('8');                      // '6' - filled rectangle, 6px

  for (m = a; m < 9; m++)

    oled.print('.');                        // '.' 1px

}


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


void band_check(unsigned long freq){

  Flg_Over = 1;

  for(int i=1;i<9;i++){

    if(freq == CH_TBL[i]){

      Flg_Over = 0;

      break;

    }

  }

  Flg_Mode = 3;

}