2021年7月25日日曜日

oled 50MHz AM VFO

表示器にoledを使った50MHz AM VFOである。50MHz AMを運用すると、周波数の連続可変は殆ど必要ない。そこで、チャンネル機能を含めたVFOを作った。特徴は、VFO/チャンネルの2モードで、共にRIT運用が可能。VFO/チャンネルはEEPROMに保存しいてる。電源off直前のVFO/チャンネルを保存してるので、電源on時の煩雑さは無い。VFO/チャンネルの切替は、STEPスイッチの長押しで行う。また、チャンネル周波数は、EEPROMに保存してるので、書き換え可能。


チャンネルモードの表示である。チャンネル切替は、エンコーダ操作。チャンネル周波数の初期値以外への変更は、RITスイッチを長押しし、VFOモードに移行して変更。











回路図


         

 


   




フォント     

今回、追加したAMフォントである。labels.hを変更修正。









スケッチ

ファイルは、JA2GQP's Download site si5351 VFOフォルダのoled_50MHz_AM.zipをダウンロードすれば良い。

//////////////////////////////////////////////////////////////////////
//  si5351a oled(128x32) VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2021/7/24
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.VFO/Channel mode
//    2.STEP(10k,1k,50)
//    3.Automatic memory
//    4.Protection Operation At The Time Of Transmission
//    5.Automatic mode switching
//    6.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
  50600000 ,50000000 ,50200000 ,50500000 ,51000000
  };

const unsigned long CH_TBL[] = {          // Initial frequency value
  50500000 ,50550000 ,50600000 ,50620000
  };
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 = 0;        // RX_IF offset 
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 = 73;             // 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;
byte Flg_Over;
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(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;
        band_check(Vfo_Dat);                        // range check
        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;

    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_50) {
    Enc_Step = STEP_1k;                     // 1000 Hz, round to XX.XXX.000
    float tmp = round (Vfo_Dat / (STEP_1k * 1.000));
    Vfo_Dat = (uint32_t)(tmp * STEP_1k);
    Disp_Freq = 1;
  }
  else if(Enc_Step == STEP_1k)
    Enc_Step = STEP_10k;                    // 10k
  else    
    Enc_Step = STEP_50;                     // 50 Hz
}

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

