domingo, 30 de novembro de 2025

Sketch DDS VFO si5351 Arduino 10Khz a 120Mhz

Olá pessoal, vocês que acompanha nosso blog, muito obrigado pela visita. Publico este mês um esboço ou sketch, em vídeo projeto DDS VFO autor Sr. Júlio Cesar do ano de 2020. O projeto é simples porem muito bom com variação de 10kHz a 120MHz, original STEP de 1Hz, 10Hz, 1kHz, 5kHz, 10kHz e 1MHz, ajuste de FI offset + ou  -  Super-heteródinos  conversão Direta. Display Oled SSD 1306 128x64, e placa ou IC si5351. Fiz simples modificações, algumas que o próprio Júlio Cesar responde em seus comentários em sua página. Porem algumas modificações estão muito difíceis de serem realizadas pelo meu pobre conhecimento em código C++. Tais como adicionar TX RX com FI e frequência display, adicionar AM, LSB, USB.  Na verdade eu não tinha programado este sketch para publicar este mês, mas sim um sketch em vídeo, com RIT, memória, s-meter, menu calibração. Tive que copiar o sketch do vídeo, ao gravar tive a surpresa dos caracteres e outros comandos não funcionavam corretamente. Revisei varias vezes o vídeo e sketch que copiei, adicionei e retirei bibliotecas,  não tinha nada errado, compilava ok, mas ao gravar fiquei tentando consertar o programa, mas perdi muito tempo e não conseguir. Infelizmente pessoal tem pessoas que brincam com o que publicam. Eu não e jamais vou divulgar o nome do autor vídeo e nem o sketch que copiei. Não estou aqui para denunciar e nem jogar pedras em nenhum colega que fazem isso, só fico triste porque acho que temos que ter uma ética no nosso hobby como em todos os segmentos da sociedade. Mas voltando ao sketch do Sr. Júlio Cesar, quero parabeniza-lo pelos brilhantes projetos que ele fez e faz em seus vídeos e publicações de programas Arduino DDS VFO código livre e que funciona de verdade, alias ele responde e ensina em comentários em suas páginas e vídeos. Parabéns Júlio Cesar fico feliz e sei que todos que tem o hobby de radioamador ou PX nas veias ficam felizes com sua contribuição e colaboração. Bem eu fiz algumas modificações mesmo sem tempo, mas não acertei em alguns que estão desabilitados como comentário no esboço, fiz como muitos colegas já fizeram deste e outros programas esboços,  coloquei o STEP para 100Khz saindo o de 5Khz, acrescentei mais os de 1.5 Mhz e 2 Mhz sugeridos pelo autor em comentários, coloquei o nome todo STEP sem pontos, pois com pontos no final do nome STEP quando estivesse selecionado em 1.5 Mhz o M ficava atrapalhando o nome RX e TX, mas vocês poderão deixar original TS ou ST, ou desabilitar este comando de 1.5MHz no STEP, e se for colocar os modos AM, LSB, USB no local onde estão IF KHz será usado para mostrar, por isso deixe só a letra K como original, se for colocar os modos AM, LSB, USB. Habilitei a saída do si5351 CLK1 para FI BFO, o que ficou melhor para alguns transceptores AM, Modifiquei frequência  para 220 Mhz, o que funcionou perfeitamente. Os modos AM, LSB, USB, não funcionaram bem, pois eu teria de modificar muito o programa em void setup()  si5351. Em void loop() void tunegen(), o TX e RX também não funcionou corretamente, pois em TX deveria ter frequência de VFO + ou - FI, e em RX a frequência do display, mas não deu certo, também não tentei muito nesta função, a que eu perdi mais tempo foi para modos AM, LSB, USB em teclas diferentes, assim ele serviria para o Cobra 148 GTL. Se alguém que é programador ou sabe, e quiser mandar um comentário ou e-mail para mim como fazer esta modificação e onde estou errando, por favor eu agradeço muito e vou divulgar o seu nome e modificação, como fiz na publicação do mês passado onde eu me passei e errei em não observar o número 3 em long last_freq[9]; como tinha nome freq[3], eu achei que fosse os modos AM, LSB, USB, mesmo depois de modificar todas ás 9 bandas e com a EEPROM dando erro e ficando instável, eu desabilitei logo a EEPROM sem procurar o problema. Pois a minha intenção era partir para ter os modos AM, LSB, USB, em teclas diferentes, e não conseguir como a publicação do mês 08. Foi o colega Sr. Evaldo quem mandou um comentário informando do meu erro, que logo retifiquei todos os sketches da publicação. Neste programa sketch abaixo publicado, notei uma anormalidade que notei ao girar o Encoder até 220 Mhz, ao retornar novamente para KHz o VFO ficou instável ou desencontrados sem função, tive que resetar ou reset para que ele ficasse bom novamente. Não fiz este teste no original, por isso não sei se é só no que modifiquei. Vejam ás fotos como ficou o projeto modificado parcialmente do Sr. Júlio Cesar. Esta publicação poderá ser alterada atualizada posteriormente. Muito obrigado a todos.
Assistam ao vídeo como ficou o DDS VFO com ás modificações.
Como referência canal 1 PX CB 11 metros.
FI está para o Cobra 148 GTL.
Frequência IF em CLK1.
Frequência CLK0 VFO.
Esquema elétrico DDS VFO 10KHz 120 MHz Júlio Cesar.
Abaixo o programa algoritmo esboço que fiz ás simples modificações para nossos projetos RTX em AM ou FM. Não é aconselhável, mas vocês poderão colocar no Cobra 148 GTL ou outros equipamentos SSB, pois ele tem STEP de 1Hz e 10Hz o que dá para clarificar voz mesmo fora de frequência no display em SSB, Vou ver se dá para colocar um potenciômetro como fine ajuste os Hz, assim ficaria melhor.
Selecione e copie todo programa esboço, abra o seu Arduino IDE na área de trabalho, limpe tudo e cole, vá em Arquivo depois Salvar como, renomeei o esboço e escolha a pasta a ser salvo.
1- Esboço abaixo copie e cole no Arduino IDE.
/********************************************************************************************************
  10kHz to 120MHz VFO / RF Generator with Si5351 and Arduino Nano, with Intermediate Frequency (IF)
  offset (+ or -). See the schematics for wiring details. By J. CesarSound - ver 1.0 - Dec/2020.
  MODDED BY 2e0myn september 2021
  Sketch modified by Waldir Cardoso 11/20/2025.
  Blog:https://projetosetransceptores.blogspot.com/2025/10/dds-vfo-am-ssb-hf-vhf-9-bandas-oled.html
*********************************************************************************************************/
//---------------------------------------------------
//Modified functions.
//STEP 100Khz, 1.5Mhz, 2Mhz.
//Frequency up to 220 MHz
//CLK1 FI BFO
//--------------------------------------------------

