Olá a todos que acompanha nosso blog. Sou agora um caçador pesquisador de sketch código livre para Si5351 e Oled SSD1306. Encontrei mais um campeão em modificações, desde sua primeira publicação 2015, com PIC e código livre na WEB. Achei na página holandesa do PAQRWE, Ver.3.2, por favor leia toda página do link. Há alguns meses atrás, gravei um destes sketch esboço, que não lembro a versão. O display ficou pequeno para os caracteres principalmente os números das frequências, a resolução não estava correta. Então fui pesquisar. Depois de assistir a diversos vídeos destes sketches e versões, e nenhum deles menciona sobre display, em um fórum, aliás tem um vídeo colega brasileiro que mostra o dele também com os nomes e números da frequências grandes e resolução errada, em sua página o colega PA3EQP, PAQRWE em Versão Arduino Notas: ele cita a biblioteca Si5351 que melhor funcionou em seu programa sketch, ele também escreve sobre a resolução do display Oled SSD1306. Eu ainda não tinha lido, e mesmo assim não entenderia o que ele escreveu: " Pelos construtores, entendi que a resolução padrão na biblioteca de SSD1306 do Adafruit está definida para um valor incorreto. Você deve mudar a resolução no arquivo Adafruit_SSD1306.h para 128×64. É uma afirmação #define no início do arquivo." O problema não é a biblioteca, o sketch não aceita esta instrução, ou linha de código. Mas sim este instrução deve ser a melhor opção: Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); Ao invés do que consta lá no sketch como: Adafruit_SSD1306 display(OLED_RESET); Esta instrução deve ser banida, e a primeiro deve permanecer para algumas telas display ter resolução correta. Eu descobrir depois de tentar várias vezes em outros sketches de versões dos mesmos autores, então todos ficavam assim resolução errada no meu display, então observando nos outros sketches fui mudando o que se referencia ao display, e observei que as maiorias dos sketches são com esta instrução início para o display: Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); então assim modifiquei tive êxito e aprendi, meu display ficou bom e pude fazer minhas modificações neste e em outros sketches dos autores de referência. Vou passar para vocês minhas atualizações para esta versão 3.2 do PA3EQP, PAQRWE e quem teve este problema há anos atrás e deixou de gravar o sketch, agora tem ele muito melhor. Abaixo colocarei algumas fotos, de outros colegas que se depararam com este erro. Além do mais ás linhas de instrução ou comando do Si5351 teriam que acrescentar este: SI5351_PLL_FIXED, no meio da linha assim Ex: si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0); em void setup e void loop, e muitas vezes a biblioteca Si5351 dava erros. Ver 3.0, não compila sempre erros em Si5351, não vou perder tempo, a versão Si5351_RxTx_VFO_EQP, atualização de 19/06/2023, compila com aquele mesmo problema da resolução no Oled, vou adicionar foto abaixo, também está para 14.5 Mhz, em: #define F_MAX 14500000UL . O RIT não atua em nenhuma das saídas CLK,
Esses são apenas alguns erros no Oled, mas existem outros erros.
Vejam esta versão como ficou no display.
Vou gravar novamente o original da versão V.3.2 e vejam foto abaixo como fica.
Vejam o tamanho dos números e letras, está original com linha de comando Adafruit_SSD1306 display(OLED_RESET);
Vejam o tamanho da fonte.
Com a linha de comando atualizada para: Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); ficou bom, nenhuma das versões que gravei não atua o RIT, e nem calibra o cristal de 25Mhz, mesmo adicionando a biblioteca <RWE_si5351.h> ou <si5351.h> não tem efeito, mas a FI dá para colocar nos modos AM, SSB, a EEPROM está atuando na frequência e na FI.
Outra versão abaixo que também não tem efeito no RIT e calibração do cristal, é limitado em 14 Mhz. Já está com a linha de comando atualizado para. Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
São usados ás 3 saídas do SI5351, CLK0 = Frequência de saída de TX 10 Khz a 160 Mhz, mesma frequência do display. CLK1 = Frequência calculada CLK0 e CLK2 da soma ou diferença + ou - FI, pronta para TRX. CLK2 = Frequência de saída da FI digitada e salva na tecla D7. Sem perder tempo atualizei a correção na inicialização do Si5351 para: si5351.set_correction(18000); este é o melhor ajuste para o meu cristal Si5351.
Vejam foto abaixo a versão V3.2 já com a linha de comando Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
Começarei a fazer ás modificações, vou pensando e tentando fazer o melhor vejam abaixo ás fotos.
Arrumando os caracteres nomes e números acho que dá para colocar um S/Meter na parte de baixo então vamos ver como vai ficar. Já atualizei escala para 30db é para o 148 GTL e outros transceptores comerciais HF. É acho que conseguir colocar o S/meter de outro sketch código livre
Já estava esquecendo da tela inicial tem que ser do Cobra 148 GTL como sempre, mas vocês poderão colocar e digitar o que quiser.
Sim ficou muito bom, agora vamos atualizar com algumas perfumarias, vamos ver se dá para colocar algumas linhas divisórias.
Ok, dá para colocar ás linhas divisórias, mas tem algo ainda errado nestas linhas, estão muito grossas, vamos verificar o que esta acontecendo.
Vamos ver como fica outra linha acima do S/meter.
Ok, afinei mais a linha e vamos ver como fica aos 100Mhz. Ótimo para os 160 Mhz.
Vamos então atualizar ás linhas divisórias, parece que ainda tem algo errado, a linha esta curta não esta toda abaixo do último zero "0" da frequência. Vamos mudar o TRx por CLK0, afinal de contas é o que mostra ele na frequência e no display. O STEP pode ser ST ou TS, qual o melhor para entender? CLK0 é bom para transceptores conversão direta.
Ok abaixo foto, estiquei um pouco a linha do zero "0" e diminuir a Mhz, é o máximo. Vamos ver se o S/meter dá para ficar parecido com o 148 GTL. Acho que o SIG está desproporcional, pois a sensibilidade do S/meter deve ser alterada nos resistores variáveis trimpots ou programa esboço.
Vejam agora coloquei em D7 TRx ou BFO com IF CLK2, é o que mostra no display e saída de frequência em CLK2, poderá ser ajustada para AM, LSB ou USB. Ajuste assim para o Cobra 148 GTL: AM = 7.800.000, LSB = 7.798.500, USB = 7.801.500. CLK2 poderá ser ligado no Carrier do Cobra 148 GTL se não tiver, perdeu ou danificado o cristal de 7.800Mhz. Estas frequências mixadas vocês poderão ver acionando a tecla D6 antes era RIT, agora é a frequência que mostra e sai em CLK1. Vejam segunda foto abaixo.
Fiz outras modificações Abaixo foto. STEP TS, CLK2 IF separado IF, escala S/Meter.
Mas o CLK2 IF esta com F encostado na linha de descida, vai ficar junto mesmo.
Está no modo FI USB Cobra 148 GTL.
Uma boa modificação que fiz e poderemos manter pressionada a chave D6 e ver a frequência de saída em CLK1. Vocês poderão colocar no Cobra 148 GTL no modo AM = 34.765.000, no modo LSB = 34.763.500, no modo USB 34.766.500, essa mudança é feita no modo CLK2 foto acima. É só adicionar um circuito de pré amplificador ou Buffer e ligar sua saída em L20 do Cobra 148 GTL, se quiser poderá deixar o UHIC 070 mas a banda de frequência será estreita e mais silenciosa.
Bem nas fotos acima vocês já observaram, fiz algumas modificações na escala do S/Meter, com o nome SIG acima dos números 1 e 3 da escala, assim o sinal fica sensível em relação a recepção e transmissão. E com o SIG no começo da escala, assim o número 1 fica mais longe e a escala fica menos sensível no começo da escala. Vocês poderão abreviar o SIG por simplesmente S no começo da escala assim ficaria S1. Vejam no esquema elétrico do DDS VFO que coloquei dois trimpots ou resistores variáveis, um de 100K atenua á entrada de sinal vindo do transceptor, outro de 1K para ajuste a GND do sinal de entrada, estes resistores variáveis trimpot, poderão serem modificados por valores maiores ou menores e por resistores fixos depois dos ajustes no transceptor.
STEP iniciais vamos atualizar para TS.
Abaixo esquema elétrico DDS VFO BFO.
Vejam que alimentação do SI5351 e Oled é de 3.3 Volts.
Pessoal estes esboços não vai até os 160 MHz, o autor colocou esta versão até os 100MHz, mas eu achei que poderia colocar para os 160 MHz, então entrei em uma pegadinha. Fiz a modificação e teste colocando CLK0 rapidamente ao 160 MHz e ele foi e mostrou no Frequencímetro 160 MHz, eu achava que estava tudo bem indo tranquilamente aos 160 MHz. Depois de todas modificações já agora no final do mês de Janeiro, ao sair testando e verificando ás frequências com calma lentamente, em CLK0 no Frequencímetro, notei que só ia até os 118 MHz, depois dos 118 MHz fica instável e se prosseguir fica louca a frequência, então prosseguindo aos 150 MHz fica bom e estável até os 160 MHz, incrível. Depois da decepção, fui atrás do prejuízo, até achei que fosse o Arduino ou SI5351 com defeito, comecei a testar outras bibliotecas para ver se ficava como queria até os 160 MHz corrido continuamente, até coloquei a alimentação do SI5351 e Oled com os 3V3 do Arduino como sugere o autor, desinstalei e instalei várias outras bibliotecas davam erros, tive mesmo que deixar a do autor RWE_si5351.h ou si5351.h e versões 1.1.2 ou 1.1.1. Então notei que voltando o VFO para os 26.965 MHz a frequência continuava louca no Frequencímetro, surgiu então outro problema, eu dava o RESET e ficava em 150 MHz no Frequencímetro e 26.965MHz no display CLK0, achei que fosse por causa da EEPROM, mas o correto era voltar para 26.965 MHz também no Frequencímetro, partir para consertar mais esse erro, testei e coloquei ás linhas de comando em void setup habilitando a saída de CLK0 e void loop habilitando ás saídas de CLK0, CLK1, CLK2. Ficou bom, ao dá o RESET no Arduino o programa DDS volta a frequência de 26.965 MHz. Estes meus relatos que passei, serviram de mais e mais experiência para mim, estou aprendendo mais a cada esboço que me habilito a fazer modificações.
Vamos ao esboços da versão com nome SIG no começo da escala S/Meter, foto acima desatualizada.
Vocês poderão colocar só o S assim ficaria com mais espaço na escala. Á sensibilidade do S/Meter poderá também ser alterada em db no programa esboço.
Abra seu Arduino IDE na área de trabalho e delete tudo. Copie todo programa esboço e cole dentro do Arduino IDE. Tenham ás bibliotecas instaladas, e lembre-se que muitas bibliotecas para o mesmo arquivo pode causar conflitos e provocar erros na compilação.
/*
A part of this program is taken from Jason Mildrum, NT7S.
All extra functions are written by me, Rob Engberts PA0RWE
References:
http://nt7s.com/
http://sq9nje.pl/
http://ak2b.blogspot.com/
http://pa0rwe.nl/?page_id=804
* SI5351_VFO control program for Arduino NANO
* Copyright PA0RWE Rob Engberts
*
* Using the old Si5351 library by Jason Mildrun nt7s
*
* Functions:
* - CLK0 - Tx frequency = Display frequency
* - CLK1 - Rx / RIT frequency = Tx +/- BFO (upper- or lower mixing)
* When RIT active, RIT frequency is displayed and is tunable.
* When RIT is inactive Rx = Tx +/- BFO
* - CLK2 - BFO frequency, tunable
*
* - Stepsize: select (pushbutton)
* - Calibrate: (pushbutton) calculates difference between X-tal and measured
* x-tal frequency, to correct x-tal frequency.
* - Selection: (pushbutton) Switch between TRx and BFO mode
* - RITswitch: tunable Rx frequency, while Tx frequency not changed
*
* Si5351 settings: I2C address is in the .h file
* X-tal freq is in the .h file but set in line 354
*
***************************************************************************
* 02-04-2015 1.0 Start building program based on the PIC version
* 18-06-2019 1.1 Extend frequency range to 10 KHz down
* Update frequency display. Added 'MHz' and 'KHz'
* Set max frequency to 100 MHz.
* 21-06-2020 2.0 Changed text BFO to IF
* Changed bfo_t => vfo_if, vfo_t => vfo_hf, vfo_r => vfo_lo
* Make possible to use the vfo for upper or lower mixing
* by using a #define statement (upper or lower)
* Store of actual frequency after 5 seconds of not tuning
* Set Hz digit of frequency at start to zero (0)
* Stepsize was not saved. Repaired
* 06-04-2021 3.0 Added backward stepsize by pressing stepsize button longer
* 06-08-2022 3.1 DG1KPN has changed the lower frequency to 8kHz.
* but on the display starting at 9.999kHz the number is shifted
* down covering the "Step" The same thing happens if you select
* an IF below 10kHz for a direct conversion receiver.
* This all is repaired in this version.
* 02-04-2023 3.2 Declaration of Hertz[] changed from 7 to 9. This was causing
* strange errors.
* Make some small improvements.
* 19-06-2023 3.2 Changed library RWE_Si5351.cpp with Arduino on FreeBSD patch.*
* Modification by: Waldir Cardoso.
* 09-01-2026 3.2 S/meter scale for 148 GTL +30db, or other transceivers HF at 60dB.
* Press the RIT key to view the CLK1 34Mhz frequency on the display.
* Adjust A7 IF by pressing the key for CW, AM, LSB, or USB.
* This MIXER frequency will be displayed on the screen by pressing the RIT A6 key.
***************************************************************************
* Includes
**************************************************************************/
//Version with "SIG" on the S/Meter scale 01-29-2026.
#include <Rotary.h>
//#include <RWE_si5351.h> //RWE_si5351.h https://pa0rwe.nl/?page_id=804
#include <si5351.h> // SI5351Arduino-1.1.2 https://github.com/etherkit/Si5351Arduino/releases
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
/**************************************************************************
* Define Upper / Lower mixing (Remark // if not true)
**************************************************************************/
#define UPPER
//#define LOWER
/**************************************************************************
* (Pin) Definitions
**************************************************************************/
#define ENCODER_A 2 // Encoder pin A INT0/PCINT18 D2
#define ENCODER_B 3 // Encoder pin B INT1/PCINT19 D3
#define ENCODER_BTN 4 // Encoder pushbutton D4
#define CALIBRBTN 5 // Calibrate
#define RITSWITCH 6 // RIT Switch
#define TXSWITCH 7 // Select TRx or BFO
#define OLED_RESET -1 // OLED reset
#define F_MIN 10000UL // Lower frequency limit 1 KHz
#define F_MAX 100000000UL // Upper frequency limit 100 MHz
/**************************************************************************
* EEPROM data locations
**************************************************************************/
#define EE_SAVED_RADIX 0 // Stepsize pointer
#define EE_SAVED_AFREQ 4 // Actual Tx Frequency (CLK0)
#define EE_SAVED_BFREQ 8 // BFO (IF) Frequency (CLK2)
#define EE_SAVED_XFREQ 12 // X-tal frequency (25 or 27 MHz)
#define EE_SAVED_OFSET 16 // store correction
#define EE_SAVED_CALBR 20 // calibrated indicator
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); //Best resolution for my OLED SSD1306 0.96.
//Adafruit_SSD1306 display(OLED_RESET);
Si5351 si5351;
Rotary r = Rotary(ENCODER_A, ENCODER_B);
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
const int sampleWindowPeak = 200;
int lastPeak;
unsigned int sample;
/**************************************************************************
* Declarations
**************************************************************************/
volatile uint32_t vfo_if = 780000000ULL / SI5351_FREQ_MULT; // CLK0 start IF
volatile uint32_t vfo_hf = 2696500000ULL / SI5351_FREQ_MULT; // CLK2 start Tx freq
volatile uint32_t vfo_lo = vfo_hf - vfo_if; // CLK1 start Rx freq
volatile uint32_t vfo_s = vfo_hf; // Saved for RIT
uint32_t vco_c = 0; // X-tal correction factor
uint32_t xt_freq;
uint32_t test = 0;
uint32_t radix = 10000ULL, old_radix = 10000ULL; //start step size
int act_clk = 0, disp_txt = 0;
bool stepflag = false, calflag = false, modeflag = false, ritset = true;
bool kalibrate = false, actupdt = false;
volatile bool changed_f = false;
unsigned long startTime;
unsigned long duration;
unsigned long startMillis = 0;
/**************************************/
/* Interrupt service routine for */
/* encoder frequency change */
/**************************************/
ISR(PCINT2_vect) {
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)
{
switch (act_clk)
{
case 0: // HF frequency
if (dir == 1)
vfo_hf += radix;
if (dir == -1) {
if (vfo_hf < radix) break; // to prevent negative value
vfo_hf -= radix;
}
break;
case 1: // HF frequency (only if RIT is on)
if (dir == 1)
vfo_hf += radix;
if (dir == -1) {
if (vfo_hf < radix) break; // to prevent negative value
vfo_hf -= radix;
}
break;
case 2: // IF frequency
if (dir == 1)
vfo_if += radix;
if (dir == -1) {
if (vfo_if < radix) break; // to prevent negative value
vfo_if -= radix;
}
break;
}
if(vfo_hf > F_MAX)
vfo_hf = F_MAX;
if(vfo_hf < F_MIN)
vfo_hf = F_MIN;
changed_f = true;
}
/**************************************/
/* Read the buttons with debouncing */
/**************************************/
boolean get_button()
{
if (!digitalRead(ENCODER_BTN)) // Stepsize
{
delay(20);
startTime = millis(); // Start counting
if (!digitalRead(ENCODER_BTN))
{
while (!digitalRead(ENCODER_BTN));
stepflag = true;
}
duration = millis() - startTime; // Total time pressed
}
else if (!digitalRead(CALIBRBTN)) // Calibrate
{
delay(20);
if (!digitalRead(CALIBRBTN))
{
while (!digitalRead(CALIBRBTN));
calflag = true;
}
}
else if (!digitalRead(TXSWITCH)) // Selection
{
delay(20);
if (!digitalRead(TXSWITCH))
{
while (!digitalRead(TXSWITCH));
modeflag = true;
}
}
if (stepflag | calflag | modeflag) return 1;
else return 0;
}
/********************************************************************************
* RIT switch handling
* Switch to small stepsize (100 Hz)
*******************************************************************************/
void rit_switch() // Read RIT_SWITCH
{
if (!digitalRead(RITSWITCH) && !ritset){ // RIT on
act_clk = 1;
ritset = true;
vfo_s = vfo_hf; // Save Tx freq
old_radix = radix; // Save actual stepsize
radix = 100; // Set stepsize to 100 Hz
}
else if (digitalRead(RITSWITCH) && ritset){ // RIT 0ff
act_clk = 0; // RTx mode
ritset = false;
vfo_hf = vfo_s; // Restore to original vco_t
radix = old_radix; // Back to old stepsize
disp_txt = 0; // Clear line
// Update LO frequency based on the restored HF frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
}
}
/**************************************/
/* Displays the frequency and stepsize*/
/**************************************/
void display_frequency()
{
char LCDstr[10];
char Hertz[9];
int p,q = 0;
uint32_t freq = 0ULL;
display.clearDisplay();
switch(act_clk)
{
case 0: // HF frequency
freq = vfo_hf;
break;
case 1: // HF frequency (Used in RIT Mode)
freq = vfo_lo;
break;
case 2: // IF frequency
freq = vfo_if;
break;
}
display.setCursor(110,18); //MHz display position
display.setTextSize(1);
Hertz[1]='\0'; // empty array
sprintf(LCDstr, "%ld", freq); // convert freq to string
p=strlen(LCDstr); // determine length
if (p>6){ // MHz
display.print(F("MHz"));
q=p-6;
strcpy(Hertz,LCDstr); // get Herz digits (6)
strcpy(LCDstr+q,Hertz+(q-1)); // copy into LCDstr and add to MHz
LCDstr[q]='.'; // decimal point
}
else { // KHz
display.print(F("KHz"));
q=p-3;
strcpy(Hertz,LCDstr); // get Herz digits (3)
strcpy(LCDstr+q,Hertz+(q-1)); // copy into LCDstr and add to KHz
LCDstr[q]='.'; // decimal point
}
switch (p) // display stepsize
{
case 4: // 1 KHZ
display.setCursor(48,0);
break;
case 5: // 10 KHZ
display.setCursor(36,0);
break;
case 6: // 100 KHZ
display.setCursor(24,0);
break;
case 7: // 1 MHZ
display.setCursor(12,0);
break;
case 8: // 10 MHZ
display.setCursor(0,0);
break;
case 9: // 100 MHZ
display.setCursor(0,0);
break;
}
display.setTextSize(2);
display.println(LCDstr);
display_settings();
}
/********************************************/
/* Displays S/Meter, step, mode and version */
/********************************************/
void display_settings()
//S/meter scale for 148 GTL +30db, or other transceivers HF at 60dB.
{
unsigned long startMillis = millis(); // Start of sample window
float peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0; //minimum value
unsigned int signalMin = 1023; //maximum value
// collect data for 3000 mS
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(A0); //get reading from microphone
if (sample < 1023) // toss out spurious readings
{
if (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
float db = map(peakToPeak, 80 , 1023, 50, 550 ); //calibrate for deciBels
for (int x = 5; x < 114; x = x + 8) {
//for (int x = 5; x < 114; x = x + 10) { //draw scale
display.drawLine(x, 55, x, 52, WHITE);
}
display.drawRect(0, 55, 125, 9, WHITE); //draw outline of bar graph
int r = map(db, 50, 550, 1, 127); //set bar graph for width of screen
display.fillRect(1, 56, r, 7, WHITE); //draw bar graph with a width of r
display.fillRect(lastPeak - 10, 56, 2, 7, WHITE);
if (r > lastPeak)
lastPeak = r;
else
lastPeak = lastPeak - 5;
///////////////////////////////
// Stepsize ////
///////////////////////////////
display.setCursor(1, 18);
display.setTextSize(1);
display.print(F("TS:"));
switch (radix)
{
case 1:
display.println(F(" 1Hz"));
break;
case 10:
display.println(F(" 10Hz"));
break;
case 100:
display.println(F(" 100Hz"));
break;
case 1000:
display.println(F(" 1KHz"));
break;
case 10000:
display.println(F(" 10KHz"));
break;
case 100000:
display.println(F("100KHz"));
break;
case 1000000:
display.println(F(" 1MHz"));
break;
}
// Mode display
display.setCursor(63, 18); //display position CLK0, CLK1, IFCLK2.
switch (act_clk)
{
case 0:
display.println(F("CLK0"));
//display.println(F("RTx"));
break;
case 1:
display.println(F("CLK1"));
//display.println(F("RIT"));
break;
case 2:
display.println(F("CLK2IF"));
//display.println(F("IF"));
break;
}
// Version
//display.setCursor(15, 55);
//display.print(F("Si5351 vfo V.3.2"));
display.setCursor(1, 42);
display.setTextSize(1);
//Choose one of the scales, enabling the chosen one, and disabling it with //.
//display.println("1 3 5 7 9 +30db +60"); //Scale for Cobra 148 gtl, or HF transceivers.
display.println("SIG 1 3 5 7 9 +30 60"); //Scale for Cobra 148 gtl.
//display.println("S1 3 5 7 9 +30 40 60"); //Other commercial HF transceivers.
//display.println("S1 3 5 7 9 20 40 +60"); //Other commercial HF transceivers.
//Enabled or // disabled lines for SIG, S1 on the S/Meter scale.
display.setTextColor(WHITE);
display.drawLine(0, 16, 103, 16, WHITE);
display.drawLine(103, 16, 107, 27, WHITE);
display.drawLine(107, 27, 127, 27, WHITE);
//Remove // if you want to enable SIG above the S/meter.
//display.setCursor(1, 30); // Display position SIG
//display.setTextSize(1); // Font size.
//display.println("SIG");
//Remove // if you want to enable the line above the SIG and above the S/meter.
//display.drawLine(0, 27, 19, 27, WHITE);
//display.drawLine(19, 27, 24, 39, WHITE);
//display.drawLine(24, 39, 127, 39, WHITE);
//Disable // if you want SIG above S/meter.
display.drawLine(0, 39, 127, 39, WHITE);// linha de cima S/Meter.
/////////////////////////
///// Messages /////
/////////////////////////
display.setCursor(12, 30);
switch (disp_txt)
{
case 0:
display.print(F(" ")); // clear line
break;
case 1:
display.print(F(" Turn RIT Off "));
break;
case 2:
display.print(F(" Set to CLK0 "));
//display.print(F(" Set to TRx "));
break;
case 3:
display.print(F(" Calibration "));
break;
case 4:
display.print(F(" Calibration OK! "));
break;
}
display.display();
}
/**************************************/
/* S E T U P */
/**************************************/
void setup()
{
Serial.begin(115200);
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)
// Clear the buffer
display.clearDisplay();
// Initial text Cobra 148 GTL display.
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(10,10);
display.println("Cobra 148");
display.setCursor(45,40);
display.print("GTL");
display.display();
delay(2000);
display.clearDisplay();
// Read EEPROM
radix = eeprom_read_dword((const uint32_t *)EE_SAVED_RADIX);
if ((radix < 10ULL) | (radix > 1000000ULL)) radix = 100ULL;
vfo_hf = eeprom_read_dword((const uint32_t *)EE_SAVED_AFREQ);
if ((vfo_hf < F_MIN) | (vfo_hf > F_MAX)) vfo_hf = 26965000ULL;
test = (vfo_hf / 10); // Round to 10Hz
vfo_hf = test * 10;
vfo_if = eeprom_read_dword((const uint32_t *)EE_SAVED_BFREQ);
if ((vfo_if < F_MIN) | (vfo_if > F_MAX)) vfo_if = 7800000ULL;
test = (vfo_if / 10); // Round to 10Hz
vfo_if = test * 10;
vco_c = 0;
if (eeprom_read_dword((const uint32_t *)EE_SAVED_CALBR) == 0x60) {
vco_c = eeprom_read_dword((const uint32_t *)EE_SAVED_OFSET);
}
xt_freq = SI5351_XTAL_FREQ + vco_c;
//initialize the Si5351
si5351.set_correction(18000); // Set to zero because I'm using an other calibration method
si5351.init(SI5351_CRYSTAL_LOAD_8PF, xt_freq); // Frequency get from settings in VFO_si5351.h file
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
// Set CLK0 to output the starting "HF" frequency as set above by vfo = ?
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
// Set CLK1 to output the LO frequncy = HF +/- IF frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
// Set CLK2 to output bfo frequency
si5351.set_freq((vfo_if * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK2);
si5351.output_enable(SI5351_CLK0, 1); // Enable CLK0
si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);
si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_4MA);
si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_4MA);
// Encoder setup
pinMode(ENCODER_BTN, INPUT_PULLUP);
PCICR |= (1 << PCIE2); // Enable pin change interrupt for the encoder
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei(); // Start interrupts
// Pin Setup
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(CALIBRBTN, INPUT_PULLUP); // Calibrate
pinMode(RITSWITCH, INPUT_PULLUP); // RIT Switch
pinMode(TXSWITCH, INPUT_PULLUP); // Select TRx or BFO
// Timer
startMillis = millis(); // Reset actual freq store timer
actupdt = true;
// Display first time
display.clearDisplay();
display.setTextColor(WHITE);
display_frequency(); // Update the display
}
/**************************************/
/* L O O P */
/**************************************/
void loop()
{
if (disp_txt == 4) {
delay(3000); // Display calibration OK and wait 3 seconds
disp_txt = 0; // Clear text line
}
// Serial.println(actupdt);
// Update the display if the frequency has been changed
if (changed_f) {
display_frequency();
si5351.output_enable(SI5351_CLK0, 1); // Enable CLK0
if (act_clk == 0 && !kalibrate) // No Tx update during calibrate
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
else if (act_clk == 2) // BFO update
si5351.output_enable(SI5351_CLK2, 1); // Enable CLK2
si5351.set_freq((vfo_if * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK2);
// Update LO frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.output_enable(SI5351_CLK1, 1); // Enable CLK1
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
changed_f = false;
disp_txt = 0; // Clear line
startMillis = millis(); // Reset actual freq store timer
actupdt = false;
}
rit_switch(); // read RIT switch
// Store actual freq and other settings once per 5 seceonds after frequency change
if((millis() - startMillis) > 5000 && !actupdt) {
eeprom_write_dword((uint32_t *)EE_SAVED_AFREQ, vfo_hf);
eeprom_write_dword((uint32_t *)EE_SAVED_BFREQ, vfo_if);
startMillis = millis(); // Reset actual freq store timer
actupdt = true;
Serial.println("Act freq and BFO stored");
}
// Button press
// Also stored the last used frequency together with the step size before store
//
if (get_button()) {
if (stepflag) { // Stepsize button
if (duration > 500) { // For a long time then prev step
switch (radix)
{
case 1000000:
radix = 100000;
break;
case 100000:
radix = 10000;
break;
case 10000:
radix = 1000;
break;
case 1000:
radix = 100;
break;
case 100:
radix = 10;
break;
case 10:
radix = 1;
break;
case 1:
radix = 1000000;
break;
}
}
else {
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;
}
}
eeprom_write_dword((uint32_t *)EE_SAVED_RADIX, radix); // Store stepsize
stepflag = false;
}
else if (modeflag) { // Mode button
if (act_clk == 0) act_clk = 2; else act_clk = 0;
eeprom_write_dword((uint32_t *)EE_SAVED_BFREQ, vfo_if);
modeflag = false;
disp_txt = 0; // Clear line
}
else if (calflag) { // Calibrate button
if (!digitalRead(RITSWITCH)){ // RIT is on
disp_txt = 1; // Message set RIT off
}
else if (act_clk == 2){ // BFO mode on
disp_txt = 2; // Message set BFO off
}
else if (!kalibrate) { // Start calibrate
vfo_s = vfo_hf; // Save actual freq
old_radix = radix; // and stepsize
vfo_hf = SI5351_XTAL_FREQ; // en set to default x-tal
disp_txt = 3; // Message Calibrate
kalibrate = true;
radix = 10; // Set to 10 Hz
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0); // Set HF Freq
}
else if (kalibrate) { // after tuning x-tal freq
kalibrate = false;
vco_c = vfo_hf - SI5351_XTAL_FREQ; // difference
vfo_hf = vfo_s; // restore freq
radix = old_radix; // and stepsize
disp_txt = 4; // Message Calibrate OK
eeprom_write_dword((uint32_t *)EE_SAVED_OFSET, vco_c); // store correction
xt_freq = SI5351_XTAL_FREQ + vco_c; // Calibrated x-tal freq
eeprom_write_dword((uint32_t *)EE_SAVED_CALBR, 0x60); // Calibrated
si5351.init(SI5351_CRYSTAL_LOAD_8PF, xt_freq); // Initialize
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(vfo_if * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK2); // correct BFO frequency
si5351.set_freq(vfo_hf * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0); // Correct HF freq
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq(vfo_lo * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK1); // correct LO frequency
}
calflag = false;
}
}
display_frequency(); // Update display
} // end while loop
Fotos e esboço abaixo S/Meter com SIG acima da escala e frequências de saídas em CLK0, CLK1, CLK2.
Frequência do display e saída em CLK0.
Frequência Mixer em CLK1 modo AM ao pressionar chave D6.
Frequências CLK1 Mixer modo USB.
Frequência FI ajustada modo AM Cobra 148 GTL.
Frequência FI ajustada modo USB Cobra 148 GTL.
Vamos ao esboços da versão com SIG na parte de cima escala S/Meter, foto acima.
Abra seu Arduino IDE na área de trabalho e delete tudo. Copie todo programa esboço e cole dentro do Arduino IDE. Tenham ás bibliotecas instaladas, e lembre-se que muitas bibliotecas para o mesmo arquivo pode causar conflitos e provocar erros na compilação.
/*
A part of this program is taken from Jason Mildrum, NT7S.
All extra functions are written by me, Rob Engberts PA0RWE
References:
http://nt7s.com/
http://sq9nje.pl/
http://ak2b.blogspot.com/
http://pa0rwe.nl/?page_id=804
* SI5351_VFO control program for Arduino NANO
* Copyright PA0RWE Rob Engberts
*
* Using the old Si5351 library by Jason Mildrun nt7s
*
* Functions:
* - CLK0 - Tx frequency = Display frequency
* - CLK1 - Rx / RIT frequency = Tx +/- BFO (upper- or lower mixing)
* When RIT active, RIT frequency is displayed and is tunable.
* When RIT is inactive Rx = Tx +/- BFO
* - CLK2 - BFO frequency, tunable
*
* - Stepsize: select (pushbutton)
* - Calibrate: (pushbutton) calculates difference between X-tal and measured
* x-tal frequency, to correct x-tal frequency.
* - Selection: (pushbutton) Switch between TRx and BFO mode
* - RITswitch: tunable Rx frequency, while Tx frequency not changed
*
* Si5351 settings: I2C address is in the .h file
* X-tal freq is in the .h file but set in line 354
*
***************************************************************************
* 02-04-2015 1.0 Start building program based on the PIC version
* 18-06-2019 1.1 Extend frequency range to 10 KHz down
* Update frequency display. Added 'MHz' and 'KHz'
* Set max frequency to 100 MHz.
* 21-06-2020 2.0 Changed text BFO to IF
* Changed bfo_t => vfo_if, vfo_t => vfo_hf, vfo_r => vfo_lo
* Make possible to use the vfo for upper or lower mixing
* by using a #define statement (upper or lower)
* Store of actual frequency after 5 seconds of not tuning
* Set Hz digit of frequency at start to zero (0)
* Stepsize was not saved. Repaired
* 06-04-2021 3.0 Added backward stepsize by pressing stepsize button longer
* 06-08-2022 3.1 DG1KPN has changed the lower frequency to 8kHz.
* but on the display starting at 9.999kHz the number is shifted
* down covering the "Step" The same thing happens if you select
* an IF below 10kHz for a direct conversion receiver.
* This all is repaired in this version.
* 02-04-2023 3.2 Declaration of Hertz[] changed from 7 to 9. This was causing
* strange errors.
* Make some small improvements.
* 19-06-2023 3.2 Changed library RWE_Si5351.cpp with Arduino on FreeBSD patch.*
* Modification by: Waldir Cardoso.
* 09-01-2026 3.2 S/meter scale for 148 GTL +30db, or other transceivers HF at 60dB.
* Press the RIT key to view the CLK1 34MHz MIXER frequency on the display.
* Adjust A7 IF by pressing the key for CW, AM, LSB, or USB.
* This MIXER frequency will be displayed on the screen by pressing the RIT A6 key.
***************************************************************************
* Includes
**************************************************************************/
//Version with "SIG" above S/Meter.
#include <Rotary.h>
//#include <RWE_si5351.h> //RWE_si5351.h https://pa0rwe.nl/?page_id=804
#include <si5351.h> // SI5351Arduino-1.1.2 https://github.com/etherkit/Si5351Arduino/releases
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
/**************************************************************************
* Define Upper / Lower mixing (Remark // if not true)
**************************************************************************/
#define UPPER
//#define LOWER
/**************************************************************************
* (Pin) Definitions
**************************************************************************/
#define ENCODER_A 2 // Encoder pin A INT0/PCINT18 D2.
#define ENCODER_B 3 // Encoder pin B INT1/PCINT19 D3.
#define ENCODER_BTN 4 // STEP Encoder pushbutton D4.
#define CALIBRBTN 5 // Calibration has no function.
#define RITSWITCH 6 // View output frequency on CLK1.
#define TXSWITCH 7 // Calibrate IF BFO AM SSB frequency output CLK2.
#define OLED_RESET -1 // OLED reset
#define F_MIN 1000UL // Lower frequency limit 1 KHz
#define F_MAX 100000000UL // Upper frequency limit 100 MHz
/**************************************************************************
* EEPROM data locations
**************************************************************************/
#define EE_SAVED_RADIX 0 // Stepsize pointer
#define EE_SAVED_AFREQ 4 // Actual Tx Frequency (CLK0)
#define EE_SAVED_BFREQ 8 // BFO (IF) Frequency (CLK2)
#define EE_SAVED_XFREQ 12 // X-tal frequency (25 or 27 MHz)
#define EE_SAVED_OFSET 16 // store correction
#define EE_SAVED_CALBR 20 // calibrated indicator
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); //Best resolution for my OLED SSD1306 0.96.
//Adafruit_SSD1306 display(OLED_RESET);
Si5351 si5351;
Rotary r = Rotary(ENCODER_A, ENCODER_B);
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
const int sampleWindowPeak = 200;
int lastPeak;
unsigned int sample;
/**************************************************************************
* Declarations
**************************************************************************/
volatile uint32_t vfo_if = 780000000ULL / SI5351_FREQ_MULT; // CLK0 start IF
volatile uint32_t vfo_hf = 2696500000ULL / SI5351_FREQ_MULT; // CLK2 start Tx freq
volatile uint32_t vfo_lo = vfo_hf - vfo_if; // CLK1 start Rx freq
volatile uint32_t vfo_s = vfo_hf; // Saved for RIT
uint32_t vco_c = 0; // X-tal correction factor
uint32_t xt_freq;
uint32_t test = 0;
uint32_t radix = 10000ULL, old_radix = 10000ULL; //start step size
int act_clk = 0, disp_txt = 0;
bool stepflag = false, calflag = false, modeflag = false, ritset = true;
bool kalibrate = false, actupdt = false;
volatile bool changed_f = false;
unsigned long startTime;
unsigned long duration;
unsigned long startMillis = 0;
/**************************************/
/* Interrupt service routine for */
/* encoder frequency change */
/**************************************/
ISR(PCINT2_vect) {
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)
{
switch (act_clk)
{
case 0: // HF frequency
if (dir == 1)
vfo_hf += radix;
if (dir == -1) {
if (vfo_hf < radix) break; // to prevent negative value
vfo_hf -= radix;
}
break;
case 1: // HF frequency (only if RIT is on)
if (dir == 1)
vfo_hf += radix;
if (dir == -1) {
if (vfo_hf < radix) break; // to prevent negative value
vfo_hf -= radix;
}
break;
case 2: // IF frequency
if (dir == 1)
vfo_if += radix;
if (dir == -1) {
if (vfo_if < radix) break; // to prevent negative value
vfo_if -= radix;
}
break;
}
if(vfo_hf > F_MAX)
vfo_hf = F_MAX;
if(vfo_hf < F_MIN)
vfo_hf = F_MIN;
changed_f = true;
}
/**************************************/
/* Read the buttons with debouncing */
/**************************************/
boolean get_button()
{
if (!digitalRead(ENCODER_BTN)) // Stepsize
{
delay(20);
startTime = millis(); // Start counting
if (!digitalRead(ENCODER_BTN))
{
while (!digitalRead(ENCODER_BTN));
stepflag = true;
}
duration = millis() - startTime; // Total time pressed
}
else if (!digitalRead(CALIBRBTN)) // Calibrate
{
delay(20);
if (!digitalRead(CALIBRBTN))
{
while (!digitalRead(CALIBRBTN));
calflag = true;
}
}
else if (!digitalRead(TXSWITCH)) // Selection
{
delay(20);
if (!digitalRead(TXSWITCH))
{
while (!digitalRead(TXSWITCH));
modeflag = true;
}
}
if (stepflag | calflag | modeflag) return 1;
else return 0;
}
/********************************************************************************
* RIT switch handling
* Switch to small stepsize (100 Hz)
*******************************************************************************/
void rit_switch() // Read RIT_SWITCH
{
if (!digitalRead(RITSWITCH) && !ritset){ // RIT on
act_clk = 1;
ritset = true;
vfo_s = vfo_hf; // Save Tx freq
old_radix = radix; // Save actual stepsize
radix = 100; // Set stepsize to 100 Hz
}
else if (digitalRead(RITSWITCH) && ritset){ // RIT 0ff
act_clk = 0; // RTx mode
ritset = false;
vfo_hf = vfo_s; // Restore to original vco_t
radix = old_radix; // Back to old stepsize
disp_txt = 0; // Clear line
// Update LO frequency based on the restored HF frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
}
}
/**************************************/
/* Displays the frequency and stepsize*/
/**************************************/
void display_frequency()
{
char LCDstr[10];
char Hertz[9];
int p,q = 0;
uint32_t freq = 0ULL;
display.clearDisplay();
switch(act_clk)
{
case 0: // HF frequency
freq = vfo_hf;
break;
case 1: // HF frequency (Used in RIT Mode)
freq = vfo_lo;
break;
case 2: // IF frequency
freq = vfo_if;
break;
}
display.setCursor(110,18); //MHz display position
display.setTextSize(1);
Hertz[1]='\0'; // empty array
sprintf(LCDstr, "%ld", freq); // convert freq to string
p=strlen(LCDstr); // determine length
if (p>6){ // MHz
display.print(F("MHz"));
q=p-6;
strcpy(Hertz,LCDstr); // get Herz digits (6)
strcpy(LCDstr+q,Hertz+(q-1)); // copy into LCDstr and add to MHz
LCDstr[q]='.'; // decimal point
}
else { // KHz
display.print(F("KHz"));
q=p-3;
strcpy(Hertz,LCDstr); // get Herz digits (3)
strcpy(LCDstr+q,Hertz+(q-1)); // copy into LCDstr and add to KHz
LCDstr[q]='.'; // decimal point
}
switch (p) // display stepsize
{
case 4: // 1 KHZ
display.setCursor(48,0);
break;
case 5: // 10 KHZ
display.setCursor(36,0);
break;
case 6: // 100 KHZ
display.setCursor(24,0);
break;
case 7: // 1 MHZ
display.setCursor(12,0);
break;
case 8: // 10 MHZ
display.setCursor(0,0);
break;
case 9: // 100 MHZ
display.setCursor(0,0);
break;
}
display.setTextSize(2);
display.println(LCDstr);
display_settings();
}
/********************************************/
/* Displays S/Meter, step, mode and version */
/********************************************/
void display_settings()
//S/meter scale for 148 GTL +30db, or other transceivers HF at 60dB.
{
unsigned long startMillis = millis(); // Start of sample window
float peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0; //minimum value
unsigned int signalMin = 1023; //maximum value
// collect data for 3000 mS
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(A0); //get reading from microphone
if (sample < 1023) // toss out spurious readings
{
if (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
float db = map(peakToPeak, 80 , 1023, 50, 550 ); //calibrate for deciBels
for (int x = 5; x < 114; x = x + 8) { //draw scale
display.drawLine(x, 55, x, 52, WHITE);
}
display.drawRect(0, 55, 125, 9, WHITE); //draw outline of bar graph
int r = map(db, 50, 550, 1, 127); //set bar graph for width of screen
display.fillRect(1, 56, r, 7, WHITE); //draw bar graph with a width of r
display.fillRect(lastPeak - 10, 56, 2, 7, WHITE);
if (r > lastPeak)
lastPeak = r;
else
lastPeak = lastPeak - 5;
// Stepsize
display.setCursor(1, 18);
display.setTextSize(1);
display.print(F("TS:"));
switch (radix)
{
case 1:
display.println(F(" 1Hz"));
break;
case 10:
display.println(F(" 10Hz"));
break;
case 100:
display.println(F(" 100Hz"));
break;
case 1000:
display.println(F(" 1KHz"));
break;
case 10000:
display.println(F(" 10KHz"));
break;
case 100000:
display.println(F("100KHz"));
break;
case 1000000:
display.println(F(" 1MHz"));
break;
}
// Mode display
display.setCursor(63, 18); //display position CLK0, CLK1, IFCLK2.
switch (act_clk)
{
case 0:
display.println(F("CLK0"));
//display.println(F("RTx"));
break;
case 1:
display.println(F("CLK1"));
//display.println(F("RIT"));
break;
case 2:
display.println(F("CLK2IF"));
//display.println(F("IF"));
break;
}
// Version
//display.setCursor(15, 55);
//display.print(F("Si5351 vfo V.3.2"));
display.setCursor(1, 42);
display.setTextSize(1);
display.println("1 3 5 7 9 +30db +60"); //Scale for Cobra 148 gtl, or HF transceivers.
//display.println("S1 3 5 7 9 20 40 +60"); //Other commercial HF transceivers.
display.setCursor(1, 30); // Display position SIG
display.setTextSize(1); // Font size.
display.println("SIG");
display.setTextColor(WHITE);
display.drawLine(0, 16, 103, 16, WHITE);
display.drawLine(103, 16, 107, 27, WHITE);
display.drawLine(107, 27, 127, 27, WHITE);
display.drawLine(0, 27, 19, 27, WHITE);
display.drawLine(19, 27, 24, 39, WHITE);
display.drawLine(24, 39, 127, 39, WHITE);
// Messages
display.setCursor(12, 30);
switch (disp_txt)
{
case 0:
display.print(F(" ")); // clear line
break;
case 1:
display.print(F(" Turn RIT Off "));
break;
case 2:
display.print(F(" Set to CLK0 "));
//display.print(F(" Set to TRx "));
break;
case 3:
display.print(F(" Calibration "));
break;
case 4:
display.print(F(" Calibration OK! "));
break;
}
display.display();
}
/**************************************/
/* S E T U P */
/**************************************/
void setup()
{
Serial.begin(115200);
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)
// Clear the buffer
display.clearDisplay();
// Initial text Cobra 148 GTL display.
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(10,10);
display.println("Cobra 148");
display.setCursor(45,40);
display.print("GTL");
display.display();
delay(2000);
display.clearDisplay();
// Read EEPROM
radix = eeprom_read_dword((const uint32_t *)EE_SAVED_RADIX);
if ((radix < 10ULL) | (radix > 1000000ULL)) radix = 100ULL;
vfo_hf = eeprom_read_dword((const uint32_t *)EE_SAVED_AFREQ);
if ((vfo_hf < F_MIN) | (vfo_hf > F_MAX)) vfo_hf = 26965000ULL;
test = (vfo_hf / 10); // Round to 10Hz
vfo_hf = test * 10;
vfo_if = eeprom_read_dword((const uint32_t *)EE_SAVED_BFREQ);
if ((vfo_if < F_MIN) | (vfo_if > F_MAX)) vfo_if = 7800000ULL;
test = (vfo_if / 10); // Round to 10Hz
vfo_if = test * 10;
vco_c = 0;
if (eeprom_read_dword((const uint32_t *)EE_SAVED_CALBR) == 0x60) {
vco_c = eeprom_read_dword((const uint32_t *)EE_SAVED_OFSET);
}
xt_freq = SI5351_XTAL_FREQ + vco_c;
//initialize the Si5351
si5351.set_correction(18000); // Set to zero because I'm using an other calibration method
si5351.init(SI5351_CRYSTAL_LOAD_8PF, xt_freq); // Frequency get from settings in VFO_si5351.h file
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
// Set CLK0 to output the starting "HF" frequency as set above by vfo = ?
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
// Set CLK1 to output the LO frequncy = HF +/- IF frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
// Set CLK2 to output bfo frequency
si5351.set_freq((vfo_if * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK2);
si5351.output_enable(SI5351_CLK0, 1); // Enable CLK0
si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);
si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_4MA);
si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_4MA);
// Encoder setup
pinMode(ENCODER_BTN, INPUT_PULLUP);
PCICR |= (1 << PCIE2); // Enable pin change interrupt for the encoder
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei(); // Start interrupts
// Pin Setup
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(CALIBRBTN, INPUT_PULLUP); // Calibrate
pinMode(RITSWITCH, INPUT_PULLUP); // RIT Switch
pinMode(TXSWITCH, INPUT_PULLUP); // Select TRx or BFO
// Timer
startMillis = millis(); // Reset actual freq store timer
actupdt = true;
// Display first time
display.clearDisplay();
display.setTextColor(WHITE);
display_frequency(); // Update the display
}
/**************************************/
/* L O O P */
/**************************************/
void loop()
{
if (disp_txt == 4) {
delay(3000); // Display calibration OK and wait 3 seconds
disp_txt = 0; // Clear text line
}
// Serial.println(actupdt);
// Update the display if the frequency has been changed
if (changed_f) {
display_frequency();
si5351.output_enable(SI5351_CLK0, 1); // Enable CLK0
if (act_clk == 0 && !kalibrate) // No Tx update during calibrate
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
else if (act_clk == 2) // BFO update
si5351.set_freq((vfo_if * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK2);
si5351.output_enable(SI5351_CLK2, 1); // Enable CLK2
// Update LO frequency
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq((vfo_lo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK1);
si5351.output_enable(SI5351_CLK1, 1); // Enable CLK1
changed_f = false;
disp_txt = 0; // Clear line
startMillis = millis(); // Reset actual freq store timer
actupdt = false;
}
rit_switch(); // read RIT switch
// Store actual freq and other settings once per 5 seceonds after frequency change
if((millis() - startMillis) > 5000 && !actupdt) {
eeprom_write_dword((uint32_t *)EE_SAVED_AFREQ, vfo_hf);
eeprom_write_dword((uint32_t *)EE_SAVED_BFREQ, vfo_if);
startMillis = millis(); // Reset actual freq store timer
actupdt = true;
Serial.println("Act freq and BFO stored");
}
// Button press
// Also stored the last used frequency together with the step size before store
if (get_button()) {
if (stepflag) { // Stepsize button
if (duration > 500) { // For a long time then prev step
switch (radix)
{
case 1000000:
radix = 100000;
break;
case 100000:
radix = 10000;
break;
case 10000:
radix = 1000;
break;
case 1000:
radix = 100;
break;
case 100:
radix = 10;
break;
case 10:
radix = 1;
break;
case 1:
radix = 1000000;
break;
}
}
else {
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;
}
}
eeprom_write_dword((uint32_t *)EE_SAVED_RADIX, radix); // Store stepsize
stepflag = false;
}
else if (modeflag) { // Mode button
if (act_clk == 0) act_clk = 2; else act_clk = 0;
eeprom_write_dword((uint32_t *)EE_SAVED_BFREQ, vfo_if);
modeflag = false;
disp_txt = 0; // Clear line
}
else if (calflag) { // Calibrate button
if (!digitalRead(RITSWITCH)){ // RIT is on
disp_txt = 1; // Message set RIT off
}
else if (act_clk == 2){ // BFO mode on
disp_txt = 2; // Message set BFO off
}
else if (!kalibrate) { // Start calibrate
vfo_s = vfo_hf; // Save actual freq
old_radix = radix; // and stepsize
vfo_hf = SI5351_XTAL_FREQ; // en set to default x-tal
disp_txt = 3; // Message Calibrate
kalibrate = true;
radix = 10; // Set to 10 Hz
si5351.set_freq((vfo_hf * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0); // Set HF Freq
}
else if (kalibrate) { // after tuning x-tal freq
kalibrate = false;
vco_c = vfo_hf - SI5351_XTAL_FREQ; // difference
vfo_hf = vfo_s; // restore freq
radix = old_radix; // and stepsize
disp_txt = 4; // Message Calibrate OK
eeprom_write_dword((uint32_t *)EE_SAVED_OFSET, vco_c); // store correction
xt_freq = SI5351_XTAL_FREQ + vco_c; // Calibrated x-tal freq
eeprom_write_dword((uint32_t *)EE_SAVED_CALBR, 0x60); // Calibrated
si5351.init(SI5351_CRYSTAL_LOAD_8PF, xt_freq); // Initialize
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(vfo_if * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK2); // correct BFO frequency
si5351.set_freq(vfo_hf * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0); // Correct HF freq
#if defined(UPPER)
vfo_lo = vfo_hf + vfo_if; // Upper / lower mixing
#elif defined(LOWER)
vfo_lo = vfo_hf - vfo_if;
#endif
si5351.set_freq(vfo_lo * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK1); // correct LO frequency
}
calflag = false;
}
}
display_frequency(); // Update display
} // end while loop