void step_disp() {
  oled.setCursor(109, 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("/");                        // "4" is "USB"
    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){
  if((freq >= FRQ_TBL[CW_F])
      && (freq < FRQ_TBL[SSB_F])){
    Flg_Mode = 0;                             // CW(0=CW, 1=LSB, 2=USB, 3=AM)    
    Flg_Over = 0;
  }
  else if((freq >= FRQ_TBL[SSB_F])
      && (freq < FRQ_TBL[AM_F])){
    if(freq >= 10000000)                      // greate than 10MHz                        
      Flg_Mode = 2;                           // USB(0=CW, 1=LSB, 2=USB, 3=AM)
    else
      Flg_Mode = 1;                           // LSB(0=CW, 1=LSB, 2=USB, 3=AM)
    Flg_Over = 0;
  }
  else if((freq >= FRQ_TBL[AM_F])
      && (freq < FRQ_TBL[HI_F])){
    Flg_Mode = 3;                             // AM(0=CW, 1=LSB, 2=USB, 3=AM)
    Flg_Over = 0;
  }
  else
    Flg_Over = 1;
}   






        

      

    


2021年7月14日水曜日

NE602 AM Module PCB Version

このNE602 AM Moduleは、si5351などのローカル発振器と組み合わせて、AMを発生させるモジュールである。先に公開したNE602 AM ModuleのPCB化したものである。PCB化に伴って、定数の見直を一部した。何時もながら、PCBサイズ50x50に収めた。リード部品を使っているので、組み立ても容易であろう。



回路図










    



レギュレータに78L08を使って、TA2011とNE602に供給している。MSA0886は13.8Vに接続している。当初、8V単一電源を考えたが、MSA0886を含めると100mAを超える為、2電圧とした。

部品面実装シルク

PCBにシルクが入れてあるが、4面とした時に何らかのミスを犯したようだ。部品番号が1面のみ入っているが、3面は部品番号が入ってない。確認の意味でシルク図を書いておく。


ガーバーデータ      

今回のPCBガーバーデータの4面付け PCBサイズ 99.06x99.06 公開。部品番号のシルクが1面しかない不具合を改修したものを公開。4面付けのガーバーデータは、JA2GQP's Download siteのNE602フォルダからダウンロード可能。

調整

VR1、VR2でマイクゲインの調整。VR3でキャリアレベルを調整。

キャリアレベルは、パワー計を接続して音声入力無しの時にVR3を回して最大出力の1/4に調整する。

動作確認

受信機でハウリング音を聴いた時、音に濁りが無く、歪みが無いことを確認する。澄んだ音になるまで、VR1,VR3を調整する。調整後、7mW~10mWほどのAM出力が得られる。





       

2021年6月30日水曜日

NE602 AM Modulation

NE602を使った、AM変調の確認を50MHz帯で行った。AF AMPにTA2011、LOにsi5351、RF AMPにMSA0886とした。
ユニバーサル基板で組んだので、動作確認のために余分な部品が付いている。
当初、LOに水晶発振子50.62MhzをVXOして、50.60MHzで使う予定だった。VXOする事で、周波数は合っているものの、ニアバイが有り、期待した音質が得られなかった。しかし、水晶発振子周辺の定数の見直しをすれば問題ない筈だ。
今回の目的は、LOの確認ではないので、VXO定数選定に時間を割くことなく、PLL si5351を使う事にした。

回路図

レギュレータ7808は、負荷電流20~30mAのため、100mAシリーズで問題ない。キャリアレベルは、NE602 pin1に繋がっているVRで最大キャリア(音声無入力で最大出力)の1/4位にセットすれば良い。出力は、50MHzで約7mWのAMを得ることができる。バッファAMPなしで、RQA0009をドライブできるであろう。


スケッチ

動作確認のためのスケッチです。周波数は50.60Mhz、出力は4mA設定し4mW出力にしてます。この設定で、問題なくAM変調されている事は確認しましたが、最適値であるかは不問。

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

//  si5351a NE602 AM test program ver.1.0

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

//

//                                                2021/6/30

//                                                  JA2GQP

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


#include "si5351.h"

#include "Wire.h"


Si5351 si5351;


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


void setup(){

  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);

                                        // SI5351_DRIVE_2MA : 1mW 

                                        // SI5351_DRIVE_4MA : 4mW  

                                        // SI5351_DRIVE_6MA : 9mW  

                                        // SI5351_DRIVE_8MA : 15mW  

  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA); 


                                        // frequency 50.60Mhz set

  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);

  si5351.set_freq(5060000000ULL, SI5351_PLL_FIXED, SI5351_CLK0);

}


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


void loop(){

}

   

2021年5月29日土曜日

OPC-2350LU互換ユニット

ID-31をPCと通信できるユニットである。ICOMの純正は、OPC-2350LUでありFTDI製チップを使用。AliexpressでFTI232RLを使ったTTLユニットを販売している。そこで、FTI232RL TTLユニットと232Cレベルコンバータと組合せ、OPC-2350LU互換ユニットにした。



回路図


3Pプラグ
2.5φ3Pプラグを使ってID-31と繋ぐが、プラグが直径7mm以下でなければならない。市販の2.5φ3pプラグは、直径8mmであった。このため、2.5φプラグ(直径6mm)ー3.5φソケット変換と3.5φプラグー3.5φプラグのケーブルを組み合わせて使う事にした。写真上は、3.5φプラグー3,5φプラグ。写真下は、3,5φソケットー2.5φプラグである。    
       




 






   




2021年5月4日火曜日

ESP8266 Multi encorder

ESP8266を使って、Multi encorder(2個)の動作確認を行った。割込みでencorderを読み取る事とし、Rotary.hを使った。Rotary.hは、バージョンによる違いが有るので、比較的新しいバージョンを使った。



動作確認用なので、結果はモニタ表示する事にした。

ESP8266固有の所は、

void ICACHE_RAM_ATTR enc1_proc();

void ICACHE_RAM_ATTR enc2_proc();

である。





スケッチ

Rotaly.hを含め、スケッチはJA2GQP's Download siteのesp8266フォルダからダウンロードできる。

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

//  ESP8266 Multi encorder test

//                                              2010/5/4

//                                              JA2GQP

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


#include "src/Rotary.h"


Rotary r1 = Rotary(D3,D4);               // encorder 1

void ICACHE_RAM_ATTR enc1_proc();


Rotary r2 = Rotary(D1,D2);               // encorder 2

void ICACHE_RAM_ATTR enc2_proc();


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


