sábado, 30 de agosto de 2025

Esboço sketch Arduíno VFO DDS si5351 AM SSB Cobra 148 GTL.

Vocês, que curte e gosta dos projetos do blog, por gentileza olhem às propagandas e click para maiores detalhes no que interessar, desta forma vocês estarão ajudando o blog. Muito obrigado por acessar e ajudar o blog, conto com vocês muito agradecido.

Olá, assistam aos meus vídeos explicativos para incentivarem todos iniciantes em DDS VFO. Escrevi sobre algumas modificações que já fiz em sketches mês passado, e publiquei algumas modificações no sketch do oled_cb_vfo4 do Sr. Akio Mizuno JA2GQP.  Agora publico o algoritmo programa ou sketch postado em vídeo YouTube do Sr.  CT7API Manuel Ferreira. Meus agradecimentos Sr. Manuel Ferreira, por mostrar e disponibilizar a todos o programa que será bem vindo para todos do hobby. O sketch é simples e pequeno, tem os modos AM, LSB, USB, com deslocamento de 1.5 Khz para cada lado da banda AM. Step de 1Hz até 1 Mhz,   Os sketches podem ser adicionados em transceptores comerciais AM, SSB ou feitos em casa, basta modificar a FI e bandas laterais no sketch. Servirão para Naja, Minhoca, Lagunero 40M, Ararinha, e outros transceptores. 
São três sketches que vocês poderão escolher para seu equipamento ou construção do seu projeto transceptor. O primeiro só AM VFO, BFO conversão direta, o segundo AM, LSB, USB, em VFO e BFO FI 7.800 Khz Cobra 148 GTL. Terceiro é VFO AM conversão direta, e BFO AM, LSB, USB FI 10 Mhz. 
Modificações: Step com números em Khz ou 1 Mhz, selecionado acima de cada dígito a mudar frequência. Coloquei para 1 Mhz para melhor cobrir toda faixa entre 80 metros a 6 metros, modo AM, LSB, USB em CLK0, VFO e CLK1, BFO, ideal para Cobra 148 GTL e outros equipamentos comerciais. Rotary Encoder simples 5 pinos. Aceita os dois tipos de oled 128 x 32 e 128 x 64 SSD 1306.
Os sketches aqui postados poderão ser modificados, para modo CW, FM. FI pode ser qualquer uma entre 455 Khz AM até 21.4 Mhz FM, TX e RX. Memória em VFO, S/meter, modo canal etc. 
Para quem tem vasta experiência ou é programador poderá colocar alguns destes recursos, eu como já escrevi e disse no vídeo, não sou programador e nem sei muita coisa, estou aprendendo mas o que sei estou disponibilizando para incentivar os que tem dificuldades em fazer modificações e acha que é difícil. Nada é fácil se não tentarmos fazer ou executar, 
Bibliotecas para si5351 usadas neste projeto: SI5351 Arduino V.1.1.2 NT7S SI5351 Arduino V1.1.0
Pois outras mais recentes atualizadas não funcionaram e quebrei muito a cabeça para descobrir, pois a compilação do sketch só dava erro, eu procurava os erros no programa e não achava, depois de muito tentar claro que temos que fazer testes e fiz o teste com a biblioteca SI5351 Arduino V1.1.0 que compilou sem erros e pude assim começar minhas modificações. Depois de adicionar e modificar ás linhas de comando do si5351 em void setup e void loop, mesmo assim desinstalei a antiga e instalei  uma mais recente atualizada do NT7S e deu erro. então ficou assim com ás antigas mesmo. Por isso pessoal nem sempre o programa sketch está com erro, o erro poderá esta nas bibliotecas que não são para aquele sketch. 
Sugestão: Para equipamentos ou montagens em transceptores deve-se ter um buffer nas saídas usadas do si5351, as saídas CLK0, CLK1, CLK2, tem alguns mW de saída RF, melhor amplificar o sinal antes do circuito do transceptor. O sketch mesmo sem TX poderá serem adicionado circuitos no próprio equipamento para misturar uma frequência com a outra, formando uma frequência de TX. Ex: CLK0 + CLK1, ou CLK0 - CLK1, tudo depende do montador ou técnico e também da modificação no sketch. Temos como exemplo o Cobra 148 GTL com Mixer IC5 em L47 34.765 Mhz e L48 7.800 Khz, = 27.965 Mhz. Este sketch aceitou os dois oleds 128x32 e 128x64 SSD 1306. 
Sketch modificação FI, frequencia de inicialização, calibração si5351: FI poderá ser modificado para 455 Khz ou outra FI, modifiquem nas linhas "volatile int32_t AM  = 780000000ULL; ". Modifique a frequência de inicialização VFO de onde o equipamento vai operar em  volatile int32_t vfo = 2696500000ULL /  SI5351_FREQ_MULT; // frequency display initialization. No começo do sketch. Calibração do meu si5351 foi a melhor que se aproximou da correta frequência no meu Frequencímetro Minipa 7110, cada si5351 tem uma frequência de tolerância modifiquem na linha abaixo em void setup si5351.set_correction(18000); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason.
Vocês podem colocar o oled 128x32 0.91 SSD 1306 na versão Walkie Talkie. 
O sketch que preferir gravar, selecione todo conteúdo, e cole no Arduino IDE, adicione todas ás bibliotecas. 
Boa sorte e bons QSOs no seu novo DDS VFO montado por você.
Abaixo o esquema elétrico DDS VFO BFO para transceptores AM SSB. 
Bibliotecas na pasta Arduino libraries para este sketch.
Nunca atualizo o Arduino IDE, pois poderá criar conflitos de bibliotecas erros.
Conversão direta em CLK0.

Conversão direta AM CLK1 FI 7.800 Mhz.
1- sketch abaixo é para equipamentos Walkie Talkie homebrew conversão direta ou super heteródino AM com VFO em 27 Mhz  CLK0 e BFO CLK1 FI 7.800 Khz, do Cobra 148 GTL, . 
O sketch que preferir gravar, selecione todo conteúdo, e cole no Arduino IDE, adicione todas ás bibliotecas.

