筐体の大きさは、10cm×10cmで高さは5cmです。
内部の様子です。
回路は、JA2GQPブログに掲載されている回路とほぼ同じですが、LDOは、TA48M033F(3.3V 0.5A)に変更しました。
電源、AFアンプ基板とSi4735、Arduino Pro Mini基板に分けました。
Arduino Pro Miniへのスケッチの書き込みは、右側に出ているピンヘッダにFT-232RL USBシリアル変換モジュールを接続して行います。
パネル面です。
表示は、OLEDです。周波数以外の数字は小さいのですが、表示が明るいので、視認性は良いです。が・・・、やはり年寄りには厳しいです。Hi
周波数STEPは、メインダイヤルと兼用です。スイッチ付きのロータリーエンコーダを使いました。
VOL+ VOL-のボタンは付けず、Si4735のヴォリュームは50固定として、AFアンプの前に10Kオームのボリュームを入れました。この方が実戦的だと思います。
実際にアンテナに接続して受信テストをしてみました。感度的には、問題ありません。周波数は、最小STEPが1KHzですので、SSB,CWともまず目的信号におおまかに合わせて、BFOスイッチを押し、BFO周波数を変化させて聞きやすい音に調整します。また、混信状況に応じてBand Widthスイッチでバンド幅を調整します。AGCは、ON/OFFできますが、ONのままでOKだと思います。MODEボタンを押すたびに、USBとLSBが切り替えられます。
参考までに以下にスケッチを掲載します。オリジナルは、PU2CLRのスケッチで、JA2GQP局のスケッチと合わせて若干の改良を加えました。試作段階ですので、まだバグがある可能性があります。
SI4735.h
SSD1306AsciiAvrI2c.h
patch_init.h
周波数表示は、lcdnums14x24というフォントを使っていますが、数字と「:」と「.」のフォントしかありません。スペースのフォントがないので、「:」のフォントデータをすべて0x00に変更しておき、「:」を表示すると実際にはスペースが表示されるようにして対応しています。
---------------------------------------------------------------------
/*
Si4735 HF SSB CW Reciever
JH7UBC Keiji Hata
2021.4.13
オリジナルはPU2CLR 、その改良版JA2GQPのスケッチを改良
*/
#include <SI4735.h>
#include <SSD1306AsciiAvrI2c.h>
#include "Rotary.h"
#include "patch_init.h" // SSB patch for whole SSBRX initialization string
const uint16_t size_content = sizeof ssb_patch_content; // see ssb_patch_content in patch_full.h or patch_init.h
#define AM_FUNCTION 1
#define RESET_PIN 7
// Enconder PINs
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
// Buttons controllers
#define STEP_SWITCH 8 // STEP(1, 5 or 10 KHz)
#define BFO_SWITCH 9 // BFO(BFO or VFO)
#define VOL_DOWN 10 // Volume Down
#define VOL_UP 11 // Up
#define BAND_BUTTON_DOWN 12 // Band Down
#define BAND_BUTTON_UP 14 // Up
#define AGC_SWITCH 15 // AGC ON/OF
#define BANDWIDTH_BUTTON 16 // banddwith(1.2, 2.2, 3.0, 4.0, 0.5, 1.0 KHz)
#define MODE_SWITCH 17 // MODE (LSB/USB)
#define LSB 1
#define USB 2
bool bfoOn = false;
bool disableAgc = true;
int currentBFO = 0;
// Encoder control variables
volatile int encoderCount = 0;
// Some variables to check the SI4735 status
uint16_t currentFrequency;
uint8_t currentStep = 1;
uint8_t currentBFOStep = 50;
uint8_t bandwidthIdx = 2;
const char *bandwitdth[] = {"1.2", "2.2", "3.0", "4.0", "0.5", "1.0"};
typedef struct
{
uint16_t minimumFreq;
uint16_t maximumFreq;
uint16_t currentFreq;
uint16_t currentStep;
uint8_t currentSSB;
} Band;
Band band[] = {
{1800, 2000, 1900, 1, LSB}, //160m
{3500, 4000, 3535, 1, LSB}, //80m
{7000, 7200, 7045, 1, LSB}, //40m
{10100, 10150, 10130, 1, USB},//30m
{14000, 14350, 14100, 1, USB},//20m
{18068, 18168, 18110, 1, USB},//17m
{21000, 21450, 21150, 1, USB},//15m
{24890, 24990, 24930, 1, USB},//13m
// {27000, 27700, 27300, 1, USB},
{28000, 28500, 28200, 1, USB}};//10m
const int lastBand = (sizeof band / sizeof(Band)) - 1;
byte bandIdx = 0;
int currentFreqIdx = 2;//40m band
SI4735 si4735;
SSD1306AsciiAvrI2c oled;
Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B);
//---------- Encorder procedure(INT) ---------------
void rotaryEncoder()
{
unsigned char result = encoder.process();
if (result){
if (result == DIR_CW){
encoderCount = 1;
}else{
encoderCount = -1;
}
}
}
void setup(){
// Encoder pins
pinMode(ENCODER_PIN_A, INPUT_PULLUP);
pinMode(ENCODER_PIN_B, INPUT_PULLUP);
pinMode(BANDWIDTH_BUTTON, INPUT_PULLUP);
pinMode(BAND_BUTTON_UP, INPUT_PULLUP);
pinMode(BAND_BUTTON_DOWN, INPUT_PULLUP);
pinMode(VOL_UP, INPUT_PULLUP);
pinMode(VOL_DOWN, INPUT_PULLUP);
pinMode(BFO_SWITCH, INPUT_PULLUP);
pinMode(AGC_SWITCH, INPUT_PULLUP);
pinMode(STEP_SWITCH, INPUT_PULLUP);
pinMode(MODE_SWITCH, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); // Encoder interrupt
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE);
oled.begin(&Adafruit128x64, 0x3C); //OLED initialize
oled.setFont(font5x7);
oled.setCursor(10,3);
oled.print("Loading SSB patch");
si4735.getDeviceI2CAddress(RESET_PIN); // Looks for the I2C buss address and set it. Returns 0 if error
si4735.setup(RESET_PIN, AM_FUNCTION);
loadSSB();
delay(100);
oled.clear();
si4735.setTuneFrequencyAntennaCapacitor(1); // Set antenna tuning capacitor for SW.
si4735.setSSB(band[currentFreqIdx].minimumFreq, band[currentFreqIdx].maximumFreq, band[currentFreqIdx].currentFreq, band[currentFreqIdx].currentStep, band[currentFreqIdx].currentSSB);
delay(100);
//----- 初期画面表示 --------
currentFrequency = si4735.getFrequency();
showFrequency();
oled.setFont(font5x7);
oled.setCursor(60,4);
oled.print("STEP KHz");
showStep();
oled.setCursor(0,4);
oled.print("Mode");
showMode();
oled.setCursor(0,5);
oled.print("B W");
showBandwidth();
oled.setCursor(60,5);
oled.print("BFO");
showBFO();
oled.setCursor(0,6);
oled.print("AGC");
showAGC();
si4735.setVolume(50);
}
//------ SSB patch Load ------
void loadSSB()
{
si4735.queryLibraryId(); // Is it really necessary here? I will check it.
si4735.patchPowerUp();
delay(50);
si4735.downloadPatch(ssb_patch_content, size_content);
// Parameters
// AUDIOBW - SSB Audio bandwidth; 0 = 1.2KHz (default); 1=2.2KHz; 2=3KHz; 3=4KHz; 4=500Hz; 5=1KHz;
// SBCUTFLT SSB - side band cutoff filter for band passand low pass filter ( 0 or 1)
// AVC_DIVIDER - set 0 for SSB mode; set 3 for SYNC mode.
// AVCEN - SSB Automatic Volume Control (AVC) enable; 0=disable; 1=enable (default).
// SMUTESEL - SSB Soft-mute Based on RSSI or SNR (0 or 1).
// DSP_AFCDIS - DSP AFC Disable or enable; 0=SYNC MODE, AFC enable; 1=SSB MODE, AFC disable.
si4735.setSSBConfig(bandwidthIdx, 1, 0, 1, 0, 1);
}
//------ Frequency display ---------
void showFrequency()
{
unsigned long fr;
unsigned long sf_rx;
sf_rx = (float)currentFrequency * 1000.0;
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);
}
//------ Step Display ---------
void showStep(){
oled.setFont(font5x7);
oled.setCursor(85,4);
if(currentStep == 1){
oled.print(" 1");
}else if(currentStep == 5){
oled.print(" 5");
}else{
oled.print("10");
}
}
//----- Mode Display -----
void showMode(){
oled.setFont(font5x7);
oled.setCursor(30,4);
if(band[currentFreqIdx].currentSSB == 1){
oled.print("LSB");
}else if(band[currentFreqIdx].currentSSB == 2){
oled.print("USB");
}
}
//------ Bandwidth Display ---------
void showBandwidth(){
oled.setFont(font5x7);
oled.setCursor(30,5);
oled.print(bandwitdth[bandwidthIdx]);
}
//------- BFO Displsy -------
void showBFO(){
oled.setFont(font5x7);
oled.setCursor(90, 5);
if(bfoOn == 1)
{
oled.print("ON ");
oled.setCursor(90,6);
oled.print(" ");
oled.setCursor(90,6);
oled.print(currentBFO);
}else{
oled.print("OFF");
oled.setCursor(90,6);
oled.print(" ");
}
}
//------ AGC Display --------
void showAGC(){
oled.setFont(font5x7);
si4735.getAutomaticGainControl();
oled.setCursor(30, 6);
oled.print((si4735.isAgcEnabled()) ? "ON " : "OFF");
}
//----- Status Display -----
void showStatus(){
showFrequency();
showMode();
showStep();
showBandwidth();
showBFO();
showAGC();
}
//------ BAND UP ------
void bandUp()
{
// save the current frequency for the band
band[currentFreqIdx].currentFreq = currentFrequency;
if (currentFreqIdx < lastBand)
{
currentFreqIdx++;
}
else
{
currentFreqIdx = 0;
}
// si4735.setTuneFrequencyAntennaCapacitor(1); // Set antenna tuning capacitor for SW.
si4735.setSSB(band[currentFreqIdx].minimumFreq, band[currentFreqIdx].maximumFreq, band[currentFreqIdx].currentFreq, band[currentFreqIdx].currentStep, band[currentFreqIdx].currentSSB);
currentStep = band[currentFreqIdx].currentStep;
delay(250);
currentFrequency = si4735.getCurrentFrequency();
showStatus();
}
//------- BAND DOWN -------
void bandDown()
{
// save the current frequency for the band
band[currentFreqIdx].currentFreq = currentFrequency;
if (currentFreqIdx > 0)
{
currentFreqIdx--;
}
else
{
currentFreqIdx = lastBand;
}
// si4735.setTuneFrequencyAntennaCapacitor(1); // Set antenna tuning capacitor for SW.
si4735.setSSB(band[currentFreqIdx].minimumFreq, band[currentFreqIdx].maximumFreq, band[currentFreqIdx].currentFreq, band[currentFreqIdx].currentStep, band[currentFreqIdx].currentSSB);
currentStep = band[currentFreqIdx].currentStep;
delay(250);
currentFrequency = si4735.getCurrentFrequency();
showStatus();
}
//------ MODE Change -------
void modeChange()
{
if(band[currentFreqIdx].currentSSB == 1)
{
band[currentFreqIdx].currentSSB = 2;
}else{
band[currentFreqIdx].currentSSB = 1;
}
band[currentFreqIdx].currentFreq = currentFrequency;
band[currentFreqIdx].currentStep = currentStep;
si4735.setSSB(band[currentFreqIdx].minimumFreq, band[currentFreqIdx].maximumFreq, band[currentFreqIdx].currentFreq, band[currentFreqIdx].currentStep, band[currentFreqIdx].currentSSB);
currentFrequency = si4735.getCurrentFrequency();
showStatus();
}
//------ main loop ----------
void loop()
{
char bfo_disp=0;
// Check if the encoder has moved.
if (encoderCount != 0)
{
if (bfoOn)
{
currentBFO = (encoderCount == 1) ? (currentBFO + currentBFOStep) : (currentBFO - currentBFOStep);
si4735.setSSBBfo(currentBFO);
if(currentBFO >= 1000){
currentBFO = currentBFO - 1000;
si4735.frequencyUp();
currentFrequency = si4735.getFrequency();
}
else if(currentBFO < 0){
currentBFO = currentBFO + 1000;
si4735.frequencyDown();
currentFrequency = si4735.getFrequency();
}
showBFO();
showFrequency();
}
else
{
if (encoderCount == 1)
si4735.frequencyUp();
else
si4735.frequencyDown();
// Show the current frequency only if it has changed
currentFrequency = si4735.getFrequency();
showFrequency();
}
encoderCount = 0;
}
//------ STEP switch -------
if (digitalRead(STEP_SWITCH) == LOW){
if(currentStep == 1){
currentStep = 5;
}else if(currentStep == 5){
currentStep = 10;
}else{
currentStep = 1;
}
si4735.setFrequencyStep(currentStep);
band[currentFreqIdx].currentStep = currentStep;
showStep();
while(digitalRead(STEP_SWITCH) == LOW);
}
//--------- Band UP -----------
if(digitalRead(BAND_BUTTON_UP) == LOW){
bandUp();
while(digitalRead(BAND_BUTTON_UP) == LOW);
}
//--------- Band DOWN ---------
if(digitalRead(BAND_BUTTON_DOWN) == LOW){
bandDown();
while(digitalRead(BAND_BUTTON_DOWN) == LOW);
}
//--------- BANDWIDTH switch ---------
if (digitalRead(BANDWIDTH_BUTTON) == LOW)
{
bandwidthIdx++;
if (bandwidthIdx > 5)
bandwidthIdx = 0;
si4735.setSSBAudioBandwidth(bandwidthIdx);
// If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
if (bandwidthIdx == 0 || bandwidthIdx == 4 || bandwidthIdx == 5)
si4735.setSBBSidebandCutoffFilter(0);
else
si4735.setSBBSidebandCutoffFilter(1);
showBandwidth();
while(digitalRead(BANDWIDTH_BUTTON) == LOW);
}
//------- AGC switch ---------
if (digitalRead(AGC_SWITCH) == LOW)
{
disableAgc = !disableAgc;
// siwtch on/off ACG; AGC Index = 0. It means Minimum attenuation (max gain)
si4735.setAutomaticGainControl(disableAgc, 1);
showAGC();
while(digitalRead(AGC_SWITCH) == LOW);
}
//------- BFO switch --------
if(digitalRead(BFO_SWITCH) == LOW)
{
bfoOn = !bfoOn;
showBFO();
while(digitalRead(BFO_SWITCH) == LOW);
}
//-------- MODE switch ------
if (digitalRead(MODE_SWITCH) == LOW)
{
modeChange();
while(digitalRead(MODE_SWITCH) == LOW);
}
delay(10);
}