void setup() {

  Serial.begin(115200);

  r1.begin(0);

  attachInterrupt(digitalPinToInterrupt(D3), enc1_proc, CHANGE);

  attachInterrupt(digitalPinToInterrupt(D4), enc1_proc, CHANGE);


  r2.begin(1);

  attachInterrupt(digitalPinToInterrupt(D1), enc2_proc, CHANGE);

  attachInterrupt(digitalPinToInterrupt(D2), enc2_proc, CHANGE);

}


//---------- main loop ----------------------------------------


void loop() {

}


//----------  encorder 1 proc ---------------------------------


void enc1_proc() {                     // rotary encoder events

  unsigned char result = r1.process();

  if (result == DIR_NONE) {

    // do nothing

  }

  else if (result == DIR_CW) {

    Serial.println("Enc 1 CW");

  }

  else if (result == DIR_CCW) {

    Serial.println("Enc 1 CCW");

  }

}


//----------  encorder 2 proc ---------------------------------


void enc2_proc() {                     // rotary encoder events

  unsigned char result = r2.process();

  if (result == DIR_NONE) {

    // do nothing

  }

  else if (result == DIR_CW) {

    Serial.println("Enc 2 CW");

  }

  else if (result == DIR_CCW) {

    Serial.println("Enc 2 CCW");

  }

}   


    




     



2020年10月24日土曜日

si4735 oled改造

PU2CLRのAll in one OLED表示タイプを改造した。H/Wは公開済みのベースボードを使い、スケッチを書いた。実用性を高めるため、SSB時に使うBFO操作を見直した。基本的には、SSB時のSTEPと云った概念に仕上げた。AMのSTEPは、1K、5K、10kの3段階。SSBのSTEPは、50Hz、1kの2段階にした。メモリ機能は、周波数、BFO、ボリュームとし、モード、バンドは除いた。

回路図

公開済みのベースボード回路を一部変更した。
・BFOのSW8は、未使用。
・音質改善のため、AF AMP入力部を変更。

BFO

STEPをFMなし、AM3段階、SSB2段階とした。SSB時のBFO設定は、STEPと云った概念としたので操作上は存在しない。ただ、S/W上BFOが存在しているが、何も意識することはない筈だ。

スケッチ

JA2GQP's Download site2のsi4735フォルダから必要なファイルがダウンロード可能。

/////////////////////////////////////////////////////////////////////
//  si4735 DSP Radio program(PU2CLR all in one OLE based) Ver1,01
//
//                                              2020/10/22
//                                                JA2GQP        
//-------------------------------------------------------------------
//  Ver1.01 Eep initialaz(STEP SW)
/////////////////////////////////////////////////////////////////////

#include <SI4735.h>
#include "src/SSD1306AsciiAvrI2c.h"   // https://github.com/greiman/SSD1306Ascii
#include "src/Rotary.h"
#include <EEPROM.h>

#include "src/patch_init.h" // SSB patch for whole SSBRX initialization string

const uint16_t size_content = sizeof ssb_patch_content; // see ssb_patch_content in patch_full.h or patch_init.h

#define FM_BAND_TYPE 0
#define MW_BAND_TYPE 1
#define SW_BAND_TYPE 2
#define LW_BAND_TYPE 3

// OLED Diaplay constants
#define I2C_ADDRESS 0x3C
#define RST_PIN -1 // Define proper RST_PIN if required.

#define RESET_PIN 7

// Enconder PINs
#define ENCODER_PIN_A     2
#define ENCODER_PIN_B     3

// Buttons controllers
#define STEP_SWITCH       8       // STEP(1, 5 or 10 KHz)
#define BFO_SWITCH        9       // BFO(BFO or VFO)
#define VOL_DOWN          10      // Volume Down
#define VOL_UP            11      //        Up
#define BAND_BUTTON_DOWN  12      // Band Down
#define BAND_BUTTON_UP    14      //      Up
#define AGC_SWITCH        15      // AGC ON/OF
#define BANDWIDTH_BUTTON  16      // banddwith(1.2, 2.2, 3.0, 4.0, 0.5, 1.0 KHz)
#define MODE_SWITCH       17      // MODE (Am/LSB/USB)

#define MIN_ELAPSED_TIME 100
#define MIN_ELAPSED_RSSI_TIME 150

#define DEFAULT_VOLUME 40 // change it for your favorite sound volume