/********************************************************************************
 sketch modified and copied from the youtube open source 
 CT7API Manuel Ferreira. 
 by Waldir Cardoso. 08/2025. Blog: Projetos e Transceptores Brasil.
 this sketch is a VFO for Direct Conversion equipments or frequency generator
*********************************************************************************/
//---------------------------------------------------------------------------------
//  Function
// 1.VFO CLK0 Direct conversion
// 2.BFO CLK1 7.800 Khz AM mode
// 3.STEP(1MHz,100,10,1kHz,100,10,1Hz)
////////////////////////////////////////////////////////////////////////    
//Libraries
#include <si5351.h>                //https://github.com/etherkit/Si5351Arduino/releases/tag/v1.1.2
#include <Wire.h>                  //IDE Standard
#include <Adafruit_SSD1306.h>      //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <Rotary.h>                //Ben  Buxton https://github.com/brianlow/Rotary
#include <Adafruit_GFX.h>          //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
//----------------------------------------------------------------------------------------------------
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define Adafruit Si5351 BUS BASE ADDR
#define F_MIN        100000000UL
#define F_MAX        23000000000UL
#define ENCODER_A    2
#define ENCODER_B    3
#define ENCODER_BTN  5
#define USB_PIN 6
#define LSB_PIN 7
#define AM_PIN  8
int lsbstate = 0;
int amstate = 0;
int usbstate = 0;

Si5351 si5351;  //Si5351 I2C Address 0x60
Rotary r = Rotary(3, 2);
volatile int32_t AM  = 780000000ULL; //Enter IF of your AM SSB transceiver
volatile int32_t LSB = 779850000ULL; //LSB = IF - 1500Hz
volatile int32_t USB = 780150000ULL; //USB = IF + 1500Hz
volatile int32_t bfo = 780000000ULL; //Intermediary frequency IF
volatile int32_t vfo = 2696500000ULL /  SI5351_FREQ_MULT; // frequency display initialization
volatile uint32_t radix = 10000;  //start step size - change to suit
boolean changed_f = 0;
String tbfo = "";

#define Direct_conversion //What you see on display is what you get

/**************************************/
/* Interrupt service routine for      */
/* encoder frequency change           */
/**************************************/
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_CW) 
  set_frequency(1);
  else if (result == DIR_CCW) 
  set_frequency(-1);
}
/**************************************/
/* Change the frequency               */
/* dir = 1    Increment               */
/* dir = -1   Decrement               */
/**************************************/
void set_frequency(short dir)
{
  if (dir == 1)
     vfo += radix;
     if (dir == -1){
     if  (vfo > radix ) {
    vfo -= radix;
     }}
  //if (dir == 1)
    //vfo += radix;
  //if (dir == -1)
    //vfo -= radix;
  changed_f = 1;
 }  
