kopia lustrzana https://github.com/kk4das/SSB_Radio_Control
Initial Upload
commit
b8605a9ad6
|
@ -0,0 +1,362 @@
|
||||||
|
// Button handler
|
||||||
|
#include "RadioControl.h"
|
||||||
|
|
||||||
|
//#define BTN_DEBUG
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Button Control Variables //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
byte TuneButtonState = 0;
|
||||||
|
byte lastTuneButtonState = 0;
|
||||||
|
|
||||||
|
// Encoder button control
|
||||||
|
byte EncButtonState = 0;
|
||||||
|
byte lastEncButtonState = 0;
|
||||||
|
|
||||||
|
// Sideband select button control
|
||||||
|
byte SideBandButtonState = 0;
|
||||||
|
byte lastSideBandButtonState = 0;
|
||||||
|
|
||||||
|
// Band Switch button control
|
||||||
|
byte BandButtonState = 0;
|
||||||
|
byte lastBandButtonState = 0;
|
||||||
|
|
||||||
|
// VFO select button control
|
||||||
|
byte VFOButtonState = 0;
|
||||||
|
byte lastVFOButtonState = 0;
|
||||||
|
|
||||||
|
// PTT Control
|
||||||
|
byte PTTState = 0;
|
||||||
|
byte lastPTTState = 0;
|
||||||
|
|
||||||
|
|
||||||
|
//*********************Check Band ************************************
|
||||||
|
void SwapBand() {
|
||||||
|
|
||||||
|
uint32_t freq;
|
||||||
|
|
||||||
|
if (band == BAND20) { // Switch to 40 Meter Band
|
||||||
|
band=BAND40;
|
||||||
|
sideband=band40Sideband;
|
||||||
|
freq=band40Freq;
|
||||||
|
} else { // Switch to 20 Meter Band
|
||||||
|
band = BAND20;
|
||||||
|
sideband=band20Sideband;
|
||||||
|
freq=band20Freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure BFO clock tracks with band change
|
||||||
|
//
|
||||||
|
if (sideband == USB) {
|
||||||
|
bfo = USB_BFO;
|
||||||
|
} else {
|
||||||
|
bfo = LSB_BFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Change the clock frequency to the new bfo
|
||||||
|
//
|
||||||
|
setBFO(bfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set the active VFO to new frequency
|
||||||
|
//
|
||||||
|
switch (active_vfo) {
|
||||||
|
case VFOA:
|
||||||
|
vfoAfreq=freq;
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
displayActVFO(vfoAfreq);
|
||||||
|
break;
|
||||||
|
case VFOB:
|
||||||
|
vfoBfreq=freq;
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
displayActVFO(vfoBfreq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the display to show the active mode/sideband
|
||||||
|
//
|
||||||
|
displayMode(sideband);
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckBand() {
|
||||||
|
BandButtonState = digitalRead(BAND_BTN);
|
||||||
|
if (BandButtonState != lastBandButtonState) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// On button press, change band 20/40
|
||||||
|
//
|
||||||
|
if (BandButtonState == LOW) { // if button pressed
|
||||||
|
|
||||||
|
#ifdef BTN_DEBUG
|
||||||
|
ToggleLED();
|
||||||
|
String msg = F("CheckBand");
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
SwapBand();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastBandButtonState = BandButtonState;
|
||||||
|
Delay(50);
|
||||||
|
BandButtonState = digitalRead(BAND_BTN); //debounce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//*********************Check Sideband ************************************
|
||||||
|
void SwapSB() {
|
||||||
|
if (sideband == USB) { // Switch to LSB
|
||||||
|
sideband = LSB;
|
||||||
|
bfo = LSB_BFO;
|
||||||
|
} else { // Switch to USB
|
||||||
|
sideband = USB;
|
||||||
|
bfo = USB_BFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Keep track of which SSB is currently selected for current band
|
||||||
|
//
|
||||||
|
if (band == BAND20) {
|
||||||
|
band20Sideband = sideband;
|
||||||
|
} else {
|
||||||
|
band40Sideband = sideband;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Change the clock frequency to the new bfo
|
||||||
|
//
|
||||||
|
setBFO(bfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set the active VFO to adjusted frequency
|
||||||
|
//
|
||||||
|
switch (active_vfo) {
|
||||||
|
case VFOA:
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
vfoASideband=sideband;
|
||||||
|
break;
|
||||||
|
case VFOB:
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
vfoBSideband=sideband;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the display to show the active mode/sideband
|
||||||
|
//
|
||||||
|
displayMode(sideband);
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckSB() {
|
||||||
|
SideBandButtonState = digitalRead(SIDEBAND_BTN);
|
||||||
|
if (SideBandButtonState != lastSideBandButtonState) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// On button press, change active sideband
|
||||||
|
// Set the BFO as appropriate
|
||||||
|
//
|
||||||
|
if (SideBandButtonState == LOW) { // if button pressed
|
||||||
|
SwapSB();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSideBandButtonState = SideBandButtonState;
|
||||||
|
Delay(50);
|
||||||
|
SideBandButtonState = digitalRead(SIDEBAND_BTN); //debounce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//********************* Tune Button Handling ************************************
|
||||||
|
void DoTune() {
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// CW experiment - call doCW to turn off BFO, set LO to operating frequencey+700
|
||||||
|
// Then activate Tx - should generate a carrier
|
||||||
|
//
|
||||||
|
// Does but level is too low to drive the Tx amps
|
||||||
|
//
|
||||||
|
|
||||||
|
displayTune(true);
|
||||||
|
|
||||||
|
setCW();
|
||||||
|
|
||||||
|
startTx(PTT_TUNE);
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
// tone(TONE_PIN, NOTE_B5);
|
||||||
|
Delay(50);
|
||||||
|
// noTone(TONE_PIN);
|
||||||
|
Delay(50);
|
||||||
|
}
|
||||||
|
stopTx();
|
||||||
|
displayTune(false);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//********************* Tune Button Handling ************************************
|
||||||
|
void DoTune() {
|
||||||
|
//
|
||||||
|
// Temp code - 30 second full duty tone for debugging circuits - cheap tone generator.
|
||||||
|
// Delay(12);
|
||||||
|
// tone(TONE_PIN, NOTE_B5);
|
||||||
|
// Delay(30000);
|
||||||
|
// noTone(TONE_PIN);
|
||||||
|
|
||||||
|
displayTune(true);
|
||||||
|
startTx(PTT_TUNE);
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
tone(TONE_PIN, NOTE_B5);
|
||||||
|
Delay(50);
|
||||||
|
noTone(TONE_PIN);
|
||||||
|
Delay(50);
|
||||||
|
}
|
||||||
|
stopTx();
|
||||||
|
displayTune(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CheckTune() {
|
||||||
|
TuneButtonState = digitalRead(TUNE_BTN); // creates a 10 second tuning pulse trani 50% duty cycle and makes TUNE appear on the screen
|
||||||
|
if (TuneButtonState != lastTuneButtonState) {
|
||||||
|
if (TuneButtonState == LOW) {
|
||||||
|
DoTune();
|
||||||
|
}
|
||||||
|
lastTuneButtonState = TuneButtonState;
|
||||||
|
Delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//*********************VFO switch******* VFO A or B ****************
|
||||||
|
void SwapVFO() {
|
||||||
|
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
active_vfo = VFOB; // Make VFOB Active
|
||||||
|
sideband=vfoBSideband;
|
||||||
|
} else {
|
||||||
|
active_vfo = VFOA; // Make VFOA Active
|
||||||
|
sideband=vfoASideband;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Adjust BFO in case sideband has changed
|
||||||
|
//
|
||||||
|
if (sideband == USB) { // Switch to LSB
|
||||||
|
bfo = USB_BFO;
|
||||||
|
} else { // Switch to USB
|
||||||
|
bfo = LSB_BFO;
|
||||||
|
}
|
||||||
|
setBFO(bfo);
|
||||||
|
displayMode(sideband); // Change sideband indicator
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BTN_DEBUG
|
||||||
|
ToggleLED();
|
||||||
|
String msg = F("SwapVFO: active_vfo=");
|
||||||
|
msg += active_vfo;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Swap Active/Alternate frequency displays
|
||||||
|
//
|
||||||
|
switch (active_vfo) {
|
||||||
|
case VFOA:
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
displayActVFO(vfoAfreq);
|
||||||
|
displayAltVFO(vfoBfreq);
|
||||||
|
break;
|
||||||
|
case VFOB:
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
displayActVFO(vfoBfreq);
|
||||||
|
displayAltVFO(vfoAfreq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
displayVFOAB(active_vfo); // Change the A/B indicator
|
||||||
|
displayMode(sideband); // Change sideband indicator
|
||||||
|
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckVFO() {
|
||||||
|
|
||||||
|
VFOButtonState = digitalRead(VFO_BTN);
|
||||||
|
if (VFOButtonState != lastVFOButtonState) {
|
||||||
|
if (VFOButtonState == LOW) { // button pressed
|
||||||
|
SwapVFO();
|
||||||
|
}
|
||||||
|
lastVFOButtonState = VFOButtonState;
|
||||||
|
Delay(50);
|
||||||
|
VFOButtonState = digitalRead(VFO_BTN); //debounce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// startSplit
|
||||||
|
// Turn on split mode and update the display
|
||||||
|
void startSplit() {
|
||||||
|
split = true;
|
||||||
|
displaySplit(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turnb off split mode and update the display
|
||||||
|
void stopSplit() {
|
||||||
|
split = false;
|
||||||
|
displaySplit(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
// startTx
|
||||||
|
// if split mode is on swap Act and Alt VFO
|
||||||
|
// Put the rig in to TX by triggering the PTT pin
|
||||||
|
// Update the display to Tx
|
||||||
|
void startTx(byte PTT_source) {
|
||||||
|
if (split) SwapVFO();
|
||||||
|
digitalWrite(PTT,HIGH);
|
||||||
|
displayTxRx(TX);
|
||||||
|
TxRxState = TX;
|
||||||
|
txSource = PTT_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stopTx
|
||||||
|
// Return the rig to Rx by lowering the PTT pin
|
||||||
|
// If split mode is on swap Act and Alt VFO
|
||||||
|
// Update the display to Tx
|
||||||
|
void stopTx() {
|
||||||
|
digitalWrite(PTT,LOW);
|
||||||
|
if (split) SwapVFO();
|
||||||
|
displayTxRx(RX);
|
||||||
|
TxRxState = RX;
|
||||||
|
}
|
||||||
|
|
||||||
|
//********************* PTT ****************************
|
||||||
|
|
||||||
|
void CheckPTT(){
|
||||||
|
|
||||||
|
if ((TxRxState==TX) && (txSource != PTT_MIC)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxRxState = digitalRead(PTT_SENSE);
|
||||||
|
|
||||||
|
if(TxRxState != lastTxRxState){
|
||||||
|
|
||||||
|
if(TxRxState == TX){
|
||||||
|
startTx(PTT_MIC);
|
||||||
|
} else {
|
||||||
|
if (txSource == PTT_MIC) {
|
||||||
|
stopTx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTxRxState = TxRxState;
|
||||||
|
Delay(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,287 @@
|
||||||
|
/**************************************************************
|
||||||
|
F40 CAT Control
|
||||||
|
1/24/2021 - KK4DAS Version 1.3
|
||||||
|
Changed to IC746 CAT Library
|
||||||
|
|
||||||
|
***************************************************************/
|
||||||
|
#include "RadioControl.h"
|
||||||
|
#include <IC746.h>
|
||||||
|
|
||||||
|
IC746 radio = IC746();
|
||||||
|
|
||||||
|
//#define CAT_DEBUG
|
||||||
|
|
||||||
|
// radio modes
|
||||||
|
#define MODE_LSB 00
|
||||||
|
#define MODE_USB 01
|
||||||
|
#define MODE_CW 02
|
||||||
|
|
||||||
|
// TX Rx State
|
||||||
|
// FT857D combine TX and RX status
|
||||||
|
// 0b abcdefgh
|
||||||
|
// a = 0 = PTT on
|
||||||
|
// a = 1 = PTT off
|
||||||
|
// b = 0 = HI SWR off
|
||||||
|
// b = 1 = HI SWR on
|
||||||
|
// c = 0 = split on
|
||||||
|
// c = 1 = split off
|
||||||
|
// d = dummy data
|
||||||
|
// efgh = SWR meter data ??
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// function to run when we must put radio on TX/RX
|
||||||
|
// If PTT requests transmit and the rig is not already transmitting, start TX
|
||||||
|
// If PTT request stop transmit and the current trasnmit source is PTT_CAT then stop TX
|
||||||
|
//
|
||||||
|
void catGoPtt(boolean pttf) {
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("CatGoPtt ");
|
||||||
|
msg += pttf;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((TxRxState == TX) && (txSource != PTT_CAT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pttf) {
|
||||||
|
// displayDebug("CAT PTT ON ");
|
||||||
|
if (TxRxState == RX) {
|
||||||
|
startTx(PTT_CAT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (txSource == PTT_CAT) {
|
||||||
|
// displayDebug("CAT PTT OFF");
|
||||||
|
stopTx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean catGetPtt() {
|
||||||
|
#if defined (DEBUG)
|
||||||
|
String msg = "GetPTT: ";
|
||||||
|
if (ptt == PTT_TX) {
|
||||||
|
msg += "Tx";
|
||||||
|
} else {
|
||||||
|
msg += "Rx";
|
||||||
|
}
|
||||||
|
displayPrintln(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (TxRxState == TX) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void catGoSplit(boolean cat_split) {
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("CatGoSplit ");
|
||||||
|
msg += cat_split;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
if (cat_split) {
|
||||||
|
startSplit();
|
||||||
|
} else {
|
||||||
|
stopSplit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to run when VFOs A/B are toggled
|
||||||
|
void catSwapVfo() {
|
||||||
|
SwapVFO();
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("catGoToggleVFOs");
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to set a freq from CAT
|
||||||
|
void catSetFreq(long f) {
|
||||||
|
|
||||||
|
if (f == 0) return; // ignore spurious command
|
||||||
|
|
||||||
|
//
|
||||||
|
// Change the frequency of the current active VFO
|
||||||
|
// Clock frequency is the operating frequecy plus the BFO
|
||||||
|
//
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
vfoAfreq = f;
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
displayActVFO(vfoAfreq);
|
||||||
|
} else {
|
||||||
|
vfoBfreq = f;
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
displayActVFO(vfoBfreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("catsetFreq ");
|
||||||
|
msg += f;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to set the mode(LSB or USB) from the cat command
|
||||||
|
void catSetMode(byte m) {
|
||||||
|
// If the new mode is different from the current sideband, then swap USB/LSB
|
||||||
|
switch (sideband) {
|
||||||
|
case USB:
|
||||||
|
if (m == MODE_LSB) SwapSB();
|
||||||
|
break;
|
||||||
|
case LSB:
|
||||||
|
if (m == MODE_USB) SwapSB();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("CatSetMode ");
|
||||||
|
if (sideband == USB ) {
|
||||||
|
msg += F("USB ");
|
||||||
|
} else {
|
||||||
|
msg += F("LSB ");
|
||||||
|
}
|
||||||
|
msg += m;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to pass the freq to the cat library
|
||||||
|
long catGetFreq() {
|
||||||
|
// this must return the freq as an unsigned long in Hz, you must prepare it before
|
||||||
|
long freq;
|
||||||
|
|
||||||
|
// displayPrintln("catGetFreq called");
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
freq = vfoAfreq;
|
||||||
|
} else {
|
||||||
|
freq = vfoBfreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("catGetFreq ");
|
||||||
|
msg += freq;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to pass the mode to the cat library
|
||||||
|
byte catGetMode() {
|
||||||
|
// this must return the mode in the wat the CAT protocol expect it
|
||||||
|
byte mode;
|
||||||
|
|
||||||
|
if (sideband == USB) {
|
||||||
|
mode = MODE_USB;
|
||||||
|
} else {
|
||||||
|
mode = MODE_LSB;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("CatGetMode ");
|
||||||
|
if (sideband == USB ) {
|
||||||
|
msg += F("USB ");
|
||||||
|
} else {
|
||||||
|
msg += F("LSB ");
|
||||||
|
}
|
||||||
|
msg += mode;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to pass the smeter reading in RX mode
|
||||||
|
byte catGetSMeter() {
|
||||||
|
// this must return a byte in with the 4 LSB are the S meter data
|
||||||
|
// so this procedure must take care of convert your S meter and scale it
|
||||||
|
// up to just 4 bits
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
String msg = F("CatGetSMeter ");
|
||||||
|
msg += smeter;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return smeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to select the active VFO from the cat command
|
||||||
|
// If requested VFO is not already active then swap active and alternate
|
||||||
|
void catSetVFO(byte catVfo) {
|
||||||
|
if ( ((catVfo == CAT_VFO_A) && (active_vfo == VFOB)) ||
|
||||||
|
((catVfo == CAT_VFO_B) && (active_vfo == VFOA))) {
|
||||||
|
SwapVFO();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (DEBUG)
|
||||||
|
String msg = "SetVFO: ";
|
||||||
|
if (v == CAT_VFO_A) {
|
||||||
|
msg += "VFO-A";
|
||||||
|
} else {
|
||||||
|
msg += "VFO-B";
|
||||||
|
}
|
||||||
|
displayPrintln(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to make VFOS the same
|
||||||
|
void catVfoAtoB() {
|
||||||
|
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
vfoBfreq = vfoAfreq;
|
||||||
|
vfoBSideband = vfoASideband;
|
||||||
|
displayAltVFO(vfoBfreq);
|
||||||
|
} else {
|
||||||
|
vfoAfreq = vfoBfreq;
|
||||||
|
vfoASideband = vfoBSideband;
|
||||||
|
displayAltVFO(vfoAfreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (DEBUG)
|
||||||
|
String msg = "VfoAtoB";
|
||||||
|
displayPrintln(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setupCat() {
|
||||||
|
|
||||||
|
// Setup the CAT control command handlers
|
||||||
|
radio.addCATPtt(catGoPtt);
|
||||||
|
radio.addCATGetPtt(catGetPtt);
|
||||||
|
radio.addCATAtoB(catVfoAtoB);
|
||||||
|
radio.addCATSwapVfo(catSwapVfo);
|
||||||
|
radio.addCATsplit(catGoSplit);
|
||||||
|
radio.addCATFSet(catSetFreq);
|
||||||
|
radio.addCATMSet(catSetMode);
|
||||||
|
radio.addCATVSet(catSetVFO);
|
||||||
|
radio.addCATGetFreq(catGetFreq);
|
||||||
|
radio.addCATGetMode(catGetMode);
|
||||||
|
radio.addCATSMeter(catGetSMeter);
|
||||||
|
|
||||||
|
// now we activate the library
|
||||||
|
|
||||||
|
radio.begin(19200, SERIAL_8N1);
|
||||||
|
|
||||||
|
#ifdef CAT_DEBUG
|
||||||
|
displayBanner(String(F("setupCAT")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckCat() {
|
||||||
|
radio.check();
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef CAT_h
|
||||||
|
#define CAT_H
|
||||||
|
/**************************************************************
|
||||||
|
F40 CAT Control
|
||||||
|
|
||||||
|
5/23/202 - KK4DAS Version 0.9
|
||||||
|
Incorporated CAT Control Library
|
||||||
|
FT857D CAT Library, by Pavel Milanes, CO7WT, pavelmc@gmail.
|
||||||
|
https://github.com/pavelmc/FT857d/
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
|
//=============== Function Prototypes ============================================
|
||||||
|
extern void setupCat();
|
||||||
|
extern void CheckCat();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,243 @@
|
||||||
|
//
|
||||||
|
// Encoder fuctions
|
||||||
|
//
|
||||||
|
#include "RadioControl.h"
|
||||||
|
|
||||||
|
//#DEFINE DEBUG_ENC
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Rotary Enconder //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
Rotary encoder = Rotary(ENCODER_A, ENCODER_B, ENCODER_BTN); // Setup Encoder
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Rotary Encoder Variables //
|
||||||
|
// Number of clockwise and counterclockwise ticks //
|
||||||
|
// Delta between successive measurements //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
volatile int encoder_count = 0; // count of encoder clicks +1 for CW, -1 for CCW (volatile since used in ISR)
|
||||||
|
int prev_encoder_count = 0; // used to measure changes over time
|
||||||
|
int encoder_delta = 0; // differrnce between successive checks of encoder count
|
||||||
|
|
||||||
|
bool incrementChanged = false;
|
||||||
|
|
||||||
|
// when multiplied by tuning increment tells what the frequency change on
|
||||||
|
// on the active VFO will be
|
||||||
|
// Encoder button control
|
||||||
|
extern byte EncButtonState;
|
||||||
|
extern byte lastEncButtonState;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// ************* ISR **************** //
|
||||||
|
// Interrupt service routine, called on encoder movement //
|
||||||
|
// Only interested in completed clicks //
|
||||||
|
// +1 for Clocwwise //
|
||||||
|
// -1 for Counter Clockwise //
|
||||||
|
// Ignore intermediate values //
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if _BOARDTYPE != Every
|
||||||
|
ISR(PCINT2_vect) {
|
||||||
|
unsigned char result = encoder.process();
|
||||||
|
if (result == DIR_CW) {
|
||||||
|
encoder_count++;
|
||||||
|
} else if (result == DIR_CCW) {
|
||||||
|
encoder_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define PA0_INTERRUPT PORTA.INTFLAGS & PIN0_bm
|
||||||
|
#define PA0_CLEAR_INTERRUPT_FLAG PORTA.INTFLAGS &= PIN0_bm
|
||||||
|
#define PF5_INTERRUPT PORTF.INTFLAGS & PIN5_bm
|
||||||
|
#define PF5_CLEAR_INTERRUPT_FLAG PORTF.INTFLAGS &= PIN5_bm
|
||||||
|
|
||||||
|
ISR(PORTA_PORT_vect) {
|
||||||
|
unsigned char result = encoder.process();
|
||||||
|
|
||||||
|
if (PA0_INTERRUPT)
|
||||||
|
PA0_CLEAR_INTERRUPT_FLAG;
|
||||||
|
|
||||||
|
if (result == DIR_CW) {
|
||||||
|
encoder_count++;
|
||||||
|
} else if (result == DIR_CCW) {
|
||||||
|
encoder_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(PORTF_PORT_vect) {
|
||||||
|
unsigned char result = encoder.process();
|
||||||
|
|
||||||
|
if (PF5_INTERRUPT)
|
||||||
|
PF5_CLEAR_INTERRUPT_FLAG;
|
||||||
|
|
||||||
|
if (result == DIR_CW) {
|
||||||
|
encoder_count++;
|
||||||
|
} else if (result == DIR_CCW) {
|
||||||
|
encoder_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//*******************Setup Interrupt Service Routine********************
|
||||||
|
|
||||||
|
void setupEncoderISR() {
|
||||||
|
cli();
|
||||||
|
// Set up for the rotary encoder interrupts
|
||||||
|
#if _BOARDTYPE != Every
|
||||||
|
PCICR |= (1 << PCIE2);
|
||||||
|
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
|
||||||
|
#endif
|
||||||
|
#if _BOARDTYPE == Every
|
||||||
|
PORTA.PIN0CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
|
||||||
|
PORTF.PIN5CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
|
||||||
|
#endif
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//********************CheckEncoder*******************************************
|
||||||
|
|
||||||
|
// roundedF - Calculates new frequency
|
||||||
|
// After an increment change rounds up or down to the next even increment
|
||||||
|
// Otherwise just changes increments or decrements the frequency
|
||||||
|
//
|
||||||
|
uint32_t roundedF(uint32_t freq, int32_t delta) {
|
||||||
|
uint32_t f = freq;
|
||||||
|
|
||||||
|
/* if (incrementChanged) {
|
||||||
|
if (f % increment) {
|
||||||
|
if (delta > 0 ) {
|
||||||
|
f = f + increment - (f % increment);
|
||||||
|
} else {
|
||||||
|
f = f - (f % increment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
incrementChanged = false;
|
||||||
|
} else {
|
||||||
|
f = f + delta;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (f % increment) {
|
||||||
|
if (delta > 0 ) {
|
||||||
|
f = f + increment - (f % increment);
|
||||||
|
} else {
|
||||||
|
f = f - (f % increment);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f = f + delta;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdjustVFO(long delta) {
|
||||||
|
|
||||||
|
switch (active_vfo) {
|
||||||
|
case VFOA:
|
||||||
|
// vfoAfreq = vfoAfreq + delta;
|
||||||
|
vfoAfreq = roundedF(vfoAfreq, delta);
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
displayActVFO(vfoAfreq);
|
||||||
|
break;
|
||||||
|
case VFOB:
|
||||||
|
// vfoBfreq = vfoBfreq + delta;
|
||||||
|
vfoBfreq = roundedF(vfoBfreq, delta);
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
displayActVFO(vfoBfreq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckEncoder() {
|
||||||
|
int current_count = encoder_count; // grab the current encoder_count
|
||||||
|
int32_t encoder_delta = 0;
|
||||||
|
|
||||||
|
if (current_count != prev_encoder_count) { // if there is any change in the encoder coount
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENC
|
||||||
|
sprintf(debugmsg, "VFOA: %ld", vfoAfreq);
|
||||||
|
Serial.println(debugmsg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Calculate the delta (how many click positive or negaitve)
|
||||||
|
//
|
||||||
|
encoder_delta = current_count - prev_encoder_count;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Adjust the currently selected VFO
|
||||||
|
//
|
||||||
|
AdjustVFO(encoder_delta * increment);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
sprintf(debugmsg, "current_count: %d, New VFOA: %ld", current_count, vfoAfreq);
|
||||||
|
Serial.println(debugmsg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
prev_encoder_count = current_count; // save the current_count for next time around
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//********************CheckIncrement*******************************************
|
||||||
|
// Cycle through tuning increment values on button press
|
||||||
|
// 10, 100, 1K, 10K, 100K, 1M
|
||||||
|
//********************CheckIncrement*******************************************
|
||||||
|
void AdvanceIncrement() {
|
||||||
|
if (increment == 10) {
|
||||||
|
increment = 100;
|
||||||
|
}
|
||||||
|
else if (increment == 100) {
|
||||||
|
increment = 1000;
|
||||||
|
}
|
||||||
|
else if (increment == 1000) {
|
||||||
|
increment = 10000;
|
||||||
|
}
|
||||||
|
else if (increment == 10000) {
|
||||||
|
increment = 100000;
|
||||||
|
}
|
||||||
|
else if (increment == 100000) {
|
||||||
|
increment = 1000000;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
increment = 10;
|
||||||
|
}
|
||||||
|
displayIncr(increment);
|
||||||
|
incrementChanged = true;
|
||||||
|
startSettingsTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckIncrement () {
|
||||||
|
|
||||||
|
EncButtonState = encoder.buttonState();
|
||||||
|
//EncButtonState = digitalRead(ENCODER_BTN);
|
||||||
|
#ifdef DEBUG
|
||||||
|
sprintf(debugmsg, "Encoder button state: %d", EncButtonState);
|
||||||
|
Serial.println(debugmsg);
|
||||||
|
Delay(1000);
|
||||||
|
#endif
|
||||||
|
if (EncButtonState != lastEncButtonState) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
sprintf(debugmsg, "Encoder button state: %d", EncButtonState);
|
||||||
|
Serial.println(debugmsg);
|
||||||
|
#endif
|
||||||
|
if (EncButtonState == LOW) {
|
||||||
|
AdvanceIncrement();
|
||||||
|
|
||||||
|
}
|
||||||
|
lastEncButtonState = EncButtonState;
|
||||||
|
Delay(50);
|
||||||
|
EncButtonState = encoder.buttonState(); //debounce
|
||||||
|
//EncButtonState = digitalRead(ENCODER_BTN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupEncoder() {
|
||||||
|
setupEncoderISR();
|
||||||
|
}
|
|
@ -0,0 +1,229 @@
|
||||||
|
#ifndef RadioControl_h
|
||||||
|
#define RadioControl_h
|
||||||
|
|
||||||
|
#define CALLSIGN "KK4DAS"
|
||||||
|
#define VERSION "1.4"
|
||||||
|
#define RIGNAME "SimpleSSB"
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG
|
||||||
|
#ifdef DEBUG
|
||||||
|
extern char debugmsg[];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//=============================== FEATURE SELECTION =========================
|
||||||
|
// Each flag below enables optional features
|
||||||
|
// DISPLAY_X - uncomment one line depending on the display that is attached
|
||||||
|
// BFOxXMHS - IF filter center frequency - eitherr 9MHz or 12MHZ
|
||||||
|
// SMNETER - Uncomment if S-meter sensor circuit is installed
|
||||||
|
// DUAL_BAND - Uncomment to enable band switching 20/40 if installed
|
||||||
|
// CW - Uncomment if CW mod installed (future)
|
||||||
|
|
||||||
|
//=============================== DISPLAY TYPE ==============================
|
||||||
|
#define DISPLAY_LCD //uncomment for 20x4 LCD
|
||||||
|
//#define DISPLAY_TFT // uncomment for 320x240 TFT
|
||||||
|
//#define DISPLAY_NEXTION //uncomment for 2.8" Nextion
|
||||||
|
|
||||||
|
//=============================== IF FILTER FREQ ============================
|
||||||
|
#define BFO9MHZ // uncomment for 9.0 MHz IF
|
||||||
|
//#define BFO12MHZ // uncomment for 12.0 MHz IF
|
||||||
|
|
||||||
|
//=============================== S-METER INSTALLED =========================
|
||||||
|
//#define SMETER // uncomment if SMETER mod installed
|
||||||
|
|
||||||
|
//=============================== DUAL BAND MOD INSTALLED ===================
|
||||||
|
//#define DUAL_BAND // uncomment if dual band mod installed 20/40
|
||||||
|
|
||||||
|
//=============================== DISPLAY TYPE ==============================
|
||||||
|
//#define CW // uncomment if CW enabled (not complete)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//============================== BOARD TYPE (Nano, Every) =====================
|
||||||
|
#define Nano 0
|
||||||
|
#define Every 1
|
||||||
|
#define Uno 2
|
||||||
|
|
||||||
|
#if defined(ARDUINO_AVR_NANO)
|
||||||
|
#define _BOARDTYPE Nano
|
||||||
|
#endif
|
||||||
|
#if defined(ARDUINO_AVR_NANO_EVERY)
|
||||||
|
#define _BOARDTYPE Every
|
||||||
|
#endif
|
||||||
|
#if defined (ARDUINO_AVR_UNO)
|
||||||
|
#define _BOARDTYPE Uno
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//============================= INCLUDES =====================================
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Utility.h" // General purpose common functions
|
||||||
|
#include "SSB_Display.h" // Display handling
|
||||||
|
#include "RotaryEnc.h" // Rotary encoder handling
|
||||||
|
#include "si5351.h" // SI 5351 clock
|
||||||
|
#include "CAT.h" // CAT Control handling
|
||||||
|
#include "Settings.h" // Get/Store Settings in EEPROM
|
||||||
|
#include "Smeter.h" // Smeter definitions
|
||||||
|
|
||||||
|
|
||||||
|
//============================== Symbolic constants ==========================
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Arduino Pin Definitions //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define ENCODER_A 2 // Rotary Lib Default - Encoder pin A D2 (interrupt pin)
|
||||||
|
#define ENCODER_B 3 // Rotary Lib Default - Encoder pin B D3 (interrupt pin)
|
||||||
|
#define PTT_SENSE 4 // Detect Mic PT
|
||||||
|
#define PTT 5 // LOW=Rx, HIGH=Tx
|
||||||
|
|
||||||
|
// Dual Band Pins (requires dual band mod)
|
||||||
|
#define BAND_BTN 6 // Band Switch momentary button
|
||||||
|
#define BAND_PIN 7 // Band Switch Relay - LOW = Band A (40M), HIGH = Band B (20M)
|
||||||
|
|
||||||
|
#define VFO_BTN A0 // VFO A/B button
|
||||||
|
#define SIDEBAND_BTN A1 // USB/LSB button
|
||||||
|
#define TUNE_BTN A2 // Tune Button
|
||||||
|
#define ENCODER_BTN A3 // Rotary Lib Default - Encoder push button
|
||||||
|
#define I2C_SDA A4 // I2C SDA Pin
|
||||||
|
#define I2C_SCL A5 // I2C SCL Pin
|
||||||
|
#define TONE_PIN A6 // Audio out for tune tone
|
||||||
|
|
||||||
|
// Smeter
|
||||||
|
#define SMETER_PIN A7 // Requires Signal Strength Sensor
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// For Nextion / Nano Every Only
|
||||||
|
// Pin 0,1 Serial RX/TX
|
||||||
|
|
||||||
|
|
||||||
|
// For color TFT Only
|
||||||
|
// Arduino
|
||||||
|
// Pin TFT Pin
|
||||||
|
// -----------|---------
|
||||||
|
// 8 | RST - any free Arduino Pin (not used in this sketch)
|
||||||
|
// 9 | DC - any free Arduino Pin
|
||||||
|
// 10 | CS - any free Arduino Pin
|
||||||
|
// 11 | MOSI - fixed
|
||||||
|
// 12 | MISO - fixed
|
||||||
|
// 13 | CLK - fixed
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// Tune Tone
|
||||||
|
#define NOTE_B5 988 // Tune tone
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dual Band Mode Constants (requires DUAL_BAND)
|
||||||
|
//
|
||||||
|
#define BAND20 1
|
||||||
|
#define BAND40 2
|
||||||
|
#define BAND20_EDGE 14000000
|
||||||
|
#define BAND40_EDGE 7000000
|
||||||
|
|
||||||
|
// VFO, Sideband and TX state are used both here and in the display
|
||||||
|
// Define here if not already defined
|
||||||
|
|
||||||
|
// VFO selection
|
||||||
|
#define VFOA 0
|
||||||
|
#define VFOB 1
|
||||||
|
|
||||||
|
// Sideband selection
|
||||||
|
#define USB 0
|
||||||
|
#define LSB 1
|
||||||
|
|
||||||
|
// Transmit state
|
||||||
|
#define TX LOW // TX is on when PTT switched to ground
|
||||||
|
#define RX HIGH
|
||||||
|
|
||||||
|
|
||||||
|
// PTT Source
|
||||||
|
#define PTT_MIC 0
|
||||||
|
#define PTT_CAT 1
|
||||||
|
#define PTT_TUNE 2
|
||||||
|
|
||||||
|
//=============== Globals ============================================
|
||||||
|
|
||||||
|
// Rotary Encoder
|
||||||
|
extern Rotary encoder;
|
||||||
|
|
||||||
|
// BFO
|
||||||
|
extern const uint32_t USB_BFO;
|
||||||
|
extern const uint32_t LSB_BFO;
|
||||||
|
extern uint32_t bfo; // Startup BFO frequency
|
||||||
|
extern const uint32_t BFO_DELTA; // Difference between USB and LSB for BFO change
|
||||||
|
|
||||||
|
// VFO A/B frequencies
|
||||||
|
extern uint32_t vfoAfreq;
|
||||||
|
extern uint32_t vfoBfreq;
|
||||||
|
extern byte vfoASideband;
|
||||||
|
extern byte vfoBSideband;
|
||||||
|
|
||||||
|
// Tuning increment
|
||||||
|
extern uint32_t increment;
|
||||||
|
|
||||||
|
// Active VFO indicator
|
||||||
|
extern byte active_vfo;
|
||||||
|
|
||||||
|
|
||||||
|
// Active sideband (USB or LSB)
|
||||||
|
extern byte sideband;
|
||||||
|
|
||||||
|
|
||||||
|
// Band specific memory - requires DUAL_BAND
|
||||||
|
extern uint32_t band20Freq;
|
||||||
|
extern uint32_t band40Freq;
|
||||||
|
extern byte band20Sideband;
|
||||||
|
extern byte band40Sideband;
|
||||||
|
extern byte band;
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit state
|
||||||
|
extern byte TxRxState;
|
||||||
|
extern byte lastTxRxState;
|
||||||
|
|
||||||
|
// Transmoit source (mic, CAT)
|
||||||
|
extern byte txSource;
|
||||||
|
|
||||||
|
// S Meter
|
||||||
|
extern byte smeter;
|
||||||
|
|
||||||
|
// Split mode
|
||||||
|
extern bool split;
|
||||||
|
|
||||||
|
|
||||||
|
//=============== Function Prototypes ============================================
|
||||||
|
|
||||||
|
extern void setupEncoder();
|
||||||
|
extern void setVFO(uint32_t freq);
|
||||||
|
extern void setBFO(uint32_t freq);
|
||||||
|
extern void CheckIncrement();
|
||||||
|
extern void AdvanceIncrement();
|
||||||
|
extern void CheckEncoder();
|
||||||
|
extern void AdjustVFO(long delta);
|
||||||
|
|
||||||
|
extern void CheckSB();
|
||||||
|
extern void SwapSB();
|
||||||
|
extern void CheckTune();
|
||||||
|
extern void DoTune();
|
||||||
|
extern void CheckVFO();
|
||||||
|
extern void SwapVFO();
|
||||||
|
extern void CheckPTT();
|
||||||
|
extern void setupPins();
|
||||||
|
extern void startTx(byte PTT_source);
|
||||||
|
extern void stopTx();
|
||||||
|
extern void startSplit();
|
||||||
|
extern void stopSplit();
|
||||||
|
extern void CheckBand(); // Only called if DUAL_BAND enabled
|
||||||
|
extern void CheckSmeter(); // Only called if SMETER enabled
|
||||||
|
|
||||||
|
#ifdef CW
|
||||||
|
extern void setCW();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,122 @@
|
||||||
|
/* Rotary encoder handler for arduino.
|
||||||
|
*
|
||||||
|
* Revision: 2020 Dean Souleles, KK4DAS, Licensed under the GNU GPL Version 3.
|
||||||
|
* Contact: kk4das@gmail.com
|
||||||
|
* Added default pins and button handling
|
||||||
|
*
|
||||||
|
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
|
||||||
|
* Contact: bb@cactii.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "RotaryEnc.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The below state table has, for each state (row), the new state
|
||||||
|
* to set based on the next encoder output. From left to right in,
|
||||||
|
* the table, the encoder outputs are 00, 01, 10, 11, and the value
|
||||||
|
* in that position is the new state to set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define R_START 0x0
|
||||||
|
|
||||||
|
#ifdef HALF_STEP
|
||||||
|
// Use the half-step state table (emits a code at 00 and 11) // nulled out to only use full step
|
||||||
|
#define R_CCW_BEGIN 0x1
|
||||||
|
#define R_CW_BEGIN 0x2
|
||||||
|
#define R_START_M 0x3
|
||||||
|
#define R_CW_BEGIN_M 0x4
|
||||||
|
#define R_CCW_BEGIN_M 0x5
|
||||||
|
const unsigned char ttable[6][4] = {
|
||||||
|
// R_START (00)
|
||||||
|
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CCW_BEGIN
|
||||||
|
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CW_BEGIN
|
||||||
|
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START},
|
||||||
|
// R_START_M (11)
|
||||||
|
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START},
|
||||||
|
// R_CW_BEGIN_M
|
||||||
|
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW},
|
||||||
|
// R_CCW_BEGIN_M
|
||||||
|
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW},
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
// Use the full-step state table (emits a code at 00 only)
|
||||||
|
#define R_CW_FINAL 0x1
|
||||||
|
#define R_CW_BEGIN 0x2
|
||||||
|
#define R_CW_NEXT 0x3
|
||||||
|
#define R_CCW_BEGIN 0x4
|
||||||
|
#define R_CCW_FINAL 0x5
|
||||||
|
#define R_CCW_NEXT 0x6
|
||||||
|
|
||||||
|
const unsigned char ttable[7][4] = {
|
||||||
|
// R_START
|
||||||
|
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CW_FINAL
|
||||||
|
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
|
||||||
|
// R_CW_BEGIN
|
||||||
|
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
|
||||||
|
// R_CW_NEXT
|
||||||
|
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
|
||||||
|
// R_CCW_BEGIN
|
||||||
|
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CCW_FINAL
|
||||||
|
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
|
||||||
|
// R_CCW_NEXT
|
||||||
|
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor Common
|
||||||
|
*/
|
||||||
|
void Rotary::Constructor_Common() {
|
||||||
|
// Set pins to input.
|
||||||
|
pinMode(pin1, INPUT);
|
||||||
|
pinMode(pin2, INPUT);
|
||||||
|
pinMode(btn, INPUT);
|
||||||
|
#ifdef ENABLE_PULLUPS
|
||||||
|
digitalWrite(pin1, HIGH);
|
||||||
|
digitalWrite(pin2, HIGH);
|
||||||
|
digitalWrite(btn, HIGH);
|
||||||
|
#endif
|
||||||
|
// Initialise state.
|
||||||
|
state = R_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor. Each arg is the pin number for each encoder contact.
|
||||||
|
*/
|
||||||
|
Rotary::Rotary(char _pin1, char _pin2,char _btn) {
|
||||||
|
// Assign variables.
|
||||||
|
pin1 = _pin1;
|
||||||
|
pin2 = _pin2;
|
||||||
|
btn = _btn;
|
||||||
|
Constructor_Common();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rotary::Rotary() {
|
||||||
|
// Assign variables to defaults
|
||||||
|
pin1 = ENCODER_A;
|
||||||
|
pin2 = ENCODER_B;
|
||||||
|
btn = ENCODER_BTN;
|
||||||
|
Constructor_Common();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char Rotary::process() {
|
||||||
|
// Grab state of input pins.
|
||||||
|
unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1);
|
||||||
|
// Determine new state from the pins and state table.
|
||||||
|
state = ttable[state & 0xf][pinstate];
|
||||||
|
// Return emit bits, ie the generated event.
|
||||||
|
return state & 0x30;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte Rotary::buttonState() { // returns the state of the Encoder button LOW=pressed
|
||||||
|
// return byte(digitalRead(btn));
|
||||||
|
return digitalRead(btn);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Rotary encoder library for Arduino.
|
||||||
|
*
|
||||||
|
* Revision: 2020 Dean Souleles, KK4DAS, Licensed under the GNU GPL Version 3.
|
||||||
|
* Contact: kk4das@gmail.com
|
||||||
|
* Added default pins and button handling and setup for clean interrupt handling
|
||||||
|
*
|
||||||
|
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
|
||||||
|
* Contact: bb@cactii.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RotaryEnc_h
|
||||||
|
#define RotaryEnc_h
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
// Enable this to emit codes twice per step.
|
||||||
|
//#define HALF_STEP
|
||||||
|
|
||||||
|
// Enable weak pullups
|
||||||
|
#define ENABLE_PULLUPS
|
||||||
|
|
||||||
|
// Values returned by 'process'
|
||||||
|
// No complete step yet.
|
||||||
|
#define DIR_NONE 0x0
|
||||||
|
// Clockwise step.
|
||||||
|
#define DIR_CW 0x10
|
||||||
|
// Counter-clockwise step.
|
||||||
|
#define DIR_CCW 0x20
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Default Encoder wiring Pins D2 and D3 for the encoder, A3 for the pushbutton
|
||||||
|
//
|
||||||
|
#ifndef ENCODER_A
|
||||||
|
#define ENCODER_A 2 // Encoder pin A D2 (interrupt pin)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENCODER_B
|
||||||
|
#define ENCODER_B 3 // Encoder pin B D3 (interrupt pin)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ENCODER_BTN
|
||||||
|
#define ENCODER_BTN A3 // Encoder push buttonh
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class Rotary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Rotary(char, char, char);
|
||||||
|
Rotary();
|
||||||
|
unsigned char process();
|
||||||
|
byte buttonState();
|
||||||
|
private:
|
||||||
|
unsigned char state;
|
||||||
|
unsigned char pin1;
|
||||||
|
unsigned char pin2;
|
||||||
|
unsigned char btn;
|
||||||
|
void Constructor_Common();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef SSB_Display_h
|
||||||
|
#define SSB_Display_h
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSB_Display.h
|
||||||
|
* KK4DAS, Dean Souleles, KK4DAS@gmail.com
|
||||||
|
* May 30, 2020
|
||||||
|
*
|
||||||
|
* Constants and function prototypes used in the Display Routines
|
||||||
|
* This file needs to be in included in the main sketch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "RadioControl.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===========================Function Prototypes==================================
|
||||||
|
|
||||||
|
extern void displaySMeter(byte level);
|
||||||
|
extern void displayBanner(String s);
|
||||||
|
extern void displayActVFO(uint32_t freq);
|
||||||
|
extern void displayAltVFO(uint32_t freq);
|
||||||
|
extern void displayVFOAB(int vfo);
|
||||||
|
extern void displayTxRx(int tx_rx);
|
||||||
|
extern void displayMode(int mode);
|
||||||
|
extern void displayIncr(uint32_t increment);
|
||||||
|
extern void displayTune(boolean on_off);
|
||||||
|
extern void displaySplit(boolean splt);
|
||||||
|
extern void displaySetup(String banner,
|
||||||
|
uint32_t vfoActfreq, uint32_t vfoAltfreq,
|
||||||
|
uint32_t activeVFO,
|
||||||
|
int tx_rx,
|
||||||
|
int sideband,
|
||||||
|
boolean split,
|
||||||
|
uint32_t increment,
|
||||||
|
byte s_meter);
|
||||||
|
|
||||||
|
#ifdef DISPLAY_NEXTION
|
||||||
|
extern void CheckTouch();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,280 @@
|
||||||
|
#include "RadioControl.h"
|
||||||
|
#ifdef DISPLAY_LCD
|
||||||
|
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
|
||||||
|
LiquidCrystal_I2C lcd(0x27, 20, 4); //0x27 or 0x3F 0r 0x20 //1604 LCD/w I2C
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Display Routines //
|
||||||
|
// Display routines for 20x4 LCD //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Display Layout 20x4 //
|
||||||
|
// 1111111111 //
|
||||||
|
// 01234567890123456789 //
|
||||||
|
// 0: A>xX.XXX.XXX xSB //
|
||||||
|
// 1: B>xX.XXX.XXX SPLIT //
|
||||||
|
// 2: RX S:123456789+++ //
|
||||||
|
// 3: CALL xxxx Hz vX.X //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//************************displayBanner*****************************
|
||||||
|
// Sets up display line 3 - call sign, increment display and version
|
||||||
|
//
|
||||||
|
// Note - Does not use the input String (included for code compatibility
|
||||||
|
// with other display modules)
|
||||||
|
//
|
||||||
|
void displayBanner(String s) {
|
||||||
|
|
||||||
|
// Setup line 3 call and tuning increment
|
||||||
|
lcd.setCursor(0, 3);
|
||||||
|
lcd.print(CALLSIGN);
|
||||||
|
|
||||||
|
lcd.setCursor(12, 3);
|
||||||
|
lcd.print("Hz");
|
||||||
|
|
||||||
|
lcd.setCursor(16, 3);
|
||||||
|
lcd.print(F("v"));
|
||||||
|
lcd.print(VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//************************displaySmeter****************************
|
||||||
|
void displaySMeter(byte level) {
|
||||||
|
int i;
|
||||||
|
lcd.setCursor(6, 2);
|
||||||
|
lcd.print("S:");
|
||||||
|
for (i = 0; i < MAXSLEVELS; i++) {
|
||||||
|
if (level >= i) {
|
||||||
|
if (i < 9) {
|
||||||
|
lcd.print(i + 1);
|
||||||
|
} else {
|
||||||
|
lcd.print("+");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lcd.print(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//************************displayVFOAB*******************************
|
||||||
|
// displayVFOAB(int VFO)
|
||||||
|
// Updates the display with flag for the currently active VFO - A or B
|
||||||
|
//
|
||||||
|
void displayVFOAB(int vfo) {
|
||||||
|
// Reset the active VFO flag
|
||||||
|
|
||||||
|
// Clear previous flag
|
||||||
|
lcd.setCursor(1,0);
|
||||||
|
lcd.print(F(" "));
|
||||||
|
lcd.setCursor(1,1);
|
||||||
|
lcd.print(F(" "));
|
||||||
|
|
||||||
|
// Set flag by currently active VFO
|
||||||
|
if (vfo == VFOA) {
|
||||||
|
lcd.setCursor(1, 0);
|
||||||
|
} else {
|
||||||
|
lcd.setCursor(1, 1);
|
||||||
|
}
|
||||||
|
lcd.print(F(">"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//************************displayVFO*******************************
|
||||||
|
|
||||||
|
// displayVFO(vfo,freq)
|
||||||
|
// Updates the dispaly for the input vfo
|
||||||
|
// Displays the frequency subracting the bfo - sideband inversion
|
||||||
|
//
|
||||||
|
void displayVFO(int vfo, long freq) {
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
if (vfo == VFOA) {
|
||||||
|
row = 0;
|
||||||
|
} else if (vfo == VFOB) {
|
||||||
|
row = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the frequency
|
||||||
|
lcd.setCursor(2, row);
|
||||||
|
if (freq < 10000000) {
|
||||||
|
lcd.print(" ");
|
||||||
|
}
|
||||||
|
lcd.print(int((freq / 1000000))); //millions
|
||||||
|
lcd.print(".");
|
||||||
|
lcd.print(((freq / 100000) % 10)); //hundredthousands
|
||||||
|
lcd.print(((freq / 10000) % 10)); //tenthousands
|
||||||
|
lcd.print(((freq / 1000) % 10)); //thousands
|
||||||
|
lcd.print(".");
|
||||||
|
lcd.print(((freq / 100) % 10)); //hundreds
|
||||||
|
lcd.print(((freq / 10) % 10)); //tens
|
||||||
|
lcd.print(((freq / 1) % 10)); //ones
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************displayActVFO*******************************
|
||||||
|
// Updates the frequencey display for the current active VFO A or B
|
||||||
|
void displayActVFO(uint32_t freq) {
|
||||||
|
displayVFO(active_vfo, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************displayActVFO*******************************
|
||||||
|
// Updates the frequencey display for the current alternate VFO A or B
|
||||||
|
extern void displayAltVFO(uint32_t freq) {
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
displayVFO(VFOB, freq);
|
||||||
|
} else{
|
||||||
|
displayVFO(VFOA, freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//************************displayMode*******************************
|
||||||
|
//
|
||||||
|
// displaySideband(mode)
|
||||||
|
// Updates the current mode (sideband) indicator U or L
|
||||||
|
//
|
||||||
|
void displayMode(int mode) {
|
||||||
|
char sb = ' ';
|
||||||
|
|
||||||
|
if (mode == USB) {
|
||||||
|
sb = 'U';
|
||||||
|
} else if (mode == LSB) {
|
||||||
|
sb = 'L';
|
||||||
|
}
|
||||||
|
lcd.setCursor(13, 0);
|
||||||
|
lcd.print(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayTxRx(int tx_rx) {
|
||||||
|
lcd.setCursor(0, 2);
|
||||||
|
if (tx_rx == RX) {
|
||||||
|
lcd.print(F("RX"));
|
||||||
|
} else {
|
||||||
|
lcd.print(F("TX"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************displayIncr*******************************
|
||||||
|
//
|
||||||
|
// displayIncr(increment)
|
||||||
|
// Display the tuning increment
|
||||||
|
//
|
||||||
|
void displayIncr(unsigned long increment) {
|
||||||
|
|
||||||
|
String hertz = " "; // tune step display
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
sprintf(debugmsg, "Increment: %ld", increment);
|
||||||
|
Serial.println(debugmsg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (increment == 10) {
|
||||||
|
hertz = F(" 10");
|
||||||
|
} else if (increment == 100) {
|
||||||
|
hertz = F(" 100");
|
||||||
|
} else if (increment == 1000) {
|
||||||
|
hertz = F(" 1K");
|
||||||
|
} else if (increment == 10000) {
|
||||||
|
hertz = F(" 10K");
|
||||||
|
} else if (increment == 100000) {
|
||||||
|
hertz = F("100K");
|
||||||
|
} else if (increment == 1000000) {
|
||||||
|
hertz = F(" 1M");
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd.setCursor(7, 3);
|
||||||
|
lcd.print(hertz);
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************displayTune*******************************
|
||||||
|
//
|
||||||
|
// displayTune(On)
|
||||||
|
// If On is TRUE displays TUNE message else clears it
|
||||||
|
//
|
||||||
|
void displayTune(bool On) {
|
||||||
|
lcd.setCursor(7, 2);
|
||||||
|
if (On == true) {
|
||||||
|
lcd.print(F("TUNE"));
|
||||||
|
} else {
|
||||||
|
lcd.print(F(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayDebug(String msg) {
|
||||||
|
lcd.setCursor(7, 2);
|
||||||
|
lcd.print(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************displaySplit*******************************
|
||||||
|
//
|
||||||
|
// displayTune(split)
|
||||||
|
// If split is TRUE displays SPLIT message else clears it
|
||||||
|
//
|
||||||
|
void displaySplit(boolean splt) {
|
||||||
|
lcd.setCursor(14, 1);
|
||||||
|
if (split == true) {
|
||||||
|
lcd.print(F("SPLIT"));
|
||||||
|
} else {
|
||||||
|
lcd.print(F(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displaySetup()
|
||||||
|
// Initialze and populate the display
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displaySetup(String banner,
|
||||||
|
uint32_t vfoActfreq, uint32_t vfoAltfreq,
|
||||||
|
uint32_t activeVFO,
|
||||||
|
int tx_rx,
|
||||||
|
int sideband,
|
||||||
|
boolean split,
|
||||||
|
uint32_t increment,
|
||||||
|
byte s_meter) {
|
||||||
|
lcd.init();
|
||||||
|
lcd.clear();
|
||||||
|
delay(100);
|
||||||
|
lcd.backlight();
|
||||||
|
|
||||||
|
// Setup fixed screeen elements
|
||||||
|
|
||||||
|
// Line 0 - VFO A
|
||||||
|
lcd.setCursor(0,0);
|
||||||
|
lcd.print(F("A"));
|
||||||
|
|
||||||
|
// Line 0 - sideband indicator
|
||||||
|
lcd.setCursor(14,0);
|
||||||
|
lcd.print(F("SB"));
|
||||||
|
|
||||||
|
// Line 1 VFO B
|
||||||
|
lcd.setCursor(0,1);
|
||||||
|
lcd.print(F("B"));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Display the intitial values
|
||||||
|
//
|
||||||
|
displayBanner(banner);
|
||||||
|
displayActVFO(vfoActfreq);
|
||||||
|
displayAltVFO(vfoAltfreq);
|
||||||
|
displayVFOAB(activeVFO);
|
||||||
|
displayTxRx(tx_rx);
|
||||||
|
displayMode(sideband);
|
||||||
|
displaySplit(split);
|
||||||
|
displayIncr(increment);
|
||||||
|
#ifdef SMETER
|
||||||
|
displaySMeter(s_meter);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,521 @@
|
||||||
|
#include "RadioControl.h"
|
||||||
|
#ifdef DISPLAY_NEXTION
|
||||||
|
|
||||||
|
//#define NEX_DEBUG // sends debug messages to the banner (bottom of screen)
|
||||||
|
//#define NEX_DEBUG1
|
||||||
|
|
||||||
|
// Dean Souleles, KK4DAS
|
||||||
|
// 7/25/2020
|
||||||
|
//
|
||||||
|
// Nextion Display Module for SSB_Radio_Contrl
|
||||||
|
// Nextion 2.8 Inch display
|
||||||
|
// Tested on Arduino Nano
|
||||||
|
//
|
||||||
|
// Arduino Nano Every
|
||||||
|
// PINS / WIRING
|
||||||
|
// ----------------------
|
||||||
|
// Arduino | Nextion
|
||||||
|
// --------|-------------
|
||||||
|
// 1 (RX) | TX (Blu) -- pins 0 and 1 are for Serial1
|
||||||
|
// 0 (TX) | RX (Yel)
|
||||||
|
// +5 | +5 (Red)
|
||||||
|
// Gnd | Gnd (Blk)
|
||||||
|
//------------------------
|
||||||
|
//
|
||||||
|
// Arduino Nano // deprecated - runs out of memory / stack crash
|
||||||
|
// PINS / WIRING
|
||||||
|
// ----------------------
|
||||||
|
// Arduino | Nextion
|
||||||
|
// --------|-------------
|
||||||
|
// 8 (RX) | TX (Blu) -- pins 8 and 9 are requuired by AltSoftSerial
|
||||||
|
// 9 (TX) | RX (Yel) -- to use other pins you can use SofwareSerial
|
||||||
|
// +5 | +5 (Red) -- but that will conflict with the interrupt
|
||||||
|
// Gnd | Gnd (Blk) -- used by the Rotary Encoder
|
||||||
|
//------------------------
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// We use the AltSoftSerial library to pins (8 and 9) rather than default 0,1
|
||||||
|
// AltSoftSerial is used rather than SoftwareSerial to free up the interrupt vector
|
||||||
|
// required for the digital encoder
|
||||||
|
// NOTE:
|
||||||
|
// AltSoftSerial uses only fixed pins 8,9 on an Uno or Nano
|
||||||
|
// Pin 10 cannot be used for PWM
|
||||||
|
// For other boards see: https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||||
|
//
|
||||||
|
// First, apply the serial fixes from Ray Livingston to the Nextion Libarary:
|
||||||
|
// https://forum.arduino.cc/index.php?topic=620821.0
|
||||||
|
// Replace files in the ../libraries/ITEADLIB_Arduino_Nextion folder:
|
||||||
|
// NexConfig.h
|
||||||
|
// NexHardware.h
|
||||||
|
// NexHardware.cpp
|
||||||
|
// Plus one more patch to fix a bug that was including SoftwareSerial when it should not have
|
||||||
|
// NexUpload.cpp (change by me, Dean Souleles)
|
||||||
|
//
|
||||||
|
// Next we edit NexConfig.h in the ../libraries/ITEADLIB_Arduino_Nextion folder as follows:
|
||||||
|
// Comment out this line:
|
||||||
|
// #define nexSerial Serial
|
||||||
|
//
|
||||||
|
// Add these three lines:
|
||||||
|
// #include <AltSoftSerial.h>
|
||||||
|
// extern AltSoftSerial HMISerial;
|
||||||
|
// #define nexSerial HMISerial
|
||||||
|
//
|
||||||
|
// Add the following to your sketch
|
||||||
|
// #include <AltSoftSerial.h>
|
||||||
|
// AltSoftSerial HMISerial; //RX TX - connect to Nextion TX RX - Must use Pins 9,10 on Uno/Nano
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <Nextion.h> // https://github.com/itead/ITEADLIB_Arduino_Nextion
|
||||||
|
|
||||||
|
//#include <SoftwareSerial.h>
|
||||||
|
//SoftwareSerial HMISerial(8,9); //RX TX - connect to Nextion TX RX - Pins 8,9 on Uno/Nano
|
||||||
|
|
||||||
|
#if _BOARDTYPE == Nano
|
||||||
|
#include <AltSoftSerial.h>
|
||||||
|
AltSoftSerial HMISerial; //RX TX - connect to Nextion TX RX - Must use Pins 8,9 on Uno/Nano
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Declare Nextion objects
|
||||||
|
// Use the page ID, component id and component name from the Nextion IDE
|
||||||
|
//
|
||||||
|
|
||||||
|
// Nextion PAGE
|
||||||
|
// Current design only uses one page of the display
|
||||||
|
#define PAGE 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Nextion Component IDs for buttons and text displays
|
||||||
|
// These ID's must match the ID's Nextion IDE
|
||||||
|
#define VFO_ID 2
|
||||||
|
#define SIDEBAND_ID 5
|
||||||
|
#define TUNE_ID 7
|
||||||
|
#define INCREMENT_ID 6
|
||||||
|
#define ACT_VFO_ID 3
|
||||||
|
#define ALT_VFO_ID 4
|
||||||
|
#define TX_RX_ID 8
|
||||||
|
#define SMETER_ID 10
|
||||||
|
#define BANNER_ID 9
|
||||||
|
#define TUNE_PLUS_ID 11
|
||||||
|
#define TUNE_MINUS_ID 12
|
||||||
|
#define TUNE_STATE_ID 15
|
||||||
|
#define ATOB_ID 18 // VFO A copy to B button
|
||||||
|
#define SPLIT_ID 19
|
||||||
|
#define VAB_ID 20 // VFO Inidicator
|
||||||
|
|
||||||
|
// Nextion Component names
|
||||||
|
// These names must match the names in the Nextion IDE
|
||||||
|
#define VFO_NAME "bVFO"
|
||||||
|
#define SIDEBAND_NAME "bSideband"
|
||||||
|
#define TUNE_NAME "bTune"
|
||||||
|
#define INCREMENT_NAME "bIncr"
|
||||||
|
#define ACT_VFO_NAME "tActVFO"
|
||||||
|
#define ALT_VFO_NAME "tAltVFO"
|
||||||
|
#define BANNER_NAME "tBanner"
|
||||||
|
#define TX_RX_NAME "tTxRx"
|
||||||
|
#define SMETER_NAME "pSmeter"
|
||||||
|
#define TUNE_PLUS_NAME "bPlus"
|
||||||
|
#define TUNE_MINUS_NAME "bMinus"
|
||||||
|
#define TUNE_STATE_NAME "tTuneState"
|
||||||
|
#define ATOB_NAME "bAtoB"
|
||||||
|
#define SPLIT_NAME "btSplit"
|
||||||
|
#define VAB_NAME "tVAB"
|
||||||
|
|
||||||
|
// Nextion Buttons
|
||||||
|
// Each user interface object that the user touches needs to be defined here
|
||||||
|
//
|
||||||
|
NexButton bVFO = NexButton(PAGE, VFO_ID, VFO_NAME);
|
||||||
|
NexButton bSideband = NexButton(PAGE, SIDEBAND_ID, SIDEBAND_NAME);
|
||||||
|
NexButton bTune = NexButton(PAGE, TUNE_ID, TUNE_NAME);
|
||||||
|
NexButton bIncrement = NexButton(PAGE, INCREMENT_ID, INCREMENT_NAME);
|
||||||
|
NexButton bTunePlus = NexButton(PAGE, TUNE_PLUS_ID, TUNE_PLUS_NAME);
|
||||||
|
NexButton bTuneMinus = NexButton(PAGE, TUNE_MINUS_ID, TUNE_MINUS_NAME);
|
||||||
|
NexButton bAtoB = NexButton(PAGE, ATOB_ID, ATOB_NAME);
|
||||||
|
NexDSButton btSplit = NexDSButton(PAGE, SPLIT_ID, SPLIT_NAME);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Setup a list of objects to respond to a touch event
|
||||||
|
//
|
||||||
|
NexTouch *nex_listen_list[] = {
|
||||||
|
&bVFO,
|
||||||
|
&bSideband,
|
||||||
|
&bTune,
|
||||||
|
&bIncrement,
|
||||||
|
&bTunePlus,
|
||||||
|
&bTuneMinus,
|
||||||
|
&bAtoB,
|
||||||
|
&btSplit,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// HMI_send_command(cmd)
|
||||||
|
// Sends one command to the Nextion Display
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
// String cmd;
|
||||||
|
// cmd = F("vis ");
|
||||||
|
// cmd = cmd + F(TUNE_STATE_NAME);
|
||||||
|
// cmd = cmd + F(",");
|
||||||
|
// if (on_off) {
|
||||||
|
// cmd = cmd + F("1");
|
||||||
|
// } else {
|
||||||
|
// cmd = cmd + F("0");
|
||||||
|
// }
|
||||||
|
// HMI_send_command(cmd.c_str());
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void HMI_send_command(char* cmd) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Send the command
|
||||||
|
HMISerial.print(cmd);
|
||||||
|
|
||||||
|
// Send end-of-message per Nextion protocol
|
||||||
|
HMISerial.write(0xff);
|
||||||
|
HMISerial.write(0xff);
|
||||||
|
HMISerial.write(0xff);
|
||||||
|
|
||||||
|
*/
|
||||||
|
// Send the command to the Nextion
|
||||||
|
nexSerial.print(cmd);
|
||||||
|
|
||||||
|
// Send end-of-message per Nextion protocol
|
||||||
|
nexSerial.write(0xff);
|
||||||
|
nexSerial.write(0xff);
|
||||||
|
nexSerial.write(0xff);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayActVFO(uint32_t freq)
|
||||||
|
// Formats and displays Active and Alternat VFO frequencies
|
||||||
|
// Legal values: Frequency in Hz
|
||||||
|
//
|
||||||
|
// To do - comibine into one function
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void displayActVFO(uint32_t freq) {
|
||||||
|
String cmd;
|
||||||
|
String fmt;
|
||||||
|
char f[11];
|
||||||
|
uint32_t mil, hund_thou, ten_thou, thou, hund, tens, ones;
|
||||||
|
|
||||||
|
// Format number as nn.nnn.nnn
|
||||||
|
mil = freq / 1000000;
|
||||||
|
hund_thou = (freq/100000)%10;
|
||||||
|
ten_thou = (freq/10000)%10;
|
||||||
|
thou = (freq/1000)%10;
|
||||||
|
hund = (freq/100)%10;
|
||||||
|
tens = (freq/10)%10;
|
||||||
|
ones = freq%10;
|
||||||
|
fmt=F("%2ld.%ld%ld%ld.%ld%ld%ld");
|
||||||
|
snprintf(f, sizeof(f),fmt.c_str(),mil, hund_thou, ten_thou,thou, hund, tens, ones);
|
||||||
|
|
||||||
|
cmd = F(ACT_VFO_NAME);
|
||||||
|
cmd += F(".txt=\"");
|
||||||
|
cmd += f;
|
||||||
|
cmd += F("\"");
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayAltVFO(uint32_t freq) {
|
||||||
|
String cmd;
|
||||||
|
String fmt;
|
||||||
|
char f[11];
|
||||||
|
uint32_t mil, hund_thou, ten_thou, thou, hund, tens, ones;
|
||||||
|
|
||||||
|
// Format number as nn.nnn.nnn
|
||||||
|
mil = freq / 1000000;
|
||||||
|
hund_thou = (freq/100000)%10;
|
||||||
|
ten_thou = (freq/10000)%10;
|
||||||
|
thou = (freq/1000)%10;
|
||||||
|
hund = (freq/100)%10;
|
||||||
|
tens = (freq/10)%10;
|
||||||
|
ones = freq%10;
|
||||||
|
fmt=F("%2ld.%ld%ld%ld.%ld%ld%ld");
|
||||||
|
snprintf(f, sizeof(f),fmt.c_str(),mil, hund_thou, ten_thou,thou, hund, tens, ones);
|
||||||
|
|
||||||
|
cmd = F(ALT_VFO_NAME);
|
||||||
|
cmd += F(".txt=\"");
|
||||||
|
cmd += f;
|
||||||
|
cmd += F("\"");
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displaySMeter(byte level) {
|
||||||
|
// Nextion dipslay bar graph is set by integer percent 0-100
|
||||||
|
// Convert the S level into a % and send to display
|
||||||
|
|
||||||
|
String cmd;
|
||||||
|
float pct;
|
||||||
|
int scaled_level;
|
||||||
|
pct = (float(level)/13.0)*100.0;
|
||||||
|
scaled_level=pct;
|
||||||
|
|
||||||
|
cmd = F(SMETER_NAME);
|
||||||
|
cmd += F(".val=");
|
||||||
|
cmd += scaled_level;
|
||||||
|
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG1
|
||||||
|
String msg = F("displaySMeter: ");
|
||||||
|
msg = msg + level;
|
||||||
|
msg = msg + F(" ");
|
||||||
|
msg = msg + scaled_level;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayBanner(String s) {
|
||||||
|
String cmd;
|
||||||
|
cmd = F(BANNER_NAME);
|
||||||
|
cmd += F(".txt=\"");
|
||||||
|
cmd += s;
|
||||||
|
cmd += F("\"");
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayVFOAB(int vfo) {
|
||||||
|
String cmd;
|
||||||
|
cmd = F(VAB_NAME);
|
||||||
|
cmd += F(".txt=\"");
|
||||||
|
if (vfo == VFOA) {
|
||||||
|
cmd += F("A");
|
||||||
|
} else {
|
||||||
|
cmd += F("B");
|
||||||
|
}
|
||||||
|
cmd += F("\"");
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("displayVFOAB: ");
|
||||||
|
msg = msg + vfo;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayTxRx(int tx_rx) {
|
||||||
|
String cmd;
|
||||||
|
cmd = F(TX_RX_NAME);
|
||||||
|
cmd += F(".txt=\"");
|
||||||
|
if (tx_rx == TX) {
|
||||||
|
cmd += F("TX");
|
||||||
|
} else {
|
||||||
|
cmd += F("RX");
|
||||||
|
}
|
||||||
|
cmd += F("\"");
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("displayTxRx: ");
|
||||||
|
msg = msg + tx_rx;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayMode(int mode) {
|
||||||
|
String modeString;
|
||||||
|
if (mode == USB) {
|
||||||
|
modeString = F("USB");
|
||||||
|
} else {
|
||||||
|
modeString = F("LSB");
|
||||||
|
}
|
||||||
|
bSideband.setText(modeString.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("displayMode: ");
|
||||||
|
msg = msg + modeString;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayIncr(uint32_t increment) {
|
||||||
|
String hertz;
|
||||||
|
switch (increment) {
|
||||||
|
case 1: hertz = F(" 1"); break;
|
||||||
|
case 10: hertz = F(" 10"); break;
|
||||||
|
case 100: hertz = F(" 100"); break;
|
||||||
|
case 1000: hertz = F(" 1K"); break;
|
||||||
|
case 10000: hertz = F(" 10K"); break;
|
||||||
|
case 100000: hertz= F("100K"); break;
|
||||||
|
case 1000000:hertz = F(" 1M");
|
||||||
|
}
|
||||||
|
bIncrement.setText(hertz.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("displayIncr: ");
|
||||||
|
msg = msg + hertz;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayTune(boolean on_off) {
|
||||||
|
// Send visibility command to Nextion
|
||||||
|
// Format: vis <object_name),<0/1>
|
||||||
|
String cmd;
|
||||||
|
cmd = F("vis ");
|
||||||
|
cmd += F(TUNE_STATE_NAME);
|
||||||
|
cmd += F(",");
|
||||||
|
cmd += on_off;
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
displayBanner(cmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void displaySplit(boolean split) {
|
||||||
|
// The split button is a dual state button
|
||||||
|
// State 0 = blue = ff
|
||||||
|
// State 1 = green = on
|
||||||
|
// To change the state and color set the val property to 0/1
|
||||||
|
// This mimics a button press on the display
|
||||||
|
|
||||||
|
String cmd =F(SPLIT_NAME);
|
||||||
|
cmd += F(".val=");
|
||||||
|
if (split) {
|
||||||
|
cmd += 1;
|
||||||
|
} else {
|
||||||
|
cmd += 0;
|
||||||
|
}
|
||||||
|
HMI_send_command(cmd.c_str());
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("displaySplit: ");
|
||||||
|
msg = msg + split;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Button Callback Functions
|
||||||
|
// Called whenever a button is pressed and released
|
||||||
|
//
|
||||||
|
|
||||||
|
// VFO A/B Button
|
||||||
|
void bVFOPopCallback(void *ptr) {
|
||||||
|
SwapVFO(); // call the VFO switch button handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSB/USB Button
|
||||||
|
void bSidebandPopCallback(void *ptr) {
|
||||||
|
SwapSB();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tune Plus (increase VFO)
|
||||||
|
void bTunePlusPushCallback(void *ptr) {
|
||||||
|
AdjustVFO(increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tune Minus (decrease VFO)
|
||||||
|
void bTuneMinusPushCallback(void *ptr) {
|
||||||
|
AdjustVFO(-1 * increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tune Tone
|
||||||
|
void bTunePopCallback(void *ptr) {
|
||||||
|
DoTune();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuning Increment Change
|
||||||
|
void bIncrementPopCallback(void *ptr) {
|
||||||
|
AdvanceIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on/off
|
||||||
|
void btSplitPopCallback(void *ptr) {
|
||||||
|
uint32_t split_val;
|
||||||
|
|
||||||
|
btSplit.getValue(&split_val);
|
||||||
|
if (split_val==1) {
|
||||||
|
startSplit();
|
||||||
|
} else {
|
||||||
|
stopSplit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NEX_DEBUG
|
||||||
|
String msg = F("btSplitPopCallback: ");
|
||||||
|
msg = msg + split_val;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make Act and Alt VFO the same
|
||||||
|
void bAtoBPopCallback(void *ptr) {
|
||||||
|
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
vfoBfreq = vfoAfreq;
|
||||||
|
} else {
|
||||||
|
vfoAfreq = vfoBfreq;
|
||||||
|
}
|
||||||
|
displayAltVFO(vfoAfreq); // update the Alt VFO display
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Setup
|
||||||
|
// Called once at startup
|
||||||
|
//
|
||||||
|
void displaySetup(String banner,
|
||||||
|
uint32_t vfoActfreq, uint32_t vfoAltfreq,
|
||||||
|
uint32_t activeVFO,
|
||||||
|
int tx_rx,
|
||||||
|
int sideband,
|
||||||
|
boolean split,
|
||||||
|
uint32_t increment,
|
||||||
|
byte s_meter) {
|
||||||
|
|
||||||
|
nexInit(9600); // Initialize the Nextion library
|
||||||
|
|
||||||
|
//
|
||||||
|
// Attach callback routines for each button
|
||||||
|
// attachPop will set the library to invoke the specified funtion each time a button is released
|
||||||
|
//
|
||||||
|
bVFO.attachPop(bVFOPopCallback, &bVFO); // VFO A/B button
|
||||||
|
bSideband.attachPop(bSidebandPopCallback, &bSideband); // LSB/USB button
|
||||||
|
bTune.attachPop(bTunePopCallback, &bTune); // Tune
|
||||||
|
bIncrement.attachPop(bIncrementPopCallback, &bIncrement); // Change Incrmement
|
||||||
|
btSplit.attachPop(btSplitPopCallback, &btSplit); // Split On/Off
|
||||||
|
bAtoB.attachPop(bAtoBPopCallback, &bAtoB); // Make Alt VFO = Act VFO
|
||||||
|
|
||||||
|
bTunePlus.attachPush(bTunePlusPushCallback, &bTunePlus); // Tune up
|
||||||
|
bTuneMinus.attachPush(bTuneMinusPushCallback, &bTuneMinus); // Tune down
|
||||||
|
|
||||||
|
//
|
||||||
|
// Display the intitial values
|
||||||
|
//
|
||||||
|
displayBanner(banner);
|
||||||
|
displayActVFO(vfoActfreq);
|
||||||
|
displayAltVFO(vfoAltfreq);
|
||||||
|
displayVFOAB(activeVFO);
|
||||||
|
displayTxRx(tx_rx);
|
||||||
|
displayMode(sideband);
|
||||||
|
displaySplit(split);
|
||||||
|
displayIncr(increment);
|
||||||
|
displaySMeter(s_meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CheckTouch() {
|
||||||
|
// Call the Nextion check function to look for activites on your listen list
|
||||||
|
nexLoop(nex_listen_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,375 @@
|
||||||
|
/*
|
||||||
|
SSB_Radio_Control
|
||||||
|
Dean Souleles, KK4DAS, contact kk4das@gmail.com
|
||||||
|
|
||||||
|
This sketch implement s basic SSB radio control panel with the following features:
|
||||||
|
Dual VFO A/B
|
||||||
|
Rotary encoder for tuning, push button to change tuning increment
|
||||||
|
SI5351 Clock generator for the LO and BFO
|
||||||
|
CAT Control (emulates an ICOM IC-746)
|
||||||
|
Split mode support (Split from CAT, manual split requres Nextion touch screen)
|
||||||
|
Settings memory (last frequency and mode are saved)
|
||||||
|
Optional S-Meter
|
||||||
|
|
||||||
|
Controls
|
||||||
|
* Rotary encoder to change frequence
|
||||||
|
* Rotary encode button changes tuning increment
|
||||||
|
* VFO A/B Select toggle
|
||||||
|
* Mode select USB/LSB toggle
|
||||||
|
* Tune button (emits 10 second pulsed tone at 800Hz for tuning)
|
||||||
|
* MOX toggle - puts rig in to Tx/Rx
|
||||||
|
* Optional dual-band support 20/40
|
||||||
|
|
||||||
|
Modules for different display types
|
||||||
|
* 20x4 LCD
|
||||||
|
* 320x240 TFT color display
|
||||||
|
* 2.8" Nextion Touch Screen
|
||||||
|
|
||||||
|
Display features
|
||||||
|
* Dual VFOS
|
||||||
|
* Mode indicator SSB/LSB
|
||||||
|
* Tx/Rx ndicator
|
||||||
|
* TuningStep Inidicator
|
||||||
|
* Optional S Meter
|
||||||
|
* Banner including callsign
|
||||||
|
|
||||||
|
Additional controls with the Nextion display
|
||||||
|
* Continuous scanning
|
||||||
|
* Split mode
|
||||||
|
|
||||||
|
Version 1.4
|
||||||
|
March 9 2021
|
||||||
|
Rstored LCD Display Option
|
||||||
|
Compile time selection of S-Meter and Dual Band mods
|
||||||
|
Refactoring of encoder handling
|
||||||
|
|
||||||
|
Version 1.3
|
||||||
|
Jan 25, 2021
|
||||||
|
Changed CAT module to IC-746
|
||||||
|
|
||||||
|
Version 1.2
|
||||||
|
Dec 14, 2020
|
||||||
|
Dual Band 20/40 Support
|
||||||
|
S-Meter
|
||||||
|
|
||||||
|
Version 1.1
|
||||||
|
Dec 13. 2020
|
||||||
|
Ported to Nano Every for more sweet SRAM
|
||||||
|
* * Updated interrupt handling in Encoder.cpp to work with Nano Every (as well as UNO/Nano)
|
||||||
|
* * Replaced SoftwareSerial connections to Nextion with Hardware Serial - Serial1
|
||||||
|
|
||||||
|
Version 1.0
|
||||||
|
Aug 13, 2020
|
||||||
|
Adapted from SimpleSSB Sketch by N6QW, Pete Juliano and others
|
||||||
|
|
||||||
|
|
||||||
|
NOTE TO BUILDERS
|
||||||
|
This is a reference implementation of an SSB radio control program for a homebrew SSB transceiver.
|
||||||
|
It is a fully functioning SSB radio control program. While it has been built for my particular hardware
|
||||||
|
configuration every attempt has been made to make it modular in design so that the builder can swap out
|
||||||
|
modules at will. It should be fairly straight forward to adapt to the builder's hardware selection
|
||||||
|
of switches, buttons and knobs or even alternate displays.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RadioControl.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
char debugmsg[25];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//=============== Globals ============================================
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// si5351 Clock Module //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
Si5351 si5351;
|
||||||
|
|
||||||
|
// Calibration offest - adjust for variability in the SI5351 module
|
||||||
|
// crystal - must be set for the particular SI5351 installed
|
||||||
|
//#define CALIBRATION_OFFSET 1190 // Calibration for the SI-5351
|
||||||
|
#define CALIBRATION_OFFSET 880 // Calibration for the SI-5351
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// BFO and VFO Constants and Variables //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef BFO12MHZ
|
||||||
|
const uint32_t USB_BFO = 12001600L;
|
||||||
|
const uint32_t LSB_BFO = 11998600L;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BFO9MHZ
|
||||||
|
const uint32_t USB_BFO = 9001500L;
|
||||||
|
const uint32_t LSB_BFO = 8998500L;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t bfo = LSB_BFO; // Startup BFO frequency
|
||||||
|
const uint32_t BFO_DELTA = USB_BFO - LSB_BFO; // Difference between USB and LSB for BFO change
|
||||||
|
|
||||||
|
//
|
||||||
|
// Startup VFO A/B frequencies
|
||||||
|
//
|
||||||
|
uint32_t vfoAfreq = 7200000L; // 7.200.000
|
||||||
|
uint32_t vfoBfreq = 7074000L; // FT-8 7.074.000
|
||||||
|
byte vfoASideband = LSB;
|
||||||
|
byte vfoBSideband = USB;
|
||||||
|
|
||||||
|
uint32_t increment = 1000; // startup VFO tuning increment in HZ.
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Active VFO //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
byte active_vfo = VFOA; // startup on VFOA
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Sideband Selection //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
byte sideband = LSB; // startup in LSB
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Sideband Selection //
|
||||||
|
// //
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
uint32_t band20Freq = 14200000L; // 14.200.000
|
||||||
|
uint32_t band40Freq = 7200000L; // 7.200.000
|
||||||
|
byte band20Sideband = USB;
|
||||||
|
byte band40Sideband = LSB;
|
||||||
|
byte band = BAND40;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Transmit / Receive Indicators //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
byte TxRxState = RX; // startup in RX
|
||||||
|
byte lastTxRxState = RX; // previous TxRxState
|
||||||
|
byte txSource = PTT_MIC; // transmit source - Mic, Tune, CAT
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// S Meter 0-9 +10, +20 +30 //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
byte smeter = 0; // startup s_meter reading (requires SMETER)
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Split Mode On/Off //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
bool split = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// setBandFilters(band) //
|
||||||
|
// For 20 meters turn relay ON (NO) //
|
||||||
|
// For 40 meters turn relay OFF (NC) //
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
void setBandFilters(int band) {
|
||||||
|
#ifdef DUAL_BAND
|
||||||
|
switch (band) {
|
||||||
|
case BAND20:
|
||||||
|
digitalWrite(BAND_PIN, HIGH);
|
||||||
|
break;
|
||||||
|
case BAND40:
|
||||||
|
digitalWrite(BAND_PIN, LOW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CW
|
||||||
|
//
|
||||||
|
// setCW()
|
||||||
|
//
|
||||||
|
// Experimental code to generate CW tone on key down at 700Hz above the dial frequency
|
||||||
|
// Needs a bunch of scaffolding to implement CW mode
|
||||||
|
//
|
||||||
|
// Turns off the BFO, sets the LO to the VFO frequency + 700
|
||||||
|
//
|
||||||
|
// After testing -
|
||||||
|
// tone produced OK but needs an amplifier to get significant power out
|
||||||
|
//
|
||||||
|
void setCW() {
|
||||||
|
si5351.set_freq(0, 0, SI5351_CLK2); // turn off BFO
|
||||||
|
si5351.set_freq(vfoAfreq+700L , SI5351_PLL_FIXED, SI5351_CLK0); // set LO to operating freq
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//********************setVFO******************************************
|
||||||
|
void setVFO(uint32_t freq) {
|
||||||
|
//
|
||||||
|
// Set CLK0 to the to input frequency adjusted for the current BFO frequency
|
||||||
|
|
||||||
|
#ifdef DUAL_BAND
|
||||||
|
//
|
||||||
|
// Set filters for the band based on frequency
|
||||||
|
// Save frequency and sideband for band switching
|
||||||
|
//
|
||||||
|
if (freq >= BAND20_EDGE) {
|
||||||
|
setBandFilters(BAND20);
|
||||||
|
band20Freq = freq;
|
||||||
|
band20Sideband = sideband;
|
||||||
|
band = BAND20;
|
||||||
|
} else if (freq >= BAND40_EDGE) {
|
||||||
|
setBandFilters(BAND40);
|
||||||
|
band40Freq = freq;
|
||||||
|
band40Sideband = sideband;
|
||||||
|
band=BAND40;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
si5351.set_freq(freq + bfo, SI5351_PLL_FIXED, SI5351_CLK0);
|
||||||
|
startSettingsTimer(); // start timer to save current settings
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBFO(uint32_t freq) {
|
||||||
|
//
|
||||||
|
// Set CLK2 to the to input frequency
|
||||||
|
//
|
||||||
|
|
||||||
|
si5351.set_freq(freq, 0, SI5351_CLK2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//*********************Setup Arduino Pins*****************************
|
||||||
|
|
||||||
|
void setupPins() {
|
||||||
|
//
|
||||||
|
// Set the control buttons to INPUT_PULLUP
|
||||||
|
// Button state will be HIGH when open and LOW when pressed
|
||||||
|
//
|
||||||
|
pinMode(TUNE_BTN, INPUT_PULLUP); // Tune - momentary button
|
||||||
|
pinMode(VFO_BTN, INPUT_PULLUP); // VFO A/B Select - momentary button
|
||||||
|
pinMode(SIDEBAND_BTN, INPUT_PULLUP); // Upper/lower SB Select - momentary button
|
||||||
|
pinMode(BAND_BTN, INPUT_PULLUP); // Band Switch 20/40 - momentary button
|
||||||
|
pinMode(PTT_SENSE, INPUT_PULLUP); // Mic PTT swtich
|
||||||
|
pinMode(PTT, OUTPUT); digitalWrite(PTT, LOW); // HIGH to enable TX
|
||||||
|
pinMode(BAND_PIN, OUTPUT); digitalWrite(BAND_PIN, LOW); // Band Switch Relay (LOW = NC = 40m, HIGH = NO = 20m)
|
||||||
|
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//**********************Initialize SI5351******************************
|
||||||
|
|
||||||
|
void setupSI5351() {
|
||||||
|
si5351.init(SI5351_CRYSTAL_LOAD_8PF);
|
||||||
|
si5351.set_correction(CALIBRATION_OFFSET); // calibration offset
|
||||||
|
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
|
||||||
|
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Higher Drive since it is a ADE-1 DBM
|
||||||
|
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
|
||||||
|
si5351.set_freq(bfo, 0, SI5351_CLK2); // Initialize the bfo
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize the VFO
|
||||||
|
//
|
||||||
|
switch (active_vfo) {
|
||||||
|
case VFOA:
|
||||||
|
setVFO(vfoAfreq);
|
||||||
|
break;
|
||||||
|
case VFOB:
|
||||||
|
setVFO(vfoBfreq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Setup //
|
||||||
|
// //
|
||||||
|
// Called once by the Arduino operating system at startup //
|
||||||
|
// All initialization code goes here //
|
||||||
|
// //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
uint32_t vfoActfreq;
|
||||||
|
uint32_t vfoAltfreq;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
Serial.begin(57600);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setupPins(); // Initialize arduino pins
|
||||||
|
setupEncoder(); // Initialize interrupt service for rotary encoder
|
||||||
|
setupSettings(); // Retrive settings from EEPROM
|
||||||
|
setupSI5351();
|
||||||
|
|
||||||
|
|
||||||
|
if (active_vfo == VFOA) {
|
||||||
|
vfoActfreq = vfoAfreq;
|
||||||
|
vfoAltfreq = vfoBfreq;
|
||||||
|
} else {
|
||||||
|
vfoActfreq = vfoBfreq;
|
||||||
|
vfoAltfreq = vfoAfreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize the display with startup values
|
||||||
|
Delay(500); // short delay to let the display initialize - needed for Nextion
|
||||||
|
|
||||||
|
// Construct banner for TFT or Nextion display
|
||||||
|
// "Vx.x RIGNAME CALLSIGN"
|
||||||
|
String banner;
|
||||||
|
banner = F("V");
|
||||||
|
banner += F(VERSION);
|
||||||
|
banner += F(" ");
|
||||||
|
banner += F(RIGNAME);
|
||||||
|
banner += F(" ");
|
||||||
|
banner += F(CALLSIGN);
|
||||||
|
|
||||||
|
displaySetup(banner, // version number. call sign
|
||||||
|
vfoActfreq, vfoAltfreq, // Initial active and alternate VFO
|
||||||
|
active_vfo, // VFO A/B indicator
|
||||||
|
TxRxState, // TX/RX indicator
|
||||||
|
sideband, // LSB/USB,
|
||||||
|
split, // Split mode on/off
|
||||||
|
increment, // Tuning increment
|
||||||
|
smeter); // S Meter
|
||||||
|
|
||||||
|
setupCat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
CheckEncoder(); //VFO frequency changes
|
||||||
|
CheckIncrement(); // Encoder Button
|
||||||
|
|
||||||
|
#ifdef DUAL_BAND
|
||||||
|
CheckBand(); // Band Switch 20/40
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CheckVFO(); // VFO A/B change
|
||||||
|
CheckSB(); // USB/LSB change
|
||||||
|
CheckPTT(); // Check for Mic PTT
|
||||||
|
CheckTune(); // Check for Tune button press
|
||||||
|
|
||||||
|
#ifdef SMETER
|
||||||
|
CheckSmeter(); // Signal strength
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CheckCat(); // CAT Control
|
||||||
|
CheckSettings(); // Update EEPROM on Settings Change
|
||||||
|
|
||||||
|
#ifdef DISPLAY_NEXTION
|
||||||
|
CheckTouch(); // Check for touch screen action
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,551 @@
|
||||||
|
#include "RadioControl.h"
|
||||||
|
#ifdef DISPLAY_TFT
|
||||||
|
|
||||||
|
//
|
||||||
|
// 7/31/202 - Dean Souleles
|
||||||
|
// Added stubbed out Split indicator function to stay consistent with main sketch
|
||||||
|
// TO DO:
|
||||||
|
// ADD constants and code for Split indicator
|
||||||
|
//
|
||||||
|
|
||||||
|
/* SSB_TFT_Display
|
||||||
|
* KK4DAS, Dean Souleles, KK4DAS@gmail.com
|
||||||
|
* May 30, 2020
|
||||||
|
*
|
||||||
|
* Basic radio display panel for a SSB transsceiver
|
||||||
|
* Designed for a 320x240 Color TFT (non touch)
|
||||||
|
* Tested with an ILI9341 display
|
||||||
|
* Requires the following libraries be installed:
|
||||||
|
* Adafruit_GFX
|
||||||
|
* Adafruit_ILI9341
|
||||||
|
*
|
||||||
|
* Implements a basic SSB display console with the following features
|
||||||
|
* Dual VFO A/B
|
||||||
|
* Mode indicator SSB/LSB
|
||||||
|
* Tx/Rx ndicator
|
||||||
|
* TuningStep Inidicator
|
||||||
|
* S Meter
|
||||||
|
* Banner including Call sign
|
||||||
|
*
|
||||||
|
* Fully customizable. Fast display makins use of minimal resources./
|
||||||
|
* Room is left on the screen for additional features
|
||||||
|
* There is room on the screen for another row of features
|
||||||
|
*
|
||||||
|
* Easily change colors, font sizes and layout
|
||||||
|
*
|
||||||
|
* Default Screeen Layout
|
||||||
|
* A 7.200.000 LSB -- VFO A/B indicator, Active VFO Freq, LSB/USB inidicator
|
||||||
|
* Rx 7.048.000 100K -- Rx/Tx indicator, Alternate VFO Freq, Tuning Increment
|
||||||
|
*
|
||||||
|
* S |_|_|_|_|_|_|_|_|_|_|_|_| -- S Meter
|
||||||
|
* 1 3 5 7 9
|
||||||
|
*
|
||||||
|
* AGC SPL RIT -- (Planned) AGC on/of, Split On/Off, RIT On/OFF
|
||||||
|
*
|
||||||
|
* Ver Rig Name Call
|
||||||
|
*
|
||||||
|
* This module provides the following radio console dsiplayfunctions:
|
||||||
|
* displaySetup - initialize the display and displays the startup values - call once from your setup function
|
||||||
|
* displayBanner - Displays a text banner across the bottom of the screen
|
||||||
|
* displayActVFO - Displays the frequency of the Active VFO
|
||||||
|
* displayAltVFO - Displays the frequency of the Alternate VFO
|
||||||
|
* displayVFOAB - Displays the indicator which VFO is active (A or B)
|
||||||
|
* displayTxRx - Displays whether the rig is in (Tx or Rx)
|
||||||
|
* displayMode - Displays the whiuch sideband is selected (LSB or USB)
|
||||||
|
* displayIncr - Displays the tuning increment (10, 100, 1K 10K, 100K, 1M)
|
||||||
|
* displaySMeter - Displays the S Meter (1-9 are gray, +10 +20 and +30 are red
|
||||||
|
*
|
||||||
|
* This module also provides the following general purpose displauy functions:
|
||||||
|
* displayClearScreen - fills the screen with the selected backgrond color
|
||||||
|
* displayPrintat - prints text or nubmers on the screen at a specific location
|
||||||
|
* displayDrawBoundingBox - draw a box on the screen and fills it with a background color
|
||||||
|
* displayDrawTextBox - displays text inside a boundig box
|
||||||
|
*
|
||||||
|
* Design notes and how to use the code
|
||||||
|
*
|
||||||
|
* NOTE TO BUILDERS
|
||||||
|
* This is not a complete radio control sketch. It is the Display software only. In the spirit of modular design
|
||||||
|
* it is stand-alone and not dependent on using an SI-5351 or any other specific hardware, or on my particular
|
||||||
|
* hardware selection of switches, buttons and knobs. The demonstration sketch shows how to update the display,
|
||||||
|
* but you need to provide the code to determine what the actual values should be. You will likely need other
|
||||||
|
* libraries like the Si5351 and a Rotary encoder library aside from the GFX and the ILI9341.
|
||||||
|
*
|
||||||
|
* DESIGN PRINCIPLES
|
||||||
|
* Good software design principles are to use as few hard-coded numbers as possible.
|
||||||
|
* Wherever possible I have used #defines for any number that will be used more than one place in the code.
|
||||||
|
* For example #define DSP_VFO_ACT_X 60 defines the X coordinate (how far from the left of the screen)
|
||||||
|
* of the Active VFO frequency display. You will see multiple references to DSP_VFO_ACT_X throughout the code,
|
||||||
|
* but I never use the hardcoded number 60 again. Change it once – and it is changed throughout.
|
||||||
|
*
|
||||||
|
* Taking the S-Meter as an example:
|
||||||
|
*
|
||||||
|
* To update the S-Meter display you make a call to displaySMeter(n); where n is an integer from 1 to 12
|
||||||
|
* (representing S1-9, +10, +20 +30). Your sketch will need a way of monitoring signal strength (an analog input
|
||||||
|
* pin on the Arduino attached to an appropriate place on your rig) and converting it to the logarithmic S scale.
|
||||||
|
*
|
||||||
|
* SCREEN COORDINATES
|
||||||
|
* Coordinates work differently on displays than a typical graph where the origin 0,0 is in the middle abd positive
|
||||||
|
* and negative values move you away from the origin. For displays 0,0, the origin, is always upper left hand corner
|
||||||
|
* of the display and you only use positive numbers for the coordinates +X is pixels from the left edge, +Y is pixels
|
||||||
|
* down from the top. This particular example based on a 320x240 display but should be easily portable to other
|
||||||
|
* display sizes – but you have to keep in mind how the coordinate system works.
|
||||||
|
*
|
||||||
|
* SCREEN LAYOUT
|
||||||
|
* Here are a few notes about how the demonstration display is laid out. This should help you understand the design
|
||||||
|
* concept and allow you to begin to modify it.
|
||||||
|
*
|
||||||
|
* The VFO display is setup for a dual VFO rig. The currently Active VFO is always on the top and the alternate VFO
|
||||||
|
* is just below it. Your code will need to keep track of whether VFO A or VFO B is currently selected and call the
|
||||||
|
* display routines to update the display. I’ll describe how the VFO displays are are defined and that will give you
|
||||||
|
* an idea how you might modify or enhance the display.
|
||||||
|
*
|
||||||
|
* Active VFO - top center of the screen
|
||||||
|
* #define DSP_VFO_ACT_X 60 // Active VFO begins 60 pixels from the left hand edge (I picked 60 by experimenting)
|
||||||
|
* #define DSP_VFO_ACT_Y 30 // Active VFO box starts 30 pixels down from the top of the screen (Try changing it to 50
|
||||||
|
* // and see what happens)
|
||||||
|
* #define DSP_VFO_ACT_COLOR ILI9341_GREEN // This sets the text color for the Frequency display
|
||||||
|
* // use whatever colors you like
|
||||||
|
* #define DSP_VFO_ACT_BK ILI9341_BLACK // This sets the background color for the Active VFO
|
||||||
|
* #define DSP_VFO_ACT_SZ 3 // This is text size from Arduino TFT, values 1-5 1 is small 5 is large
|
||||||
|
* // (2 was too small, 4 was too large, 3 was just right)
|
||||||
|
*
|
||||||
|
* Alternate VFO – the second VFO is placed directly below the Active VFO on the screen. There are a couple of things of
|
||||||
|
* interest here. For the X coordinate, instead of putting in a hard coded number I refer back to the #define that I used
|
||||||
|
* for the Active VFO (DSP_VFO_ACT). That way, if I want to move VFO section to a different part of the screen I only need
|
||||||
|
* to change one number DSP_VFO_ACT_X, and the alternate VFO will move as well. Figuring out the Y coordinate for the
|
||||||
|
* alternate VFO is a little more challenging. Some math is involved. Starting with the Y coordinate of the Active VFO
|
||||||
|
* I need to calculate where how far down the display I need to go to place the second VFO. To do that I need calculate
|
||||||
|
* how many pixels tall the text characters in the Active VFO are and use that as an offset. It turns out we have everything
|
||||||
|
* we need already defined. CH_W and CH_H are #defines that specify the height and width of a text character in pixels for
|
||||||
|
* TFT font size 1. Size 2 through 5 are even multiples of that – so font height for size 2 is 2*CH_H pixels and font width
|
||||||
|
* for size 4 is 4*CH_W pixels and so on. so we have everything we need to calculate how many pixels the Active VFO takes
|
||||||
|
* on the screen – we multiply the font size by the character height and add 16 pixels offset. The 16 was determined by
|
||||||
|
* experimentation for something that looked good. The code looks like this:
|
||||||
|
*
|
||||||
|
* #define DSP_VFO_ALT_X DSP_VFO_ACT_X
|
||||||
|
* #define DSP_VFO_ALT_Y DSP_VFO_ACT_Y + (DSP_VFO_ACT_SZ * CH_H) + 16
|
||||||
|
*
|
||||||
|
* Take a look the other sections of the display code and you will see similar references and calculations. The VFO A//B
|
||||||
|
* indicator and LSB/USB mode indicator, for example are similarly “pinned” to the Active VFO display, so if you move the
|
||||||
|
* Active VFO display to another screen location they will move also.
|
||||||
|
*
|
||||||
|
* In summary - each object is on the display is defined by a set of constants that indicate the X,Y coordinates
|
||||||
|
* of the object on the screen and various other attributes like text size and color. The basic user interface display
|
||||||
|
* object is a bounded/filed text box. You can control the text size and color, and the box fill color. With this basic
|
||||||
|
* set of features you can implement a wide variety of user interface elements. The S-meter, for example, is a row of
|
||||||
|
* filled boxes.
|
||||||
|
*
|
||||||
|
* HARDWARE NOTES
|
||||||
|
* My test sketch uses an Arduino Nano. The display is an HiLetgo 2.2 Inch ILI9341 SPI TFT LCD Display 240x320 ILI9341,
|
||||||
|
* but any ILI9341 display should work. There are many sources. Please note that the Arduino has 5V logic levels,
|
||||||
|
* but the display requires 3.3V - so you need some sort of level shifter. I used the"HiLetgo 10pcs 4 Channels IIC I2C
|
||||||
|
* Logic Level Converter Bi-Directional 3.3V-5V Shifter Module for Arduino" I used hardware SPI and the pinouts are
|
||||||
|
* standard as follows:
|
||||||
|
*
|
||||||
|
* Arduino
|
||||||
|
* Pin TFT Pin
|
||||||
|
* -----------|---------
|
||||||
|
* 8 | RST - any free Arduino Pin (not used in this sketch)
|
||||||
|
* 9 | DC - any free Arduino Pin
|
||||||
|
* 10 | CS - any free Arduino Pin
|
||||||
|
* 11 | MOSI - fixed
|
||||||
|
* 12 | MISO - fixed
|
||||||
|
* 13 | CLK - fixed
|
||||||
|
*
|
||||||
|
* That is all the wiring you need for the demonstration sketch.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* BUILDING THE DEMONSTRATION SKETCH
|
||||||
|
*
|
||||||
|
* Create a folder called SSB_TFT_Display_Demo
|
||||||
|
*
|
||||||
|
* Copy all three files to that folder
|
||||||
|
* SSB_TFT_Display_Demo.ino
|
||||||
|
* SSB_TFT_Display.h
|
||||||
|
* SSB_TFT_Display.cpp
|
||||||
|
*
|
||||||
|
* Use the Arduino IDE library manager to install teh following libraries
|
||||||
|
* Adafruit_GFX
|
||||||
|
* Adafruit_ILI9341
|
||||||
|
*
|
||||||
|
* Compile and upload the sketch
|
||||||
|
******************************************************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_ILI9341.h>
|
||||||
|
|
||||||
|
|
||||||
|
// For the Adafruit shield, these are the default.
|
||||||
|
#define TFT_DC 9
|
||||||
|
#define TFT_CS 10
|
||||||
|
#define TFT_MOSI 11
|
||||||
|
#define TFT_CLK 13
|
||||||
|
#define TFT_RST 8
|
||||||
|
#define TFT_MISO 12
|
||||||
|
|
||||||
|
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
|
||||||
|
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
|
||||||
|
|
||||||
|
#define CH_W 6 // default TFT Character width in pixels
|
||||||
|
#define CH_H 8 // default TFT Charachter height in pixels
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define DSP_BG_COLOR ILI9341_NAVY
|
||||||
|
|
||||||
|
// Banner - Version, Rig Name, Call sign
|
||||||
|
#define DSP_BANNER_X 10
|
||||||
|
#define DSP_BANNER_Y 220
|
||||||
|
#define DSP_BANNER_COLOR ILI9341_BLACK
|
||||||
|
#define DSP_BANNER_BK ILI9341_WHITE
|
||||||
|
#define DSP_BANNER_SZ 2
|
||||||
|
|
||||||
|
|
||||||
|
// Active VFO - top center of the screen
|
||||||
|
#define DSP_VFO_ACT_X 60
|
||||||
|
#define DSP_VFO_ACT_Y 30
|
||||||
|
#define DSP_VFO_ACT_COLOR ILI9341_GREEN
|
||||||
|
#define DSP_VFO_ACT_BK ILI9341_BLACK
|
||||||
|
#define DSP_VFO_ACT_SZ 3
|
||||||
|
|
||||||
|
// Alternate VFO - directly below the Active VFO
|
||||||
|
#define DSP_VFO_ALT_X DSP_VFO_ACT_X
|
||||||
|
#define DSP_VFO_ALT_Y DSP_VFO_ACT_Y + (DSP_VFO_ACT_SZ * CH_H) + 16
|
||||||
|
#define DSP_VFO_ALT_COLOR ILI9341_WHITE
|
||||||
|
#define DSP_VFO_ALT_BK DSP_VFO_ACT_BK
|
||||||
|
#define DSP_VFO_ALT_SZ 3
|
||||||
|
|
||||||
|
// VFP A/B indidcator - to the left of the Active VFO
|
||||||
|
#define DSP_VFO_AB_X DSP_VFO_ACT_X-40
|
||||||
|
#define DSP_VFO_AB_Y DSP_VFO_ACT_Y + 6
|
||||||
|
#define DSP_VFO_AB_COLOR DSP_VFO_ACT_COLOR
|
||||||
|
#define DSP_VFO_AB_BK DSP_VFO_ACT_BK
|
||||||
|
#define DSP_VFO_AB_SZ 2
|
||||||
|
|
||||||
|
// Tx/Rx Indicator - to the left of the Alternate VFO
|
||||||
|
#define DSP_RX_TX_X DSP_VFO_ACT_X-45
|
||||||
|
#define DSP_RX_TX_Y DSP_VFO_ALT_Y + 4
|
||||||
|
#define DSP_RX_COLOR DSP_VFO_ACT_COLOR
|
||||||
|
#define DSP_TX_COLOR ILI9341_RED
|
||||||
|
#define DSP_RX_TX_BK DSP_VFO_ACT_BK
|
||||||
|
#define DSP_RX_TX_SZ 2
|
||||||
|
|
||||||
|
// Mode (LSB/USB) inidcator - to the right of the Active VFO
|
||||||
|
#define DSP_MODE_X DSP_VFO_ACT_X+205
|
||||||
|
#define DSP_MODE_Y DSP_VFO_ACT_Y + 6
|
||||||
|
#define DSP_MODE_COLOR DSP_VFO_ACT_COLOR
|
||||||
|
#define DSP_MODE_BK DSP_VFO_ACT_BK
|
||||||
|
#define DSP_MODE_SZ 2
|
||||||
|
|
||||||
|
// Tuning Increment - to the right of teh Alternate VFO
|
||||||
|
#define DSP_INCR_X DSP_VFO_ACT_X+200
|
||||||
|
#define DSP_INCR_Y DSP_VFO_ALT_Y + 4
|
||||||
|
#define DSP_INCR_COLOR DSP_VFO_ACT_COLOR
|
||||||
|
#define DSP_INCR_BK DSP_VFO_ACT_BK
|
||||||
|
#define DSP_INCR_SZ 2
|
||||||
|
|
||||||
|
// S Meter - below the the Alt VFO (1/2 way down the screen)
|
||||||
|
#define DSP_S_METER_X 56
|
||||||
|
#define DSP_S_METER_Y 120
|
||||||
|
#define DSP_S_METER_SEGMENTS 12
|
||||||
|
#define DSP_S_METER_UNIT_SIZE 20
|
||||||
|
#define DSP_S_METER_LOW_COLOR ILI9341_DARKGREY //below S9
|
||||||
|
#define DSP_S_METER_HI_COLOR ILI9341_RED //above S9
|
||||||
|
|
||||||
|
// S Meter Label "S" to the left of the S Meter
|
||||||
|
#define DSP_S_LABEL_X DSP_S_METER_X - 18
|
||||||
|
#define DSP_S_LABEL_Y DSP_S_METER_Y + 4
|
||||||
|
#define DSP_S_LABEL_COLOR ILI9341_BLACK
|
||||||
|
#define DSP_S_LABEL_SZ 2
|
||||||
|
|
||||||
|
// S Meter Scale immediately belwo the S Meter
|
||||||
|
#define DSP_S_METER_SCALE_X DSP_S_METER_X + 5
|
||||||
|
#define DSP_S_METER_SCALE_Y DSP_S_METER_Y + DSP_S_METER_UNIT_SIZE + 5
|
||||||
|
#define DSP_S_METER_SCALE_COLOR DSP_S_LABEL_COLOR
|
||||||
|
#define DSP_S_METER_SCALE_SZ 2
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayPrintln(s)
|
||||||
|
// Prints one line of text at a time on the display - for debug messages
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifdef DEBUG
|
||||||
|
int ln=0;
|
||||||
|
void displayPrintln(String s ) {
|
||||||
|
if (ln==14) {
|
||||||
|
tft.fillScreen(ILI9341_BLACK);
|
||||||
|
tft.setCursor(0, 0);
|
||||||
|
ln=0;
|
||||||
|
}
|
||||||
|
tft.println(s);
|
||||||
|
ln++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayClearScreen()
|
||||||
|
// Fills the screen with selected bacground color
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayClearScreen() {
|
||||||
|
tft.fillScreen(DSP_BG_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayPrintat String
|
||||||
|
// Prints a string on the display
|
||||||
|
// x,y: upper left pixel coordinates
|
||||||
|
// fontsize: Arduino Font size (1-5)
|
||||||
|
// color: text color
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayPrintat(String s, int x, int y, int fontsize, int color) {
|
||||||
|
tft.setCursor(x,y);
|
||||||
|
tft.setTextSize(fontsize);
|
||||||
|
tft.setTextColor(color);
|
||||||
|
tft.print(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayPrintat Integer
|
||||||
|
// Prints a whole number on the display
|
||||||
|
// x,y: upper left pixel coordinates
|
||||||
|
// fontsize: Arduino Font size (1-5)
|
||||||
|
// color: text color
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayPrintat(int i, int x, int y, int fontsize, int color) {
|
||||||
|
tft.setCursor(x,y);
|
||||||
|
tft.setTextSize(fontsize);
|
||||||
|
tft.setTextColor(color);
|
||||||
|
tft.print(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayDrawBoundingBox
|
||||||
|
// Draw and fill a bounding box for text
|
||||||
|
// Draw a recrtanglie slightly larger than the text
|
||||||
|
// Fill it with the color - leave the border white
|
||||||
|
//
|
||||||
|
// len - length of the box in characxters
|
||||||
|
// x - x coord
|
||||||
|
// y - y coord
|
||||||
|
// fontsiez - font size - fontsize*CH_W is the width of a character, fontsize*CH_H is the height
|
||||||
|
// fillcolor - color
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayDrawBoundingBox(int len, int x, int y, int fontsize, int fillcolor) {
|
||||||
|
|
||||||
|
tft.drawRect(x-8, y-6, ((len*fontsize*CH_W))+16, ((fontsize*CH_H)+8), ILI9341_WHITE);
|
||||||
|
tft.fillRect(x-6, y-4, ((len*fontsize*CH_W))+12, ((fontsize*CH_H)+4), fillcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayDrawTextBox
|
||||||
|
// Display text in an outlined and filled box
|
||||||
|
//
|
||||||
|
// s: text to display
|
||||||
|
// x,y: upper left pixel coordinates
|
||||||
|
// fontsize: ARduino font size (1-5)
|
||||||
|
// fillcolor: color to fill the box with
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayDrawTextBox(String s, int x, int y, int fontsize, int color, int fillcolor) {
|
||||||
|
displayDrawBoundingBox(s.length(), x, y, fontsize, fillcolor); // draw the box
|
||||||
|
displayPrintat(s, x, y, fontsize, color); // display the text
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displaySMeter
|
||||||
|
// Display the S meter as a line of filled squares - up to the input level (1-12) S1-S0, +10dB, +20dB, +30dB
|
||||||
|
// S 1-9 are filled with the Low color S9+ is filled with the High color
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displaySMeter(byte level) {
|
||||||
|
int i;
|
||||||
|
int color;
|
||||||
|
|
||||||
|
displayPrintat(F("S"), DSP_S_LABEL_X, DSP_S_LABEL_Y, DSP_S_LABEL_SZ, DSP_S_LABEL_COLOR);
|
||||||
|
|
||||||
|
for (i=0; i<DSP_S_METER_SEGMENTS; i++) {
|
||||||
|
color = ILI9341_BLACK;
|
||||||
|
if (i<level) {
|
||||||
|
color = DSP_S_METER_LOW_COLOR;
|
||||||
|
if (i>8 && level>9) {
|
||||||
|
color = DSP_S_METER_HI_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tft.drawRect(DSP_S_METER_X+(i*DSP_S_METER_UNIT_SIZE), DSP_S_METER_Y, DSP_S_METER_UNIT_SIZE, DSP_S_METER_UNIT_SIZE, ILI9341_WHITE);
|
||||||
|
tft.fillRect(DSP_S_METER_X+(i*DSP_S_METER_UNIT_SIZE)+2, DSP_S_METER_Y+2, DSP_S_METER_UNIT_SIZE-4, DSP_S_METER_UNIT_SIZE-4, color);
|
||||||
|
|
||||||
|
// Print scale odd numbers up to 9
|
||||||
|
if ((i % 2) == 0 && i<9) {
|
||||||
|
displayPrintat(i+1, DSP_S_METER_SCALE_X+(i*DSP_S_METER_UNIT_SIZE), DSP_S_METER_SCALE_Y, DSP_S_METER_SCALE_SZ, DSP_S_METER_SCALE_COLOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayBanner
|
||||||
|
// Displays a banner acroos the bottom of the display
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayBanner(String s) {
|
||||||
|
displayDrawTextBox(s,DSP_BANNER_X, DSP_BANNER_Y, DSP_BANNER_SZ, DSP_BANNER_COLOR, DSP_BANNER_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayActVFO(uint32_t freq)
|
||||||
|
// displayAltVFO(uint32_t freq
|
||||||
|
// Formats and displays Active and Alternat VFO frequencies
|
||||||
|
// Legal values: Frequency in Hz
|
||||||
|
//
|
||||||
|
// To do - comibine into one function
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayActVFO(uint32_t freq) {
|
||||||
|
char f[11];
|
||||||
|
uint32_t mil, hund_thou, ten_thou, thou, hund, tens, ones;
|
||||||
|
|
||||||
|
// Format number as nn.nnn.nnn
|
||||||
|
mil = freq / 1000000;
|
||||||
|
hund_thou = (freq/100000)%10;
|
||||||
|
ten_thou = (freq/10000)%10;
|
||||||
|
thou = (freq/1000)%10;
|
||||||
|
hund = (freq/100)%10;
|
||||||
|
tens = (freq/10)%10;
|
||||||
|
ones = freq%10;
|
||||||
|
snprintf(f, sizeof(f),"%2ld.%ld%ld%ld.%ld%ld%ld",mil, hund_thou, ten_thou,thou, hund, tens, ones);
|
||||||
|
displayDrawTextBox(f,DSP_VFO_ACT_X, DSP_VFO_ACT_Y, DSP_VFO_ACT_SZ, DSP_VFO_ACT_COLOR, DSP_VFO_ACT_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayAltVFO(uint32_t freq) {
|
||||||
|
char f[11];
|
||||||
|
uint32_t mil, hund_thou, ten_thou, thou, hund, tens, ones;
|
||||||
|
|
||||||
|
// Format number as nn.nnn.nnn
|
||||||
|
mil = freq / 1000000;
|
||||||
|
hund_thou = (freq/100000)%10;
|
||||||
|
ten_thou = (freq/10000)%10;
|
||||||
|
thou = (freq/1000)%10;
|
||||||
|
hund = (freq/100)%10;
|
||||||
|
tens = (freq/10)%10;
|
||||||
|
ones = freq%10;
|
||||||
|
snprintf(f, sizeof(f),"%2ld.%ld%ld%ld.%ld%ld%ld",mil, hund_thou, ten_thou,thou, hund, tens, ones);
|
||||||
|
displayDrawTextBox(f,DSP_VFO_ALT_X, DSP_VFO_ALT_Y, DSP_VFO_ALT_SZ, DSP_VFO_ALT_COLOR, DSP_VFO_ALT_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayVFOAB(int vfo)
|
||||||
|
// Displays which VFO is currently active A or B
|
||||||
|
// Legal values are VFOA and VFOB
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayVFOAB(int vfo) {
|
||||||
|
String vfo_str;
|
||||||
|
if (vfo == VFOA) {
|
||||||
|
vfo_str = F("A");
|
||||||
|
} else {
|
||||||
|
vfo_str = F("B");
|
||||||
|
}
|
||||||
|
displayDrawTextBox(vfo_str,DSP_VFO_AB_X, DSP_VFO_AB_Y, DSP_VFO_AB_SZ, DSP_VFO_AB_COLOR, DSP_VFO_AB_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayTxRx(int tx_rx
|
||||||
|
// Displays whether the radio is currenlty transmitting or receiveing
|
||||||
|
// Legal values are TX and RX
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayTxRx(int tx_rx) {
|
||||||
|
String tx_rx_str;
|
||||||
|
int color;
|
||||||
|
if (tx_rx == RX) {
|
||||||
|
tx_rx_str = F("Rx");
|
||||||
|
color = DSP_RX_COLOR;
|
||||||
|
} else {
|
||||||
|
tx_rx_str = F("Tx");
|
||||||
|
color = DSP_TX_COLOR;
|
||||||
|
}
|
||||||
|
displayDrawTextBox(tx_rx_str,DSP_RX_TX_X, DSP_RX_TX_Y, DSP_RX_TX_SZ, color, DSP_RX_TX_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayMode(int mode)
|
||||||
|
// Displays whether the radio is LSB or USB
|
||||||
|
// Legal values are LSB and USB
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayMode(int mode) {
|
||||||
|
String mode_str;
|
||||||
|
if (mode == LSB) {
|
||||||
|
mode_str = F("LSB");
|
||||||
|
} else {
|
||||||
|
mode_str = F("USB");
|
||||||
|
}
|
||||||
|
displayDrawTextBox(mode_str,DSP_MODE_X, DSP_MODE_Y, DSP_MODE_SZ, DSP_MODE_COLOR, DSP_MODE_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displayIncr(uint32_t increment)
|
||||||
|
// Changes display of the tuning incrementindicator
|
||||||
|
// Legal values in Hz are 1, 10, 100, 1000, 10000, 100000, 1000000
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayIncr(uint32_t increment) {
|
||||||
|
String hertz;
|
||||||
|
switch (increment) {
|
||||||
|
case 1: hertz = F(" 1"); break;
|
||||||
|
case 10: hertz = F(" 10"); break;
|
||||||
|
case 100: hertz = F(" 100"); break;
|
||||||
|
case 1000: hertz = F(" 1K"); break;
|
||||||
|
case 10000: hertz = F(" 10K"); break;
|
||||||
|
case 100000: hertz= F("100K"); break;
|
||||||
|
case 1000000:hertz = F(" 1M");
|
||||||
|
}
|
||||||
|
displayDrawTextBox(hertz,DSP_INCR_X, DSP_INCR_Y, DSP_INCR_SZ, DSP_INCR_COLOR, DSP_INCR_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displaySplit(boolean split)
|
||||||
|
// Turns split mode indicator on/off
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displaySplit(boolean splt) {
|
||||||
|
if (split) {
|
||||||
|
// todo
|
||||||
|
} else {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displaySplit(boolean split)
|
||||||
|
// Turns split mode indicator on/off
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displayTune(boolean on_off) {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// displaySetup()
|
||||||
|
// Initialze and populate the display
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void displaySetup(String banner,
|
||||||
|
uint32_t vfoActfreq, uint32_t vfoAltfreq,
|
||||||
|
uint32_t activeVFO,
|
||||||
|
int tx_rx,
|
||||||
|
int sideband,
|
||||||
|
boolean split,
|
||||||
|
uint32_t increment,
|
||||||
|
byte s_meter) {
|
||||||
|
|
||||||
|
tft.begin(); // Initialize the TFT
|
||||||
|
tft.setRotation(1); // Set landscape orientation
|
||||||
|
displayClearScreen(); // Fill teh screen with the background color
|
||||||
|
|
||||||
|
//
|
||||||
|
// Display the intitial values
|
||||||
|
//
|
||||||
|
displayBanner(banner);
|
||||||
|
displayActVFO(vfoActfreq);
|
||||||
|
displayAltVFO(vfoAltfreq);
|
||||||
|
displayVFOAB(activeVFO);
|
||||||
|
displayTxRx(tx_rx);
|
||||||
|
displayMode(sideband);
|
||||||
|
displaySplit(split);
|
||||||
|
displayIncr(increment);
|
||||||
|
displaySMeter(s_meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,104 @@
|
||||||
|
#include "RadioControl.h"
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
#define EE_MAGIC_ADDR 0
|
||||||
|
#define EE_SETTINGS_ADDR 4
|
||||||
|
#define SETTINGS_TIMER 3000 // time to wait after a settings change before writing to EEPROM
|
||||||
|
|
||||||
|
const byte magic[4] = {'4', 'D', 'A', 'S'};
|
||||||
|
|
||||||
|
|
||||||
|
struct Settings_struct {
|
||||||
|
uint32_t vfoAfreq;
|
||||||
|
uint32_t vfoBfreq;
|
||||||
|
uint32_t increment;
|
||||||
|
byte active_vfo;
|
||||||
|
byte sideband;
|
||||||
|
// Band specific memory
|
||||||
|
uint32_t band20Freq;
|
||||||
|
uint32_t band40Freq;
|
||||||
|
byte band20Sideband;
|
||||||
|
byte band40Sideband;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Settings Change
|
||||||
|
unsigned long settings_time;
|
||||||
|
bool settings_changed;
|
||||||
|
|
||||||
|
|
||||||
|
void readSettings() {
|
||||||
|
Settings_struct settings;
|
||||||
|
EEPROM.get(EE_SETTINGS_ADDR, settings);
|
||||||
|
vfoAfreq = settings.vfoAfreq;
|
||||||
|
vfoBfreq = settings.vfoBfreq;
|
||||||
|
increment = settings.increment;
|
||||||
|
active_vfo = settings.active_vfo;
|
||||||
|
sideband = settings.sideband;
|
||||||
|
band20Freq = settings.band20Freq;
|
||||||
|
band40Freq = settings.band40Freq;
|
||||||
|
band20Sideband = settings.band20Sideband;
|
||||||
|
band40Sideband = settings.band40Sideband;
|
||||||
|
|
||||||
|
if (sideband == LSB) {
|
||||||
|
bfo = LSB_BFO;
|
||||||
|
} else {
|
||||||
|
bfo = USB_BFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeSettings() {
|
||||||
|
Settings_struct settings;
|
||||||
|
settings.vfoAfreq = vfoAfreq;
|
||||||
|
settings.vfoBfreq = vfoBfreq;
|
||||||
|
settings.increment = increment;
|
||||||
|
settings.active_vfo = active_vfo;
|
||||||
|
settings.sideband = sideband;
|
||||||
|
settings.band20Freq = band20Freq;
|
||||||
|
settings.band40Freq = band40Freq;
|
||||||
|
settings.band20Sideband = band20Sideband;
|
||||||
|
settings.band40Sideband = band40Sideband;
|
||||||
|
EEPROM.put(EE_SETTINGS_ADDR, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initSettings() {
|
||||||
|
EEPROM.put(EE_MAGIC_ADDR, magic);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupSettings() {
|
||||||
|
byte buff[4];
|
||||||
|
bool magic_ok = true;
|
||||||
|
|
||||||
|
EEPROM.get(EE_MAGIC_ADDR, buff);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (buff[i] != magic[i]) {
|
||||||
|
magic_ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic_ok) {
|
||||||
|
readSettings();
|
||||||
|
} else {
|
||||||
|
initSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_time = millis();
|
||||||
|
settings_changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckSettings() {
|
||||||
|
|
||||||
|
if (settings_changed && (millis() - SETTINGS_TIMER) > settings_time) {
|
||||||
|
writeSettings();
|
||||||
|
settings_time = millis();
|
||||||
|
settings_changed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSettingsTimer() {
|
||||||
|
settings_time = millis();
|
||||||
|
settings_changed = true;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef Settings_h
|
||||||
|
#define Settings_h
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
|
||||||
|
extern void setupSettings();
|
||||||
|
extern void initSettings();
|
||||||
|
extern void readSettings();
|
||||||
|
extern void writeSettings();
|
||||||
|
extern void startSettingsTimer();
|
||||||
|
extern void CheckSettings();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// S Meter
|
||||||
|
//
|
||||||
|
#include "RadioControl.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#define S_DEBUG
|
||||||
|
|
||||||
|
const int slevels[MAXSLEVELS] = {25, 100, 200, 375, 500, 575, 650, 775, 900, 950, 975, 1000};
|
||||||
|
|
||||||
|
//byte smeter = 0;
|
||||||
|
unsigned long smeter_raw = 0;
|
||||||
|
unsigned long smeter_sample_count = 0;
|
||||||
|
unsigned long smeter_time = 0;
|
||||||
|
|
||||||
|
//*****************************************
|
||||||
|
void CheckSmeter() {
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int smeter_avg;
|
||||||
|
|
||||||
|
if (TxRxState == TX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
smeter_raw = smeter_raw + analogRead(SMETER_PIN);
|
||||||
|
smeter_sample_count++;
|
||||||
|
|
||||||
|
|
||||||
|
if ((millis() - SMETER_SAMPLE_TIMER) > smeter_time) {
|
||||||
|
/*
|
||||||
|
lcd.setCursor(12, 1);
|
||||||
|
lcd.print(" ");
|
||||||
|
lcd.setCursor(12, 1);
|
||||||
|
lcd.print(smeter_raw);
|
||||||
|
*/
|
||||||
|
|
||||||
|
smeter_avg = int(smeter_raw / smeter_sample_count);
|
||||||
|
|
||||||
|
#ifdef S_DEBUG
|
||||||
|
String msg = F("S raw:");
|
||||||
|
msg+=smeter_avg;
|
||||||
|
displayBanner(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
smeter = 0;
|
||||||
|
for (i = 0; i < MAXSLEVELS; i++) {
|
||||||
|
if (smeter_avg >= slevels[i]) {
|
||||||
|
smeter = byte(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
displaySMeter(smeter);
|
||||||
|
smeter_raw = 0;
|
||||||
|
smeter_sample_count = 0;
|
||||||
|
smeter_time = millis();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef Smeter_h
|
||||||
|
#define Smeter_h
|
||||||
|
/*
|
||||||
|
* SMeter
|
||||||
|
* Constants defining and controlling Smeter collection and display
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define SMETER_SAMPLE_TIMER 250 // Sample time
|
||||||
|
#define MAXSLEVELS 12 // S Levels 1-0, S9+10m S9+20, S9+30
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* Utility Functions
|
||||||
|
*
|
||||||
|
* Delay(interval) - non-blocking delahy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Utility.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "Delay" is a non-blocking delay function. The standard Arduino "delay" function
|
||||||
|
* turns off interrupts, which prevents the Serial I/O functions from working. That
|
||||||
|
* sometimes causes characters to be missed and junk commands to show up.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Delay ( unsigned long interval )
|
||||||
|
{
|
||||||
|
unsigned long currentTime = 0UL;
|
||||||
|
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
|
||||||
|
while ( startTime + interval > currentTime )
|
||||||
|
currentTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ToggleLED - toggles built-in LED On/Off - useful for debug
|
||||||
|
*/
|
||||||
|
byte LEDState=LOW;
|
||||||
|
void ToggleLED(){
|
||||||
|
if (LEDState == LOW){
|
||||||
|
LEDState = HIGH;
|
||||||
|
} else {
|
||||||
|
LEDState = LOW;
|
||||||
|
}
|
||||||
|
digitalWrite(LED_BUILTIN, LEDState);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef UTILITY_h
|
||||||
|
#define UTILITY_h
|
||||||
|
/**************************************************************
|
||||||
|
Uttility functions - general purpose
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
|
//=============== Function Prototypes ============================================
|
||||||
|
extern void Delay (unsigned long interval); // Non-blocking delay
|
||||||
|
extern void ToggleLED(); // Toggle built-in LED
|
||||||
|
#endif
|
|
@ -0,0 +1,770 @@
|
||||||
|
/*
|
||||||
|
* si5351.cpp - Si5351 library for Arduino
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Jason Milldrum <milldrum@gmail.com>
|
||||||
|
*
|
||||||
|
* Some tuning algorithms derived from clk-si5351.c in the Linux kernel.
|
||||||
|
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||||
|
* Rabeeh Khoury <rabeeh@solid-run.com>
|
||||||
|
*
|
||||||
|
* rational_best_approximation() derived from lib/rational.c in
|
||||||
|
* the Linux kernel.
|
||||||
|
* Copyright (C) 2009 emlix GmbH, Oskar Schirmer <oskar@scara.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/eeprom.h>
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Wire.h"
|
||||||
|
#include "si5351.h"
|
||||||
|
|
||||||
|
uint32_t EEMEM ee_ref_correction = 0;
|
||||||
|
|
||||||
|
/********************/
|
||||||
|
/* Public functions */
|
||||||
|
/********************/
|
||||||
|
|
||||||
|
Si5351::Si5351(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init(uint8_t xtal_load_c)
|
||||||
|
*
|
||||||
|
* Setup communications to the Si5351 and set the crystal
|
||||||
|
* load capacitance.
|
||||||
|
*
|
||||||
|
* xtal_load_c - Crystal load capacitance. Use the SI5351_CRYSTAL_LOAD_*PF
|
||||||
|
* defines in the header file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Si5351::init(uint8_t xtal_load_c)
|
||||||
|
{
|
||||||
|
// Start I2C comms
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
// Set crystal load capacitance
|
||||||
|
si5351_write(SI5351_CRYSTAL_LOAD, xtal_load_c);
|
||||||
|
|
||||||
|
// Get the correction factor from EEPROM
|
||||||
|
ref_correction = eeprom_read_dword(&ee_ref_correction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_freq(uint32_t freq, uint32_t pll_freq, enum si5351_clock output)
|
||||||
|
*
|
||||||
|
* Sets the clock frequency of the specified CLK output
|
||||||
|
*
|
||||||
|
* freq - Output frequency in Hz
|
||||||
|
* pll_freq - Frequency of the PLL driving the Multisynth
|
||||||
|
* Use a 0 to have the function choose a PLL frequency
|
||||||
|
* clk - Clock output
|
||||||
|
* (use the si5351_clock enum)
|
||||||
|
*/
|
||||||
|
void Si5351::set_freq(uint32_t freq, uint32_t pll_freq, enum si5351_clock clk)
|
||||||
|
{
|
||||||
|
struct Si5351RegSet ms_reg, pll_reg;
|
||||||
|
enum si5351_pll target_pll;
|
||||||
|
uint8_t set_pll = 0;
|
||||||
|
|
||||||
|
/* Calculate the synth parameters */
|
||||||
|
/* If pll_freq is 0, let the algorithm pick a PLL frequency */
|
||||||
|
if(pll_freq == 0)
|
||||||
|
{
|
||||||
|
pll_freq = multisynth_calc(freq, &ms_reg);
|
||||||
|
set_pll = 1;
|
||||||
|
}
|
||||||
|
/* TODO: bounds checking */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
multisynth_recalc(freq, pll_freq, &ms_reg);
|
||||||
|
set_pll = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine which PLL to use */
|
||||||
|
/* CLK0 gets PLLA, CLK1 gets PLLB */
|
||||||
|
/* CLK2 gets PLLB if necessary */
|
||||||
|
/* Only good for Si5351A3 variant at the moment */
|
||||||
|
if(clk == SI5351_CLK0)
|
||||||
|
{
|
||||||
|
target_pll = SI5351_PLLA;
|
||||||
|
si5351_set_ms_source(SI5351_CLK0, SI5351_PLLA);
|
||||||
|
plla_freq = pll_freq;
|
||||||
|
}
|
||||||
|
else if(clk == SI5351_CLK1)
|
||||||
|
{
|
||||||
|
target_pll = SI5351_PLLB;
|
||||||
|
si5351_set_ms_source(SI5351_CLK1, SI5351_PLLB);
|
||||||
|
pllb_freq = pll_freq;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* need to account for CLK2 set before CLK1 */
|
||||||
|
if(pllb_freq == 0)
|
||||||
|
{
|
||||||
|
target_pll = SI5351_PLLB;
|
||||||
|
si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
|
||||||
|
pllb_freq = pll_freq;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target_pll = SI5351_PLLB;
|
||||||
|
si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
|
||||||
|
pll_freq = pllb_freq;
|
||||||
|
multisynth_recalc(freq, pll_freq, &ms_reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pll_calc(pll_freq, &pll_reg, ref_correction);
|
||||||
|
|
||||||
|
/* Derive the register values to write */
|
||||||
|
|
||||||
|
/* Prepare an array for parameters to be written to */
|
||||||
|
uint8_t *params = new uint8_t[20];
|
||||||
|
uint8_t i = 0;
|
||||||
|
uint8_t temp;
|
||||||
|
|
||||||
|
/* PLL parameters first */
|
||||||
|
|
||||||
|
if(set_pll == 1)
|
||||||
|
{
|
||||||
|
/* Registers 26-27 */
|
||||||
|
temp = ((pll_reg.p3 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p3 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 28 */
|
||||||
|
temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 29-30 */
|
||||||
|
temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p1 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 31 */
|
||||||
|
temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0);
|
||||||
|
temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 32-33 */
|
||||||
|
temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p2 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Write the parameters */
|
||||||
|
if(target_pll == SI5351_PLLA)
|
||||||
|
{
|
||||||
|
si5351_write_bulk(SI5351_PLLA_PARAMETERS, i + 1, params);
|
||||||
|
}
|
||||||
|
else if(target_pll == SI5351_PLLB)
|
||||||
|
{
|
||||||
|
si5351_write_bulk(SI5351_PLLB_PARAMETERS, i + 1, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete params;
|
||||||
|
|
||||||
|
/* Now the multisynth parameters */
|
||||||
|
params = new uint8_t[20];
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
/* Registers 42-43 */
|
||||||
|
temp = (uint8_t)((ms_reg.p3 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(ms_reg.p3 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 44 */
|
||||||
|
/* TODO: add code for output divider */
|
||||||
|
temp = (uint8_t)((ms_reg.p1 >> 16) & 0x03);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 45-46 */
|
||||||
|
temp = (uint8_t)((ms_reg.p1 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(ms_reg.p1 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 47 */
|
||||||
|
temp = (uint8_t)((ms_reg.p3 >> 12) & 0xF0);
|
||||||
|
temp += (uint8_t)((ms_reg.p2 >> 16) & 0x0F);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 48-49 */
|
||||||
|
temp = (uint8_t)((ms_reg.p2 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(ms_reg.p2 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Write the parameters */
|
||||||
|
switch(clk)
|
||||||
|
{
|
||||||
|
case SI5351_CLK0:
|
||||||
|
si5351_write_bulk(SI5351_CLK0_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK1:
|
||||||
|
si5351_write_bulk(SI5351_CLK1_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK2:
|
||||||
|
si5351_write_bulk(SI5351_CLK2_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK3:
|
||||||
|
si5351_write_bulk(SI5351_CLK3_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK4:
|
||||||
|
si5351_write_bulk(SI5351_CLK4_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK5:
|
||||||
|
si5351_write_bulk(SI5351_CLK5_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK6:
|
||||||
|
si5351_write_bulk(SI5351_CLK6_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
case SI5351_CLK7:
|
||||||
|
si5351_write_bulk(SI5351_CLK7_PARAMETERS, i + 1, params);
|
||||||
|
si5351_set_ms_source(clk, target_pll);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_pll(uint32_t pll_freq, enum si5351_pll target_pll)
|
||||||
|
*
|
||||||
|
* Set the specified PLL to a specific oscillation frequency
|
||||||
|
*
|
||||||
|
* pll_freq - Desired PLL frequency
|
||||||
|
* target_pll - Which PLL to set
|
||||||
|
* (use the si5351_pll enum)
|
||||||
|
*/
|
||||||
|
void Si5351::set_pll(uint32_t pll_freq, enum si5351_pll target_pll)
|
||||||
|
{
|
||||||
|
struct Si5351RegSet pll_reg;
|
||||||
|
|
||||||
|
pll_calc(pll_freq, &pll_reg, ref_correction);
|
||||||
|
|
||||||
|
/* Derive the register values to write */
|
||||||
|
|
||||||
|
/* Prepare an array for parameters to be written to */
|
||||||
|
uint8_t *params = new uint8_t[20];
|
||||||
|
;
|
||||||
|
uint8_t i = 0;
|
||||||
|
uint8_t temp;
|
||||||
|
|
||||||
|
/* Registers 26-27 */
|
||||||
|
temp = ((pll_reg.p3 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p3 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 28 */
|
||||||
|
temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 29-30 */
|
||||||
|
temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p1 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Register 31 */
|
||||||
|
temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0);
|
||||||
|
temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Registers 32-33 */
|
||||||
|
temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
temp = (uint8_t)(pll_reg.p2 & 0xFF);
|
||||||
|
params[i++] = temp;
|
||||||
|
|
||||||
|
/* Write the parameters */
|
||||||
|
if(target_pll == SI5351_PLLA)
|
||||||
|
{
|
||||||
|
si5351_write_bulk(SI5351_PLLA_PARAMETERS, i + 1, params);
|
||||||
|
}
|
||||||
|
else if(target_pll == SI5351_PLLB)
|
||||||
|
{
|
||||||
|
si5351_write_bulk(SI5351_PLLB_PARAMETERS, i + 1, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* clock_enable(enum si5351_clock clk, uint8_t enable)
|
||||||
|
*
|
||||||
|
* Enable or disable a chosen clock
|
||||||
|
* clk - Clock output
|
||||||
|
* (use the si5351_clock enum)
|
||||||
|
* enable - Set to 1 to enable, 0 to disable
|
||||||
|
*/
|
||||||
|
void Si5351::clock_enable(enum si5351_clock clk, uint8_t enable)
|
||||||
|
{
|
||||||
|
uint8_t reg_val;
|
||||||
|
|
||||||
|
reg_val = si5351_read(SI5351_OUTPUT_ENABLE_CTRL);
|
||||||
|
|
||||||
|
if(enable == 1)
|
||||||
|
{
|
||||||
|
reg_val &= ~(1<<(uint8_t)clk);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reg_val |= (1<<(uint8_t)clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
si5351_write(SI5351_OUTPUT_ENABLE_CTRL, reg_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drive_strength(enum si5351_clock clk, enum si5351_drive drive)
|
||||||
|
*
|
||||||
|
* Sets the drive strength of the specified clock output
|
||||||
|
*
|
||||||
|
* clk - Clock output
|
||||||
|
* (use the si5351_clock enum)
|
||||||
|
* drive - Desired drive level
|
||||||
|
* (use the si5351_drive enum)
|
||||||
|
*/
|
||||||
|
void Si5351::drive_strength(enum si5351_clock clk, enum si5351_drive drive)
|
||||||
|
{
|
||||||
|
uint8_t reg_val;
|
||||||
|
const uint8_t mask = 0x03;
|
||||||
|
|
||||||
|
reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
|
||||||
|
|
||||||
|
switch(drive)
|
||||||
|
{
|
||||||
|
case SI5351_DRIVE_2MA:
|
||||||
|
reg_val &= ~(mask);
|
||||||
|
reg_val |= 0x00;
|
||||||
|
break;
|
||||||
|
case SI5351_DRIVE_4MA:
|
||||||
|
reg_val &= ~(mask);
|
||||||
|
reg_val |= 0x01;
|
||||||
|
break;
|
||||||
|
case SI5351_DRIVE_6MA:
|
||||||
|
reg_val &= ~(mask);
|
||||||
|
reg_val |= 0x02;
|
||||||
|
break;
|
||||||
|
case SI5351_DRIVE_8MA:
|
||||||
|
reg_val &= ~(mask);
|
||||||
|
reg_val |= 0x03;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* update_status(void)
|
||||||
|
*
|
||||||
|
* Call this to update the status structs, then access them
|
||||||
|
* via the dev_status and dev_int_status global variables.
|
||||||
|
*
|
||||||
|
* See the header file for the struct definitions. These
|
||||||
|
* correspond to the flag names for registers 0 and 1 in
|
||||||
|
* the Si5351 datasheet.
|
||||||
|
*/
|
||||||
|
void Si5351::update_status(void)
|
||||||
|
{
|
||||||
|
si5351_update_sys_status(&dev_status);
|
||||||
|
si5351_update_int_status(&dev_int_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_correction(int32_t corr)
|
||||||
|
*
|
||||||
|
* Use this to set the oscillator correction factor to
|
||||||
|
* EEPROM. This value is a signed 32-bit integer of the
|
||||||
|
* parts-per-10 million value that the actual oscillation
|
||||||
|
* frequency deviates from the specified frequency.
|
||||||
|
*
|
||||||
|
* The frequency calibration is done as a one-time procedure.
|
||||||
|
* Any desired test frequency within the normal range of the
|
||||||
|
* Si5351 should be set, then the actual output frequency
|
||||||
|
* should be measured as accurately as possible. The
|
||||||
|
* difference between the measured and specified frequencies
|
||||||
|
* should be calculated in Hertz, then multiplied by 10 in
|
||||||
|
* order to get the parts-per-10 million value.
|
||||||
|
*
|
||||||
|
* Since the Si5351 itself has an intrinsic 0 PPM error, this
|
||||||
|
* correction factor is good across the entire tuning range of
|
||||||
|
* the Si5351. Once this calibration is done accurately, it
|
||||||
|
* should not have to be done again for the same Si5351 and
|
||||||
|
* crystal. The library will read the correction factor from
|
||||||
|
* EEPROM during initialization for use by the tuning
|
||||||
|
* algorithms.
|
||||||
|
*/
|
||||||
|
void Si5351::set_correction(int32_t corr)
|
||||||
|
{
|
||||||
|
eeprom_write_dword(&ee_ref_correction, corr);
|
||||||
|
ref_correction = corr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_correction(void)
|
||||||
|
*
|
||||||
|
* Returns the oscillator correction factor stored
|
||||||
|
* in EEPROM.
|
||||||
|
*/
|
||||||
|
int32_t Si5351::get_correction(void)
|
||||||
|
{
|
||||||
|
return eeprom_read_dword(&ee_ref_correction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t Si5351::si5351_write_bulk(uint8_t addr, uint8_t bytes, uint8_t *data)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
|
||||||
|
Wire.write(addr);
|
||||||
|
for(int i = 0; i < bytes; i++)
|
||||||
|
{
|
||||||
|
Wire.write(data[i]);
|
||||||
|
}
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Si5351::si5351_write(uint8_t addr, uint8_t data)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
|
||||||
|
Wire.write(addr);
|
||||||
|
Wire.write(data);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Si5351::si5351_read(uint8_t addr)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(SI5351_BUS_BASE_ADDR);
|
||||||
|
Wire.write(addr);
|
||||||
|
Wire.endTransmission();
|
||||||
|
|
||||||
|
Wire.requestFrom(SI5351_BUS_BASE_ADDR, 1);
|
||||||
|
|
||||||
|
return Wire.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/* Private functions */
|
||||||
|
/*********************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate best rational approximation for a given fraction
|
||||||
|
* taking into account restricted register size, e.g. to find
|
||||||
|
* appropriate values for a pll with 5 bit denominator and
|
||||||
|
* 8 bit numerator register fields, trying to set up with a
|
||||||
|
* frequency ratio of 3.1415, one would say:
|
||||||
|
*
|
||||||
|
* rational_best_approximation(31415, 10000,
|
||||||
|
* (1 << 8) - 1, (1 << 5) - 1, &n, &d);
|
||||||
|
*
|
||||||
|
* you may look at given_numerator as a fixed point number,
|
||||||
|
* with the fractional part size described in given_denominator.
|
||||||
|
*
|
||||||
|
* for theoretical background, see:
|
||||||
|
* http://en.wikipedia.org/wiki/Continued_fraction
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Si5351::rational_best_approximation(
|
||||||
|
unsigned long given_numerator, unsigned long given_denominator,
|
||||||
|
unsigned long max_numerator, unsigned long max_denominator,
|
||||||
|
unsigned long *best_numerator, unsigned long *best_denominator)
|
||||||
|
{
|
||||||
|
unsigned long n, d, n0, d0, n1, d1;
|
||||||
|
n = given_numerator;
|
||||||
|
d = given_denominator;
|
||||||
|
n0 = d1 = 0;
|
||||||
|
n1 = d0 = 1;
|
||||||
|
for (;;) {
|
||||||
|
unsigned long t, a;
|
||||||
|
if ((n1 > max_numerator) || (d1 > max_denominator)) {
|
||||||
|
n1 = n0;
|
||||||
|
d1 = d0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (d == 0)
|
||||||
|
break;
|
||||||
|
t = d;
|
||||||
|
a = n / d;
|
||||||
|
d = n % d;
|
||||||
|
n = t;
|
||||||
|
t = n0 + a * n1;
|
||||||
|
n0 = n1;
|
||||||
|
n1 = t;
|
||||||
|
t = d0 + a * d1;
|
||||||
|
d0 = d1;
|
||||||
|
d1 = t;
|
||||||
|
}
|
||||||
|
*best_numerator = n1;
|
||||||
|
*best_denominator = d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Si5351::pll_calc(uint32_t freq, struct Si5351RegSet *reg, int32_t correction)
|
||||||
|
{
|
||||||
|
uint32_t ref_freq = SI5351_XTAL_FREQ;
|
||||||
|
uint32_t rfrac, denom, a, b, c, p1, p2, p3;
|
||||||
|
uint64_t lltmp;
|
||||||
|
|
||||||
|
/* Factor calibration value into nominal crystal frequency */
|
||||||
|
/* Measured in parts-per-ten million */
|
||||||
|
ref_freq += (uint32_t)((double)(correction / 10000000.0) * (double)ref_freq);
|
||||||
|
|
||||||
|
/* PLL bounds checking */
|
||||||
|
if (freq < SI5351_PLL_VCO_MIN)
|
||||||
|
freq = SI5351_PLL_VCO_MIN;
|
||||||
|
if (freq > SI5351_PLL_VCO_MAX)
|
||||||
|
freq = SI5351_PLL_VCO_MAX;
|
||||||
|
|
||||||
|
/* Determine integer part of feedback equation */
|
||||||
|
a = freq / ref_freq;
|
||||||
|
|
||||||
|
if (a < SI5351_PLL_A_MIN)
|
||||||
|
freq = ref_freq * SI5351_PLL_A_MIN;
|
||||||
|
if (a > SI5351_PLL_A_MAX)
|
||||||
|
freq = ref_freq * SI5351_PLL_A_MAX;
|
||||||
|
|
||||||
|
/* find best approximation for b/c = fVCO mod fIN */
|
||||||
|
denom = 1000L * 1000L;
|
||||||
|
lltmp = freq % ref_freq;
|
||||||
|
lltmp *= denom;
|
||||||
|
do_div(lltmp, ref_freq);
|
||||||
|
rfrac = (uint32_t)lltmp;
|
||||||
|
|
||||||
|
b = 0;
|
||||||
|
c = 1;
|
||||||
|
if (rfrac)
|
||||||
|
rational_best_approximation(rfrac, denom,
|
||||||
|
SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
|
||||||
|
|
||||||
|
/* calculate parameters */
|
||||||
|
p3 = c;
|
||||||
|
p2 = (128 * b) % c;
|
||||||
|
p1 = 128 * a;
|
||||||
|
p1 += (128 * b / c);
|
||||||
|
p1 -= 512;
|
||||||
|
|
||||||
|
/* recalculate rate by fIN * (a + b/c) */
|
||||||
|
lltmp = ref_freq;
|
||||||
|
lltmp *= b;
|
||||||
|
do_div(lltmp, c);
|
||||||
|
|
||||||
|
freq = (uint32_t)lltmp;
|
||||||
|
freq += ref_freq * a;
|
||||||
|
|
||||||
|
reg->p1 = p1;
|
||||||
|
reg->p2 = p2;
|
||||||
|
reg->p3 = p3;
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Si5351::multisynth_calc(uint32_t freq, struct Si5351RegSet *reg)
|
||||||
|
{
|
||||||
|
uint32_t pll_freq;
|
||||||
|
uint64_t lltmp;
|
||||||
|
uint32_t a, b, c, p1, p2, p3;
|
||||||
|
uint8_t divby4;
|
||||||
|
|
||||||
|
/* Multisynth bounds checking */
|
||||||
|
if (freq > SI5351_MULTISYNTH_MAX_FREQ)
|
||||||
|
freq = SI5351_MULTISYNTH_MAX_FREQ;
|
||||||
|
if (freq < SI5351_MULTISYNTH_MIN_FREQ)
|
||||||
|
freq = SI5351_MULTISYNTH_MIN_FREQ;
|
||||||
|
|
||||||
|
divby4 = 0;
|
||||||
|
if (freq > SI5351_MULTISYNTH_DIVBY4_FREQ)
|
||||||
|
divby4 = 1;
|
||||||
|
|
||||||
|
/* Find largest integer divider for max */
|
||||||
|
/* VCO frequency and given target frequency */
|
||||||
|
if (divby4 == 0)
|
||||||
|
{
|
||||||
|
lltmp = SI5351_PLL_VCO_MAX;
|
||||||
|
do_div(lltmp, freq);
|
||||||
|
a = (uint32_t)lltmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
a = 4;
|
||||||
|
|
||||||
|
b = 0;
|
||||||
|
c = 1;
|
||||||
|
pll_freq = a * freq;
|
||||||
|
|
||||||
|
/* Recalculate output frequency by fOUT = fIN / (a + b/c) */
|
||||||
|
lltmp = pll_freq;
|
||||||
|
lltmp *= c;
|
||||||
|
do_div(lltmp, a * c + b);
|
||||||
|
freq = (unsigned long)lltmp;
|
||||||
|
|
||||||
|
/* Calculate parameters */
|
||||||
|
if (divby4)
|
||||||
|
{
|
||||||
|
p3 = 1;
|
||||||
|
p2 = 0;
|
||||||
|
p1 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p3 = c;
|
||||||
|
p2 = (128 * b) % c;
|
||||||
|
p1 = 128 * a;
|
||||||
|
p1 += (128 * b / c);
|
||||||
|
p1 -= 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg->p1 = p1;
|
||||||
|
reg->p2 = p2;
|
||||||
|
reg->p3 = p3;
|
||||||
|
|
||||||
|
return pll_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Si5351::multisynth_recalc(uint32_t freq, uint32_t pll_freq, struct Si5351RegSet *reg)
|
||||||
|
{
|
||||||
|
uint64_t lltmp;
|
||||||
|
uint32_t rfrac, denom, a, b, c, p1, p2, p3;
|
||||||
|
uint8_t divby4;
|
||||||
|
|
||||||
|
/* Multisynth bounds checking */
|
||||||
|
if (freq > SI5351_MULTISYNTH_MAX_FREQ)
|
||||||
|
freq = SI5351_MULTISYNTH_MAX_FREQ;
|
||||||
|
if (freq < SI5351_MULTISYNTH_MIN_FREQ)
|
||||||
|
freq = SI5351_MULTISYNTH_MIN_FREQ;
|
||||||
|
|
||||||
|
divby4 = 0;
|
||||||
|
if (freq > SI5351_MULTISYNTH_DIVBY4_FREQ)
|
||||||
|
divby4 = 1;
|
||||||
|
|
||||||
|
/* Determine integer part of feedback equation */
|
||||||
|
a = pll_freq / freq;
|
||||||
|
|
||||||
|
/* TODO: not sure this is correct */
|
||||||
|
if (a < SI5351_MULTISYNTH_A_MIN)
|
||||||
|
freq = pll_freq / SI5351_MULTISYNTH_A_MIN;
|
||||||
|
if (a > SI5351_MULTISYNTH_A_MAX)
|
||||||
|
freq = pll_freq / SI5351_MULTISYNTH_A_MAX;
|
||||||
|
|
||||||
|
/* find best approximation for b/c */
|
||||||
|
denom = 1000L * 1000L;
|
||||||
|
lltmp = pll_freq % freq;
|
||||||
|
lltmp *= denom;
|
||||||
|
do_div(lltmp, freq);
|
||||||
|
rfrac = (uint32_t)lltmp;
|
||||||
|
|
||||||
|
b = 0;
|
||||||
|
c = 1;
|
||||||
|
if (rfrac)
|
||||||
|
rational_best_approximation(rfrac, denom,
|
||||||
|
SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX, &b, &c);
|
||||||
|
|
||||||
|
/* Recalculate output frequency by fOUT = fIN / (a + b/c) */
|
||||||
|
lltmp = pll_freq;
|
||||||
|
lltmp *= c;
|
||||||
|
do_div(lltmp, a * c + b);
|
||||||
|
freq = (unsigned long)lltmp;
|
||||||
|
|
||||||
|
/* Calculate parameters */
|
||||||
|
if (divby4)
|
||||||
|
{
|
||||||
|
p3 = 1;
|
||||||
|
p2 = 0;
|
||||||
|
p1 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p3 = c;
|
||||||
|
p2 = (128 * b) % c;
|
||||||
|
p1 = 128 * a;
|
||||||
|
p1 += (128 * b / c);
|
||||||
|
p1 -= 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg->p1 = p1;
|
||||||
|
reg->p2 = p2;
|
||||||
|
reg->p3 = p3;
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Si5351::si5351_update_sys_status(struct Si5351Status *status)
|
||||||
|
{
|
||||||
|
uint8_t reg_val = 0;
|
||||||
|
|
||||||
|
reg_val = si5351_read(SI5351_DEVICE_STATUS);
|
||||||
|
|
||||||
|
/* Parse the register */
|
||||||
|
status->SYS_INIT = (reg_val >> 7) & 0x01;
|
||||||
|
status->LOL_B = (reg_val >> 6) & 0x01;
|
||||||
|
status->LOL_A = (reg_val >> 5) & 0x01;
|
||||||
|
status->LOS = (reg_val >> 4) & 0x01;
|
||||||
|
status->REVID = reg_val & 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Si5351::si5351_update_int_status(struct Si5351IntStatus *int_status)
|
||||||
|
{
|
||||||
|
uint8_t reg_val = 0;
|
||||||
|
|
||||||
|
reg_val = si5351_read(SI5351_DEVICE_STATUS);
|
||||||
|
|
||||||
|
/* Parse the register */
|
||||||
|
int_status->SYS_INIT_STKY = (reg_val >> 7) & 0x01;
|
||||||
|
int_status->LOL_B_STKY = (reg_val >> 6) & 0x01;
|
||||||
|
int_status->LOL_A_STKY = (reg_val >> 5) & 0x01;
|
||||||
|
int_status->LOS_STKY = (reg_val >> 4) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Si5351::si5351_set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
|
||||||
|
{
|
||||||
|
uint8_t reg_val = 0x0c;
|
||||||
|
uint8_t reg_val2;
|
||||||
|
|
||||||
|
reg_val2 = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
|
||||||
|
|
||||||
|
if(pll == SI5351_PLLA)
|
||||||
|
{
|
||||||
|
reg_val &= ~(SI5351_CLK_PLL_SELECT);
|
||||||
|
}
|
||||||
|
else if(pll == SI5351_PLLB)
|
||||||
|
{
|
||||||
|
reg_val |= SI5351_CLK_PLL_SELECT;
|
||||||
|
}
|
||||||
|
si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
/*
|
||||||
|
* si5351.h - Si5351 library for Arduino
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Jason Milldrum <milldrum@gmail.com>
|
||||||
|
*
|
||||||
|
* Many defines derived from clk-si5351.h in the Linux kernel.
|
||||||
|
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||||
|
* Rabeeh Khoury <rabeeh@solid-run.com>
|
||||||
|
*
|
||||||
|
* do_div() macro derived from /include/asm-generic/div64.h in
|
||||||
|
* the Linux kernel.
|
||||||
|
* Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SI5351_H_
|
||||||
|
#define SI5351_H_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Wire.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/eeprom.h>
|
||||||
|
|
||||||
|
/* Define definitions */
|
||||||
|
|
||||||
|
#define SI5351_BUS_BASE_ADDR 0x60
|
||||||
|
#define SI5351_XTAL_FREQ 25000000
|
||||||
|
#define SI5351_PLL_FIXED 900000000
|
||||||
|
|
||||||
|
#define SI5351_PLL_VCO_MIN 600000000
|
||||||
|
#define SI5351_PLL_VCO_MAX 900000000
|
||||||
|
#define SI5351_MULTISYNTH_MIN_FREQ 1000000
|
||||||
|
#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000
|
||||||
|
#define SI5351_MULTISYNTH_MAX_FREQ 160000000
|
||||||
|
#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ
|
||||||
|
#define SI5351_CLKOUT_MIN_FREQ 8000
|
||||||
|
#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ
|
||||||
|
#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ
|
||||||
|
|
||||||
|
#define SI5351_PLL_A_MIN 15
|
||||||
|
#define SI5351_PLL_A_MAX 90
|
||||||
|
#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1)
|
||||||
|
#define SI5351_PLL_C_MAX 1048575
|
||||||
|
#define SI5351_MULTISYNTH_A_MIN 6
|
||||||
|
#define SI5351_MULTISYNTH_A_MAX 1800
|
||||||
|
#define SI5351_MULTISYNTH67_A_MAX 254
|
||||||
|
#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1)
|
||||||
|
#define SI5351_MULTISYNTH_C_MAX 1048575
|
||||||
|
#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1)
|
||||||
|
#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1)
|
||||||
|
#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1)
|
||||||
|
|
||||||
|
#define SI5351_DEVICE_STATUS 0
|
||||||
|
#define SI5351_INTERRUPT_STATUS 1
|
||||||
|
#define SI5351_INTERRUPT_MASK 2
|
||||||
|
#define SI5351_STATUS_SYS_INIT (1<<7)
|
||||||
|
#define SI5351_STATUS_LOL_B (1<<6)
|
||||||
|
#define SI5351_STATUS_LOL_A (1<<5)
|
||||||
|
#define SI5351_STATUS_LOS (1<<4)
|
||||||
|
#define SI5351_OUTPUT_ENABLE_CTRL 3
|
||||||
|
#define SI5351_OEB_PIN_ENABLE_CTRL 9
|
||||||
|
#define SI5351_PLL_INPUT_SOURCE 15
|
||||||
|
#define SI5351_CLKIN_DIV_MASK (3<<6)
|
||||||
|
#define SI5351_CLKIN_DIV_1 (0<<6)
|
||||||
|
#define SI5351_CLKIN_DIV_2 (1<<6)
|
||||||
|
#define SI5351_CLKIN_DIV_4 (2<<6)
|
||||||
|
#define SI5351_CLKIN_DIV_8 (3<<6)
|
||||||
|
#define SI5351_PLLB_SOURCE (1<<3)
|
||||||
|
#define SI5351_PLLA_SOURCE (1<<2)
|
||||||
|
|
||||||
|
#define SI5351_CLK0_CTRL 16
|
||||||
|
#define SI5351_CLK1_CTRL 17
|
||||||
|
#define SI5351_CLK2_CTRL 18
|
||||||
|
#define SI5351_CLK3_CTRL 19
|
||||||
|
#define SI5351_CLK4_CTRL 20
|
||||||
|
#define SI5351_CLK5_CTRL 21
|
||||||
|
#define SI5351_CLK6_CTRL 22
|
||||||
|
#define SI5351_CLK7_CTRL 23
|
||||||
|
#define SI5351_CLK_POWERDOWN (1<<7)
|
||||||
|
#define SI5351_CLK_INTEGER_MODE (1<<6)
|
||||||
|
#define SI5351_CLK_PLL_SELECT (1<<5)
|
||||||
|
#define SI5351_CLK_INVERT (1<<4)
|
||||||
|
#define SI5351_CLK_INPUT_MASK (3<<2)
|
||||||
|
#define SI5351_CLK_INPUT_XTAL (0<<2)
|
||||||
|
#define SI5351_CLK_INPUT_CLKIN (1<<2)
|
||||||
|
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
|
||||||
|
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
|
||||||
|
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
|
||||||
|
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
|
||||||
|
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
|
||||||
|
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
|
||||||
|
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
|
||||||
|
|
||||||
|
#define SI5351_CLK3_0_DISABLE_STATE 24
|
||||||
|
#define SI5351_CLK7_4_DISABLE_STATE 25
|
||||||
|
#define SI5351_CLK_DISABLE_STATE_MASK 3
|
||||||
|
#define SI5351_CLK_DISABLE_STATE_LOW 0
|
||||||
|
#define SI5351_CLK_DISABLE_STATE_HIGH 1
|
||||||
|
#define SI5351_CLK_DISABLE_STATE_FLOAT 2
|
||||||
|
#define SI5351_CLK_DISABLE_STATE_NEVER 3
|
||||||
|
|
||||||
|
#define SI5351_PARAMETERS_LENGTH 8
|
||||||
|
#define SI5351_PLLA_PARAMETERS 26
|
||||||
|
#define SI5351_PLLB_PARAMETERS 34
|
||||||
|
#define SI5351_CLK0_PARAMETERS 42
|
||||||
|
#define SI5351_CLK1_PARAMETERS 50
|
||||||
|
#define SI5351_CLK2_PARAMETERS 58
|
||||||
|
#define SI5351_CLK3_PARAMETERS 66
|
||||||
|
#define SI5351_CLK4_PARAMETERS 74
|
||||||
|
#define SI5351_CLK5_PARAMETERS 82
|
||||||
|
#define SI5351_CLK6_PARAMETERS 90
|
||||||
|
#define SI5351_CLK7_PARAMETERS 91
|
||||||
|
#define SI5351_CLK6_7_OUTPUT_DIVIDER 92
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4)
|
||||||
|
#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0)
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_SHIFT 4
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_1 0
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_2 1
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_4 2
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_8 3
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_16 4
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_32 5
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_64 6
|
||||||
|
#define SI5351_OUTPUT_CLK_DIV_128 7
|
||||||
|
#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2)
|
||||||
|
|
||||||
|
#define SI5351_SSC_PARAM0 149
|
||||||
|
#define SI5351_SSC_PARAM1 150
|
||||||
|
#define SI5351_SSC_PARAM2 151
|
||||||
|
#define SI5351_SSC_PARAM3 152
|
||||||
|
#define SI5351_SSC_PARAM4 153
|
||||||
|
#define SI5351_SSC_PARAM5 154
|
||||||
|
#define SI5351_SSC_PARAM6 155
|
||||||
|
#define SI5351_SSC_PARAM7 156
|
||||||
|
#define SI5351_SSC_PARAM8 157
|
||||||
|
#define SI5351_SSC_PARAM9 158
|
||||||
|
#define SI5351_SSC_PARAM10 159
|
||||||
|
#define SI5351_SSC_PARAM11 160
|
||||||
|
#define SI5351_SSC_PARAM12 161
|
||||||
|
|
||||||
|
#define SI5351_VXCO_PARAMETERS_LOW 162
|
||||||
|
#define SI5351_VXCO_PARAMETERS_MID 163
|
||||||
|
#define SI5351_VXCO_PARAMETERS_HIGH 164
|
||||||
|
|
||||||
|
#define SI5351_CLK0_PHASE_OFFSET 165
|
||||||
|
#define SI5351_CLK1_PHASE_OFFSET 166
|
||||||
|
#define SI5351_CLK2_PHASE_OFFSET 167
|
||||||
|
#define SI5351_CLK3_PHASE_OFFSET 168
|
||||||
|
#define SI5351_CLK4_PHASE_OFFSET 169
|
||||||
|
#define SI5351_CLK5_PHASE_OFFSET 170
|
||||||
|
|
||||||
|
#define SI5351_PLL_RESET 177
|
||||||
|
#define SI5351_PLL_RESET_B (1<<7)
|
||||||
|
#define SI5351_PLL_RESET_A (1<<5)
|
||||||
|
|
||||||
|
#define SI5351_CRYSTAL_LOAD 183
|
||||||
|
#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
|
||||||
|
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
|
||||||
|
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
|
||||||
|
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
|
||||||
|
|
||||||
|
#define SI5351_FANOUT_ENABLE 187
|
||||||
|
#define SI5351_CLKIN_ENABLE (1<<7)
|
||||||
|
#define SI5351_XTAL_ENABLE (1<<6)
|
||||||
|
#define SI5351_MULTISYNTH_ENABLE (1<<4)
|
||||||
|
|
||||||
|
/* Macro definitions */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
|
||||||
|
*
|
||||||
|
* The semantics of do_div() are:
|
||||||
|
*
|
||||||
|
* uint32_t do_div(uint64_t *n, uint32_t base)
|
||||||
|
* {
|
||||||
|
* uint32_t remainder = *n % base;
|
||||||
|
* *n = *n / base;
|
||||||
|
* return remainder;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* NOTE: macro parameter n is evaluated multiple times,
|
||||||
|
* beware of side effects!
|
||||||
|
*/
|
||||||
|
|
||||||
|
# define do_div(n,base) ({ \
|
||||||
|
uint32_t __base = (base); \
|
||||||
|
uint32_t __rem; \
|
||||||
|
__rem = ((uint64_t)(n)) % __base; \
|
||||||
|
(n) = ((uint64_t)(n)) / __base; \
|
||||||
|
__rem; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Enum definitions */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enum si5351_variant - SiLabs Si5351 chip variant
|
||||||
|
* @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
|
||||||
|
* @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
|
||||||
|
* @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
|
||||||
|
* @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
|
||||||
|
*/
|
||||||
|
enum si5351_variant {
|
||||||
|
SI5351_VARIANT_A = 1,
|
||||||
|
SI5351_VARIANT_A3 = 2,
|
||||||
|
SI5351_VARIANT_B = 3,
|
||||||
|
SI5351_VARIANT_C = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum si5351_clock {SI5351_CLK0, SI5351_CLK1, SI5351_CLK2, SI5351_CLK3,
|
||||||
|
SI5351_CLK4, SI5351_CLK5, SI5351_CLK6, SI5351_CLK7};
|
||||||
|
|
||||||
|
enum si5351_pll {SI5351_PLLA, SI5351_PLLB};
|
||||||
|
|
||||||
|
enum si5351_drive {SI5351_DRIVE_2MA, SI5351_DRIVE_4MA, SI5351_DRIVE_6MA, SI5351_DRIVE_8MA};
|
||||||
|
|
||||||
|
/* Struct definitions */
|
||||||
|
|
||||||
|
struct Si5351RegSet
|
||||||
|
{
|
||||||
|
uint32_t p1;
|
||||||
|
uint32_t p2;
|
||||||
|
uint32_t p3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Si5351Status
|
||||||
|
{
|
||||||
|
uint8_t SYS_INIT;
|
||||||
|
uint8_t LOL_B;
|
||||||
|
uint8_t LOL_A;
|
||||||
|
uint8_t LOS;
|
||||||
|
uint8_t REVID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Si5351IntStatus
|
||||||
|
{
|
||||||
|
uint8_t SYS_INIT_STKY;
|
||||||
|
uint8_t LOL_B_STKY;
|
||||||
|
uint8_t LOL_A_STKY;
|
||||||
|
uint8_t LOS_STKY;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Si5351
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Si5351(void);
|
||||||
|
void init(uint8_t);
|
||||||
|
void set_freq(uint32_t, uint32_t, enum si5351_clock);
|
||||||
|
void set_pll(uint32_t, enum si5351_pll);
|
||||||
|
void clock_enable(enum si5351_clock, uint8_t);
|
||||||
|
void drive_strength(enum si5351_clock, enum si5351_drive);
|
||||||
|
void update_status(void);
|
||||||
|
void set_correction(int32_t);
|
||||||
|
int32_t get_correction(void);
|
||||||
|
uint8_t si5351_write_bulk(uint8_t, uint8_t, uint8_t *);
|
||||||
|
uint8_t si5351_write(uint8_t, uint8_t);
|
||||||
|
uint8_t si5351_read(uint8_t);
|
||||||
|
struct Si5351Status dev_status;
|
||||||
|
struct Si5351IntStatus dev_int_status;
|
||||||
|
private:
|
||||||
|
void rational_best_approximation(
|
||||||
|
unsigned long, unsigned long,
|
||||||
|
unsigned long, unsigned long,
|
||||||
|
unsigned long *, unsigned long *);
|
||||||
|
uint32_t pll_calc(uint32_t, struct Si5351RegSet *, int32_t);
|
||||||
|
uint32_t multisynth_calc(uint32_t, struct Si5351RegSet *);
|
||||||
|
uint32_t multisynth_recalc(uint32_t, uint32_t,struct Si5351RegSet *);
|
||||||
|
void si5351_update_sys_status(struct Si5351Status *);
|
||||||
|
void si5351_update_int_status(struct Si5351IntStatus *);
|
||||||
|
void si5351_set_ms_source(enum si5351_clock, enum si5351_pll);
|
||||||
|
uint32_t ee_ref_correction;
|
||||||
|
int32_t ref_correction;
|
||||||
|
uint32_t plla_freq;
|
||||||
|
uint32_t pllb_freq;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SI5351_H_ */
|
Ładowanie…
Reference in New Issue