#define FM 0
#define LSB 1
#define USB 2
#define AM 3
#define LW 4

#define SSB 1
////////////////////////////////
//  default value
////////////////////////////////
//#define DEF_AM          1431          // Gifu radio
//#define DEF_FM          7630          // FM pipi
#define DEF_VOL           40            // volume
#define DEF_B_IDX         0             // Band Index

////////////////////////////////
//  etc
////////////////////////////////
#define SIG_TIME        200
#define Int_End         73            // EEPROM init end

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
#define Eep_Top        0x00          // Eep Init(1byte)
#define Eep_End        Eep_Top       // Eep Init(1byte)
#define Eep_VOL        0x02          // Volume
#define Eep_Mode       0x04          // Mode(1byte)
#define Eep_B_IDX      0x06          // Band index
#define Eep_Bfo        0x08          // bfo data
//#define Eep_FM          0x02          // FM frequency(2byte)
//#define Eep_AM          0x04          // MF frequency(2byte)
#define Eep_Freq       0x10          // Frequency data

const char *bandModeDesc[] = {"FM ", "LSB", "USB", "AM "};
uint8_t currentMode = FM;

bool bfoOn = false;
bool disableAgc = true;
bool ssbLoaded = false;
bool fmStereo = true;

int currentBFO = 0;
byte Flg_eepWT = 0;
long elapsedRSSI = millis();
long elapsedButton = millis();
long Time_Passd;                      // int to hold the arduino miilis since startup

// Encoder control variables
volatile int encoderCount = 0;

// Some variables to check the SI4735 status
uint16_t currentFrequency;
uint16_t previousFrequency;
uint8_t currentStep = 1;
//uint8_t currentBFOStep = 25;
uint8_t currentBFOStep = 100;

uint8_t bwIdxSSB = 2;
const char *bandwitdthSSB[] = {"1.2", "2.2", "3.0", "4.0", "0.5", "1.0"};

uint8_t bwIdxAM = 0;
const char *bandwitdthAM[] = {"6", "4", "3", "2", "1", "1.8", "2.5"};

/*
   Band data structure
*/
typedef struct
{
  uint8_t bandType;     // Band type (FM, MW or SW)
  uint16_t minimumFreq; // Minimum frequency of the band
  uint16_t maximumFreq; // maximum frequency of the band
  uint16_t currentFreq; // Default frequency or current frequency
  uint16_t currentStep; // Defeult step (increment and decrement)
} Band;

////////////////////////////////
//   Band table
////////////////////////////////
Band band[] = {
  {FM_BAND_TYPE, 7600, 9500, 7630, 10},
//  {LW_BAND_TYPE, 100, 510, 300, 1},
  {MW_BAND_TYPE, 522, 1602, 1431, 9},
  {SW_BAND_TYPE, 1800, 3500, 1900, 1}, // 160 meters
  {SW_BAND_TYPE, 3500, 4500, 3535, 1}, // 80 meters
//  {SW_BAND_TYPE, 4500, 5500, 4850, 5},
//  {SW_BAND_TYPE, 5600, 6300, 6000, 5},
  {SW_BAND_TYPE, 6800, 7800, 7100, 1}, // 40 meters
//  {SW_BAND_TYPE, 9200, 10000, 9600, 5},
//  {SW_BAND_TYPE, 10000, 11000, 10100, 1}, // 30 meters
  {SW_BAND_TYPE, 10000, 11000, 10700, 1}, // 30 meters
//  {SW_BAND_TYPE, 11200, 12500, 11940, 5},
//  {SW_BAND_TYPE, 13400, 13900, 13600, 5},
  {SW_BAND_TYPE, 14000, 14500, 14200, 1}, // 20 meters
//  {SW_BAND_TYPE, 15000, 15900, 15300, 5},
//  {SW_BAND_TYPE, 17200, 17900, 17600, 5},
  {SW_BAND_TYPE, 18000, 18300, 18100, 1},  // 17 meters
  {SW_BAND_TYPE, 21000, 21900, 21200, 1},  // 15 mters
  {SW_BAND_TYPE, 24890, 26200, 24940, 1},  // 12 meters
  {SW_BAND_TYPE, 26200, 27900, 27500, 1},  // CB band (11 meters)
  {SW_BAND_TYPE, 28000, 30000, 28400, 1},
}; 

const int lastBand = (sizeof band / sizeof(Band)) - 1;
byte bandIdx = 0;