/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button()
{
  if (!digitalRead(ENCODER_BTN))
  {
    delay(20);
    if (!digitalRead(ENCODER_BTN))
    {
      while (!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
}

/**************************************/
/* Displays the frequency             */
/**************************************/
void display_frequency()
{
  uint16_t f, g;

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,1);
  display.print("      MODE ");
  display.println(tbfo);

  /**************************************/
  /* Displays the frequency change step */
  /**************************************/ 
  switch (radix)
  {
    case 1:
      display.println("                  1Hz");
      break;
    case 10:
      display.println("                 10Hz");
      break;
    case 100:
      display.println("               100Hz");
      break;
    case 1000:
      display.println("           1KHz");
      break;
    case 10000:
      display.println("         10KHz");
      break;
    case 100000:
      display.println("       100KHz");
      break;
      case 1000000:
      display.println("   1MHz");   
      break;
  }
  
  /**************************************/
  /* Displays the frequency             */
  /**************************************/

  display.setTextSize(2);

  f = vfo / 1000000;  //variable is now vfo instead of 'frequency'
  if (f < 10)
  display.print(' ');
  display.print(f);
  display.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
  display.print('0');
  if (f < 10)
  display.print('0');
  display.print(f);
  display.print('.');
  f = vfo % 1000;
  if (f < 100)
    display.print('0');
  if (f < 10)
    display.print('0');
  display.print(f);
  display.display();
  display.clearDisplay();
}
void setup()
{
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.display();
  
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(USB_PIN, INPUT_PULLUP);
  pinMode(LSB_PIN, INPUT_PULLUP);
  pinMode(AM_PIN, INPUT_PULLUP);
   
   si5351.set_correction(18000); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason
  //where you can determine the correction by using the serial monitor.
     
  //initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
  // 0 is the default crystal frequency of 25Mhz.

  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?
 
  //Serial.println((long)((vfo * SI5351_FREQ_MULT) - bfo) * -1);
  //si5351.set_freq(((vfo * SI5351_FREQ_MULT) + bfo), SI5351_PLL_FIXED, SI5351_CLK0);
#ifdef Direct_conversion
  si5351.set_freq((vfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
#endif
  volatile uint32_t vfoT =((vfo * SI5351_FREQ_MULT) - bfo);
  tbfo = "  AM";
  // Set CLK1 to output bfo frequency
  si5351.set_freq( bfo,0, SI5351_CLK1);

  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_6MA); //you can set this to 2MA, 4MA, 6MA or 8MA
  si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_6MA); //be careful though - measure into 50 ohms
  //si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_2MA); //

  Splash();

  pinMode(ENCODER_BTN, INPUT_PULLUP);
  PCICR |= (1 << PCIE2);           // Enabled pin change interrupt for the encoder
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  display_frequency();  // Update the display
}

void loop()
{
  // Update the display if the frequency has been changed
  // if (changed_f)
  {
    display_frequency();
  
    Serial.println((long)((vfo * SI5351_FREQ_MULT) + bfo) * -1);
    //si5351.set_freq(((vfo * SI5351_FREQ_MULT) - bfo), SI5351_PLL_FIXED, SI5351_CLK1);    // subtrai á IF
    //you can also subtract the bfo to suit your needs
    //si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo  , SI5351_PLL_FIXED, SI5351_CLK0);  // soma á IF
#ifdef Direct_conversion      
    si5351.set_freq((vfo * SI5351_FREQ_MULT) , SI5351_PLL_FIXED, SI5351_CLK0);  // display frequency
#endif
    usbstate = digitalRead(USB_PIN);
    if (usbstate == LOW) {
      //USB
      bfo = USB;
      tbfo = "  USB";
    }

    amstate = digitalRead(AM_PIN);
    if (amstate == LOW) {
      //AM
      bfo = AM;
      tbfo = "  AM";
    }

    lsbstate = digitalRead(LSB_PIN);
    if (lsbstate == LOW) {
      // LSB
      bfo = LSB;
      tbfo = "  LSB";
    }           
    changed_f = 0;
  }

  // Button press changes the frequency change step for 1 Hz steps 
  if (get_button())
  {
    switch (radix)
    {
      case 1:
        radix = 10;
        break;
      case 10:
        radix = 100;
        break;
      case 100:
        radix = 1000;
        break;
      case 1000:
        radix = 10000;
        break;
      case 10000:
        radix = 100000;
        break;
      case 100000:
        radix = 1000000;
        break;  
      case 1000000:
        radix = 1;
        break;
    }
    display_frequency();
  }
 }

  void Splash() {
  display.setTextSize(1);
  display.setCursor(4, 5);
  display.print("   Walkie Talkie");
  display.setCursor(4, 25);
  display.print("       AM CB");
  display.display();
  delay(3000);
  display.clearDisplay();
}


Abaixo fotos sketch para Cobra 148 GTL.
Frequência em CLK1 Modo AM.
Frequência em CLK1 Modo LSB.
Frequência em CLK1 Modo USB.
Modo AM frequência display.
Frequência Modo AM CLK0.
Frequência em Modo LSB CLK0.
Frequência Modo USB CLK0.
2- sketch para o Cobra 148 GTL, ou outros equipamentos que tenham o VFO e BFO carrier no modo AM, LSB, USB,  modifiquem a FI nas linhas" volatile int32_t "  no começo do sketch. Poderão ser para os transceptores NajaMinhoca,  Ararinha 27 Mhz e outros convertido Walkie Talkie SSB 40 ou 11 metros. Modifiquem a FI no sketch, já expliquei acima desta postagem.

/********************************************************************************
 Sketch modified and copied from the youtube open source 
 CT7API Manuel Ferreira. 
 by Waldir Cardoso. 08/2025. https://projetosetransceptores.blogspot.com/
 Blog: Projetos e Transceptores. Brasil.
 this sketch is a VFO 34 Mhz AM SSB Cobra 148 GTL equipments or frequency generator
*********************************************************************************/
//---------------------------------------------------------------------------------
//  Function
// 1.VFO CLK0 34 Mhz AM LSB USB MODE
// 2.BFO CLK1 BFO FI 7.800 Khz AM LSB USB MODE
// 3.STEP(1MHz,100,10,1kHz,100,10,1Hz)
////////////////////////////////////////////////////////////////////////    
//Libraries
#include <si5351.h>                //https://github.com/etherkit/Si5351Arduino/releases/tag/v1.1.2
#include <Wire.h>                  //IDE Standard
#include <Adafruit_SSD1306.h>      //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <Rotary.h>                //Ben  Buxton https://github.com/brianlow/Rotary
#include <Adafruit_GFX.h>          //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
//----------------------------------------------------------------------------------------------------
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define Adafruit Si5351 BUS BASE ADDR
#define F_MIN        100000000UL
#define F_MAX        23000000000UL
#define ENCODER_A    2
#define ENCODER_B    3
#define ENCODER_BTN  5
#define USB_PIN 6
#define LSB_PIN 7
#define AM_PIN  8
int lsbstate = 0;
int amstate = 0;
int usbstate = 0;

Si5351 si5351;  //Si5351 I2C Address 0x60
Rotary r = Rotary(3, 2);
volatile int32_t AM  = 780000000ULL;
volatile int32_t LSB = 779850000ULL; //LSB = IF - 1500Hz
volatile int32_t USB = 780150000ULL; //USB = IF + 1500Hz
volatile int32_t bfo = 780000000ULL; //Intermediary frequency IF
volatile int32_t vfo = 2696500000ULL /  SI5351_FREQ_MULT; // frequency display initialization
volatile uint32_t radix = 10000;  //start step size - change to suit
boolean changed_f = 0;
String tbfo = "";

#define IF_Offset //Output is the display plus or minus the bfo frequency

/**************************************/
/* Interrupt service routine for      */
/* encoder frequency change           */
/**************************************/
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_CW) 
  set_frequency(1);
  else if (result == DIR_CCW) 
  set_frequency(-1);
}
/**************************************/
/* Change the frequency               */
/* dir = 1    Increment               */
/* dir = -1   Decrement               */
/**************************************/
void set_frequency(short dir)
{
  if (dir == 1)
     vfo += radix;
     if (dir == -1){
     if  (vfo > radix ) {
    vfo -= radix;
     }}
  //if (dir == 1)
    //vfo += radix;
  //if (dir == -1)
    //vfo -= radix;
  changed_f = 1;
 }  
