回路全体 |
画面表示例 |
s-meterは、イメージデータをimage2cppでモノクロデータにして加工した。カラー表示を行うので有れば、同様なツールとしてlcd-image-converterがある。これらのツールを使えば、簡単にデータ化できる。
周波数表示に使ったフォントは、デジタル風ものを選んだが、等幅フォントでない。この為、1文字毎にロケーションを指定している。
VFOの機能は、オートモード、RIT、自動メモリー保存、バンド範囲チェックなど 際立った機能は無いが実用的に仕上がった。
回路図
DIOにTX/RXまで使っており、拡張性が無いように見えるかも知れないが、必要な機能が達成できれば、それで良い。
mini D1設定
CPUパラメータ設定 |
esp8266は曲者
開発にArduino IDEを使ったが、esp8266(WeMos mini D1)は個性的な印象だ。DIOすら制限があり、この割付がベストだと思っている。esp8266には、産業用コントローラなどで使っているウォッチドグタイマー装備されおり、トラブル時の対策を悩ませる一員となった。スケッチ
必要なファイルは、JA2GQP's Download siteのesp8266フォルダからダウンロード可能。//////////////////////////////////////////////////////////////////////
// si5351a IPS(240x240) VFO program ver.1.0
// Copyright(C)2020.JA2GQP.All rights reserved.
//
// 2020/6/18
// JA2GQP
//--------------------------------------------------------------------
// Function
// 1.STEP(1k,100,10)
// 2.Automatic memory
// 3.Protection Operation At The Time Of Transmission
// 4.Automatic mode switching
// 5.S-Meter/Power meter
//////////////////////////////////////////////////////////////////////
#include <Adafruit_GFX.h>
#include <Fonts/Org_01.h>
#include <EEPROM.h>
#include "src/Arduino_ST7789.h"
#include "src/Rotary.h"
#include "src/si5351.h"
#include <wire.h>
#include "src/smeter.h"
#include <Ticker.h>
//---------- define value ----------------------------------------------
#define TFT_DC D8 // DC
#define TFT_RST D6 // RES
#define TFT_MOSI D7 // SDA
#define TFT_SCLK D5 // SCL
#define SW_TX D0 // D0
#define SW_RIT RX // RX
#define SW_STEP TX // TX
////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
#define Eep_Init 0x00 // Eep Init(1byte*1)
#define Eep_Band 0x01 // Band(1byte*1)
#define Eep_Freq 0x10 // Frequency(4byte*8)
#define Eep_Step 0x30 // STEP(4byte*8)
////////////////////////////////
// frquency data
////////////////////////////////
const unsigned long FRQ_TBL[4] = {
// DEF LOW(cw) MID(ssb) HI
7050000 ,7000000 ,7045000 ,7200000
};
//---- data offset ------------
#define DEF_F 0
#define LOW_F 1
#define MID_F 2
#define HI_F 3
//---- IF offset --------------
#define IF 10700000
#define LSB 10701500
#define USB 10698500
#define CW 10700700
//---- ETC frequency mode -----
#define FT8_1 7041000
#define FT8_2 7074000
#define AM 7195000
////////////////////////////////
// etc
////////////////////////////////
#define Int_End 73 // Initial end code
#define LW_RIT -5000 // RIT Lower Limit
#define HI_RIT 5000 // Upper Limit
////////////////////////////////
// Set Device
////////////////////////////////
Rotary r = Rotary(D3,D4);
void ICACHE_RAM_ATTR enc_proc();
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST);
Si5351 si5351(0x60);
Ticker ticker1;
//---------- Variable setting ------------------------------------------
unsigned long Vfo_Dat = 0; // VFO data
unsigned long Bfo_Dat = 0; // BFO data
unsigned int Enc_Step = 1000; // STEP
int Rit_Dat = 0; // RIT Data
long Lng_Wk = 0; // Long Work
unsigned long Time_Passd = 0; // Time Pass(ms)
int Int_Wk = 0; // Integer Work
byte Flg_Tx = 0; // TX Flag
byte Flg_Mode = 0; // Mode Flag
byte Flg_Over = 0; // Over Flag
byte Flg_Rit = 0; // RIT Flag
byte Flg_enc = 0; // EEP Write Flag
byte Disp_Freq = 1;
byte Disp_Rit = 1;
//----------- setup ----------------------------------------------------
void setup(){
pinMode(SW_TX,INPUT);
pinMode(SW_STEP,INPUT);
pinMode(SW_RIT,INPUT);
EEPROM.begin(128);
r.begin(0);
attachInterrupt(digitalPinToInterrupt(D3), enc_proc, CHANGE);
attachInterrupt(digitalPinToInterrupt(D4), enc_proc, CHANGE);
ticker1.attach_ms(2000, save_time);
tft.init(240, 240); // initialize a ST7789 chip, 240x240 pixels
tft.setFont(&Org_01);
tft.fillScreen(BLACK);
delay(500);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_4MA);//Drive lebel 4mA set
si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_4MA);//Drive lebel 4mA set
if(EEPROM.read(Eep_Init) != Int_End){ // Eep initialaz
delay(10);
eep_init();
}
eep_rdata();
si5351_freq(Vfo_Dat);
band_check();
mode_disp();
step_disp();
dot_disp();
}
//----------- main loop ------------------------------------------------
void loop(){
meter_disp(analogRead(A0)); // s-meter,Power meter
///////////////////
// Receive
///////////////////
if(digitalRead(SW_TX) == LOW){ // RX?(TX is HIGH)
if(digitalRead(SW_STEP) == LOW){ // STEP?
step_proc();
}
if(digitalRead(SW_RIT) == LOW){ // RIT?
rit_disp(Rit_Dat);
rit_proc();
}
if(Flg_Mode == 3)
si5351.output_enable(SI5351_CLK2, 0); // VFO disable
else
si5351.output_enable(SI5351_CLK2, 1); // VFO enable
si5351_bfo(Bfo_Dat);
si5351.output_enable(SI5351_CLK0, 1); // VFO enable
freq_disp(Vfo_Dat);
if(Flg_Rit == 1)
si5351_freq(Vfo_Dat+IF+Rit_Dat); // Frequency(RIT) data out
else
si5351_freq(Vfo_Dat+IF);
Flg_Tx = 0;
band_check();
mode_disp();
yield();
}
///////////////////
// Transmit
///////////////////
else{ // TX
if(Flg_Over == 0){
si5351.output_enable(SI5351_CLK0, 1); // VFO enable
}
else{
si5351.output_enable(SI5351_CLK0, 0); // VFO disable
}
si5351_freq(Vfo_Dat+IF); // Frequency data out
Flg_Tx = 1;
yield();
}
///////////////////
// Common
///////////////////
tx_disp();
if(Flg_Rit == 1){
if(Disp_Rit == 1){
rit_disp(Rit_Dat);
Disp_Rit = 0;
}
}
}
//---------- Write EEPROM --------------------------------------------
void save_time(){
noInterrupts();
if(Flg_enc == 1){
eep_wdata();
Flg_enc = 0;
}
interrupts();
}
//---------- 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);
EEPROM.commit();
}
value = value >> 8;
address--;
}
}
//---------- Read EEPROM 4byte ---------------------------------------
long eep_read4(int address){
long value = 0;
for(int i = 0;i < 4;i++){
value = value | EEPROM.read(address);
if( i < 3){
value = value << 8;
address++;
}
}
return value;
}
//---------- EEPROM Dat Read -----------------------------------------
void eep_rdata(){
Vfo_Dat = eep_read4(Eep_Freq);
Enc_Step = eep_read4(Eep_Step);
}
//---------- EEPROM Dat Write ----------------------------------------
void eep_wdata(){
eep_write4(Vfo_Dat,Eep_Freq);
eep_write4(Enc_Step,Eep_Step);
}
//---------- EEPROM Initialization ------------------------------------
void eep_init(){
int i;
for (i=0;i<128;i++){ // 0 clear(128byte)
EEPROM.write(i, 0);
EEPROM.commit();
}
eep_write4(FRQ_TBL[DEF_F],Eep_Freq);
eep_write4(1000,Eep_Step); // Step(1kHz)
EEPROM.write(Eep_Init,Int_End); // Init end set(73)
EEPROM.commit();
}
//---------- over display ------------------------------------------------
void over_disp() {
static byte Flg_Overb = 0;
if((Flg_Over == 1) && (Flg_Overb == 0)){
tft.fillRect(0,208,240,32,BLACK);
tft.setTextSize(3);
tft.setCursor(0, 232);
tft.setTextColor(MAGENTA);
tft.print("OUT OF RANGE");
Flg_Overb = Flg_Over;
}
if((Flg_Overb == 1) && (Flg_Over == 0)){
tft.fillRect(0,208,240,32,BLACK);
Flg_Overb = Flg_Over;
}
}
//---------- TX display ------------------------------------------------
void tx_disp() {
static byte Flg_Txb = 0;
if((Flg_Tx == 1) && (Flg_Txb == 0)){
tft.fillRect(0,208,240,32,BLACK);
tft.setTextSize(3);
tft.setCursor(0, 232);
if(Flg_Over == 1){
tft.setTextColor(MAGENTA);
tft.print("OUT OF RANGE");
}
else{
tft.setTextColor(YELLOW);
tft.print("SENDING");
}
Flg_Txb = Flg_Tx;
}
if((Flg_Txb == 1) && (Flg_Tx == 0)){
tft.fillRect(0,208,240,32,BLACK);
if(Flg_Rit == 1)
rit_disp(Rit_Dat);
Flg_Txb = Flg_Tx;
}
}
//---------- Frequency display -----------------------------------------
void freq_disp(long freq){
static char buf[10]="";
static char bufb[10]="";
long_asc(buf,freq);
tft.setFont(&Org_01);
tft.setTextSize(6);
for(int i=0;i<8;i++){
if(buf[i]!=bufb[i]){
switch(i){
case 0:
tft.fillRect(132+72,155,35,46,BLACK);
tft.setCursor(135+72, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 1:
tft.fillRect(132+36,155,35,46,BLACK);
tft.setCursor(135+36, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 2:
tft.fillRect(132,155,35,46,BLACK);
tft.setCursor(135, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 3:
tft.fillRect(0+72,155,35,46,BLACK);
tft.setCursor(0+72, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 4:
tft.fillRect(0+36,155,35,46,BLACK);
tft.setCursor(0+36, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 5:
tft.fillRect(0,155,35,46,BLACK);
tft.setCursor(0, 195);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 6:
tft.fillRect(0+36,110,35,46,BLACK);
tft.setCursor(0+36, 150);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
case 7:
tft.fillRect(0,110,35,46,BLACK);
tft.setCursor(0, 150);
tft.setTextColor(GREEN);
tft.print(buf[i]);
bufb[i] = buf[i];
break;
}
}
}
}
//---------- band chek -----------------------------------------------
void band_check(){
if((Vfo_Dat >= FRQ_TBL[LOW_F])
&& (Vfo_Dat <= FRQ_TBL[MID_F])){
Flg_Mode = 0; // CW(0=CW, 1=LSB, 2=USB)
Flg_Over = 0;
Bfo_Dat = CW;
}
else if((Vfo_Dat >= FRQ_TBL[MID_F])
&& (Vfo_Dat <= FRQ_TBL[HI_F])){
if(Vfo_Dat >= 10000000){ // greate than 10MHz
Flg_Mode = 2; // USB(0=CW, 1=LSB, 2=USB)
Bfo_Dat = USB;
}
else{
Flg_Mode = 1; // LSB(0=CW, 1=LSB, 2=USB)
Bfo_Dat = LSB;
}
Flg_Over = 0;
}
else{
Flg_Mode = 3; // AM
Flg_Over = 1;
}
if((Vfo_Dat == FT8_1) || (Vfo_Dat == FT8_2)){
Flg_Mode = 2;
Bfo_Dat = USB;
Flg_Over = 0;
}
if(Vfo_Dat == AM){
Flg_Mode = 3;
Flg_Over = 0;
}
}
//---------- Mode display ----------------------------------------------
void mode_disp() {
static byte Flg_Modeb = 73;
if(Flg_Mode != Flg_Modeb){
tft.setFont(&Org_01);
tft.setTextSize(3);
if (Flg_Mode == 0){ // 0=CW
tft.fillRect(185,138,52,15,BLACK);
tft.setCursor(185, 150);
tft.setTextColor(CYAN);
tft.print("CW ");
}
else if(Flg_Mode == 1){ // 1=LSB
tft.fillRect(185,138,52,15,BLACK);
tft.setCursor(185, 150);
tft.setTextColor(CYAN);
tft.print("LSB");
}
else if(Flg_Mode == 2){ // 2=USB
tft.fillRect(185,138,52,15,BLACK);
tft.setCursor(185, 150);
tft.setTextColor(CYAN);
tft.print("USB");
}
else if(Flg_Mode == 3){ // 3=AM
tft.fillRect(185,138,52,15,BLACK);
tft.setCursor(185, 150);
tft.setTextColor(CYAN);
tft.print("AM ");
}
Flg_Modeb = Flg_Mode;
}
}
//----------- Dot display ----------------------------------------------
void dot_disp(){
tft.setFont(&Org_01);
tft.setTextSize(6);
tft.setCursor(115, 195);
tft.setTextColor(GREEN);
tft.print(".");
}
//---------- long to ascii -------------------------------------------
char *long_asc(char *str,long n){
int i = 0; // Write the number
char *p = str;
unsigned long u = abs(n);
do{
*p++ = "0123456789"[u % 10];
u = u / 10;
i++;
}
while( 0 != u )
;
*p = '\0';
}
//---------- si5351 PLL Output ---------------------------------------
void si5351_freq(unsigned long freq){
si5351.set_freq(freq * SI5351_FREQ_MULT, SI5351_CLK0);
}
//---------- si5351 PLL Output ---------------------------------------
void si5351_bfo(unsigned long bfo){
si5351.set_freq(bfo * SI5351_FREQ_MULT, SI5351_CLK2);
}
//----------- s-meter --------------------------------------------------
void meter_disp(uint16_t signalLevel){
static int a1b,a2b = 0;
const int hMeter = 120; // horizontal center
const int vMeter = 120; // vertical center
const int rMeter = 120; // needle length
// signalLevel = map(signalLevel, 0, 127, 0, 1023);
float smeterValue = (signalLevel) * 330 / 1024; // convert the signal
smeterValue = smeterValue - 34; // shifts needle to zero position
tft.drawBitmap(0, 0, myBitmap, 240,88, WHITE); // draws background
int a1 = (hMeter + (sin(smeterValue / 35.0) * rMeter)); // meter needle horizontal coordinate
int a2 = (vMeter - (cos(smeterValue / 35.0) * rMeter)); // meter needle vertical coordinate
if((a1 != a1b) || (a2 != a2b)){
tft.drawLine(a1b, a2b, hMeter, vMeter, BLACK); // clear needle
a1b = a1;
a2b = a2;
tft.drawLine(a1, a2, hMeter, vMeter, WHITE); // draws needle
delay(1);
}
}
//---------- rit proc ------------------------------------------------
void rit_proc(){
if(Flg_Rit == 0){
Flg_Rit = 1;
// eep_wdata();
}
else{
Flg_Rit = 0;
Rit_Dat = 0;
Disp_Freq = 1;
tft.fillRect(0,208,240,32,BLACK);
}
while(digitalRead(SW_RIT) == LOW)
yield();
}
//---------- RIT Display ---------------------------------------------
void rit_disp(int rit_data) {
unsigned int ri,rit;
tft.fillRect(42,208,154,32,BLACK);
tft.setFont(&Org_01);
tft.setTextSize(3);
tft.setCursor(0, 232);
tft.setTextColor(YELLOW);
tft.print("RIT ");
if (rit_data > 0)
tft.print('+');
tft.print(rit_data);
}
//---------- Rotaly Encorder -----------------------------------------
void enc_proc() { // rotary encoder events
unsigned char result = r.process();
if(Flg_Tx == 0){
if(result) {
Disp_Freq = 1;
Disp_Rit = 1;
if(result == DIR_CW){
Lng_Wk = Vfo_Dat + Enc_Step;
Int_Wk = Rit_Dat + Enc_Step;
}
else{
Lng_Wk = Vfo_Dat - Enc_Step;
Int_Wk = Rit_Dat - Enc_Step;
}
if(Flg_Rit == 1)
Rit_Dat = Int_Wk;
else{
Vfo_Dat = Lng_Wk;
Rit_Dat = 0;
}
Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT); // RIT range check
Flg_enc = 1;
}
}
}
//---------- step proc ------------------------------------------------
void step_proc(){
if(Enc_Step == 1000) // Step = 10Hz ?
Enc_Step = 10; // Yes,1khz set
else
Enc_Step = Enc_Step * 10; // Step down 1 digit
step_disp();
while(digitalRead(SW_STEP) == LOW)
yield();
}
//---------- Function STEP Display -----------------------------------
void step_disp(){
tft.fillRect(185,113,52,15,BLACK);
tft.setFont(&Org_01);
tft.setTextSize(3);
tft.setCursor(190, 125);
tft.setTextColor(CYAN);
switch(Enc_Step){
case 10:
tft.println("10 ");
break;
case 100:
tft.println("100");
break;
case 1000:
tft.println("1k ");
break;
}
}
bravooo,very nice dds project.Congratulations.
返信削除i like yours and ja2nkd projects.
all the best.
Hi Michael.
返信削除Thank you for your comment. I've always longed for analog meters. I am very satisfied with the realization of an analog meter.
Which library to use with this project?
返信削除There are many compilation errors with my library.
Which Adafruit_GFX library to use with this project?
返信削除There are many compilation errors with my library.
Hi Alex.
返信削除This 1.2.2 is installed in Arduino IDE.
https://github.com/adafruit/Adafruit-GFX-Library
Tnx, My friend!
返信削除compilation is OK.
返信削除Screen is black....
SO does not compile:
#define TFT_DC D8 // DC
#define TFT_RST D6 // RES
compiles just like this:
#define TFT_DC 15 // DC
#define TFT_RST 12 // RES
#define TFT_MOSI 13 // SDA
#define TFT_SCLK 14 // SCL
#define SW_TX 16 // D0
#define SW_RIT 3 // RX
#define SW_STEP 1 // TX
where is the problem?
:(
返信削除Alex.
Arduino IDE environment seems to be different. It is the same even if you write the port number directly. There is no problem.
Thank you, I will understand further.
返信削除Alex.
返信削除The GPIO designation may be written directly.
#define TFT_DC 15 // DC (D8)
#define TFT_RST 12 // RES(D6)
#define TFT_MOSI 13 // SDA(D7)
#define TFT_SCLK 14 // SCL(D5)
#define SW_TX 16 // D0(16)
#define SW_STEP 1 // TX(1)
#define SW_RIT 3 // TX(1)
This is no problem. Another problem is that the TFT displays black.
You do not know which esp8266 board you are using and what kind of circuit it is.
The board and Arduino IDE environment are not what I answer.
Thank!
返信削除I did it!
I used a different library
#include "src/Adafruit_ST7789.h"
//#include "src/Arduino_ST7789.h" 22-07-20
https://simple-circuit.com/esp8266-nodemcu-st7789-tft-ips-display/
Thanks for an interesting project!
I will study it further.
http://us5evd.blogspot.com/
返信削除Alex.
返信削除You are the first to be reported working on this project.
Great.
このコメントは投稿者によって削除されました。
返信削除このコメントはブログの管理者によって削除されました。
返信削除I continued to work on the project.
返信削除http://us5evd.blogspot.com/
こんにちは いつも楽しく拝見させていただいて
返信削除おります。 ESP8266VFOをDC受信機用に製作し
ました。 動作に判らないところがありコメント
欄に投稿しました。 ロータリエンコーダのコモン
をアースしていると起動しません。 アースを外し
て電源を入れると起動しますが、アースがないので
周波数は変えられません。 起動後にアースをつな
ぐと8266のアンテナ部分にあるブルーLEDが点灯し
ます。 繋いでも周波数は変えられません。 数個
のエンコーダを試しましたが同一症状でした。
使用したのはARDUINO用の安価なものです。 ご指
定のエンコーダがあれば教えていただければ幸いです。
よろしくお願いします。
JR1MOP OM
返信削除エンコーダは、手持ちの色々色々を試験しましたが、全く問題ありませんでした。
電源on時にブルーLEDが点灯するのは、エンコーダがLowレベルになっているからです。多分、エンコーダの中間位置にあるのでクリック感がある所(1クリック)に回してみてください。
それでもLEDが点灯しているならば、エンコーダA,B端子を3.3kから4.7kを3.3Vに繋いでみてください。(スケッチではpullupしてないので、A、B端子ともプルアップしてみてください)
JR1MOP OM
返信削除エンコーダが使えるか否かは、テスタでA-C間、B-C間、A-B間をあたり各間オープンで、回転させると導通が有れば問題ありません。(正常です)
JA2GQP様
返信削除早速の回答ありがとうございます。
部品を用意して試してみたいと思います。
エンコーダは、はるばる中国から来たので
たぶんお疲れなのだと思います。
73
解決しました。
返信削除JA2GQP様
ご教授ありがとうございました。無事に解決です。
原因はロータリーエンコーダでした。今回5個購入
したのですが、4個が端子間導通してました。
残りの1個を試したところバッチリ動作しました。
本当にありがとうございました。新品だったので
信用してしまいました。
JR1MOP OM
返信削除了解しました。それにしても、4個不良とは酷いですね。私も中華で購入してますが、今迄不良品に出会ったことは有りません。解決して何よりでした。
helloooo!
返信削除when i first time conecting everything is ok and i rotate rotary encoder untill 3,5mhz when i conecting again my wemos in tft said "out of range" i reprogramming wemos but still is "out of range" ...whats the problem maybe you know ???thankksssssss.
73 de 9a3xz Mikele
now is ok...tonight i put video on my YT channel.
返信削除thankssssss for another nice project !
73 de 9a3xz Mikele
https://youtu.be/bgGARo6eazc
返信削除Once again, thanks for nice project!
73 de 9a3xz
Hi Mikel.
返信削除Tweeted. My Twitter is full of flowers. Hi, Hi
https://twitter.com/ja2gqp/status/1327353790215770112
thanks...now i waiting SI4735 from ebay for your last project and will see how is working:)
返信削除all the best and see you soon.
73 de 9a3xz Mikele
Great project. Can we have a web based tuning mechanism for this project ? Host a web page on the esp8266 and accomplish a tuning control.
返信削除Hi Charlie.
返信削除It's a chip that can control the Web, so I'd like to support it. There are issues such as Web API creation, and I don't know if it will end with just the concept.