uint8_t rssi = 0;
uint8_t stereo = 1;
uint8_t volume = 0;

////////////////////////////////
// Devices class declarations
////////////////////////////////
Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B);
SI4735 si4735;
SSD1306AsciiAvrI2c oled;

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

void setup()
{
  byte ee;
  byte ssbm = 0;
  
  // Encoder pins
  pinMode(ENCODER_PIN_A, INPUT_PULLUP);
  pinMode(ENCODER_PIN_B, INPUT_PULLUP);

  pinMode(BANDWIDTH_BUTTON, INPUT_PULLUP);
  pinMode(BAND_BUTTON_UP, INPUT_PULLUP);
  pinMode(BAND_BUTTON_DOWN, INPUT_PULLUP);
  pinMode(VOL_UP, INPUT_PULLUP);
  pinMode(VOL_DOWN, INPUT_PULLUP);
  pinMode(BFO_SWITCH, INPUT_PULLUP);
  pinMode(AGC_SWITCH, INPUT_PULLUP);
  pinMode(STEP_SWITCH, INPUT_PULLUP);
  pinMode(MODE_SWITCH, INPUT_PULLUP);

  oled.begin(&Adafruit128x64, 0x3C);

  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); // Encoder interrupt
  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE);

  si4735.getDeviceI2CAddress(RESET_PIN); // Looks for the I2C buss address and set it.  Returns 0 if error
  si4735.setup(RESET_PIN, FM_BAND_TYPE);
    
  delay(300);  

//  EEPROM.get(Eep_End, ee);
  if(digitalRead(STEP_SWITCH) == LOW){                  // Eep initialaz
    oled.setFont(font5x7);
    oled.setCursor(18, 4);
    oled.print("Initialization");
    while(digitalRead(STEP_SWITCH) == LOW)
      ;
    eep_init();
  }
  eep_rdata();
  
  useBand();  // Set up the radio for the current band (see index table variable bandIdx )
  previousFrequency = currentFrequency;

  si4735.setVolume(volume);
  showStatus();
}

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

void rotaryEncoder()
{ // rotary encoder events
  uint8_t encoderStatus = encoder.process();
  if (encoderStatus)
  {
    if (encoderStatus == DIR_CW)
    {
      encoderCount = 1;
    }
    else
    {
      encoderCount = -1;
    }
    Time_Passd = millis();
    Flg_eepWT = 1;
  }
}

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

void eep_rdata(){
  EEPROM.get(Eep_VOL, volume);
  EEPROM.get(Eep_B_IDX,bandIdx);
  EEPROM.get(Eep_Mode,currentMode);
  EEPROM.get(Eep_Freq, currentFrequency);
  band[bandIdx].currentFreq = currentFrequency;
  EEPROM.get(Eep_Bfo, currentBFO);
  si4735.setSSBBfo(currentBFO);
}

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

void eep_wdata(){
  EEPROM.put(Eep_Mode, currentMode);
  EEPROM.put(Eep_B_IDX, bandIdx);
  EEPROM.put(Eep_Bfo, currentBFO);
  EEPROM.put(Eep_Freq, currentFrequency);
}  

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

void eep_init(){
  for(int i=0;i<64;i++)
    EEPROM.put(Eep_Top + i, 0);

  EEPROM.put(Eep_Mode, FM);
  EEPROM.put(Eep_B_IDX, 0);
  EEPROM.put(Eep_VOL, 40);
  EEPROM.put(Eep_Bfo, 0);
  EEPROM.put(Eep_Freq, 7630);
  EEPROM.put(Eep_End, Int_End);
}

//----------  Volume lebel display  ------------------

void showVolume()
{
  static byte v_old=0;
  byte v_new;

  v_new = si4735.getCurrentVolume();
  
  if(v_old != v_new)
  {
    oled.setFont(font5x7);
    oled.setCursor(96, 6);
    oled.print("VOL  ");
    oled.setCursor(115, 6);
    oled.print(v_new);
    v_old = v_new;
  }
}

//----------  Frequency display  ---------------------

void showFrequency()
{
  unsigned long fr;
  unsigned long sf_rx;
 
  if(currentMode == FM)                                      // FM
    sf_rx = (float)currentFrequency * 10000.0;
  else if(currentMode == AM)                                 // AM
    sf_rx = (float)currentFrequency * 1000.0;
  else if((currentMode == LSB) || (currentMode == USB)){      // SSB
    sf_rx = ((float)currentFrequency * 1000.0) + currentBFO;
  }
  else
    sf_rx = (float)currentFrequency * 1000.0;

  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);
}