/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button()
{
  if (!digitalRead(ENCODER_BTN))
  {
    delay(20);
    if (!digitalRead(ENCODER_BTN))
    {
      while (!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
}

/**************************************/
/* Displays the frequency             */
/**************************************/
void display_frequency()
{
  uint16_t f, g;

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,1);
  display.print("      MODE ");
  display.println(tbfo);

  /**************************************/
  /* Displays the frequency change step */
  /**************************************/ 
  switch (radix)
  {
    case 1:
      display.println("                  1Hz");
      break;
    case 10:
      display.println("                 10Hz");
      break;
    case 100:
      display.println("               100Hz");
      break;
    case 1000:
      display.println("           1KHz");
      break;
    case 10000:
      display.println("         10KHz");
      break;
    case 100000:
      display.println("       100KHz");
      break;
      case 1000000:
      display.println("   1MHz");   
      break;
  }
  
  /**************************************/
  /* Displays the frequency             */
  /**************************************/

  display.setTextSize(2);

  f = vfo / 1000000;  //variable is now vfo instead of 'frequency'
  if (f < 10)
  display.print(' ');
  display.print(f);
  display.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
  display.print('0');
  if (f < 10)
  display.print('0');
  display.print(f);
  display.print('.');
  f = vfo % 1000;
  if (f < 100)
    display.print('0');
  if (f < 10)
    display.print('0');
  display.print(f);
  display.display();
  display.clearDisplay();
}
void setup()
{
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.display();
  
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(USB_PIN, INPUT_PULLUP);
  pinMode(LSB_PIN, INPUT_PULLUP);
  pinMode(AM_PIN, INPUT_PULLUP);
   
   si5351.set_correction(18000); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason
  //where you can determine the correction by using the serial monitor.
     
  //initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
  // 0 is the default crystal frequency of 25Mhz.

  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?

#ifdef IF_Offset 
  //Serial.println((long)((vfo * SI5351_FREQ_MULT) - bfo) * -1);
  si5351.set_freq(((vfo * SI5351_FREQ_MULT) + bfo), SI5351_PLL_FIXED, SI5351_CLK0);

  si5351.set_freq((vfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);  
  si5351.set_freq((bfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
  
  volatile uint32_t vfoT =((vfo * SI5351_FREQ_MULT) - bfo);
  tbfo = "  AM";
  // Set CLK1 to output bfo frequency
  si5351.set_freq( bfo,0,SI5351_CLK1);

  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_6MA); //you can set this to 2MA, 4MA, 6MA or 8MA
  si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_6MA); //be careful though - measure into 50 ohms
  //si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_2MA); //
#endif
  Splash();

  pinMode(ENCODER_BTN, INPUT_PULLUP);
  PCICR |= (1 << PCIE2);           // Enabled pin change interrupt for the encoder
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  display_frequency();  // Update the display
}

void loop()
{
  // Update the display if the frequency has been changed
  // if (changed_f)
  {
    display_frequency();
 
#ifdef IF_Offset 
    
    Serial.println((long)((vfo * SI5351_FREQ_MULT) + bfo) * -1);
    //si5351.set_freq(((vfo * SI5351_FREQ_MULT) - bfo), SI5351_PLL_FIXED, SI5351_CLK1);    // subtrai á IF
    //you can also subtract the bfo to suit your needs
    si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo  , SI5351_PLL_FIXED, SI5351_CLK0);  // soma á IF
    si5351.set_freq( bfo,0,SI5351_CLK1);
 
#endif     
    usbstate = digitalRead(USB_PIN);
    if (usbstate == LOW) {
      //USB
      bfo = USB;
      tbfo = "  USB";
    }

    amstate = digitalRead(AM_PIN);
    if (amstate == LOW) {
      //AM
      bfo = AM;
      tbfo = "  AM";
    }

    lsbstate = digitalRead(LSB_PIN);
    if (lsbstate == LOW) {
      // LSB
      bfo = LSB;
      tbfo = "  LSB";
    }                 
    changed_f = 0;
  }

  // Button press changes the frequency change step for 1 Hz steps 
  if (get_button())
  {
    switch (radix)
    {
      case 1:
        radix = 10;
        break;
      case 10:
        radix = 100;
        break;
      case 100:
        radix = 1000;
        break;
      case 1000:
        radix = 10000;
        break;
      case 10000:
        radix = 100000;
        break;
      case 100000:
        radix = 1000000;
        break;  
      case 1000000:
        radix = 1;
        break;
    }
    display_frequency();
  }
 }

  void Splash() {
  display.setTextSize(1);
  display.setCursor(4, 5);
  display.print("     Cobra 148");
  display.setCursor(4, 25);
  display.print("        GTL");
  display.display();
  delay(3000);
  display.clearDisplay();
}

3-sketch para equipamentos CLK0 VFO 27 Mhz, conversão direta, CLK1 FI 10 Mhz BFO AM, LSB, USB, poderá ser usado no transceptor 27 Mhz e outros.

/**********************************************************************************
 Sketch modified and copied from the youtube open source 
 CT7API Manuel Ferreira. 
 by Waldir Cardoso. 08/2025. https://projetosetransceptores.blogspot.com/
 Blog: Projetos e Transceptores. Brasil.
 this sketch is a VFO Direct conversion BFO AM SSB equipments or frequency generator
***********************************************************************************/
//---------------------------------------------------------------------------------
//  Function
// 1.VFO CLK0 26 Mhz AM MODE
// 2.BFO CLK1 BFO FI 10.000 Khz AM LSB USB MODE
// 3.STEP(1MHz,100,10,1kHz,100,10,1Hz)
////////////////////////////////////////////////////////////////////////    
//Libraries
#include <si5351.h>                //https://github.com/etherkit/Si5351Arduino/releases/tag/v1.1.2
#include <Wire.h>                  //IDE Standard
#include <Adafruit_SSD1306.h>      //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <Rotary.h>                //Ben  Buxton https://github.com/brianlow/Rotary
#include <Adafruit_GFX.h>          //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
//----------------------------------------------------------------------------------------------------
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define Adafruit Si5351 BUS BASE ADDR
#define F_MIN        100000000UL
#define F_MAX        23000000000UL
#define ENCODER_A    2
#define ENCODER_B    3
#define ENCODER_BTN  5
#define USB_PIN 6
#define LSB_PIN 7
#define AM_PIN  8
int lsbstate = 0;
int amstate = 0;
int usbstate = 0;

Si5351 si5351;  //Si5351 I2C Address 0x60
Rotary r = Rotary(3, 2);
volatile int32_t AM  = 1000000000ULL;
volatile int32_t LSB =  999850000ULL; //LSB = IF - 1500Hz
volatile int32_t USB = 1000150000ULL; //USB = IF + 1500Hz
volatile int32_t bfo = 1000000000ULL; //Intermediary frequency IF
volatile int32_t vfo = 2696500000ULL /  SI5351_FREQ_MULT; // frequency display initialization
volatile uint32_t radix = 10000;  //start step size - change to suit
boolean changed_f = 0;
String tbfo = "";

#define Direct_conversion //What you see on display is what you get

/**************************************/
/* Interrupt service routine for      */
/* encoder frequency change           */
/**************************************/
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_CW) 
  set_frequency(1);
  else if (result == DIR_CCW) 
  set_frequency(-1);
}
/**************************************/
/* Change the frequency               */
/* dir = 1    Increment               */
/* dir = -1   Decrement               */
/**************************************/
void set_frequency(short dir)
{
  if (dir == 1)
     vfo += radix;
     if (dir == -1){
     if  (vfo > radix ) {
    vfo -= radix;
     }}
  //if (dir == 1)
    //vfo += radix;
  //if (dir == -1)
    //vfo -= radix;
  changed_f = 1;
 }  