//Libraries
#include <Wire.h>                 //IDE Standard
#include <Rotary.h>               //Ben Buxton https://github.com/brianlow/Rotary
#include <si5351.h>               //Etherkit https://github.com/etherkit/Si5351Arduino
#include <Adafruit_GFX.h>         //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_SSD1306.h>     //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306

//User preferences
//------------------------------------------------------------------------------------------------------------
#define IF         7800      //Enter your IF frequency, ex: 455 = 455kHz, 10700 = 10.7MHz, 0 = to direct convert receiver or RF generator, + will add and - will subtract IF offfset.
#define FREQ_INIT  26965000  //Enter your initial frequency at startup, ex: 7000000 = 7MHz, 10000000 = 10MHz, 840000 = 840kHz.
#define XT_CAL_F   18000     //Si5351 calibration factor, adjust to get exatcly 10MHz. Increasing this value will decreases the frequency and vice versa.
#define tunestep    A0        //Change the pin used by encoder push button if you want.
//#define am        6        //Change the pin used by mode switch if you want.
//#define usb       7        //Change the pin used by mode switch if you want.
//#define lsb       8        //Change the pin used by mode switch if you want.
#define tx_rx       9        //The pin used by RX / TX selector switch, RX = switch open, TX = switch closed to GND. When in TX
//------------------------------------------------------------------------------------------------------------

Rotary r = Rotary(2, 3);
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
Si5351 si5351;
/*
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 = "";
*/
unsigned long freq = FREQ_INIT;
unsigned long freqold, fstep;
long interfreq = IF;
long cal = XT_CAL_F;
unsigned long long pll_freq = 90000000000ULL;
byte encoder = 1;
byte stp;
unsigned int period = 100;   //millis display active
unsigned long time_now = 0;  //millis display active
bool sts = 1; // was 0

ISR(PCINT2_vect) {
  char result = r.process();
  if (result == DIR_CW) set_frequency(1);
  else if (result == DIR_CCW) set_frequency(-1);
}