//----------  Status display  ------------------------

void showStatus()
{
  showFrequency();
  oled.setFont(font5x7);

  if(!bfoOn){
      oled.setCursor(109, 4);
      oled.print("   ");
      if ( currentMode != FM){
        oled.setCursor(109, 4);
        oled.print(currentStep);
        oled.print("k");
      }
  }
  else if(bfoOn){
      oled.setCursor(109, 4);
      oled.print("   ");
      oled.setCursor(109, 4);
      oled.print(currentBFOStep);
  }

  if (currentMode == LSB || currentMode == USB)
  {
      oled.setCursor(2, 6);
      oled.print("BW   ");
      oled.setCursor(14, 6);
      oled.print(String(bandwitdthSSB[bwIdxSSB]));
  }
  else if (currentMode == AM)
  {
      oled.setCursor(2, 6);
      oled.print("BW   ");
      oled.setCursor(14, 6);
      oled.print(String(bandwitdthAM[bwIdxAM]));
  }

  else if ( currentMode == FM) {
    oled.setCursor(2, 6);
    oled.print((si4735.getCurrentPilot()) ? "STE  " : "MON  ");
  }

  // Show AGC Information
  si4735.getAutomaticGainControl();
  oled.setCursor(50, 6);
  oled.print((si4735.isAgcEnabled()) ? "AGCon " : "AGCoff");

  showRSSI();
  showVolume();
}

//----------  RSSI(signal lebel)----------------------

void showRSSI(){
  char a = 0;
  unsigned char m = 0;
  float rssi_new;
  static float rssi_old = 100.0;
  static unsigned char mode_old = 255;
  unsigned char mode_new;
  String  bandMode;
         
/////////////////////////
//  MODE display
/////////////////////////
  if (currentFrequency < 520 )
    bandMode = "LW ";
  else
    bandMode = bandModeDesc[currentMode];

  oled.setFont(font5x7);
  mode_new = currentMode;
  if(mode_new != mode_old){
    oled.setCursor(2, 4);
    oled.print(bandMode);
    mode_old = mode_new;
  }

/////////////////////////
//  s-meter display
/////////////////////////
  rssi_new = si4735.getCurrentRSSI();
  if(rssi_new != rssi_old){
    a = (rssi_new - 15) / 3.0;           // 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
    if(a < 0)
      a = 0;
    else if(a > 9)
      a = 9;
    oled.setFont(pixels);
    oled.setCursor(25, 4);
    for (m = 0; m < a; m++)
      if (m < 6)
        oled.print('7');                      // '5' - hollow rectangle, 6px
      else
        oled.print('8');                      // '6' - filled rectangle, 6px
    for (m = a; m < 9; m++)
      oled.print('.');                        // '.' 1px
    rssi_old = rssi_new;
  }
}

//----------  BAND up  -------------------------------

void bandUp()
{
  // save the current frequency for the band
  band[bandIdx].currentFreq = currentFrequency;
  band[bandIdx].currentStep = currentStep;

  if (bandIdx < lastBand)
  {
    bandIdx++;
  }
  else
  {
    bandIdx = 0;
  }
  useBand();
}

//----------  BAND down  -----------------------------

void bandDown()
{
  // save the current frequency for the band
  band[bandIdx].currentFreq = currentFrequency;
  band[bandIdx].currentStep = currentStep;
  if (bandIdx > 0)
  {
    bandIdx--;
  }
  else
  {
    bandIdx = lastBand;
  }
  useBand();
}

//----------  LOAD SSB  ------------------------------