/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button()
{
  if (!digitalRead(ENCODER_BTN))
  {
    delay(20);
    if (!digitalRead(ENCODER_BTN))
    {
      while (!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
}

/**************************************/
/* Displays the frequency             */
/**************************************/
void display_frequency()
{
  uint16_t f, g;

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,1);
  display.print("      MODE ");
  display.println(tbfo);

  /**************************************/
  /* Displays the frequency change step */
  /**************************************/ 
  switch (radix)
  {
    case 1:
      display.println("                  1Hz");
      break;
    case 10:
      display.println("                 10Hz");
      break;
    case 100:
      display.println("               100Hz");
      break;
    case 1000:
      display.println("           1KHz");
      break;
    case 10000:
      display.println("         10KHz");
      break;
    case 100000:
      display.println("       100KHz");
      break;
      case 1000000:
      display.println("   1MHz");   
      break;
  }
  
  /**************************************/
  /* Displays the frequency             */
  /**************************************/

  display.setTextSize(2);

  f = vfo / 1000000;  //variable is now vfo instead of 'frequency'
  if (f < 10)
  display.print(' ');
  display.print(f);
  display.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
  display.print('0');
  if (f < 10)
  display.print('0');
  display.print(f);
  display.print('.');
  f = vfo % 1000;
  if (f < 100)
    display.print('0');
  if (f < 10)
    display.print('0');
  display.print(f);
  display.display();
  display.clearDisplay();
}
void setup()
{
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.display();
  
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(USB_PIN, INPUT_PULLUP);
  pinMode(LSB_PIN, INPUT_PULLUP);
  pinMode(AM_PIN, INPUT_PULLUP);
   
   si5351.set_correction(18000); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason
  //where you can determine the correction by using the serial monitor.
     
  //initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
  // 0 is the default crystal frequency of 25Mhz.

  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?

//#ifdef IF_Offset 
  //Serial.println((long)((vfo * SI5351_FREQ_MULT) - bfo) * -1);
  si5351.set_freq(((vfo * SI5351_FREQ_MULT) + bfo), SI5351_PLL_FIXED, SI5351_CLK0);

#ifdef Direct_conversion
  si5351.set_freq((vfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);  
  //si5351.set_freq((bfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
#endif 
  volatile uint32_t vfoT =((vfo * SI5351_FREQ_MULT) - bfo);
  tbfo = "  AM";
  // Set CLK1 to output bfo frequency
  si5351.set_freq( bfo,0,SI5351_CLK1);

  si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_6MA); //you can set this to 2MA, 4MA, 6MA or 8MA
  si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_6MA); //be careful though - measure into 50 ohms
  //si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_2MA); //
//#endif
  Splash();

  pinMode(ENCODER_BTN, INPUT_PULLUP);
  PCICR |= (1 << PCIE2);           // Enabled pin change interrupt for the encoder
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  display_frequency();  // Update the display
}

void loop()
{
  // Update the display if the frequency has been changed
  // if (changed_f)
  {
    display_frequency();
 
//#ifdef IF_Offset 
    
    Serial.println((long)((vfo * SI5351_FREQ_MULT) + bfo) * -1);
    //si5351.set_freq(((vfo * SI5351_FREQ_MULT) - bfo), SI5351_PLL_FIXED, SI5351_CLK1); // subtrai á IF
    //you can also subtract the bfo to suit your needs
    //si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo  , SI5351_PLL_FIXED, SI5351_CLK0);  // soma á IF
    si5351.set_freq( bfo,0,SI5351_CLK1);
#ifdef Direct_conversion      
    si5351.set_freq((vfo * SI5351_FREQ_MULT) , SI5351_PLL_FIXED, SI5351_CLK0);  // display frequency
#endif
//#endif     
    usbstate = digitalRead(USB_PIN);
    if (usbstate == LOW) {
      //USB
      bfo = USB;
      tbfo = "  USB";
    }

    amstate = digitalRead(AM_PIN);
    if (amstate == LOW) {
      //AM
      bfo = AM;
      tbfo = "  AM";
    }

    lsbstate = digitalRead(LSB_PIN);
    if (lsbstate == LOW) {
      // LSB
      bfo = LSB;
      tbfo = "  LSB";
    }                 
    changed_f = 0;
  }

  // Button press changes the frequency change step for 1 Hz steps 
  if (get_button())
  {
    switch (radix)
    {
      case 1:
        radix = 10;
        break;
      case 10:
        radix = 100;
        break;
      case 100:
        radix = 1000;
        break;
      case 1000:
        radix = 10000;
        break;
      case 10000:
        radix = 100000;
        break;
      case 100000:
        radix = 1000000;
        break;  
      case 1000000:
        radix = 1;
        break;
    }
    display_frequency();
  }
 }

  void Splash() {
  display.setTextSize(1);
  display.setCursor(4, 5);
  display.print("      Transceiver");
  display.setCursor(4, 25);
  display.print("       AM SSB");
  display.display();
  delay(3000);
  display.clearDisplay();
}

quinta-feira, 31 de julho de 2025

Modificação sketch oled_cb_vfo4 JA2GQP’s Arduíno SI5351 Cobra 148 GTL.

 
Vocês, que curte e gosta dos projetos do blog, por gentileza olhem às propagandas e click para maiores detalhes no que interessar, desta forma vocês estarão ajudando o blog. Muito obrigado por acessar e ajudar o blog, conto com vocês muito agradecido.
Olá pessoal, postei aqui em Fevereiro, que comecei a fazer minhas experiências com Arduíno DDS VFO, sempre no interesse de incentivar a todos os iniciantes que querem montar um VFO digital em suas montagens ou seus transceptores AM, FM, SSB, mesmo o Cobra 148 GTL. Já fiz várias experiências modificações em sketchs e algumas deram certo. Quero deixar claro que não sou programador, não sei nada, estou aprendendo, por favor, quero aprender com todos vocês. O primeiro sketch modificado a ser postado aqui no blog e em vídeo, oled_cb_vfo4 do Sr. Akio Mizuno JA2GQP, meus agradecimentos. Download Google Driver de vários DDS VFO's e projetos como oled CB VFO em seu blog, Eu fiz algumas modificações no sketch do oled_cb_vfo4, que servirão para nossos projetos de transceptores SSB, super-heteródinos, dupla conversão, até mesmo servirá para o nosso Cobra 148 GTL. Apesar de não ter o deslocamento das bandas de SSB em relação ao AM, por exemplo: Canal 1 AM = 34.765 Mhz, LSB = 34.763,5 Mhz, USB = 34.766,5 Mhz. Estas são frequências originais na saída VCO em TP1 no esquema do Cobra 148 GTL, que para ficar correto na faixa de LSB, USB terá de ter estes deslocamentos no VFO, além da frequência ser feita por cima e a FI subtraindo. 
Modificações: Modifiquei a FI como já descrita, entre os 34Mhz. Retirei a proteção de TX no VFO. Coloquei o STEP para 1M, 10k, 1k, 100Hz, 10Hz, ideal para SSB, aumentei os canais para mais de 190, á serem gravados do VFO frequências, ao ser pressionado a chave RIT por alguns segundos. Pressionando a tecla STEP por alguns segundos você entra ou sai do modo canal.   
Quando não tem nenhum canal gravado na memória a frequência em RX, TX em CLK0 é de 7.800 Mhz frequências de FI. 
Gravando os canais na memória. Primeiro você terá de selecionar o dígito do canal Ex: canal 1 0.000 agora aperte a tecla STEP por alguns segundos, entre no modo VFO e selecione a frequência 26.965.00 que é a frequência do canal 1 PX CB, ou a que se deseja gravar no canal 1. Aperte a tecla RIT por alguns segundos, pronto já está gravado a frequência no canal 1. Faça o mesmo procedimento para quantos canais gravar na memória, os gravados depois do dígito canal 10, sumirá o último dígito da frequência, Ex: no canal 1 gravado 26.965, se for gravado no canal 10 só aparecerá 26.96 o dígito 5 não aparecerá em RX, só em TX aparece toda frequência em modo VFO. Eu tentei fazer uma modificação que explico mais abaixo. Os gravados após dígito canal 10 eles permanecerão na memória, mesmo que resetado mas poderão ser regravados novamente. Grave os canais dígito 1 frequência de 26.965,00 Mhz até dígito 80 frequência de 27.855,00 Mhz.
Alterei a biblioteca do oled 128x64, mas os caracteres ficaram muito pequenos, então retornei e deixei original como o autor programou. O oled 128x32 achei que tem mais nitidez do que o 128x64.
Atenção: Alterei o modo canal que começa do número 0 zero até 99, o inconveniente é que do canal 0 a 9 o display mostra em RX 6 dígitos escondendo o penúltimo. Depois do canal 10 até 99 em RX o display só mostra 5 dígitos da frequência, porem em TX tem a frequência total, já comentado acima.
Tecla STEP - A cada toque rápido muda de 10Hz, 100Hz, 1khz, 10khz, 1Mhz, toque seguro por alguns segundos entrar modo canal e VFO. Lembrando qua a faixa de PX (CB) tem seus canais separados em 10 Khz, exceto nos 5 canais de telecomandos que ficam entre os canais 3 e 4 = 1T freq. 26.995,00 Mhz, entre os canais 7 e 8 = 2T freq. 27.045,00 Mhz, entre os canais 11 e 12 = 3T freq. 27.095,00 Mhz, entre os canais 15 e 16 = 4T freq. 27.145,00 Mhz, entre os canais 19 e 20 = 5T freq. 27.195,00 Mhz. Obs. o canal 23 é freq. 27.255,00 Mhz, é como se ele estivesse entre os canais 25 e 26. Sejam atento ao gravar os canais no seu projeto.
Tecla RIT - Toque rápido entra modo RIT, o RIT também poderá ser selecionado em 10Hz, 1k, 10k, nesta última só subirá 5Khz ou descerá 5Khz, só muda os sinais - e + continua dígito 5. O modo RIT só funciona em RX, em TX a frequência permanecerá a mesma. Ex: CLK0 tem 34.765,00 Mhz canal 1 em RX no modo RIT com +5.00 ficará em RX 34.770,00 Mhz, com -5.00 ficará 34.760,00 Mhz. Em TX a frequência permanecerá 34.765,00 Mhz. Ou seja você transmite em uma frequência e recebe em outra. Eu já havia modificado o STEP para 10Hz, 1Khz, 10Khz, mas pesquisando encontrei o comentário lá no blog Sr. Akio JA2GQP, que o PU2XLB Sr. Wagner havia solicitado uma modificação no STEP, eu baixei o sketch e coloquei o exemplo neste, o que funcionou perfeitamente como eu queria.  
O sketch que o PU2XLB Sr. Wagner solicitou em comentário em 5 Fevereiro 2025, com modificação no STEP estava na página de download como si5351_oled_BFO_pu2xlb.zip do Sr. Akio JA2GQP, onde não mas achei, mas eu adicionei aqui neste sketch. 
No arquivo em ZIP WinRAR vocês encontrarão na pasta "src" todas ou algumas bibliotecas que precisam para adicionar neste sketch, algumas bibliotecas poderá dá erro na compilação do sketch, leiam alguns comentários na página do autor. instale ás bibliotecas em arquivos ZIP. Na parte de cima do programa Arduíno, vá em sketch - incluir biblioteca - Adicionar biblioteca .ZIP, selecione a pasta do arquivo e instale novamente e leia os comentários lá na página blog do autor. 
Esta é Protoboard laboratório VFO com componentes de testes.
Contem um Arduino Nano ATmega 328P de 2018, SI5351, Oled 0.96 128x64, chaves, Encoder.
Um regulador 7805 capacitores filtro, uma fonte de 7.5 volts por 500mA para alimentar o Arduíno e todo circuito. 
Assistam ao vídeo e olhem ás fotos aqui no blog.
  
Estas são mini chaves táctil Push Button 6x6x4,5mm, retiradas de sucatas de TV.
Vejam mais uma chave liga desliga, e Encoder.
Vejam que o sketch permite os dois oleds, 128x32, 128x64. Este melhor visível.
Vejam que CLK0 no Frequencímetro está em 34.776,85 Khz, FI 7.800 Khz para Cobra 148 GTL.
Nítido, melhor que o Oled 128x64, mas fica com letras e S/meter bem próximo aos números da frequência. Ainda está com plástico no visor. Está selecionado 100 Hz.
Este é ideal para montagens de pequenos transceptores SSB ou Walkie Talkie.
Este 128x64, eu achei melhor, pois os caracteres números são maiores, este é menos claro, menos nitidez, e o AM em RX e S/meter ficam bem posicionado, está com 10Hz. 
Vejam que em TX permanece a frequência, retirei a proteção de TX.
Este meu velho Frequencímetro está variando Hz com a temperatura, já foi muito bom na época.
Para correção calibração do SI5351 eu modifiquei a linha 132 no circulo vermelho:
si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0);    // crystal 25.000 MHz, correction 0
para: 
si5351.init( SI5351_CRYSTAL_LOAD_8PF, 0, 12500); // 62100 is the specific calibration factor for this Si5351 board.
Ficou mais próximo a frequência final Hz.
Na linha 133 em circulo vermelho, vocês poderão aumentar ou diminuir o consumo SI5351 em 2, 4, 8 mA com isso ajusta potencia RF. Mas cuidado, Melhor montar um Buffer na saída de CLK0.
Sugestão do Buffer para SI5351, se for usar FM tem que ter o diodo Varicap para entrada de modulação ajustável e dosar a modulação para o varicap. Em AM, SSB, não coloque o varicap, use este ou outros circuitos como amplificador de RF para SI5351.
 
