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