void loadSSB()
{
  si4735.reset();
  si4735.queryLibraryId(); // Is it really necessary here?  Just powerDown() maigh work!
  si4735.patchPowerUp();
  delay(50);
  // si4735.setI2CFastMode(); // Recommended
  si4735.setI2CFastModeCustom(500000); // It is a test and may crash.
  si4735.downloadPatch(ssb_patch_content, size_content);
  si4735.setI2CStandardMode(); // goes back to default (100KHz)

  // delay(50);
  // Parameters
  // AUDIOBW - SSB Audio bandwidth; 0 = 1.2KHz (default); 1=2.2KHz; 2=3KHz; 3=4KHz; 4=500Hz; 5=1KHz;
  // SBCUTFLT SSB - side band cutoff filter for band passand low pass filter ( 0 or 1)
  // AVC_DIVIDER  - set 0 for SSB mode; set 3 for SYNC mode.
  // AVCEN - SSB Automatic Volume Control (AVC) enable; 0=disable; 1=enable (default).
  // SMUTESEL - SSB Soft-mute Based on RSSI or SNR (0 or 1).
  // DSP_AFCDIS - DSP AFC Disable or enable; 0=SYNC MODE, AFC enable; 1=SSB MODE, AFC disable.
  si4735.setSSBConfig(bwIdxSSB, 1, 0, 1, 0, 1);
  delay(25);
  ssbLoaded = true;
}


//----------  Use BAND  ------------------------------

void useBand()
{
  if (band[bandIdx].bandType == FM_BAND_TYPE)
  {
    currentMode = FM;
    si4735.setTuneFrequencyAntennaCapacitor(0);
    si4735.setFM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep);
    bfoOn = ssbLoaded = false;

  }
  else
  {
    if (band[bandIdx].bandType == MW_BAND_TYPE || band[bandIdx].bandType == LW_BAND_TYPE)
      si4735.setTuneFrequencyAntennaCapacitor(0);
    else
      si4735.setTuneFrequencyAntennaCapacitor(1);

    if (ssbLoaded)
    {
      si4735.setSSB(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep, currentMode);
      si4735.setSSBAutomaticVolumeControl(1);
      si4735.setSsbSoftMuteMaxAttenuation(0); // Disable Soft Mute for SSB
    }
    else
    {
      currentMode = AM;
      si4735.setAM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep);
      si4735.setAutomaticGainControl(1, 0);
      si4735.setAmSoftMuteMaxAttenuation(0); // // Disable Soft Mute for AM
      bfoOn = false;
    }

  }
  delay(100);
  currentFrequency = band[bandIdx].currentFreq;
  currentStep = band[bandIdx].currentStep;
  showStatus();
}

//-------------------- main loop ----------------------------------------------