Vejam abaixo no quadrado vermelho ás funções que modifiquei.
Veja mas abaixo quadrado vermelho a biblioteca para o Oled 128x32 original.
Melhor adicionar ás duas bibliotecas.
Vejam no circulo vermelho, FI offset, estes valores são para FI do Cobra 148 GTL.
Vocês devem mudar se quiser outras FI em CLK0 e CLK1.
Em RX a soma das duas frequências resulta em 7.800 Mhz.
Vejam no circulo vermelho é para acrescentar mais um dígito no modo canal, deixe só dois pontos no meio, talvez apareça um número 5 no final do display onde não há gravação de canal.
Eu deixei original pois queria mais um dígito depois do canal 10, mas é fácil de resolver, basta apertar o TX PTT que mostra a frequência total em modo VFO. Quem fizer outra modificação e queira comentar agradeço.
Esquema DDS VFO  original JA2GQP
Abaixo sketch modificado por mim, para FI de 7.800 Khz Cobra 148 GTL, servirá para transceptores em 80, 40, 20, 15, 11, 10, 6 e 2 metros. É só modificar a frequência de inicio do VFO, ás frequências da FI se quiser usar CLK0, CLK1, e montar um transceptor dupla conversão, heteródino. Selecione todo sketch, copie e cole no programa Arduino IDE, adicione todas ás bibliotecas "src" contida na pasta compactada do sketch do autor.
//////////////////////////////////////////////////////////////////////
//  si5351a oled(128x32) VFO program ver.2.0
//  Copyright(C)2019.JA2GQP.All rights reserved.
//               2023/11/10 JA2GQP
//  Modified sketch FI 34Mhz Cobra 148 GTL or for superheterodyne
//  double conversion transceivers. by Waldir Cardoso
//  Blog: https://projetosetransceptores.blogspot.com/ 2025/05/20.                        
//--------------------------------------------------------------------
//  Function
//    1.VFO/200 Channel mode
//    2.STEP(1M,10k,1k,100,10)
//    3.Automatic memory VFO
//    4.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/SSD1306Ascii.h"
#include "src/SSD1306AsciiAvrI2c.h"   // https://github.com/greiman/SSD1306Ascii
#include <EEPROM.h>                   // IDE Standard
     