void set_frequency(short dir) {
  if (encoder == 1) {             //Up/Down frequency
    if (dir == 1) freq = freq + fstep;
    if (freq >= 220000000) freq = 220000000;
    if (dir == -1) freq = freq - fstep;
    if (fstep == 1000000 && freq <= 1000000) freq = 1000000;
    else if (freq < 10000) freq = 10000;
  }
}

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(tunestep, INPUT_PULLUP);
  //pinMode(am, INPUT_PULLUP);
  //pinMode(usb, INPUT_PULLUP);
  //pinMode(lsb, INPUT_PULLUP);
  pinMode(tx_rx, INPUT_PULLUP);
 
  statup_text();

  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, cal);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.output_enable(SI5351_CLK0, 1);                  //1 - Enable / 0 - Disable CLK
  si5351.output_enable(SI5351_CLK1, 1);
  
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);  //Output current 2MA, 4MA, 6MA or 8MA
  si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA);  //Output current 2MA, 4MA, 6MA or 8MA
 
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();

  stp = 4;    // stp = 4 STEP 10KHz
  setstep();
  layout();
  displayfreq();
 
}

void loop() {
        
  if (freqold != freq) {
    time_now = millis();
    tunegen();
    freqold = freq;
  }

  if (digitalRead(tunestep) == LOW) {
    time_now = (millis() + 300);
    setstep();
    delay(300);
  }
/* 
  if (digitalRead(am) == LOW) {
    time_now = (millis() + 300);            
  }
  if (digitalRead(usb) == LOW)  {
    time_now = (millis() + 300);        
  }
  if (digitalRead(lsb) == LOW) {
    time_now = (millis() + 300);        
  }
*/ 
  if (digitalRead(tx_rx) == LOW) {
    time_now = (millis() + 300);    
    sts = 0; //was 1
    
  }
  else sts = 1; //was 0
  
  if ((time_now + period) > millis()) {
    displayfreq();
    layout();
  }  
}

void tunegen() {
 
  si5351.set_freq_manual((freq + (interfreq * 1000ULL)) * 100ULL, pll_freq, SI5351_CLK0); 
  si5351.set_freq((IF * 1000ULL) * 100ULL, SI5351_CLK1);  
 }
 
void displayfreq() {
  unsigned int m = freq / 1000000;
  unsigned int k = (freq % 1000000) / 1000;
  unsigned int h = (freq % 1000) / 1;

  display.clearDisplay();
  display.setTextSize(2);

  char buffer[15] = "";
  if (m < 1) {
    display.setCursor(41, 1); sprintf(buffer, "%003d.%003d", k, h);
  }
  else if (m < 100) {
    display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%003d", m, k, h);
  }
  else if (m >= 100) {
    unsigned int h = (freq % 1000) / 10;
    display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%02d", m, k, h);
  }
  display.print(buffer);
}

// Button press changes the frequency change step for 10 Hz steps 

void setstep() {
  switch (stp) {
  case 1:
  stp = 2;
  fstep = 1;
  break;
  case 2:
  stp = 3;
  fstep = 10;
  break;
  case 3:
  stp = 4;
  fstep = 1000;
  break;
  case 4:
  stp = 5;
  fstep = 10000;
  break;
  case 5:
  stp = 6;
  fstep = 100000;
  break;
  case 6:
  stp = 7;
  fstep = 1000000;
  break;
  case 7:
  stp = 8;
  fstep = 1500000; 
  break;
  case 8:
  stp = 1;
  fstep = 2000000; 
  break;
  }
}
void layout() {
  display.setTextColor(WHITE);
  display.drawLine(0, 20, 100, 20, WHITE);
  display.drawLine(0, 20, 100, 20, WHITE);
  display.drawLine(105, 30, 127, 30, WHITE);
  display.drawLine(100, 20, 105, 30, WHITE);
  display.setTextSize(2);
  //display.setCursor(90, 48);
  //if (digitalRead(am) == LOW)
  //display.print("AM");
  //if (digitalRead(usb) == LOW)
  //display.print("USB");
  //if (digitalRead(lsb) == LOW)
  //display.print("LSB");
  display.setCursor(2, 25);
  display.print("STEP");
  if (stp == 2) display.print("1Hz");
  if (stp == 3) display.print("10Hz");
  if (stp == 4) display.print("1k");
  if (stp == 5) display.print("10k");
  if (stp == 6) display.print("100k");
  if (stp == 7) display.print("1M");
  if (stp == 8) display.print("1.5M");
  if (stp == 1) display.print("2M");

  display.setCursor(2, 48);
  display.print("IF:");
  display.print(interfreq);
  display.print("KHz");
  
  display.setTextSize(1);
  display.setCursor(110, 33);
  if (digitalRead(tx_rx) == LOW)display.print("TX");
  if (digitalRead(tx_rx) == HIGH)display.print("RX");
  display.setCursor(110, 20);
  if (freq < 1000000) display.print("kHz");
  if (freq >= 1000000) display.print("MHz");
  
  display.display();

void statup_text() {
  display.setTextSize(2);
  display.setCursor(9, 10);
  display.print("Cobra 148");
  display.setCursor(39, 45);
  display.print("GTL");
  display.display();
  delay(3000);
  display.clearDisplay();
}