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