////////////////////////////////
// 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 SW_TX = 4;                // 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;          // RIT(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  
  26965000 ,26515000 ,26515000 ,26515000 ,28855000
  };

//const unsigned long CH_TBL[] = {          // Initial frequency value                    
  //26005000 ,26968000 ,26976000 ,27040000 ,27080000 ,
  //27088000 ,27112000 ,27120000 ,28144000
  //};  
//const byte  Max_Chan = sizeof(CH_TBL) / sizeof(CH_TBL[0]);  // Max Channel

//---- IF offset data -------
const long RX_FIL = 7345000;
const long RX_LO = 455000;          
const long TX_LO = 7800000;

const unsigned long RX_IF = RX_FIL + RX_LO;   // RX_IF offset 
const unsigned long TX_IF = TX_LO;            // TX_IF offset 

//---- 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; 

////////////////////////////////
// Encorder STEP
////////////////////////////////
const long STEP_10 = 10;               // STEP 10Hz
const long STEP_100 = 100;             //      100Hz
const long STEP_1k = 1000;             //      1k
const long STEP_10k = 10000;           //      10k
const long STEP_100k = 100000;         //      100k
const long STEP_1M = 1000000;          //      1M

//const int STEP_10 = 10;               // STEP 10Hz
//const int STEP_1k = 1000;             //      1k
//const int STEP_10k = 10000;           //      10k

