回路図
公開済みのベースボード回路を一部変更した。
・BFOのSW8は、未使用。・音質改善のため、AF AMP入力部を変更。
BFO
STEPをFMなし、AM3段階、SSB2段階とした。SSB時のBFO設定は、STEPと云った概念としたので操作上は存在しない。ただ、S/W上BFOが存在しているが、何も意識することはない筈だ。
スケッチ
JA2GQP's Download site2のsi4735フォルダから必要なファイルがダウンロード可能。
/////////////////////////////////////////////////////////////////////
// si4735 DSP Radio program(PU2CLR all in one OLE based) Ver1,01
//
// 2020/10/22
// JA2GQP
//-------------------------------------------------------------------
// Ver1.01 Eep initialaz(STEP SW)
/////////////////////////////////////////////////////////////////////
#include <SI4735.h>
#include "src/SSD1306AsciiAvrI2c.h" // https://github.com/greiman/SSD1306Ascii
#include "src/Rotary.h"
#include <EEPROM.h>
#include "src/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 FM_BAND_TYPE 0
#define MW_BAND_TYPE 1
#define SW_BAND_TYPE 2
#define LW_BAND_TYPE 3
// OLED Diaplay constants
#define I2C_ADDRESS 0x3C
#define RST_PIN -1 // Define proper RST_PIN if required.
#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 (Am/LSB/USB)
#define MIN_ELAPSED_TIME 100
#define MIN_ELAPSED_RSSI_TIME 150
#define DEFAULT_VOLUME 40 // change it for your favorite sound volume
#define FM 0
#define LSB 1
#define USB 2
#define AM 3
#define LW 4
#define SSB 1
////////////////////////////////
// default value
////////////////////////////////
//#define DEF_AM 1431 // Gifu radio
//#define DEF_FM 7630 // FM pipi
#define DEF_VOL 40 // volume
#define DEF_B_IDX 0 // Band Index
////////////////////////////////
// etc
////////////////////////////////
#define SIG_TIME 200
#define Int_End 73 // EEPROM init end
////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
#define Eep_Top 0x00 // Eep Init(1byte)
#define Eep_End Eep_Top // Eep Init(1byte)
#define Eep_VOL 0x02 // Volume
#define Eep_Mode 0x04 // Mode(1byte)
#define Eep_B_IDX 0x06 // Band index
#define Eep_Bfo 0x08 // bfo data
//#define Eep_FM 0x02 // FM frequency(2byte)
//#define Eep_AM 0x04 // MF frequency(2byte)
#define Eep_Freq 0x10 // Frequency data
const char *bandModeDesc[] = {"FM ", "LSB", "USB", "AM "};
uint8_t currentMode = FM;
bool bfoOn = false;
bool disableAgc = true;
bool ssbLoaded = false;
bool fmStereo = true;
int currentBFO = 0;
byte Flg_eepWT = 0;
long elapsedRSSI = millis();
long elapsedButton = millis();
long Time_Passd; // int to hold the arduino miilis since startup
// Encoder control variables
volatile int encoderCount = 0;
// Some variables to check the SI4735 status
uint16_t currentFrequency;
uint16_t previousFrequency;
uint8_t currentStep = 1;
//uint8_t currentBFOStep = 25;
uint8_t currentBFOStep = 100;
uint8_t bwIdxSSB = 2;
const char *bandwitdthSSB[] = {"1.2", "2.2", "3.0", "4.0", "0.5", "1.0"};
uint8_t bwIdxAM = 0;
const char *bandwitdthAM[] = {"6", "4", "3", "2", "1", "1.8", "2.5"};
/*
Band data structure
*/
typedef struct
{
uint8_t bandType; // Band type (FM, MW or SW)
uint16_t minimumFreq; // Minimum frequency of the band
uint16_t maximumFreq; // maximum frequency of the band
uint16_t currentFreq; // Default frequency or current frequency
uint16_t currentStep; // Defeult step (increment and decrement)
} Band;
////////////////////////////////
// Band table
////////////////////////////////
Band band[] = {
{FM_BAND_TYPE, 7600, 9500, 7630, 10},
// {LW_BAND_TYPE, 100, 510, 300, 1},
{MW_BAND_TYPE, 522, 1602, 1431, 9},
{SW_BAND_TYPE, 1800, 3500, 1900, 1}, // 160 meters
{SW_BAND_TYPE, 3500, 4500, 3535, 1}, // 80 meters
// {SW_BAND_TYPE, 4500, 5500, 4850, 5},
// {SW_BAND_TYPE, 5600, 6300, 6000, 5},
{SW_BAND_TYPE, 6800, 7800, 7100, 1}, // 40 meters
// {SW_BAND_TYPE, 9200, 10000, 9600, 5},
// {SW_BAND_TYPE, 10000, 11000, 10100, 1}, // 30 meters
{SW_BAND_TYPE, 10000, 11000, 10700, 1}, // 30 meters
// {SW_BAND_TYPE, 11200, 12500, 11940, 5},
// {SW_BAND_TYPE, 13400, 13900, 13600, 5},
{SW_BAND_TYPE, 14000, 14500, 14200, 1}, // 20 meters
// {SW_BAND_TYPE, 15000, 15900, 15300, 5},
// {SW_BAND_TYPE, 17200, 17900, 17600, 5},
{SW_BAND_TYPE, 18000, 18300, 18100, 1}, // 17 meters
{SW_BAND_TYPE, 21000, 21900, 21200, 1}, // 15 mters
{SW_BAND_TYPE, 24890, 26200, 24940, 1}, // 12 meters
{SW_BAND_TYPE, 26200, 27900, 27500, 1}, // CB band (11 meters)
{SW_BAND_TYPE, 28000, 30000, 28400, 1},
};
const int lastBand = (sizeof band / sizeof(Band)) - 1;
byte bandIdx = 0;
uint8_t rssi = 0;
uint8_t stereo = 1;
uint8_t volume = 0;
////////////////////////////////
// Devices class declarations
////////////////////////////////
Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B);
SI4735 si4735;
SSD1306AsciiAvrI2c oled;
//---------- Setup ---------------------------------
void setup()
{
byte ee;
byte ssbm = 0;
// 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);
oled.begin(&Adafruit128x64, 0x3C);
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); // Encoder interrupt
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE);
si4735.getDeviceI2CAddress(RESET_PIN); // Looks for the I2C buss address and set it. Returns 0 if error
si4735.setup(RESET_PIN, FM_BAND_TYPE);
delay(300);
// EEPROM.get(Eep_End, ee);
if(digitalRead(STEP_SWITCH) == LOW){ // Eep initialaz
oled.setFont(font5x7);
oled.setCursor(18, 4);
oled.print("Initialization");
while(digitalRead(STEP_SWITCH) == LOW)
;
eep_init();
}
eep_rdata();
useBand(); // Set up the radio for the current band (see index table variable bandIdx )
previousFrequency = currentFrequency;
si4735.setVolume(volume);
showStatus();
}
//---------- Encorder procedure(INT) ---------------
void rotaryEncoder()
{ // rotary encoder events
uint8_t encoderStatus = encoder.process();
if (encoderStatus)
{
if (encoderStatus == DIR_CW)
{
encoderCount = 1;
}
else
{
encoderCount = -1;
}
Time_Passd = millis();
Flg_eepWT = 1;
}
}
//---------- EEPROM Dat Read ---------------------------------------
void eep_rdata(){
EEPROM.get(Eep_VOL, volume);
EEPROM.get(Eep_B_IDX,bandIdx);
EEPROM.get(Eep_Mode,currentMode);
EEPROM.get(Eep_Freq, currentFrequency);
band[bandIdx].currentFreq = currentFrequency;
EEPROM.get(Eep_Bfo, currentBFO);
si4735.setSSBBfo(currentBFO);
}
//---------- EEPROM Dat Write ----------------------------------------
void eep_wdata(){
EEPROM.put(Eep_Mode, currentMode);
EEPROM.put(Eep_B_IDX, bandIdx);
EEPROM.put(Eep_Bfo, currentBFO);
EEPROM.put(Eep_Freq, currentFrequency);
}
//---------- EEPROM Initialization ----------------------------------
void eep_init(){
for(int i=0;i<64;i++)
EEPROM.put(Eep_Top + i, 0);
EEPROM.put(Eep_Mode, FM);
EEPROM.put(Eep_B_IDX, 0);
EEPROM.put(Eep_VOL, 40);
EEPROM.put(Eep_Bfo, 0);
EEPROM.put(Eep_Freq, 7630);
EEPROM.put(Eep_End, Int_End);
}
//---------- Volume lebel display ------------------
void showVolume()
{
static byte v_old=0;
byte v_new;
v_new = si4735.getCurrentVolume();
if(v_old != v_new)
{
oled.setFont(font5x7);
oled.setCursor(96, 6);
oled.print("VOL ");
oled.setCursor(115, 6);
oled.print(v_new);
v_old = v_new;
}
}
//---------- Frequency display ---------------------
void showFrequency()
{
unsigned long fr;
unsigned long sf_rx;
if(currentMode == FM) // FM
sf_rx = (float)currentFrequency * 10000.0;
else if(currentMode == AM) // AM
sf_rx = (float)currentFrequency * 1000.0;
else if((currentMode == LSB) || (currentMode == USB)){ // SSB
sf_rx = ((float)currentFrequency * 1000.0) + currentBFO;
}
else
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);
}
//---------- Status display ------------------------
void showStatus()
{
showFrequency();
oled.setFont(font5x7);
if(!bfoOn){
oled.setCursor(109, 4);
oled.print(" ");
if ( currentMode != FM){
oled.setCursor(109, 4);
oled.print(currentStep);
oled.print("k");
}
}
else if(bfoOn){
oled.setCursor(109, 4);
oled.print(" ");
oled.setCursor(109, 4);
oled.print(currentBFOStep);
}
if (currentMode == LSB || currentMode == USB)
{
oled.setCursor(2, 6);
oled.print("BW ");
oled.setCursor(14, 6);
oled.print(String(bandwitdthSSB[bwIdxSSB]));
}
else if (currentMode == AM)
{
oled.setCursor(2, 6);
oled.print("BW ");
oled.setCursor(14, 6);
oled.print(String(bandwitdthAM[bwIdxAM]));
}
else if ( currentMode == FM) {
oled.setCursor(2, 6);
oled.print((si4735.getCurrentPilot()) ? "STE " : "MON ");
}
// Show AGC Information
si4735.getAutomaticGainControl();
oled.setCursor(50, 6);
oled.print((si4735.isAgcEnabled()) ? "AGCon " : "AGCoff");
showRSSI();
showVolume();
}
//---------- RSSI(signal lebel)----------------------
void showRSSI(){
char a = 0;
unsigned char m = 0;
float rssi_new;
static float rssi_old = 100.0;
static unsigned char mode_old = 255;
unsigned char mode_new;
String bandMode;
/////////////////////////
// MODE display
/////////////////////////
if (currentFrequency < 520 )
bandMode = "LW ";
else
bandMode = bandModeDesc[currentMode];
oled.setFont(font5x7);
mode_new = currentMode;
if(mode_new != mode_old){
oled.setCursor(2, 4);
oled.print(bandMode);
mode_old = mode_new;
}
/////////////////////////
// s-meter display
/////////////////////////
rssi_new = si4735.getCurrentRSSI();
if(rssi_new != rssi_old){
a = (rssi_new - 15) / 3.0; // 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
if(a < 0)
a = 0;
else if(a > 9)
a = 9;
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
rssi_old = rssi_new;
}
}
//---------- BAND up -------------------------------
void bandUp()
{
// save the current frequency for the band
band[bandIdx].currentFreq = currentFrequency;
band[bandIdx].currentStep = currentStep;
if (bandIdx < lastBand)
{
bandIdx++;
}
else
{
bandIdx = 0;
}
useBand();
}
//---------- BAND down -----------------------------
void bandDown()
{
// save the current frequency for the band
band[bandIdx].currentFreq = currentFrequency;
band[bandIdx].currentStep = currentStep;
if (bandIdx > 0)
{
bandIdx--;
}
else
{
bandIdx = lastBand;
}
useBand();
}
//---------- LOAD SSB ------------------------------
void loadSSB()
{
si4735.reset();
si4735.queryLibraryId(); // Is it really necessary here? Just powerDown() maigh work!
si4735.patchPowerUp();
delay(50);
// si4735.setI2CFastMode(); // Recommended
si4735.setI2CFastModeCustom(500000); // It is a test and may crash.
si4735.downloadPatch(ssb_patch_content, size_content);
si4735.setI2CStandardMode(); // goes back to default (100KHz)
// delay(50);
// 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(bwIdxSSB, 1, 0, 1, 0, 1);
delay(25);
ssbLoaded = true;
}
//---------- Use BAND ------------------------------
void useBand()
{
if (band[bandIdx].bandType == FM_BAND_TYPE)
{
currentMode = FM;
si4735.setTuneFrequencyAntennaCapacitor(0);
si4735.setFM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep);
bfoOn = ssbLoaded = false;
}
else
{
if (band[bandIdx].bandType == MW_BAND_TYPE || band[bandIdx].bandType == LW_BAND_TYPE)
si4735.setTuneFrequencyAntennaCapacitor(0);
else
si4735.setTuneFrequencyAntennaCapacitor(1);
if (ssbLoaded)
{
si4735.setSSB(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep, currentMode);
si4735.setSSBAutomaticVolumeControl(1);
si4735.setSsbSoftMuteMaxAttenuation(0); // Disable Soft Mute for SSB
}
else
{
currentMode = AM;
si4735.setAM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, band[bandIdx].currentStep);
si4735.setAutomaticGainControl(1, 0);
si4735.setAmSoftMuteMaxAttenuation(0); // // Disable Soft Mute for AM
bfoOn = false;
}
}
delay(100);
currentFrequency = band[bandIdx].currentFreq;
currentStep = band[bandIdx].currentStep;
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();
}
showFrequency();
}
else
{
if (encoderCount == 1)
si4735.frequencyUp();
else
si4735.frequencyDown();
// Show the current frequency only if it has changed
currentFrequency = si4735.getFrequency();
}
encoderCount = 0;
}
/////////////////////////
// BANDWIDTH switch
/////////////////////////
if (digitalRead(BANDWIDTH_BUTTON) == LOW)
{
if (currentMode == LSB || currentMode == USB)
{
bwIdxSSB++;
if (bwIdxSSB > 5)
bwIdxSSB = 0;
si4735.setSSBAudioBandwidth(bwIdxSSB);
// If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
if (bwIdxSSB == 0 || bwIdxSSB == 4 || bwIdxSSB == 5)
si4735.setSBBSidebandCutoffFilter(0);
else
si4735.setSBBSidebandCutoffFilter(1);
}
else if (currentMode == AM)
{
bwIdxAM++;
if (bwIdxAM > 6)
bwIdxAM = 0;
si4735.setBandwidth(bwIdxAM, 1);
}
showStatus();
while(digitalRead(BANDWIDTH_BUTTON) == LOW)
;
}
/////////////////////////
// BAND UP switch
/////////////////////////
else if (digitalRead(BAND_BUTTON_UP) == LOW){
bandUp();
while(digitalRead(BAND_BUTTON_UP) == LOW)
;
eep_wdata();
}
/////////////////////////
// BAND DOWN switch
/////////////////////////
else if (digitalRead(BAND_BUTTON_DOWN) == LOW){
bandDown();
while(digitalRead(BAND_BUTTON_DOWN) == LOW)
;
eep_wdata();
}
/////////////////////////
// VOLUME DOWN switch
/////////////////////////
else if (digitalRead(VOL_UP) == LOW)
{
si4735.volumeUp();
volume = si4735.getVolume();
showVolume();
while(digitalRead(VOL_UP) == LOW)
;
EEPROM.put(Eep_VOL, volume);
}
/////////////////////////
// VOLUME UP switch
/////////////////////////
else if (digitalRead(VOL_DOWN) == LOW)
{
si4735.volumeDown();
volume = si4735.getVolume();
showVolume();
while(digitalRead(VOL_DOWN) == LOW)
;
EEPROM.put(Eep_VOL, volume);
}
/////////////////////////
// AGC switch
/////////////////////////
else if (digitalRead(AGC_SWITCH) == LOW)
{
disableAgc = !disableAgc;
// siwtch on/off ACG; AGC Index = 0. It means Minimum attenuation (max gain)
si4735.setAutomaticGainControl(disableAgc, 1);
showStatus();
while(digitalRead(AGC_SWITCH) == LOW)
;
}
/////////////////////////
// STEP switch
/////////////////////////
else if (digitalRead(STEP_SWITCH) == LOW)
{
if ( currentMode == FM) {
fmStereo = !fmStereo;
if ( fmStereo )
si4735.setFmStereoOn();
else
si4735.setFmStereoOff(); // It is not working so far.
} else {
if ((bfoOn) && (currentBFOStep == 50)){ // SSM mode
currentStep = 1;
bfoOn = false;
si4735.setFrequencyStep(currentStep);
band[bandIdx].currentStep = currentStep;
showStatus();
}
else{ // AM mode
if (currentStep == 1)
if(currentMode == LSB || currentMode == USB){
bfoOn = !bfoOn;
currentBFOStep = 50;
}
else
currentStep = 5;
else if (currentStep == 5)
currentStep = 10;
else
currentStep = 1;
si4735.setFrequencyStep(currentStep);
band[bandIdx].currentStep = currentStep;
showStatus();
}
}
while(digitalRead(STEP_SWITCH) == LOW)
;
}
/////////////////////////
// MODE switch
/////////////////////////
else if (digitalRead(MODE_SWITCH) == LOW)
{
if (currentMode != FM ) {
if (currentMode == AM)
{
// If you were in AM mode, it is necessary to load SSB patch (avery time)
loadSSB();
currentMode = LSB;
}
else if (currentMode == LSB)
{
currentMode = USB;
}
else if (currentMode == USB)
{
currentMode = AM;
ssbLoaded = false;
bfoOn = false;
}
// Nothing to do if you are in FM mode
band[bandIdx].currentFreq = currentFrequency;
band[bandIdx].currentStep = currentStep;
useBand();
}
while(digitalRead(MODE_SWITCH) == LOW)
;
EEPROM.put(Eep_Mode, currentMode);
}
// Show the current frequency only if it has changed
if (currentFrequency != previousFrequency)
{
previousFrequency = currentFrequency;
showFrequency();
}
// Show RSSI status only if this condition has changed
if ((millis() - elapsedRSSI) > MIN_ELAPSED_RSSI_TIME * 9)
{
si4735.getCurrentReceivedSignalQuality();
int aux = si4735.getCurrentRSSI();
if (rssi != aux)
{
rssi = aux;
showRSSI();
}
elapsedRSSI = millis();
}
delay(10);
if((Time_Passd+2000 < millis()) && Flg_eepWT == 1){
eep_wdata();
Flg_eepWT = 0;
}
}
EEPROM初期化
STEP SWを押しながら電源onするとEEPROMが初期化される。
使用感
実際にSSBを聞いてみて、BFOがバンドが替わると、ズレる。また、保存したBFOも再電源投入後ズレる。(既知のバグと思っている。)今回、バンドをメモリ保存して無いが無くても不自由感は無かった。