無線機の製作記録の全てを公開してます。 https://sites.google.com/view/ja2gqpから必要なファイルをダウンロードできます。 このBlogは、詳細な説明は一切行っておらず、ある程度のスキルが要求されます。 質問に応じますが、カスタマイズは対応しません。尚、コメントは誰でも出来る様に変更しました。
2021年5月29日土曜日
OPC-2350LU互換ユニット
2021年5月4日火曜日
ESP8266 Multi encorder
ESP8266固有の所は、
void ICACHE_RAM_ATTR enc1_proc();
void ICACHE_RAM_ATTR enc2_proc();
である。
スケッチ
Rotaly.hを含め、スケッチはJA2GQP's Download siteのesp8266フォルダからダウンロードできる。
//-------------------------------------------------------------
// ESP8266 Multi encorder test
// 2010/5/4
// JA2GQP
//-------------------------------------------------------------
#include "src/Rotary.h"
Rotary r1 = Rotary(D3,D4); // encorder 1
void ICACHE_RAM_ATTR enc1_proc();
Rotary r2 = Rotary(D1,D2); // encorder 2
void ICACHE_RAM_ATTR enc2_proc();
//---------- setup --------------------------------------------
void setup() {
Serial.begin(115200);
r1.begin(0);
attachInterrupt(digitalPinToInterrupt(D3), enc1_proc, CHANGE);
attachInterrupt(digitalPinToInterrupt(D4), enc1_proc, CHANGE);
r2.begin(1);
attachInterrupt(digitalPinToInterrupt(D1), enc2_proc, CHANGE);
attachInterrupt(digitalPinToInterrupt(D2), enc2_proc, CHANGE);
}
//---------- main loop ----------------------------------------
void loop() {
}
//---------- encorder 1 proc ---------------------------------
void enc1_proc() { // rotary encoder events
unsigned char result = r1.process();
if (result == DIR_NONE) {
// do nothing
}
else if (result == DIR_CW) {
Serial.println("Enc 1 CW");
}
else if (result == DIR_CCW) {
Serial.println("Enc 1 CCW");
}
}
//---------- encorder 2 proc ---------------------------------
void enc2_proc() { // rotary encoder events
unsigned char result = r2.process();
if (result == DIR_NONE) {
// do nothing
}
else if (result == DIR_CW) {
Serial.println("Enc 2 CW");
}
else if (result == DIR_CCW) {
Serial.println("Enc 2 CCW");
}
}
2020年10月24日土曜日
si4735 oled改造
回路図
BFO
EEPROM初期化
STEP SWを押しながら電源onするとEEPROMが初期化される。
使用感
実際にSSBを聞いてみて、BFOがバンドが替わると、ズレる。また、保存したBFOも再電源投入後ズレる。(既知のバグと思っている。)今回、バンドをメモリ保存して無いが無くても不自由感は無かった。
2020年6月18日木曜日
esp8266 VFO
![]() |
| 回路全体 |
![]() |
| 画面表示例 |
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;
}
}
2020年5月30日土曜日
si4735評価ボード
![]() |
| 操作面 |
![]() |
| 部品面 |
![]() |
| oledを実装した操作面 |
![]() |
| 部品面 |
回路図
スケッチ
All Band(All Mode)のoled版PU2CLRのライブラリに含まれている物を使っている。
パターン引き回し上、I/Oポート変更とスイッチ処理の変更を行っている。これ以外に周波数設定を変更したが、不満があるが実用に耐える。
スイッチのチャタリング防止(例えば、Band Up処理)は、タイマーを使わずに確実な動作にした。修正したスケッチは、JA2GQP's Download siteのsi4735フォルダがらダウンロード可能。
一例
/////////////////////////
// BAND UP switch
/////////////////////////
else if (digitalRead(BAND_BUTTON_UP) == LOW){
bandUp();
while(digitalRead(BAND_BUTTON_UP) == LOW)
;
}
動作試験
7MHz SSBとFM放送を受信したが、実用レベルと思う。2020年5月3日日曜日
Attiny85 VFO PCB版
回路図
設定
![]() |
| デバイスの設定 |
![]() |
| ヒューズビット |
スケッチ
電源電圧3.3Vに対応させるため、LCDをAQM1602XA-RN-GBWに変更した。それに伴い変更している。ファイルはJA2GQP's Download siteのattiny85フォルダからダウンロード可能。1)インクルードファイル(si5351a21.h)
////////////////////////////////////////////////////////////////////////
// Author: Hans Summers, 2015
// Website: http://www.hanssummers.com
//
// A very very simple Si5351a demonstration
// using the Si5351a module kit http://www.hanssummers.com/synth
// Please also refer to SiLabs AN619 which describes all the registers to use
//----------------------------------------------------------------------
// Modifications: JA2GQP,2017/5/20
// 1)Output is CLK0 and CLK2.
// 2)Arduino and stm32duino Operable.
////////////////////////////////////////////////////////////////////////
#include <Wire.h>
#define OUTE_CTRL 3
#define CLK0_CTRL 16 // Register definitions
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define PLL_RESET 177
#define XTAL_LOAD_C 183
#define R_DIV_1 0b00000000 // R-division ratio definitions
#define R_DIV_2 0b00010000
#define R_DIV_4 0b00100000
#define R_DIV_8 0b00110000
#define R_DIV_16 0b01000000
#define R_DIV_32 0b01010000
#define R_DIV_64 0b01100000
#define R_DIV_128 0b01110000
#define Si5351A_ADDR 0x60
#define CLK_SRC_PLL_A 0b00000000
#define CLK_SRC_PLL_B 0b00100000
#define XTAL_FREQ 25000000 // Crystal frequency for Hans' board
//------------- momory define ------------
uint32_t xtalFreq = XTAL_FREQ; // 2017/9/29
////////////////////////////////////////////////////////////////////////
// I2C write
////////////////////////////////////////////////////////////////////////
void Si5351_write(byte Reg , byte Data){
Wire.beginTransmission(Si5351A_ADDR);
Wire.write(Reg);
Wire.write(Data);
Wire.endTransmission();
}
////////////////////////////////////////////////////////////////////////
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
///////////////////////////////////////////////////////////////////////
void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom){
uint32_t P1; // PLL config register P1
uint32_t P2; // PLL config register P2
uint32_t P3; // PLL config register P3
P1 = (uint32_t)(128 * ((float)num / (float)denom));
P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
P2 = (uint32_t)(128 * ((float)num / (float)denom));
P2 = (uint32_t)(128 * num - denom * P2);
P3 = denom;
Si5351_write(pll + 0, (P3 & 0x0000FF00) >> 8);
Si5351_write(pll + 1, (P3 & 0x000000FF));
Si5351_write(pll + 2, (P1 & 0x00030000) >> 16);
Si5351_write(pll + 3, (P1 & 0x0000FF00) >> 8);
Si5351_write(pll + 4, (P1 & 0x000000FF));
Si5351_write(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
Si5351_write(pll + 6, (P2 & 0x0000FF00) >> 8);
Si5351_write(pll + 7, (P2 & 0x000000FF));
}
////////////////////////////////////////////////////////////////////////
// Set up MultiSynth with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate
// register, it is a #define in si5351a.h
////////////////////////////////////////////////////////////////////////
void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv){
uint32_t P1; // Synth config register P1
uint32_t P2; // Synth config register P2
uint32_t P3; // Synth config register P3
P1 = 128 * divider - 512;
P2 = 0; // P2 = 0, P3 = 1 forces an integer value for the divider
P3 = 1;
Si5351_write(synth + 0, (P3 & 0x0000FF00) >> 8);
Si5351_write(synth + 1, (P3 & 0x000000FF));
Si5351_write(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
Si5351_write(synth + 3, (P1 & 0x0000FF00) >> 8);
Si5351_write(synth + 4, (P1 & 0x000000FF));
Si5351_write(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
Si5351_write(synth + 6, (P2 & 0x0000FF00) >> 8);
Si5351_write(synth + 7, (P2 & 0x000000FF));
}
////////////////////////////////////////////////////////////////////////
// output enable/disable control
// Example: si5351a_enable(0x01);
// CLK0 output disable
////////////////////////////////////////////////////////////////////////
void si5351a_enable(byte oeb){
Si5351_write(OUTE_CTRL,oeb); // AN619 Register 3
}
////////////////////////////////////////////////////////////////////////
// Set CLK0 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL A
// and MultiSynth 0
// and produces the output on CLK0
////////////////////////////////////////////////////////////////////////
void si5351aSetFrequency(uint32_t frequency){
uint32_t pllFreq;
// uint32_t xtalFreq = XTAL_FREQ; // 2017/9/29
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t divider;
divider = 900000000 / frequency; // Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer
//division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency:
//the divider * desired output frequency
mult = pllFreq / xtalFreq; // Determine the multiplier to
//get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL A with the calculated multiplication ratio
setupPLL(MSNA_ADDR, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(MS0_ADDR, divider, R_DIV_1);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
// Si5351_write(PLL_RESET, 0x20);
// Finally switch on the CLK0 output (0x4F)
// and set the MultiSynth0 input to be PLL A
Si5351_write(CLK0_CTRL, 0x4F | CLK_SRC_PLL_A); // Strength 8mA
}
////////////////////////////////////////////////////////////////////////
// Set CLK1 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency2(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL B
// and MultiSynth 1
// and produces the output on CLK1
////////////////////////////////////////////////////////////////////////
void si5351aSetFrequency2(uint32_t frequency){
uint32_t pllFreq;
// uint32_t xtalFreq = XTAL_FREQ; // 2017/9/29
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t divider;
divider = 900000000 / frequency; // Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer
//division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency:
//the divider * desired output frequency
mult = pllFreq / xtalFreq; // Determine the multiplier to
//get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL B with the calculated multiplication ratio
setupPLL(MSNB_ADDR, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(MS2_ADDR, divider, R_DIV_1);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
// Si5351_write(PLL_RESET, 0x80);
// Finally switch on the CLK1 output
// and set the MultiSynth0 input to be PLL B
Si5351_write(CLK2_CTRL, 0x6F | CLK_SRC_PLL_B); // Strength 8mA
}
2)VFOスケッチ(st7032_vfo)
///////////////////////////////////////////////////////////////////////////////////
// si5351a VFO Atiny85
//
// 2020/3/23
// JA2GQP
//---------------------------------------------------------------------------------
// fuse BIT
// low = f1 , high = DF , extend = ff
//
///////////////////////////////////////////////////////////////////////////////////
//---------- Library include --------------------------------
#include "src/si5351a21.h"
#include "src/Rotary.h"
#include "src/ST7032.h"
#include <EEPROM.h>
//---------- Define value -----------------------------------
#define SW1 (val < 682) && ( val >= 472) // SW1
#define SW2 (val < 767) && ( val >= 642) // SW2
#define SW3 (val < 818) && ( val >= 727) // SW3
#define SW_TX SW1
#define SW_RIT SW2
#define ENC_A PB1
#define ENC_B PB3
Rotary r=Rotary(ENC_B,ENC_A);
ST7032 lcd;
////////////////////////////////
// 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[4] = {
// DEF LOW(cw) MID(ssb) HI
7050000 ,7000000 ,7045000 ,7200000
};
//---- data offset -----
const byte DEF_F = 0;
const byte LOW_F = 1;
const byte MID_F = 2;
const byte HI_F = 3;
//---- Bfo data -----
const unsigned long IF_FREQ = 10700000L;
const unsigned long CW = 10700600L;
const unsigned long LSB = 10701500L;
const unsigned long USB = 10698500L;
////////////////////////////////
// etc
////////////////////////////////
const byte Int_End = 73; // Initial end code
const int LW_RIT = -5000; // RIT Lower Limit
const int HI_RIT = 5000; // RIT Upper Limit
byte bar5[8]={B00000, // s-meter barcode data
B11011,
B11011,
B11011,
B11011,
B11011,
B11011,
B00000};
byte bar1[8]={B10000,
B11000,
B11100,
B11110,
B11110,
B11100,
B11000,
B10000};
unsigned long previousMillis;
//---------- Variable setting -------------------------------
unsigned long Vfo_Dat;
unsigned long Vfo_Datb = 0;
unsigned long Bfo_Dat;
unsigned long Bfo_Datb = 0;
unsigned long Enc_Step = 10;
int Rit_Dat; // RIT Data
int Rit_Datb = 0; // old
long Lng_Wk; // Long Work
int Int_Wk; // Int Work
char Lcd_Dat[12] = " "; // Lcd Display Buffer
byte Flg_Tx = 0; // TX Flag
byte Flg_Mode = 0; // Mode Flag
byte Flg_Over = 0;
byte Flg_Rit = 0; // RIT Flag
byte Flg_Rdisp = 0; // RIT Display Flag
byte Flg_Disp = 0; // Display Flag
long Enc_Delay = 0; // Encorder Delay(ms)
long Time_Passd; // Time Pass(ms)
byte Flg_eepWT = 0; // EEP Write Flag
long Enc_Velocityb;
long Enc_Velocity; // Encorder velocity(ms)
//---------- Initialization Program ----------------------
void setup() {
unsigned int val;
pinMode(ENC_A,INPUT_PULLUP); // PB3
pinMode(ENC_B,INPUT_PULLUP); // PB4
lcd.begin(16, 2);
lcd.setContrast(40); // at 3.3V
// lcd.setContrast(10); // at 5V
lcd.createChar(0, bar5);
lcd.createChar(1, bar1);
GIMSK |= (1 << PCIE); // Enable pin change interrupt
PCMSK |= (1 << PCINT1) | (1 << PCINT3);
sei(); // INT Enable
val = analogRead(A0); // EEPROM init check
delay(1);
if(SW_RIT) // init?
EEPROM.write(Eep_Init,0x00);
do{
val = analogRead(A0);
delay(1);
if(SW_RIT){
lcd.setCursor(0, 0);
lcd.print("EEP Initialize");
}
}
while(SW_RIT);
if(EEPROM.read(Eep_Init) != Int_End){
delay(10);
eep_init();
}
eep_rdata();
delay(10);
band_check();
mode_disp();
}
//---------- Main program ---------------------------------
void loop() {
unsigned int val;
val = analogRead(A0);
delay(1);
if(SW_TX) // SW_TX
Flg_Tx = 1;
else
Flg_Tx = 0;
Fnc_TRdsp(); // T/R Display
//////////////////////////
// RX
//////////////////////////
if(Flg_Tx == 0){ // RX
display_smeter(analogRead(A2)); // s-meter Display
Fnc_Stp();
si5351a_enable(0x00);
val = analogRead(A0);
delay(1);
if(SW_RIT) // SW_RIT
Fnc_Rit(); // RIT Flag set
if(((Vfo_Dat != Vfo_Datb) && (Flg_Rit == 0)) || (Flg_Disp == 1)){
si5351aSetFrequency(Vfo_Dat + IF_FREQ);
band_check();
mode_disp();
Fnc_Fdsp(Vfo_Dat);
Vfo_Datb = Vfo_Dat;
Flg_Disp = 0;
}
if(((Rit_Dat != Rit_Datb) && (Flg_Rit == 1)) || (Flg_Rdisp == 1)){
si5351aSetFrequency(Vfo_Dat + IF_FREQ + Rit_Dat);
rit_disp(Rit_Dat);
Rit_Datb = Rit_Dat;
Flg_Rdisp = 0;
}
si5351aSetFrequency2(Bfo_Dat); // Bfo data out
}
//////////////////////////
// TX
//////////////////////////
else{
mode_disp();
if(Flg_Over == 0){
si5351a_enable(0x00);
si5351aSetFrequency(Vfo_Dat + IF_FREQ);
}
else{ // Frequency over
si5351a_enable(0x07);
}
if(Flg_Rit == 1)
Flg_Rdisp = 1;
else
Flg_Disp = 1;
}
//////////////////////////
// Save to EEPROM
//////////////////////////
if(Flg_eepWT == 1){ // EEPROM auto Write
if(Time_Passd+2000 < millis()){
eep_wdata();
Flg_eepWT = 0;
}
}
}
//---------- Function Rit ---------
void Fnc_Rit(){
unsigned int val;
if(Flg_Rit == 0){
Rit_Dat = 0;
Flg_Rit = 1;
lcd.setCursor(11,0);
lcd.print("+0 ");
}
else{
mode_disp();
si5351aSetFrequency(Vfo_Dat + IF_FREQ);
Flg_Rit = 0;
}
do{
val = analogRead(A0);
delay(1);
}
while(SW_RIT);
}
//---------- RIT Display ---------------------------------------------
void rit_disp(int rit_data) {
unsigned int ri,rit;
lcd.setCursor(11,0);
if (rit_data >= 0){
lcd.print("+ ");
lcd.setCursor(12,0);
lcd.print(rit_data,DEC);
}
else{
lcd.print(" ");
lcd.setCursor(11,0);
lcd.print(rit_data,DEC);
}
}
//---------- Mode display ----------------------------------------------
void mode_disp() {
lcd.setCursor(11,0);
if((Flg_Tx == 1) && (Flg_Over == 1))
lcd.print(" Over"); // frequency over
else if (Flg_Mode == 0)
lcd.print(" CW"); // CW
else if(Flg_Mode == 1)
lcd.print(" LSB"); // LSB
else if(Flg_Mode == 2)
lcd.print(" USB"); // USB
}
//---------- LCD Bar Display PROC. ------------------------
void display_smeter(int strength){
// range is 0 to 1024 of adc
//meter run from position 3 to 14 (12 )
int scale = (strength*15)/1024;
lcd.setCursor(0, 1);
lcd.print("S");
int smeter=(scale*11)/15;
if(smeter<10)
lcd.print(smeter);
else
lcd.print("9");
lcd.print(">");
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= 1000) {
previousMillis = currentMillis;
//clear all after a small interval
lcd.setCursor(3, 1);
lcd.print(" ");
lcd.noCursor();
}
// write it and show for 500 milli sec
for (int i = 3; i<scale; i++){
lcd.setCursor(i, 1);
lcd.write(byte(0));
if(smeter>9)
lcd.print("+");
}
}
//---------- Encorder procedure(INT) ----------------------
ISR(PCINT0_vect) {
unsigned char result = r.process();
if(Flg_Tx == 0){
if(result) {
Enc_Velocity = millis() - Enc_Velocityb;
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_eepWT = 1;
Time_Passd = Enc_Velocityb =millis();
}
}
}
//---------- Function Send/receive Display ------
void Fnc_TRdsp(){
lcd.setCursor(0,0);
if(Flg_Tx == 1)
lcd.print("T");
else
lcd.print("R");
}
//---------- Function Frequency Display ---------
void Fnc_Fdsp(long f_disp){
Fnc_Dot_Edit(Lcd_Dat,f_disp);
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print(Lcd_Dat);
}
//---------- Function String Dot Edit --------
char *Fnc_Dot_Edit(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++;
if((0 != u) && (0 == (i % 3)))
*p++ = '.';
}
while( 0 != u )
;
if ( n < 0 )
*p++ = '-';
*p = '\0';
Fnc_Revr( str );
return str;
}
//---------- Function String Reverse ---------------------------------
void Fnc_Revr(char *str){
int i,n;
char c;
n=strlen(str);
for(i = 0;i < n / 2;i++){
c=str[i];
str[i]=str[n - i - 1];
str[n - i - 1]=c;
}
}
//---------- 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(){
Vfo_Dat = eep_read4(Eep_Freq);
delay(10);
}
//---------- EEPROM Dat Write ----------------------------------------
void eep_wdata(){
eep_write4(Vfo_Dat,Eep_Freq);
delay(10);
}
//---------- EEPROM Initialization ------------------------------------
void eep_init(){
int i;
for (i=0;i<64;i++) // 0 clear(128byte)
EEPROM.write(i, 0);
eep_write4(FRQ_TBL[DEF_F],Eep_Freq);
EEPROM.write(Eep_Init,Int_End); // Init end set(73)
}
//---------- Function Encorder STEP ----------------------------------
void Fnc_Stp(){
if(Time_Passd+2000 < millis()){
Enc_Step = 10;
}
else{
if(Enc_Delay+6000 < millis()){
if(Enc_Velocity < 100) // chenge STEP?
Enc_Step = Enc_Step * 10;
if(Enc_Step > 10000) // 10kHz over?
Enc_Step = 10; // STEP = 10Hz
Enc_Delay = millis();
}
}
}
//---------- 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)
Bfo_Dat = CW;
Flg_Over = 0;
}
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_Over = 1;
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;
}
}
}
実装写真
| Adafruit純正または中華製モジュール |
| 秋月電子製モジュール |


