void loop()
{
  char bfo_disp=0;
  // Check if the encoder has moved.
  if (encoderCount != 0)
  {
    if (bfoOn)
    {
      currentBFO = (encoderCount == 1) ? (currentBFO + currentBFOStep) : (currentBFO - currentBFOStep);
      si4735.setSSBBfo(currentBFO);
        if(currentBFO >= 1000){
          currentBFO = currentBFO - 1000;
          si4735.frequencyUp();
          currentFrequency = si4735.getFrequency();
        }
        else if(currentBFO < 0){
          currentBFO = currentBFO + 1000;
          si4735.frequencyDown();
          currentFrequency = si4735.getFrequency();
        }
      showFrequency();
    }
    else
    {
      if (encoderCount == 1)
        si4735.frequencyUp();
      else
        si4735.frequencyDown();

      // Show the current frequency only if it has changed
      currentFrequency = si4735.getFrequency();
    }
    encoderCount = 0;
  }

/////////////////////////
//  BANDWIDTH switch
/////////////////////////

    if (digitalRead(BANDWIDTH_BUTTON) == LOW)
    {
      if (currentMode == LSB || currentMode == USB)
      {
        bwIdxSSB++;
        if (bwIdxSSB > 5)
          bwIdxSSB = 0;
        si4735.setSSBAudioBandwidth(bwIdxSSB);
        // If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
        if (bwIdxSSB == 0 || bwIdxSSB == 4 || bwIdxSSB == 5)
          si4735.setSBBSidebandCutoffFilter(0);
        else
          si4735.setSBBSidebandCutoffFilter(1);
      }
      else if (currentMode == AM)
      {
        bwIdxAM++;
        if (bwIdxAM > 6)
          bwIdxAM = 0;
        si4735.setBandwidth(bwIdxAM, 1);
      }
      showStatus();
      while(digitalRead(BANDWIDTH_BUTTON) == LOW)
        ;
    }

/////////////////////////
//  BAND UP switch
/////////////////////////

    else if (digitalRead(BAND_BUTTON_UP) == LOW){
      bandUp();
      while(digitalRead(BAND_BUTTON_UP) == LOW)
        ;
      eep_wdata();
    }

/////////////////////////
//  BAND DOWN switch
/////////////////////////

    else if (digitalRead(BAND_BUTTON_DOWN) == LOW){
      bandDown();
      while(digitalRead(BAND_BUTTON_DOWN) == LOW)
        ;
      eep_wdata();
    }

/////////////////////////
//  VOLUME DOWN switch
/////////////////////////

    else if (digitalRead(VOL_UP) == LOW)
    {
      si4735.volumeUp();
      volume = si4735.getVolume();
      showVolume();
      while(digitalRead(VOL_UP) == LOW)
        ;
      EEPROM.put(Eep_VOL, volume);
    }

/////////////////////////
//  VOLUME UP switch
/////////////////////////

    else if (digitalRead(VOL_DOWN) == LOW)
    {
      si4735.volumeDown();
      volume = si4735.getVolume();
      showVolume();
      while(digitalRead(VOL_DOWN) == LOW)
        ;
      EEPROM.put(Eep_VOL, volume);
    }

/////////////////////////
//  AGC switch
/////////////////////////

    else if (digitalRead(AGC_SWITCH) == LOW)
    {
      disableAgc = !disableAgc;
      // siwtch on/off ACG; AGC Index = 0. It means Minimum attenuation (max gain)
      si4735.setAutomaticGainControl(disableAgc, 1);
      showStatus();
      while(digitalRead(AGC_SWITCH) == LOW)
        ;
    }

/////////////////////////
//  STEP switch
/////////////////////////
    
    else if (digitalRead(STEP_SWITCH) == LOW)
    {
      if ( currentMode == FM) {
        fmStereo = !fmStereo;
        if ( fmStereo )
          si4735.setFmStereoOn();
        else
          si4735.setFmStereoOff(); // It is not working so far.
      } else {

        if ((bfoOn) && (currentBFOStep == 50)){   // SSM mode
          currentStep = 1;
          bfoOn = false;
          si4735.setFrequencyStep(currentStep);
          band[bandIdx].currentStep = currentStep;
          showStatus();
        }

        else{                                     // AM mode
          if (currentStep == 1)
            if(currentMode == LSB || currentMode == USB){
              bfoOn = !bfoOn;
              currentBFOStep = 50;
            }
            else  
              currentStep = 5;
          else if (currentStep == 5)
            currentStep = 10;
          else
            currentStep = 1;

          si4735.setFrequencyStep(currentStep);
          band[bandIdx].currentStep = currentStep;
          showStatus();
        }
      }
      while(digitalRead(STEP_SWITCH) == LOW)
        ;
    }

/////////////////////////
//  MODE switch
/////////////////////////

    else if (digitalRead(MODE_SWITCH) == LOW)
    {
      if (currentMode != FM ) {
        if (currentMode == AM)
        {
          // If you were in AM mode, it is necessary to load SSB patch (avery time)
         loadSSB();
         currentMode = LSB;
        }
        else if (currentMode == LSB)
        {
          currentMode = USB;
        }
        else if (currentMode == USB)
        {
          currentMode = AM;
          ssbLoaded = false;
          bfoOn = false;
        }
        // Nothing to do if you are in FM mode
        band[bandIdx].currentFreq = currentFrequency;
        band[bandIdx].currentStep = currentStep;
        useBand();
      }
      while(digitalRead(MODE_SWITCH) == LOW)
        ;
      EEPROM.put(Eep_Mode, currentMode);
    }

  // Show the current frequency only if it has changed
  if (currentFrequency != previousFrequency)
  {
    previousFrequency = currentFrequency;
    showFrequency();
  }

  // Show RSSI status only if this condition has changed
  if ((millis() - elapsedRSSI) > MIN_ELAPSED_RSSI_TIME * 9)
  {
    si4735.getCurrentReceivedSignalQuality();
    int aux = si4735.getCurrentRSSI();
    if (rssi != aux)
    {
      rssi = aux;
      showRSSI();
    }
    elapsedRSSI = millis();
  }

  delay(10);

  if((Time_Passd+2000 < millis()) && Flg_eepWT == 1){
    eep_wdata();
    Flg_eepWT = 0;
  }
}


EEPROM初期化

STEP SWを押しながら電源onするとEEPROMが初期化される。

使用感

実際にSSBを聞いてみて、BFOがバンドが替わると、ズレる。また、保存したBFOも再電源投入後ズレる。(既知のバグと思っている。)今回、バンドをメモリ保存して無いが無くても不自由感は無かった。