////////////////////////////////
// etc
////////////////////////////////
const byte  Int_End = 88;             // 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
long Time_smeter;                     // smeter scan 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 = 1;                    // 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, 12500); // 62100 is the specific calibration factor for this Si5351 board   
  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);
      
      mode_disp();
      eep_init();
      fover_disp();
      while (digitalRead(SW_STEP) == LOW);
    }
  }
  eep_rdata();
  mode_disp();
  step_disp();
  Time_smeter = millis();
}

//----------  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 
    si5351.output_enable(SI5351_CLK1, 1);   // LO enable 
    mode_disp();

    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;
        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);
        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;
    si5351.set_freq(RX_LO * SI5351_FREQ_MULT, SI5351_CLK1);
  }

////////////////////////
// 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
      si5351.output_enable(SI5351_CLK1, 1); // LO  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, 1); // VFO enable 
      si5351.output_enable(SI5351_CLK1, 1); // LO enable       
      if((Flg_Rit == 0) && (Disp_Over == 1)){
        fover_disp();                       // Display of over range
        Disp_Over = 0;
      }
    }
    if(Disp_Tx == 1){
      tx_disp();
      Disp_Tx = 0;        
    }
    Disp_Freq = 1;  
    si5351.set_freq(TX_LO * SI5351_FREQ_MULT, SI5351_CLK1);
  }

////////////////////////
// common
////////////////////////
  if(Time_smeter+100 < millis()){
    Val_Smeter = analogRead(AD_SM);
    if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter
      sm_disp();
      Val_Smeterb = Val_Smeter;
      Time_smeter = millis();
    }
  }

  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 
    si5351.output_enable(SI5351_CLK1, 1);   // LO enable 
    mode_disp();

    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;        
        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);
        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;
    si5351.set_freq(RX_LO * SI5351_FREQ_MULT, SI5351_CLK1);
  }

////////////////////////
// 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
      si5351.output_enable(SI5351_CLK1, 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, 1); // VFO enable 
      si5351.output_enable(SI5351_CLK1, 1); // LO enable 
      if((Flg_Rit == 0) && (Disp_Over == 1)){
        fover_disp();                       // Display of over range
        Disp_Over = 0;
      }
    }
    if(Disp_Tx == 1){
      tx_disp();
      Disp_Tx = 0;        
    }
    Disp_Freq = 1;  
    si5351.set_freq(TX_LO * SI5351_FREQ_MULT, SI5351_CLK1);
  }

////////////////////////
// common
////////////////////////
  if(Time_smeter+100 < millis()){
    Val_Smeter = analogRead(AD_SM);
    if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter
      sm_disp();
      Val_Smeterb = Val_Smeter;
      Time_smeter = millis();
    }
  }

  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_1M){                    // 1M?
    Enc_Step = STEP_10;                       //  yes,10Hz set
//  Disp_freq = 1;
  }
  else    
    Enc_Step = Enc_Step * 10;               
}
//void enc_step() {
  //if (Enc_Step == STEP_10) {
    //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_10;                     // 10 Hz   
//}

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

void step_disp() {
  oled.setCursor(105, 4);
  oled.setFont(GQPfont5x7);

  switch(Enc_Step){
    case STEP_10:
      oled.print("10  ");
      break;
    case STEP_100:
      oled.print("100 ");
      break;
    case STEP_1k:
      oled.print("1k  ");
      break;
    case STEP_10k:
      oled.print("10k ");
      break;
    case STEP_100k:
      oled.print("100k");
      break;
    case STEP_1M:
      oled.print("1M  ");
      break;
  }

//void step_disp() {
  //oled.setCursor(109, 4);
  //oled.setFont(GQPfont5x7);  
  //if (Enc_Step == STEP_10)
    //oled.print("10 ");                        // 10Hz
  //else if(Enc_Step == STEP_1k)
    //oled.print("1k ");                        // 1k
  //else
    //oled.print("10k");                        // 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(GQPfont5x7);
  oled.print("TX ");                          // TX
}

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

void mode_disp() {
  oled.setCursor(2, 3);
  oled.setFont(GQPfont5x7);
  if (Flg_Mode == 0){
    oled.print("CW ");                        // CW
    digitalWrite(12,LOW);
    digitalWrite(13,LOW);
  }
  else if(Flg_Mode == 1){
    oled.print("LSB");                        // LSB
    digitalWrite(12,HIGH);
    digitalWrite(13,LOW);
  }
  else if(Flg_Mode == 2){
    oled.print("USB");                        // USB
    digitalWrite(12,LOW);
    digitalWrite(13,HIGH);
  }
  else if(Flg_Mode == 3){
    oled.print("AM ");                        // 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; 
//}