128x32を実装した様子である。oled128x64との違いは、コールサイン表示有無である。この表示を見る限り、LZ2WSG, KN34PCと違いは、モード表示のみである。
回路図
回路図である。モード切替用に2本、バンド切り替え用に3本のI/O使用。スケッチ
スケッチは、JA2GQP's Download siteのsi5351 VFOフォルダからダウンロード可能。ベースにしたスケッチは、BITX用のもので逆ヘテロダインになっている。今回作ったものは、アッパーヘテロダインで目標周波数+IF周波数である。//////////////////////////////////////////////////////////////////////
// si5351a oled(128x64) VFO program ver.1.0
// Copyright(C)2019.JA2GQP.All rights reserved.
//
// 2019/4/19
// JA2GQP
//--------------------------------------------------------------------
// Function
// 1.STEP(10k,1k,50)
// 2.Automatic memory
// 3.Protection Operation At The Time Of Transmission
// 4.3 bands
// 5.Automatic mode switching
// 6.S-Meter
//--------------------------------------------------------------------
// Reference sketch
// 24.12.2018, Arduino IDE v1.8.8, LZ2WSG, KN34PC
// Si5351 VFO CLK0, 20m, OLED 0.91" 128x32 display, S-meter, RX/TX, 50Hz/1000Hz
//////////////////////////////////////////////////////////////////////
#include "src/Rotary.h" // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
#include "src/si5351.h" // https://github.com/etherkit/Si5351Arduino, v2.1.0
#include "src/SSD1306AsciiAvrI2c.h" // https://github.com/greiman/SSD1306Ascii
#include <EEPROM.h>
////////////////////////////////
// Set Device
////////////////////////////////
Rotary r = Rotary(2, 3);
Si5351 si5351(0x60); // Si5351 I2C address
SSD1306AsciiAvrI2c oled;
////////////////////////////////
// I/O Port
////////////////////////////////
const byte SW_BAND = A0; // Band SW
const byte SW_STEP = A1; // STEP SW
const byte SW_TX = A2; // TX/RX
const byte AD_SM = A7; // S-meter AD
////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte Eep_Init = 0x00; // Eep Init(1byte*1)
const byte Eep_Band = 0x01; // Band(1byte*1)
const byte Eep_Freq = 0x10; // Frequency(4byte*8)
const byte Eep_Step = 0x30; // STEP(4byte*8)
////////////////////////////////
// frquency data
////////////////////////////////
const unsigned long FRQ_TBL[3][4] = {
// DEF LOW(cw) MID(ssb) HI
7050000 ,7000000 ,7045000 ,7200000,
14090000,14000000,14100000,14350000,
18110000,18068000,18110000,18168000
};
//---- data offset -----
const byte DEF_F = 0;
const byte LOW_F = 1;
const byte MID_F = 2;
const byte HI_F = 3;
//---- IF offset -------
const unsigned long RX_IF = 10700000;
const unsigned long TX_IF = 0;
////////////////////////////////
// Encorder STEP
////////////////////////////////
const int STEP_50 = 50; // STEP 50Hz
const int STEP_1k = 1000; // 1k
const int STEP_10k = 10000; // 10k
////////////////////////////////
// etc
////////////////////////////////
const byte Max_Band = 3; // Max Channel(8ch)
const byte Def_Band = 0; // Default Band(0)
const byte Int_End = 72; // Initial end code
const char Call[9] = "JA2GQP"; // Display Call sign
////////////////////////////////
// Memory Assign
////////////////////////////////
unsigned long Vfo_Dat; // VFO Data
unsigned long Enc_Step; // STEP
char Enc_Dir = 0; // -1 DIR_CCW, 0 DIR_NONE, 1 DIR_CCW
unsigned int Val_Smeter = 0;
unsigned int Val_Smeterb = 1;
long Time_Passd; // int to hold the arduino miilis since startup
byte Flg_eepWT = 0; // EEP Write Flag
byte Byt_Band = 0;
byte Flg_Mode;
byte Flg_Over;
byte Flg_Vfo = 0;
byte Flg_Tx = 0;
byte Disp_over = 1;
byte Disp_freq = 1;
byte Disp_Tx = 0;
//---------- setup ----------------------------------------------------
void setup() {
pinMode(SW_STEP, INPUT_PULLUP);
pinMode(SW_TX, INPUT_PULLUP);
pinMode(SW_BAND, INPUT_PULLUP);
pinMode(9, OUTPUT); // Band1
pinMode(10, OUTPUT); // 2
pinMode(11, OUTPUT); // 3
pinMode(12, OUTPUT); // LOW=none, HOGH=CW
pinMode(13, OUTPUT); // LOW=USB, HIGH=LSB
attachInterrupt(0, rotary_encoder, CHANGE);
attachInterrupt(1, rotary_encoder, CHANGE);
si5351.init(SI5351_CRYSTAL_LOAD_8PF,0,0); // crystal 25.000 MHz, correction 0
si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set
oled.begin(&Adafruit128x64, 0x3C);
if(EEPROM.read(Eep_Init) != Int_End){ // Eep initialaz
delay(10);
eep_init();
}
eep_rdata();
mode_disp();
step_disp();
call_disp();
}
//---------- main ----------------------------------------------------
void loop() {
if(digitalRead(SW_TX) == HIGH){ // receive?
Flg_Tx = 0;
si5351.output_enable(SI5351_CLK0, 1); // VFO enable
if (Disp_freq == 1) {
Disp_freq = 0;
Vfo_Dat += Enc_Dir * Enc_Step;
Enc_Dir = 0;
band_check(); // range check
si5351_set_freq(Vfo_Dat + RX_IF);
freq_disp(Vfo_Dat); // frequency display
Flg_Vfo = 1;
mode_disp();
Time_Passd = millis();
Flg_eepWT = 1;
}
if (digitalRead(SW_STEP) == LOW) { // increment events
enc_step();
step_disp();
while (digitalRead(SW_STEP) == LOW);
}
if (digitalRead(SW_BAND) == LOW) {
band_set();
}
Disp_Tx = 1;
}
else{ // send
Flg_Tx = 1;
Disp_over = 1;
if(Flg_Over == 0){ // Within transmittable range?
si5351.output_enable(SI5351_CLK0, 1); // VFO enable
if(Flg_Vfo == 1){
si5351_set_freq(Vfo_Dat + TX_IF);
Flg_Vfo = 0;
}
}
else{ // Out of range
si5351.output_enable(SI5351_CLK0, 0); // VFO disable
if(Disp_over == 1){
fover_disp(); // Display of over range
Disp_over = 0;
Disp_freq = 1;
}
}
if(Disp_Tx == 1){
tx_disp();
Disp_Tx = 0;
}
Disp_freq = 1;
}
Val_Smeter = analogRead(AD_SM);
if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter
sm_disp();
Val_Smeterb = Val_Smeter;
}
if(Flg_eepWT == 1){ // EEPROM auto Write
if(Time_Passd+2000 < millis()){
eep_wdata(Byt_Band);
Flg_eepWT = 0;
}
}
}
//---------- Band set ------------------------------------------------
void band_set(){
if(Byt_Band == Max_Band-1)
Byt_Band = 0;
else
Byt_Band++;
EEPROM.write(Eep_Band,Byt_Band);
while(digitalRead(SW_BAND) == LOW);
eep_rdata();
band_check(); // range check
si5351_set_freq(Vfo_Dat + RX_IF);
freq_disp(Vfo_Dat); // frequency display
step_disp();
mode_disp();
}
//---------- Write EEPROM 4byte --------------------------------------
void eep_write4(long value,int address){
address += 3;
for(int i = 0;i < 4;i++){
byte toSave = value & 0xFF;
if(EEPROM.read(address) != toSave){
EEPROM.write(address,toSave);
}
value = value >> 8;
address--;
}
}
//---------- Read EEPROM 4byte ---------------------------------------
long eep_read4(int address){
long value = 0;
for(int i = 0;i < 4;i++){
value = value | EEPROM.read(address);
if( i < 3){
value = value << 8;
address++;
}
}
return value;
}
//---------- EEPROM Dat Read -----------------------------------------
void eep_rdata(){
Byt_Band = EEPROM.read(Eep_Band);
if(Byt_Band == 0){
digitalWrite(9,HIGH);
digitalWrite(10,LOW);
digitalWrite(11,LOW);
}
else if(Byt_Band == 1){
digitalWrite(9,LOW);
digitalWrite(10,HIGH);
digitalWrite(11,LOW);
}
else if(Byt_Band == 2){
digitalWrite(9,LOW);
digitalWrite(10,LOW);
digitalWrite(11,HIGH);
}
Vfo_Dat = eep_read4(Eep_Freq+Byt_Band*4);
Enc_Step = eep_read4(Eep_Step+Byt_Band*4);
}
//---------- EEPROM Dat Write ----------------------------------------
void eep_wdata(byte band){
EEPROM.write(Eep_Band,band);
eep_write4(Vfo_Dat,Eep_Freq+band*4);
eep_write4(Enc_Step,Eep_Step+band*4);
}
//---------- EEPROM Initialization ------------------------------------
void eep_init(){
int i;
for (i=0;i<128;i++) // 0 clear(128byte)
EEPROM.write(i, 0);
for(i=0;i<Max_Band;i++){
eep_write4(FRQ_TBL[i][DEF_F],Eep_Freq+i*4);
eep_write4(STEP_1k,Eep_Step+i*4); // Step(1kHz)
}
EEPROM.write(Eep_Band,Def_Band); // Default Band(0)
EEPROM.write(Eep_Init,Int_End); // Init end set(73)
}
//---------- Rotaly Encorder -----------------------------------------
void rotary_encoder() { // rotary encoder events
uint8_t result = r.process();
if(Flg_Tx == 0){
if (result) {
Disp_freq = 1;
if (result == DIR_CW)
Enc_Dir = 1;
else
Enc_Dir = -1;
}
}
}
//---------- si5351 PLL Output ---------------------------------------
void si5351_set_freq(uint32_t frequency) {
si5351.set_freq(frequency * SI5351_FREQ_MULT, SI5351_CLK0);
}
//---------- Encorede STEP -------------------------------------------
void enc_step() {
if (Enc_Step == STEP_50) {
Enc_Step = STEP_1k; // 1000 Hz, round to XX.XXX.000
float tmp = round (Vfo_Dat / (STEP_1k * 1.000));
Vfo_Dat = (uint32_t)(tmp * STEP_1k);
Disp_freq = 1;
}
else if(Enc_Step == STEP_1k)
Enc_Step = STEP_10k; // 10k
else
Enc_Step = STEP_50; // 50 Hz
}
//---------- STEP Display --------------------------------------------
void step_disp() {
oled.setCursor(109, 4);
oled.setFont(labels);
if (Enc_Step == STEP_50)
oled.print("6"); // 50Hz
else if(Enc_Step == STEP_1k)
oled.print("8"); // 1k
else
oled.print("9"); // 10k
}
//---------- Frequency renge over --------------------------------------
void fover_disp() {
oled.setFont(lcdnums14x24);
oled.setCursor(1, 0);
oled.print("--.---.--");
}
//---------- callsign display ------------------------------------------
void call_disp() {
oled.setFont(Arial_bold_14);
oled.setCursor(56, 6);
oled.print(Call);
}
//---------- Frequency Display ---------------------------------------
void freq_disp(uint32_t sf_rx) {
uint16_t fr;
oled.setFont(lcdnums14x24);
oled.setCursor(1, 0);
fr = sf_rx / 1000000;
if (fr < 10)
oled.print(':'); // ':' is changed to ' ' in lcdnums14x24.h
oled.print(fr);
oled.print('.');
fr = (sf_rx % 1000000) / 1000;
if (fr < 100)
oled.print('0');
if (fr < 10)
oled.print('0');
oled.print(fr);
oled.print('.');
fr = (sf_rx % 1000) / 10;
if (fr < 10)
oled.print('0');
oled.print(fr);
}
//---------- TX display ------------------------------------------------
void tx_disp() {
oled.setCursor(2, 4);
oled.setFont(labels);
oled.print("1"); // "1" is "TX" in labels.h
}
//---------- Mode display ----------------------------------------------
void mode_disp() {
oled.setCursor(2, 4);
oled.setFont(labels);
if (Flg_Mode == 0){
oled.print("2"); // "2" is "CW" in labels.h
digitalWrite(12,HIGH);
digitalWrite(13,LOW);
}
else if(Flg_Mode == 1){
oled.print("3"); // "3" is "LSB"
digitalWrite(12,LOW);
digitalWrite(13,HIGH);
}
else if(Flg_Mode == 2){
oled.print("4"); // "4" is "USB"
digitalWrite(12,LOW);
digitalWrite(13,LOW);
}
}
//---------- S-Meter Display -----------------------------------------
void sm_disp() {
uint8_t a = 0;
uint8_t m = 0;
a = (Val_Smeter + 3) / 113; // 1024 / 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
oled.setFont(pixels);
oled.setCursor(25, 4);
for (m = 0; m < a; m++)
if (m < 6)
oled.print('7'); // '5' - hollow rectangle, 6px
else
oled.print('8'); // '6' - filled rectangle, 6px
for (m = a; m < 9; m++)
oled.print('.'); // '.' 1px
}
//---------- band chek -----------------------------------------------
void band_check(){
if((Vfo_Dat >= FRQ_TBL[Byt_Band][LOW_F])
&& (Vfo_Dat <= FRQ_TBL[Byt_Band][MID_F])){
Flg_Mode = 0; // CW(0=CW, 1=LSB, 2=USB)
Flg_Over = 0;
}
else if((Vfo_Dat >= FRQ_TBL[Byt_Band][MID_F])
&& (Vfo_Dat <= FRQ_TBL[Byt_Band][HI_F])){
if(Vfo_Dat >= 10000000) // greate than 10MHz
Flg_Mode = 2; // USB(0=CW, 1=LSB, 2=USB)
else
Flg_Mode = 1; // LSB(0=CW, 1=LSB, 2=USB)
Flg_Over = 0;
}
else
Flg_Over = 1;
}
35 件のコメント:
hello i am using your code on my homemade transceiver and i need to enable the bfo on clk2 can you help me please ? thanks in advance
I made this project a simple VFO. Uploaded the BFO added sketch to the download site. I think that it works without problems, but please correct and use if there is a problem. The version with BFO vs. BFO added is si5351_oled_BFO.zip in the si5351 VFO folder.
I am a big fan of qrp i have finished my transceiver using your sketch and now i am trying to define a cw key of arduino that enables the clk1 to output signal for cw operation, and later i want to include a voltmeter in the sketch. Thanks again for your help 73!
I added this BFO because I thought it was better to have it. The clk1 output is not used because it causes isolation degradation. clk1 will not be used in the future. There are no plans to add voltmeters, clocks, etc. If you want to meet your specifications, please develop your own.
hello dear friend!
i m Mikele 9a3xz and i have one problem,when i compile sketch si5351_oled128x64 tell me:
no matching function for call to 'SSD1306AsciiAvrI2c::setFont(void (&)())'
maybe you know what is my problem ???
Thanks in advance ,73 de 9a3xz,Mikele
Hi Mikele.
Download the file from si5351a VFO folder on JA2GQP's Download site. Unzip the zip file and copy the src folder to the same folder as the sketch. The library refers to the src folder where the sketches are stored in the Arduino IDE.
THANKS for quick answer,everything now is OK.Only one question ,on outputs d9,d10 and d11 for band1,band2 and band3 can i conecting the transistors like 2n3904 or something like this ?
thanks.
Hi Mikele.
There is no problem even if the level is converted by connecting a transistor to the port.
yes ,thanks ,maybe also 74hc238 for drive relays for bpf s or lpf s.
OK. As another method, it may be interesting to use PCF8574 as I2C I / F. You can find it on my blog as a VFO for AFPSN. Look if you are interested.
thanks so much for information,i will try with simple 74hc238.all the best from 9a3xz,Mikele...your projects and ja2nkd is so beautiful,many thanks one again dear friend.
OK.He is a good friend.73
Ok, i finished, all working good, only one question, for changing Usb/Lsb on pin SCK on arduino board, i must conecting +5V or ground? Thanks in advance.
This VFO does not have a built-in BFO. SCK / D13 is used as LSB / USB switching output port. This signal is used to switch TRV LSB / USB BFO. Depending on the TRV's BFO circuit, a level converter or relay circuit may be required. If you don't need LSB / USB control, you can leave it unconnected.
yes yes ,i think for bfo circuit,for switching d13 usb/lsb goes to ground or +5v ?
USB when D13 is 0V. LSB at 5V.
sorry in my bad english, i wanted to say if i can change the usb to lsb externally with the switch but i don't think it can.73 de 9a3xz Mikele
This VFO is made with the concept of the main controller. LSB / USB and CW mode change automatically at the set frequency. These modes send the switching signal for TRX. Not an input signal.
If you switch the mode with a manual switch like many VFOs, you need to change the sketch. Again, this VFO cannot be switched manually. For VFO with BFO, si5351_oled_BFO.zip is available on the download site. The mode of the VFO with BFO is also automatically switched.
many thanks for information.dds works good !73 de 9a3xz
I intend to make it easy to operate with as few switches as possible. 73.
大変に参考になる記事を楽しく拝見させていただいております。
OLEDを使ってみたくなり、このプログラムを見つけて試して見ました。
コンパイル時、oled.setFont(pixels)とoled.setFont(labels)でエラーとなりました。
調べてみると、ダウンロードしたZipファイルを解凍したFontのフォルダーに、この2つの
フォントが入っていませんでした。
このフォントはどちらかで別途に入手されたのでしょうか?
以上 よろしくお願い致します。
先にフォントの件で質問した者です。
解決致しました。
ブログの記事をコピーして、個別にライブラリーをDlしていたのを
ダウンロードサイトから改めて作り直したところ、上手く行きました。
お騒がせして申し訳ありませんでした。
nekochan59さん
このoledライブラリは、メモリー節約のために専用フォントを作ってます。
その為、エラーが起きたと思います。正常に動作した様なので、安心しました。
このVFOを作りました。快適に動作しております。
ちょっと気になったことなのですが、少し前の製作品から、Si5351の出力設定を
4mAとして、最大にしていないのは何か理由があるのでしょうか?
si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);
//Drive lebel 4mA set
初期の作品では、出力レベルの選択機能もあった様に思いましたが・・・。
以上 よろしくお願いします。
nekochan59さん
コメント有難うございます。
このVFOのsi5351ライブラリは、メモリに余裕が有ったのでJA2GQPオリジナルを使ってません。その為、少し前のVFOと異なってます。出力レベル4mAは特に意図した値では有りません。2mA、4mA、6mA、8mAの値を設定することで、出力レベルが変わります。
(最小値2mAから最大値8mA)
setupで設定している4mAを変更したいのであれば、数値のみ書替えればOKです。
例えば最大値にする場合
si5351.drive_strength(SI5351_CLK0、SI5351_DRIVE_8MA;
となります。
Do you have any library that runs arduino nano and runs a 1602 lcd screen instead of an oled screen? Than you.
hdyasa45@hotmail.com
Hi TA3IHD.
Unfortunately, there are no plans to port it for the OLED version of 1602.
For the 1602 library, I think you should use the 1602 I2C version.
It can be easily ported by referring to the Attiny85 VFO PCB version.
#include "src/Rotary.h" or #include "Rotary.h"
#include "src/si5351.h" or #include "si5351.h"
#include "src/SSD1306AsciiAvrI2c.h" or #include "SSD1306AsciiAvrI2c.h"
maaf saiya bertanya pak
Unknown
The library is included in the zip file. The library is a src folder. If you unzip the zip file, you will not get an error without modifying the sketch.
Não funciona o link , gostaria de montar um ft73 py7pq
PY7PQ OM
What is FT73?
Is it a protocol for digital communication?
I can't reply because I don't know the specifications of the FT73.
Hello JA2GQP. i have problem with si5351_oled_BFO version because switching USB LSB - on pin D13 between 0V and 5V does not switch USB LSB. is there a bug in the software? I tested on two arduinos. the same effect. no reaction
This VFO automatically switches modes.
LSB below 10MHz. USB above 10MHz.
As written in the sketch, D8 to D13 are set as output ports.
(The mode does not change even if D13 is set to LOW or HIGH.)
I think it's easy to switch LSB/USB on a specific frequency or manually.
ah ok. i see. i have monoband vfo 7mz. how about cw mode?
コメントを投稿