Merge pull request #2 from reedbn/one_stop_settings

Merging one stop settings branch
This commit is contained in:
reedbn 2020-01-17 18:47:28 -08:00 committed by GitHub
commit 7b2057eabb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 759 additions and 898 deletions

152
keyer.cpp
View File

@ -1,4 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include "settings.h"
#include "ubitx.h" #include "ubitx.h"
/** /**
@ -28,72 +29,27 @@
*/ */
//CW ADC Range //CW ADC Range
int cwAdcSTFrom = 0; static const unsigned int cwAdcSTFrom = 0;
int cwAdcSTTo = 50; static const unsigned int cwAdcSTTo = 50;
int cwAdcBothFrom = 51; static const unsigned int cwAdcBothFrom = cwAdcSTTo + 1;
int cwAdcBothTo = 300; static const unsigned int cwAdcBothTo = 300;
int cwAdcDotFrom = 301; static const unsigned int cwAdcDotFrom = cwAdcBothTo + 1;
int cwAdcDotTo = 600; static const unsigned int cwAdcDotTo = 600;
int cwAdcDashFrom = 601; static const unsigned int cwAdcDashFrom = cwAdcDotTo + 1;
int cwAdcDashTo = 800; static const unsigned int cwAdcDashTo = 800;
//byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
byte delayBeforeCWStartTime = 50; static const unsigned int delayBeforeCWStartTime = 50;
// in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs
//#define CW_TIMEOUT (600l) //Change to CW Delaytime for value save to eeprom
#define PADDLE_DOT 1
#define PADDLE_DASH 2
#define PADDLE_BOTH 3
#define PADDLE_STRAIGHT 4
//we store the last padde's character
//to alternatively send dots and dashes
//when both are simultaneously pressed
char lastPaddle = 0;
/*
//reads the analog keyer pin and reports the paddle
byte getPaddle(){
int paddle = analogRead(ANALOG_KEYER);
//handle the ptt as the straight key
if (digitalRead(PTT) == 0)
return PADDLE_STRAIGHT;
if (paddle > 800) // above 4v is up
return 0;
if (!Iambic_Key)
return PADDLE_STRAIGHT;
if (paddle > 600) // 4-3v is dot
return PADDLE_DASH;
else if (paddle > 300) //1-2v is dash
return PADDLE_DOT;
else if (paddle > 50)
return PADDLE_BOTH; //both are between 1 and 2v
else
return PADDLE_STRAIGHT; //less than 1v is the straight key
}
*/
/** /**
* Starts transmitting the carrier with the sidetone * Starts transmitting the carrier with the sidetone
* It assumes that we have called cwTxStart and not called cwTxStop * It assumes that we have called cwTxStart and not called cwTxStop
* each time it is called, the cwTimeOut is pushed further into the future * each time it is called, the cwTimeOut is pushed further into the future
*/ */
void cwKeydown(){ void cwKeydown(){
tone(CW_TONE, globalSettings.cwSideToneFreq);
keyDown = 1; //tracks the CW_KEY
tone(CW_TONE, (int)sideTone);
digitalWrite(CW_KEY, 1); digitalWrite(CW_KEY, 1);
//Modified by KD8CEC, for CW Delay Time save to eeprom globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
//cwTimeout = millis() + CW_TIMEOUT;
cwTimeout = millis() + cwDelayTime * 10;
} }
/** /**
@ -101,13 +57,10 @@ void cwKeydown(){
* Pushes the cwTimeout further into the future * Pushes the cwTimeout further into the future
*/ */
void cwKeyUp(){ void cwKeyUp(){
keyDown = 0; //tracks the CW_KEY
noTone(CW_TONE); noTone(CW_TONE);
digitalWrite(CW_KEY, 0); digitalWrite(CW_KEY, 0);
//Modified by KD8CEC, for CW Delay Time save to eeprom globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
//cwTimeout = millis() + CW_TIMEOUT;
cwTimeout = millis() + cwDelayTime * 10;
} }
//Variables for Ron's new logic //Variables for Ron's new logic
@ -119,16 +72,14 @@ void cwKeyUp(){
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT }; enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
static unsigned long ktimer; static unsigned long ktimer;
unsigned char keyerState = IDLE; unsigned char keyerState = IDLE;
uint8_t keyerControl = 0;
//Below is a test to reduce the keying error. do not delete lines //Below is a test to reduce the keying error. do not delete lines
//create by KD8CEC for compatible with new CW Logic //create by KD8CEC for compatible with new CW Logic
char update_PaddleLatch(byte isUpdateKeyState) { char update_PaddleLatch(bool isUpdateKeyState) {
unsigned char tmpKeyerControl = 0; unsigned char tmpKeyerControl = 0;
int paddle = analogRead(ANALOG_KEYER); int paddle = analogRead(ANALOG_KEYER);
//diagnostic, VU2ESE
//itoa(paddle, b, 10);
//printLine2(b);
//use the PTT as the key for tune up, quick QSOs //use the PTT as the key for tune up, quick QSOs
if (digitalRead(PTT) == 0) if (digitalRead(PTT) == 0)
@ -139,9 +90,8 @@ char update_PaddleLatch(byte isUpdateKeyState) {
tmpKeyerControl |= DIT_L; tmpKeyerControl |= DIT_L;
else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo) else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo)
tmpKeyerControl |= (DAH_L | DIT_L) ; tmpKeyerControl |= (DAH_L | DIT_L) ;
else else{
{ if (KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode)
if (Iambic_Key)
tmpKeyerControl = 0 ; tmpKeyerControl = 0 ;
else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo) else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo)
tmpKeyerControl = DIT_L ; tmpKeyerControl = DIT_L ;
@ -149,7 +99,7 @@ char update_PaddleLatch(byte isUpdateKeyState) {
tmpKeyerControl = 0 ; tmpKeyerControl = 0 ;
} }
if (isUpdateKeyState == 1) if (isUpdateKeyState)
keyerControl |= tmpKeyerControl; keyerControl |= tmpKeyerControl;
return tmpKeyerControl; return tmpKeyerControl;
@ -160,22 +110,24 @@ char update_PaddleLatch(byte isUpdateKeyState) {
// modified by KD8CEC // modified by KD8CEC
******************************************************************************/ ******************************************************************************/
void cwKeyer(void){ void cwKeyer(void){
lastPaddle = 0;
bool continue_loop = true; bool continue_loop = true;
unsigned tmpKeyControl = 0; unsigned tmpKeyControl = 0;
if( Iambic_Key ) { if(KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode){
while(continue_loop) { while(continue_loop){
switch (keyerState) { switch(keyerState){
case IDLE: case IDLE:
tmpKeyControl = update_PaddleLatch(0); tmpKeyControl = update_PaddleLatch(0);
if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L || if((tmpKeyControl == DAH_L)//Currently dah
tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) { ||(tmpKeyControl == DIT_L)//Currently dit
update_PaddleLatch(1); ||(tmpKeyControl == (DAH_L | DIT_L))//Currently both
||( keyerControl & (DAH_L | DIT_L))){//Resolving either
update_PaddleLatch(true);
keyerState = CHK_DIT; keyerState = CHK_DIT;
}else{ }
if (0 < cwTimeout && cwTimeout < millis()){ else{
cwTimeout = 0; if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
globalSettings.cwExpirationTimeMs = 0;
stopTx(); stopTx();
} }
continue_loop = false; continue_loop = false;
@ -185,7 +137,7 @@ void cwKeyer(void){
case CHK_DIT: case CHK_DIT:
if (keyerControl & DIT_L) { if (keyerControl & DIT_L) {
keyerControl |= DIT_PROC; keyerControl |= DIT_PROC;
ktimer = cwSpeed; ktimer = globalSettings.cwDitDurationMs;
keyerState = KEYED_PREP; keyerState = KEYED_PREP;
}else{ }else{
keyerState = CHK_DAH; keyerState = CHK_DAH;
@ -194,7 +146,7 @@ void cwKeyer(void){
case CHK_DAH: case CHK_DAH:
if (keyerControl & DAH_L) { if (keyerControl & DAH_L) {
ktimer = cwSpeed*3; ktimer = 3*globalSettings.cwDitDurationMs;
keyerState = KEYED_PREP; keyerState = KEYED_PREP;
}else{ }else{
keyerState = IDLE; keyerState = IDLE;
@ -203,13 +155,11 @@ void cwKeyer(void){
case KEYED_PREP: case KEYED_PREP:
//modified KD8CEC //modified KD8CEC
if (!inTx){ if (!globalSettings.txActive){
//DelayTime Option //DelayTime Option
active_delay(delayBeforeCWStartTime * 2); active_delay(delayBeforeCWStartTime * 2);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
keyDown = 0; startTx(TuningMode_e::TUNE_CW);
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
startTx(TX_CW);
} }
ktimer += millis(); // set ktimer to interval end time ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
@ -220,10 +170,11 @@ void cwKeyer(void){
case KEYED: case KEYED:
if (millis() > ktimer) { // are we at end of key down ? if (millis() > ktimer) { // are we at end of key down ?
cwKeyUp(); cwKeyUp();
ktimer = millis() + cwSpeed; // inter-element time ktimer = millis() + (globalSettings.cwDitDurationMs / 10); // inter-element time
keyerState = INTER_ELEMENT; // next state keyerState = INTER_ELEMENT; // next state
}else if (keyerControl & IAMBICB) { }
else if(KeyerMode_e::KEYER_IAMBIC_B == globalSettings.keyerMode){
update_PaddleLatch(1); // early paddle latch in Iambic B mode update_PaddleLatch(1); // early paddle latch in Iambic B mode
} }
break; break;
@ -246,20 +197,19 @@ void cwKeyer(void){
checkCAT(); checkCAT();
} //end of while } //end of while
} }
else{ else{//KEYER_STRAIGHT
while(1){ while(1){
char state = update_PaddleLatch(0); char state = update_PaddleLatch(0);
// Serial.println((int)state); // Serial.println((int)state);
if (state == DIT_L) { if (state == DIT_L) {
// if we are here, it is only because the key is pressed // if we are here, it is only because the key is pressed
if (!inTx){ if (!globalSettings.txActive){
startTx(TX_CW); startTx(TuningMode_e::TUNE_CW);
//DelayTime Option //DelayTime Option
active_delay(delayBeforeCWStartTime * 2); active_delay(delayBeforeCWStartTime * 2);
keyDown = 0; globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwDitDurationMs;
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
} }
cwKeydown(); cwKeydown();
@ -269,24 +219,16 @@ void cwKeyer(void){
cwKeyUp(); cwKeyUp();
} }
else{ else{
if (0 < cwTimeout && cwTimeout < millis()){ if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
cwTimeout = 0; globalSettings.cwExpirationTimeMs = 0;
keyDown = 0;
stopTx(); stopTx();
} }
//if (!cwTimeout) //removed by KD8CEC return;//Tx stop control by Main Loop
// return;
// got back to the beginning of the loop, if no further activity happens on straight key
// we will time out, and return out of this routine
//delay(5);
//delay_background(5, 3); //removed by KD8CEC
//continue; //removed by KD8CEC
return; //Tx stop control by Main Loop
} }
checkCAT(); checkCAT();
} //end of while } //end of while
} //end of elese }//end of else KEYER_STRAIGHT
} }

View File

@ -1,19 +1,19 @@
#include <Arduino.h> #include <Arduino.h>
#include "ubitx.h" #include "ubitx.h"
#include "settings.h"
#include "morse.h" #include "morse.h"
struct Morse {
char letter;
unsigned char code;
};
/* /*
* Each byte of the morse table stores one letter. * Each byte of the morse table stores one letter.
* The 0 is a dot, a 1 is a dash * The 0 is a dot, a 1 is a dash
* From the Most significant byte onwards, the letter is padded with 1s. * From the Most significant byte onwards, the letter is padded with 1s.
* The first zero after the 1s indicates the start of the letter, it MUST be discarded * The first zero after the 1s indicates the start of the letter, it MUST be discarded
*/ */
extern int cwSpeed;
struct Morse {
char letter;
unsigned char code;
};
static const PROGMEM struct Morse morse_table[] = { static const PROGMEM struct Morse morse_table[] = {
{'a', 0xf9}, // 11111001 {'a', 0xf9}, // 11111001
{'b', 0xe8}, // 11101000 {'b', 0xe8}, // 11101000
@ -51,9 +51,9 @@ static const PROGMEM struct Morse morse_table[] = {
{'8', 0xdc}, // 11011100 {'8', 0xdc}, // 11011100
{'9', 0xde}, // 11011110 {'9', 0xde}, // 11011110
{'0', 0xdf}, // 11011111 {'0', 0xdf}, // 11011111
{'.', 0xd5}, // 110010101 {'.', 0x95}, // 10010101
{',', 0xd3}, // 110110011 //AD7U 20191217 {',', 0xb3}, // 10110011
{'?', 0xcc}, // 11001100 //AD7U 20191217 - Added {'?', 0x8c}, // 10001100
}; };
static void morseLetter(char c){ static void morseLetter(char c){
@ -61,8 +61,8 @@ static void morseLetter(char c){
//handle space character as three dashes //handle space character as three dashes
if (c == ' '){ if (c == ' '){
active_delay(cwSpeed * 9); active_delay(9 * globalSettings.cwDitDurationMs);
Serial.print(' '); //Serial.print(' ');
return; return;
} }
@ -79,22 +79,23 @@ static void morseLetter(char c){
//now we are at the first zero, skip and carry on //now we are at the first zero, skip and carry on
mask = mask >> 1; mask = mask >> 1;
while(mask){ while(mask){
tone(CW_TONE, sideTone,10000); tone(CW_TONE, globalSettings.cwSideToneFreq,10000);
if (mask & code){ if (mask & code){
delay(3 * (int)cwSpeed); delay(3 * globalSettings.cwDitDurationMs);
//Serial.print('-'); //Serial.print('-');
} }
else{ else{
delay((int)cwSpeed); delay(globalSettings.cwDitDurationMs);
//Serial.print('.'); //Serial.print('.');
} }
//Serial.print('#'); //Serial.print('#');
noTone(CW_TONE); noTone(CW_TONE);
delay((int)cwSpeed); // space between dots and dashes delay(globalSettings.cwDitDurationMs); // space between dots and dashes
mask = mask >> 1; mask = mask >> 1;
} }
//Serial.println('@'); //Serial.println('@');
delay(200); // space between letters is a dash (3 dots), one dot's space has already been sent delay(2*globalSettings.cwDitDurationMs); // space between letters is a dash (3 dots), one dot's space has already been sent
break;//We've played the letter, so don't bother checking the rest of the list
} }
} }
} }
@ -107,7 +108,7 @@ void morseText(char *text){
delay(1000); delay(1000);
// } // }
Serial.println(sideTone); //Serial.println(globalSettings.cwSideToneFreq);
while(*text){ while(*text){
morseLetter(*text++); morseLetter(*text++);
} }

View File

@ -1,5 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <EEPROM.h> #include "settings.h"
#include "ubitx.h" #include "ubitx.h"
#include "nano_gui.h" #include "nano_gui.h"
@ -8,30 +8,45 @@
struct Point ts_point; struct Point ts_point;
//filled from a test run of calibration routine /*
int slope_x=104, slope_y=137, offset_x=28, offset_y=29; * This formats the frequency given in f
*/
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size) {
memset(buff, 0, buff_size);
ultoa(freq, buff, DEC);
uint8_t num_digits = strlen(buff);
const uint8_t num_spacers = (num_digits-1) / 3;
const uint8_t num_leading_digits_raw = num_digits % 3;
const uint8_t num_leading_digits = (0 == num_leading_digits_raw) ? 3 : num_leading_digits_raw;
if(0 == num_spacers){
return;
}
buff += num_leading_digits;
num_digits -= num_leading_digits;
for(int i = num_digits-1; i >= 0; --i){
buff[i + (i/3 + 1)] = buff[i];
}
for(unsigned int i = 0; i < num_spacers; ++i){
memcpy_P(buff,F("."),1);
buff += 4;
}
}
void readTouchCalibration(){ void readTouchCalibration(){
EEPROM.get(SLOPE_X, slope_x); LoadSettingsFromEeprom();
EEPROM.get(SLOPE_Y, slope_y); /* for debugging
EEPROM.get(OFFSET_X, offset_x); Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
EEPROM.get(OFFSET_Y, offset_y); Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
/* Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
//for debugging //*/
Serial.print(slope_x); Serial.print(' ');
Serial.print(slope_y); Serial.print(' ');
Serial.print(offset_x); Serial.print(' ');
Serial.println(offset_y); Serial.println(' ');
*/
} }
void writeTouchCalibration(){ void writeTouchCalibration(){
EEPROM.put(SLOPE_X, slope_x); SaveSettingsToEeprom();
EEPROM.put(SLOPE_Y, slope_y);
EEPROM.put(OFFSET_X, offset_x);
EEPROM.put(OFFSET_Y, offset_y);
} }
#define Z_THRESHOLD 400 #define Z_THRESHOLD 400
@ -118,7 +133,7 @@ static void touch_update(){
} }
boolean readTouch(){ bool readTouch(){
touch_update(); touch_update();
if (zraw >= Z_THRESHOLD) { if (zraw >= Z_THRESHOLD) {
ts_point.x = xraw; ts_point.x = xraw;
@ -130,8 +145,8 @@ boolean readTouch(){
} }
void scaleTouch(struct Point *p){ void scaleTouch(struct Point *p){
p->x = ((long)(p->x - offset_x) * 10l)/ (long)slope_x; p->x = ((long)(p->x - globalSettings.touchOffsetX) * 10L)/ (long)globalSettings.touchSlopeX;
p->y = ((long)(p->y - offset_y) * 10l)/ (long)slope_y; p->y = ((long)(p->y - globalSettings.touchOffsetY) * 10L)/ (long)globalSettings.touchSlopeY;
//Serial.print(p->x); Serial.print(",");Serial.println(p->y); //Serial.print(p->x); Serial.print(",");Serial.println(p->y);
} }
@ -169,7 +184,6 @@ void displayInit(void){
tft.setRotation(1); tft.setRotation(1);
xpt2046_Init(); xpt2046_Init();
readTouchCalibration();
} }
void displayPixel(unsigned int x, unsigned int y, unsigned int c){ void displayPixel(unsigned int x, unsigned int y, unsigned int c){
@ -291,14 +305,14 @@ void setupTouch(){
// we average two readings and divide them by half and store them as scaled integers 10 times their actual, fractional value // we average two readings and divide them by half and store them as scaled integers 10 times their actual, fractional value
//the x points are located at 20 and 300 on x axis, hence, the delta x is 280, we take 28 instead, to preserve fractional value, //the x points are located at 20 and 300 on x axis, hence, the delta x is 280, we take 28 instead, to preserve fractional value,
//there are two readings (x1,x2) and (x3, x4). Hence, we have to divide by 28 * 2 = 56 //there are two readings (x1,x2) and (x3, x4). Hence, we have to divide by 28 * 2 = 56
slope_x = ((x4 - x3) + (x2 - x1))/56; globalSettings.touchSlopeX = ((x4 - x3) + (x2 - x1))/56;
//the y points are located at 20 and 220 on the y axis, hence, the delta is 200. we take it as 20 instead, to preserve the fraction value //the y points are located at 20 and 220 on the y axis, hence, the delta is 200. we take it as 20 instead, to preserve the fraction value
//there are two readings (y1, y2) and (y3, y4). Hence we have to divide by 20 * 2 = 40 //there are two readings (y1, y2) and (y3, y4). Hence we have to divide by 20 * 2 = 40
slope_y = ((y3 - y1) + (y4 - y2))/40; globalSettings.touchSlopeY = ((y3 - y1) + (y4 - y2))/40;
//x1, y1 is at 20 pixels //x1, y1 is at 20 pixels
offset_x = x1 + -((20 * slope_x)/10); globalSettings.touchOffsetX = x1 + -((20 * globalSettings.touchSlopeX)/10);
offset_y = y1 + -((20 * slope_y)/10); globalSettings.touchOffsetY = y1 + -((20 * globalSettings.touchSlopeY)/10);
/* /*
Serial.print(x1);Serial.print(':');Serial.println(y1); Serial.print(x1);Serial.print(':');Serial.println(y1);
@ -307,10 +321,10 @@ void setupTouch(){
Serial.print(x4);Serial.print(':');Serial.println(y4); Serial.print(x4);Serial.print(':');Serial.println(y4);
//for debugging //for debugging
Serial.print(slope_x); Serial.print(' '); Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
Serial.print(slope_y); Serial.print(' '); Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(offset_x); Serial.print(' '); Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
Serial.println(offset_y); Serial.println(' '); Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
*/ */
writeTouchCalibration(); writeTouchCalibration();
displayClear(DISPLAY_BLACK); displayClear(DISPLAY_BLACK);

View File

@ -18,6 +18,8 @@ void displayChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t
void displayRawText(char *text, int x1, int y1, int color, int background); void displayRawText(char *text, int x1, int y1, int color, int background);
void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border); void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border);
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size);
/* touch functions */ /* touch functions */
boolean readTouch(); boolean readTouch();

168
settings.cpp Normal file
View File

@ -0,0 +1,168 @@
#include <string.h>//memset
#include <stdint.h>
#include <EEPROM.h>
#include <Arduino.h>//only needed for debugging's Serial.print stuff
#include "settings.h"
#include "ubitx.h"//redrawVFOs() function
/**
* These are the "magic" indices where these user changable settinngs are stored in the EEPROM
*/
static const uint16_t EEPROM_ADDR_MASTER_CAL = 0;//int32_t
//4 is currently unused, but may have been LSB_CAL on other versions
static const uint16_t EEPROM_ADDR_USB_CAL = 8;//uint32_t
//12 is currently unused, but may have been CW_SIDETONE on other versions?
static const uint16_t EEPROM_ADDR_VFO_A_FREQ = 16;//uint32_t
static const uint16_t EEPROM_ADDR_VFO_B_FREQ = 20;//uint32_t
static const uint16_t EEPROM_ADDR_CW_SIDETONE = 24;//uint32_t
static const uint16_t EEPROM_ADDR_CW_DIT_TIME = 28;//uint32_t
static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_X = 32;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_Y = 36;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_X = 40;//int16_t
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_Y = 44;//int16_t
static const uint16_t EEPROM_ADDR_CW_DELAYTIME = 48;
static const uint16_t EEPROM_ADDR_VFO_A_MODE = 256;
static const uint16_t EEPROM_ADDR_VFO_B_MODE = 257;
static const uint16_t EEPROM_ADDR_CW_KEY_TYPE = 358;
template<typename T>
bool LoadSane(T& dest,uint16_t addr, T min, T max)
{
T read_value;
EEPROM.get(addr,read_value);
if((min <= read_value) && (read_value <= max)){
dest = read_value;
//Serial.print(addr);
//Serial.print(F(":"));
//Serial.println(dest);
return true;
}
//Serial.print(addr);
//Serial.print(F(": Not valid: "));
//Serial.print(read_value);
//Serial.print(F(" Leaving value at "));
//Serial.println(dest);
return false;
}
//This is the non-extern version that actually gets built
SettingsRam globalSettings;
void LoadDefaultSettings()
{
memset(&globalSettings,0x00,sizeof(globalSettings));
globalSettings.oscillatorCal = 0L;
globalSettings.usbCarrierFreq = 11052000UL;
globalSettings.activeVfo = Vfo_e::VFO_A;
globalSettings.vfoA.frequency = 7150000UL;
globalSettings.vfoA.mode = VFO_MODE_LSB;
globalSettings.vfoB.frequency = 14150000UL;
globalSettings.vfoB.mode = VFO_MODE_USB;
globalSettings.keyerMode = KEYER_STRAIGHT;
globalSettings.cwSideToneFreq = 800;
globalSettings.cwDitDurationMs = 100;
globalSettings.cwActiveTimeoutMs = 50;
globalSettings.touchSlopeX = 104;
globalSettings.touchSlopeY = 137;
globalSettings.touchOffsetX = 28;
globalSettings.touchOffsetY = 29;
globalSettings.ritOn = false;
globalSettings.ritFrequency = globalSettings.vfoA.frequency;
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
globalSettings.splitOn = false;
globalSettings.txActive = false;
globalSettings.txCatActive = false;
globalSettings.cwExpirationTimeMs = 0;
}
void LoadSettingsFromEeprom()
{
LoadSane(globalSettings.usbCarrierFreq,EEPROM_ADDR_USB_CAL,11048000UL,11060000UL);
LoadSane(globalSettings.vfoA.frequency,EEPROM_ADDR_VFO_A_FREQ,500000UL+1,109000000UL-1);//Allow all freq supported by si5351 driver
LoadSane(globalSettings.vfoB.frequency,EEPROM_ADDR_VFO_B_FREQ,500000UL+1,109000000UL-1);//Allow all freq supported by si5351 driver
LoadSane(globalSettings.cwSideToneFreq,EEPROM_ADDR_CW_SIDETONE,100UL,2000UL);
LoadSane(globalSettings.cwDitDurationMs,EEPROM_ADDR_CW_DIT_TIME,10U,1000U);
if(LoadSane(globalSettings.cwActiveTimeoutMs,EEPROM_ADDR_CW_DELAYTIME,10U,100U)){
globalSettings.cwActiveTimeoutMs *= 10;//scale by 10 for legacy reasons
}
LoadSane(globalSettings.vfoA.mode,EEPROM_ADDR_VFO_A_MODE,VFO_MODE_LSB,VFO_MODE_USB);
LoadSane(globalSettings.vfoB.mode,EEPROM_ADDR_VFO_B_MODE,VFO_MODE_LSB,VFO_MODE_USB);
LoadSane(globalSettings.keyerMode,EEPROM_ADDR_CW_KEY_TYPE,KEYER_STRAIGHT,KEYER_IAMBIC_B);
//No sanity check on these - cal your heart out
EEPROM.get(EEPROM_ADDR_MASTER_CAL,globalSettings.oscillatorCal);
EEPROM.get(EEPROM_ADDR_TOUCH_SLOPE_X,globalSettings.touchSlopeX);
EEPROM.get(EEPROM_ADDR_TOUCH_SLOPE_Y,globalSettings.touchSlopeY);
EEPROM.get(EEPROM_ADDR_TOUCH_OFFSET_X,globalSettings.touchOffsetX);
EEPROM.get(EEPROM_ADDR_TOUCH_OFFSET_Y,globalSettings.touchOffsetY);
}
void SaveSettingsToEeprom()
{
//Serial.println(F("Saving..."));
EEPROM.put(EEPROM_ADDR_MASTER_CAL,globalSettings.oscillatorCal);
EEPROM.put(EEPROM_ADDR_USB_CAL,globalSettings.usbCarrierFreq);
EEPROM.put(EEPROM_ADDR_VFO_A_FREQ,globalSettings.vfoA.frequency);
EEPROM.put(EEPROM_ADDR_VFO_B_FREQ,globalSettings.vfoB.frequency);
EEPROM.put(EEPROM_ADDR_CW_SIDETONE,globalSettings.cwSideToneFreq);
EEPROM.put(EEPROM_ADDR_CW_DIT_TIME,globalSettings.cwDitDurationMs);
EEPROM.put(EEPROM_ADDR_TOUCH_SLOPE_X,globalSettings.touchSlopeX);
EEPROM.put(EEPROM_ADDR_TOUCH_SLOPE_Y,globalSettings.touchSlopeY);
EEPROM.put(EEPROM_ADDR_TOUCH_OFFSET_X,globalSettings.touchOffsetX);
EEPROM.put(EEPROM_ADDR_TOUCH_OFFSET_Y,globalSettings.touchOffsetY);
EEPROM.put(EEPROM_ADDR_CW_DELAYTIME,globalSettings.cwActiveTimeoutMs/10);//scale by 10 for legacy reasons
EEPROM.put(EEPROM_ADDR_VFO_A_MODE,globalSettings.vfoA.mode);
EEPROM.put(EEPROM_ADDR_VFO_B_MODE,globalSettings.vfoB.mode);
EEPROM.put(EEPROM_ADDR_CW_KEY_TYPE,globalSettings.keyerMode);
}
uint32_t GetActiveVfoFreq()
{
if(VFO_A == globalSettings.activeVfo){
return globalSettings.vfoA.frequency;
}
else{
return globalSettings.vfoB.frequency;
}
}
void SetActiveVfoFreq(uint32_t frequency)
{
if(VFO_A == globalSettings.activeVfo)
{
globalSettings.vfoA.frequency = frequency;
}
else{
globalSettings.vfoB.frequency = frequency;
}
}
VfoMode_e GetActiveVfoMode()
{
if(VFO_A == globalSettings.activeVfo){
return globalSettings.vfoA.mode;
}
else{
return globalSettings.vfoB.mode;
}
}
void SetActiveVfoMode(VfoMode_e mode)
{
if(VFO_A == globalSettings.activeVfo)
{
globalSettings.vfoA.mode = mode;
}
else{
globalSettings.vfoB.mode = mode;
}
redrawVFOs();
}

109
settings.h Normal file
View File

@ -0,0 +1,109 @@
/*
* This class deals with all of the radio settings,
* so that other areas of the code doesn't have to
*
* Example usage:
* LoadSettingsFromEeprom();
* Serial.println(globalSettings.vfoAFreq);
* globalSettings.vfoAFreq = 12345678;
* SaveSettingsToEeprom();
* Serial.println(globalSettings.vfoAFreq);
*/
#pragma once
/*
* Loads default values for all settings
*/
void LoadDefaultSettings();
/*
* Loads all persistent settings from the EEPROM
*/
void LoadSettingsFromEeprom();
/*
* Saves all persistent settings to the EEPROM
*
* It's a little CPU-cycle-wasteful to save EVERYTHING
* each time, but keeps things simple
*/
void SaveSettingsToEeprom();
/*
* These are all of the settings
* Note that not all settings are saved to the EEPROM
*/
enum Vfo_e : uint8_t
{
VFO_A,
VFO_B
};
enum VfoMode_e : uint8_t
{
VFO_MODE_LSB = 2,
VFO_MODE_USB = 3
};
struct VfoSettings_t
{
uint32_t frequency;
VfoMode_e mode;
};
enum TuningMode_e : uint8_t
{
TUNE_SSB,
TUNE_CW
};
enum KeyerMode_e : uint8_t
{
KEYER_STRAIGHT,
KEYER_IAMBIC_A,
KEYER_IAMBIC_B
};
/*
* This is the definition of the settings/state variables
*/
struct SettingsRam
{
uint32_t oscillatorCal;
uint32_t usbCarrierFreq;
Vfo_e activeVfo;
VfoSettings_t vfoA;
VfoSettings_t vfoB;
KeyerMode_e keyerMode;
uint32_t cwSideToneFreq;
uint16_t cwDitDurationMs;
uint16_t cwActiveTimeoutMs;
int16_t touchSlopeX;
int16_t touchSlopeY;
int16_t touchOffsetX;
int16_t touchOffsetY;
bool ritOn;
uint32_t ritFrequency;
TuningMode_e tuningMode;
bool splitOn;
bool txActive;
bool txCatActive;
uint32_t cwExpirationTimeMs;
};
//This is the shared declaration
extern SettingsRam globalSettings;
//Some convenience functions
uint32_t GetActiveVfoFreq();
void SetActiveVfoFreq(uint32_t frequency);
VfoMode_e GetActiveVfoMode();
void SetActiveVfoMode(VfoMode_e mode);

164
setup.cpp
View File

@ -1,6 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <EEPROM.h> #include <EEPROM.h>
#include "morse.h" #include "morse.h"
#include "settings.h"
#include "ubitx.h" #include "ubitx.h"
#include "nano_gui.h" #include "nano_gui.h"
@ -22,7 +23,6 @@ void setupExit(){
} }
//this is used by the si5351 routines in the ubitx_5351 file //this is used by the si5351 routines in the ubitx_5351 file
extern int32_t calibration;
extern uint32_t si5351bx_vcoa; extern uint32_t si5351bx_vcoa;
static const unsigned int COLOR_TEXT = DISPLAY_WHITE; static const unsigned int COLOR_TEXT = DISPLAY_WHITE;
@ -73,73 +73,61 @@ void displayDialog(const __FlashStringHelper* title, const __FlashStringHelper*
displayText(c, LAYOUT_INSTRUCTION_TEXT_X, LAYOUT_INSTRUCTION_TEXT_Y, LAYOUT_INSTRUCTION_TEXT_WIDTH, LAYOUT_INSTRUCTION_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_INSTRUCTION_TEXT_X, LAYOUT_INSTRUCTION_TEXT_Y, LAYOUT_INSTRUCTION_TEXT_WIDTH, LAYOUT_INSTRUCTION_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
} }
void printCarrierFreq(unsigned long freq){ void printCarrierFreq(unsigned long freq)
{
memset(c, 0, sizeof(c)); formatFreq(freq,c,sizeof(c));
memset(b, 0, sizeof(b));
ultoa(freq, b, DEC);
strncat(c, b, 2);
strcat_P(c,(const char*)F("."));
strncat(c, &b[2], 3);
strcat(c,(const char*)F("."));
strncat(c, &b[5], 1);
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND);
} }
void setupFreq(){ void setupFreq(){
int knob = 0;
int32_t prev_calibration;
displayDialog(F("Set Frequency"),F("Push TUNE to Save")); displayDialog(F("Set Frequency"),F("Push TUNE to Save"));
//round off the the nearest khz //round off the the nearest khz
frequency = (frequency/1000l)* 1000l; {
setFrequency(frequency); uint32_t freq = GetActiveVfoFreq();
freq = (freq/1000l)* 1000l;
setFrequency(freq);
}
strcpy_P(c,(const char*)F("You should have a")); strcpy_P(c,(const char*)F("You should have a"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
strcpy_P(c,(const char*)F("signal exactly at")); strcpy_P(c,(const char*)F("signal exactly at"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 1*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 1*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
ltoa(frequency/1000l, c, 10); ltoa(GetActiveVfoFreq()/1000L, c, 10);
strcat_P(c,(const char*)F(" KHz")); strcat_P(c,(const char*)F(" KHz"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 2*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 2*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
strcpy_P(c,(const char*)F("Rotate to zerobeat")); strcpy_P(c,(const char*)F("Rotate to zerobeat"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 4*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_ITEM_Y + 4*LAYOUT_ITEM_PITCH_Y, LAYOUT_ITEM_WIDTH, LAYOUT_ITEM_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
ltoa(calibration, b, 10); ltoa(globalSettings.oscillatorCal, b, 10);
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND);
//keep clear of any previous button press //keep clear of any previous button press
while (btnDown()) while (btnDown())
active_delay(100); active_delay(100);
active_delay(100); active_delay(100);
prev_calibration = calibration;
calibration = 0;
while (!btnDown()) while (!btnDown())
{ {
knob = enc_read(); int knob = enc_read();
if (knob != 0) if(knob != 0){
calibration += knob * 875; globalSettings.oscillatorCal += knob * 875;
/* else if (knob < 0) }
calibration -= 875; */ else{
else
continue; //don't update the frequency or the display continue; //don't update the frequency or the display
}
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off si5351bx_setfreq(0, globalSettings.usbCarrierFreq); //set back the carrier oscillator anyway, cw tx switches it off
si5351_set_calibration(calibration); si5351_set_calibration(globalSettings.oscillatorCal);
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
ltoa(calibration, b, 10); ltoa(globalSettings.oscillatorCal, b, 10);
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_TITLE_BACKGROUND, COLOR_BACKGROUND);
} }
EEPROM.put(MASTER_CAL, calibration); SaveSettingsToEeprom();
initOscillators(); initOscillators();
si5351_set_calibration(calibration); si5351_set_calibration(globalSettings.oscillatorCal);
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
//debounce and delay //debounce and delay
while(btnDown()) while(btnDown())
@ -148,37 +136,30 @@ void setupFreq(){
} }
void setupBFO(){ void setupBFO(){
int knob = 0;
unsigned long prevCarrier;
prevCarrier = usbCarrier;
displayDialog(F("Set BFO"),F("Press TUNE to Save")); displayDialog(F("Set BFO"),F("Press TUNE to Save"));
usbCarrier = 11053000l; si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
si5351bx_setfreq(0, usbCarrier); printCarrierFreq(globalSettings.usbCarrierFreq);
printCarrierFreq(usbCarrier);
while (!btnDown()){ while (!btnDown()){
knob = enc_read(); int knob = enc_read();
if(knob != 0){
if (knob != 0) globalSettings.usbCarrierFreq -= 50 * knob;
usbCarrier -= 50 * knob; }
else else{
continue; //don't update the frequency or the display continue; //don't update the frequency or the display
}
si5351bx_setfreq(0, usbCarrier); si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
printCarrierFreq(usbCarrier); printCarrierFreq(globalSettings.usbCarrierFreq);
active_delay(100); active_delay(100);
} }
EEPROM.put(USB_CAL, usbCarrier); SaveSettingsToEeprom();
si5351bx_setfreq(0, usbCarrier); si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
updateDisplay();
menuOn = 0;
} }
void setupCwDelay(){ void setupCwDelay(){
@ -188,44 +169,41 @@ void setupCwDelay(){
displayDialog(F("Set CW T/R Delay"),F("Press tune to Save")); displayDialog(F("Set CW T/R Delay"),F("Press tune to Save"));
active_delay(500); active_delay(500);
prev_cw_delay = cwDelayTime; prev_cw_delay = globalSettings.cwActiveTimeoutMs;
itoa(10 * (int)cwDelayTime, b, 10); ltoa(globalSettings.cwActiveTimeoutMs, b, 10);
strcat_P(b,(const char*)F(" msec")); strcat_P(b,(const char*)F(" msec"));
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
while (!btnDown()){ while (!btnDown()){
knob = enc_read(); knob = enc_read();
if (knob < 0 && cwDelayTime > 10) if (knob < 0 && globalSettings.cwActiveTimeoutMs > 100)
cwDelayTime -= 10; globalSettings.cwActiveTimeoutMs -= 100;
else if (knob > 0 && cwDelayTime < 100) else if (knob > 0 && globalSettings.cwActiveTimeoutMs < 1000)
cwDelayTime += 10; globalSettings.cwActiveTimeoutMs += 100;
else else
continue; //don't update the frequency or the display continue; //don't update the frequency or the display
itoa(10 * (int)cwDelayTime, b, 10); ltoa(globalSettings.cwActiveTimeoutMs, b, 10);
strcat_P(b,(const char*)F(" msec")); strcat_P(b,(const char*)F(" msec"));
displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
EEPROM.put(CW_DELAYTIME, cwDelayTime); SaveSettingsToEeprom();
active_delay(500); active_delay(500);
menuOn = 0; setupExit();
} }
void setupKeyer(){ void setupKeyer(){
int tmp_key, knob;
displayDialog(F("Set CW Keyer"),F("Press tune to Save")); displayDialog(F("Set CW Keyer"),F("Press tune to Save"));
if (!Iambic_Key){ if(KeyerMode_e::KEYER_STRAIGHT == globalSettings.keyerMode){
strcpy_P(c,(const char*)F("< Hand Key >")); strcpy_P(c,(const char*)F("< Hand Key >"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
else if (keyerControl & IAMBICB){ else if(KeyerMode_e::KEYER_IAMBIC_A == globalSettings.keyerMode){
strcpy_P(c,(const char*)F("< Iambic A >")); strcpy_P(c,(const char*)F("< Iambic A >"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
@ -234,56 +212,42 @@ void setupKeyer(){
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
if (!Iambic_Key) int knob = 0;
tmp_key = 0; //hand key uint32_t tmp_mode = globalSettings.keyerMode;
else if (keyerControl & IAMBICB)
tmp_key = 2; //Iambic B
else
tmp_key = 1;
while (!btnDown()) while (!btnDown())
{ {
knob = enc_read(); knob = enc_read();
if (knob == 0){ if(knob == 0){
active_delay(50); active_delay(50);
continue; continue;
} }
if (knob < 0 && tmp_key > 0) if(knob < 0 && tmp_mode > KeyerMode_e::KEYER_STRAIGHT){
tmp_key--; tmp_mode--;
if (knob > 0) }
tmp_key++; if(knob > 0 && tmp_mode < KeyerMode_e::KEYER_IAMBIC_B){
if (tmp_key > 2) tmp_mode++;
tmp_key = 0; }
if (tmp_key == 0){ if (KeyerMode_e::KEYER_STRAIGHT == tmp_mode){
strcpy_P(c,(const char*)F("< Hand Key >")); strcpy_P(c,(const char*)F("< Hand Key >"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
else if (tmp_key == 1){ else if(KeyerMode_e::KEYER_IAMBIC_A == tmp_mode){
strcpy_P(c,(const char*)F("< Iambic A >")); strcpy_P(c,(const char*)F("< Iambic A >"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
else if (tmp_key == 2){ else if (KeyerMode_e::KEYER_IAMBIC_B == tmp_mode){
strcpy_P(c,(const char*)F("< Iambic B >")); strcpy_P(c,(const char*)F("< Iambic B >"));
displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_SETTING_VALUE_X, LAYOUT_SETTING_VALUE_Y, LAYOUT_SETTING_VALUE_WIDTH, LAYOUT_SETTING_VALUE_HEIGHT, COLOR_TEXT, COLOR_SETTING_BACKGROUND, COLOR_BACKGROUND);
} }
} }
active_delay(500); active_delay(500);
if (tmp_key == 0)
Iambic_Key = false;
else if (tmp_key == 1){
Iambic_Key = true;
keyerControl &= ~IAMBICB;
}
else if (tmp_key == 2){
Iambic_Key = true;
keyerControl |= IAMBICB;
}
EEPROM.put(CW_KEY_TYPE, tmp_key); globalSettings.keyerMode = tmp_mode;
SaveSettingsToEeprom();
menuOn = 0; setupExit();
} }
const char MI_SET_FREQ [] PROGMEM = "Set Freq..."; const char MI_SET_FREQ [] PROGMEM = "Set Freq...";

84
ubitx.h
View File

@ -1,3 +1,4 @@
#include "settings.h"
/* The ubitx is powered by an arduino nano. The pin assignment is as folows /* The ubitx is powered by an arduino nano. The pin assignment is as folows
* *
@ -56,7 +57,6 @@ it uses an ILI9341 display controller and an XPT2046 touch controller.
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
*/ */
extern char c[30], b[30]; extern char c[30], b[30];
extern char printBuff[2][20]; //mirrors what is showing on the two lines of the display
/** /**
* The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig. * The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig.
@ -69,37 +69,6 @@ extern char printBuff[2][20]; //mirrors what is showing on the two lines of the
*/ */
/**
* These are the indices where these user changable settinngs are stored in the EEPROM
*/
#define MASTER_CAL 0
#define LSB_CAL 4
#define USB_CAL 8
#define SIDE_TONE 12
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
#define VFO_A 16
#define VFO_B 20
#define CW_SIDETONE 24
#define CW_SPEED 28
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
#define SLOPE_X 32
#define SLOPE_Y 36
#define OFFSET_X 40
#define OFFSET_Y 44
#define CW_DELAYTIME 48
//These are defines for the new features back-ported from KD8CEC's software
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
#define VFO_A_MODE 256 // 2: LSB, 3: USB
#define VFO_B_MODE 257
//values that are stroed for the VFO modes
#define VFO_MODE_LSB 2
#define VFO_MODE_USB 3
// handkey, iambic a, iambic b : 0,1,2f
#define CW_KEY_TYPE 358
/** /**
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz. * The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower, * The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
@ -121,62 +90,23 @@ extern char printBuff[2][20]; //mirrors what is showing on the two lines of the
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz // limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ (100000l) #define LOWEST_FREQ (100000l)
#define HIGHEST_FREQ (30000000l) #define HIGHEST_FREQ (30000000l)
static const uint32_t THRESHOLD_USB_LSB = 10000000L;
//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes
//these are the parameter passed to startTx
#define TX_SSB 0
#define TX_CW 1
extern char ritOn;
extern char vfoActive;
extern unsigned long vfoA, vfoB, sideTone, usbCarrier;
extern char isUsbVfoA, isUsbVfoB;
extern unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
extern unsigned long firstIF; extern unsigned long firstIF;
// if cwMode is flipped on, the rx frequency is tuned down by sidetone hz instead of being zerobeat extern uint8_t menuOn;
extern int cwMode;
//these are variables that control the keyer behaviour
extern int cwSpeed; //this is actuall the dot period in milliseconds
extern int32_t calibration;
extern int cwDelayTime;
extern bool Iambic_Key;
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
extern unsigned char keyerControl;
//during CAT commands, we will freeeze the display until CAT is disengaged
extern unsigned char doingCAT;
/**
* Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
*/
extern boolean txCAT; //turned on if the transmitting due to a CAT command
extern char inTx; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
extern int splitOn; //working split, uses VFO B as the transmit frequency
extern char keyDown; //in cw mode, denotes the carrier is being transmitted
extern char isUSB; //upper sideband was selected, this is reset to the default for the
//frequency when it crosses the frequency border of 10 MHz
extern byte menuOn; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
extern unsigned long cwTimeout; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
extern unsigned long dbgCount; //not used now
extern unsigned char txFilter ; //which of the four transmit filters are in use
extern boolean modeCalibrate;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
//beat frequency
/* these are functions implemented in the main file named as ubitx_xxx.ino */ /* these are functions implemented in the main file named as ubitx_xxx.ino */
void active_delay(int delay_by); void active_delay(int delay_by);
void saveVFOs(); void saveVFOs();
void setFrequency(unsigned long f); void setFrequency(unsigned long f);
void startTx(byte txMode); void startTx(TuningMode_e tx_mode);
void stopTx(); void stopTx();
void ritEnable(unsigned long f); void ritEnable(unsigned long f);
void ritDisable(); void ritDisable();
void checkCAT(); void checkCAT();
void cwKeyer(void); void cwKeyer(void);
void switchVFO(int vfoSelect); void switchVFO(Vfo_e vfoSelect);
int enc_read(void); // returns the number of ticks in a short interval, +ve in clockwise, -ve in anti-clockwise int enc_read(void); // returns the number of ticks in a short interval, +ve in clockwise, -ve in anti-clockwise
void enc_setup(void); // Setups up initial values and interrupts. void enc_setup(void); // Setups up initial values and interrupts.
@ -203,10 +133,6 @@ void checkTouch(); //does the commands with a touch on the buttons
/* these are functiosn implemented in ubitx_si5351.cpp */ /* these are functiosn implemented in ubitx_si5351.cpp */
void si5351bx_setfreq(uint8_t clknum, uint32_t fout); void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
void initOscillators(); void initOscillators();

View File

@ -1,4 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include "settings.h"
#include "ubitx.h" #include "ubitx.h"
#include "nano_gui.h" #include "nano_gui.h"
@ -159,7 +160,7 @@ void catReadEEPRom(void)
//5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE
//6 : //6 :
//7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0) //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0)
cat[0] = 0x80 + (vfoActive == VFO_B ? 1 : 0); cat[0] = 0x80 + ((VFO_B == globalSettings.activeVfo) ? 1 : 0);
cat[1] = 0x00; cat[1] = 0x00;
break; break;
case 0x57 : // case 0x57 : //
@ -187,11 +188,11 @@ void catReadEEPRom(void)
//5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel //5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel
//7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW //7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW
//CAT_BUFF[0] = 0x08; //CAT_BUFF[0] = 0x08;
cat[0] = (sideTone - 300)/50; cat[0] = (globalSettings.cwSideToneFreq - 300)/50;
cat[1] = 0x25; cat[1] = 0x25;
break; break;
case 0x61 : //Sidetone (Volume) (#44) case 0x61 : //globalSettings.cwSideToneFreq (Volume) (#44)
cat[0] = sideTone % 50; cat[0] = globalSettings.cwSideToneFreq % 50;
cat[1] = 0x08; cat[1] = 0x08;
break; break;
case 0x5F : // case 0x5F : //
@ -203,14 +204,14 @@ void catReadEEPRom(void)
cat[1] = 0x08; cat[1] = 0x08;
break; break;
case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
cat[0] = cwDelayTime; cat[0] = globalSettings.cwActiveTimeoutMs / 10;
cat[1] = 0x32; cat[1] = 0x32;
break; break;
case 0x62 : // case 0x62 : //
//5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps) //5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps)
//7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours //7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours
//CAT_BUFF[0] = 0x08; //CAT_BUFF[0] = 0x08;
cat[0] = 1200 / cwSpeed - 4; cat[0] = 1200 / globalSettings.cwDitDurationMs - 4;
cat[1] = 0xB2; cat[1] = 0xB2;
break; break;
case 0x63 : // case 0x63 : //
@ -226,7 +227,7 @@ void catReadEEPRom(void)
cat[1] = 0xB2; cat[1] = 0xB2;
break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed
case 0x78 : case 0x78 :
if (isUSB) if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode())
cat[0] = CAT_MODE_USB; cat[0] = CAT_MODE_USB;
else else
cat[0] = CAT_MODE_LSB; cat[0] = CAT_MODE_LSB;
@ -252,7 +253,7 @@ void catReadEEPRom(void)
//7A 6 ? ? //7A 6 ? ?
//7A 7 SPL On/Off 0 = Off, 1 = On //7A 7 SPL On/Off 0 = Off, 1 = On
cat[0] = (splitOn ? 0xFF : 0x7F); cat[0] = (globalSettings.splitOn ? 0xFF : 0x7F);
break; break;
case 0xB3 : // case 0xB3 : //
cat[0] = 0x00; cat[0] = 0x00;
@ -288,16 +289,16 @@ void processCATCommand2(byte* cmd) {
case 0x02: case 0x02:
//split on //split on
splitOn = 1; globalSettings.splitOn = 1;
break; break;
case 0x82: case 0x82:
//split off //split off
splitOn = 0; globalSettings.splitOn = 0;
break; break;
case 0x03: case 0x03:
writeFreq(frequency,response); // Put the frequency into the buffer writeFreq(GetActiveVfoFreq(),response); // Put the frequency into the buffer
if (isUSB) if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode())
response[4] = 0x01; //USB response[4] = 0x01; //USB
else else
response[4] = 0x00; //LSB response[4] = 0x00; //LSB
@ -307,21 +308,21 @@ void processCATCommand2(byte* cmd) {
case 0x07: // set mode case 0x07: // set mode
if (cmd[0] == 0x00 || cmd[0] == 0x03) if (cmd[0] == 0x00 || cmd[0] == 0x03)
isUSB = 0; SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
else else
isUSB = 1; SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
response[0] = 0x00; response[0] = 0x00;
Serial.write(response, 1); Serial.write(response, 1);
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
//printLine2("cat: mode changed"); //printLine2("cat: mode changed");
//updateDisplay(); //updateDisplay();
break; break;
case 0x08: // PTT On case 0x08: // PTT On
if (!inTx) { if (!globalSettings.txActive) {
response[0] = 0; response[0] = 0;
txCAT = true; globalSettings.txCatActive = true;
startTx(TX_SSB); startTx(TuningMode_e::TUNE_SSB);
updateDisplay(); updateDisplay();
} else { } else {
response[0] = 0xf0; response[0] = 0xf0;
@ -331,9 +332,9 @@ void processCATCommand2(byte* cmd) {
break; break;
case 0x88 : //PTT OFF case 0x88 : //PTT OFF
if (inTx) { if (globalSettings.txActive) {
stopTx(); stopTx();
txCAT = false; globalSettings.txCatActive = false;
} }
response[0] = 0; response[0] = 0;
Serial.write(response,1); Serial.write(response,1);
@ -343,7 +344,7 @@ void processCATCommand2(byte* cmd) {
case 0x81: case 0x81:
//toggle the VFOs //toggle the VFOs
response[0] = 0; response[0] = 0;
if (vfoActive == VFO_A) if (VFO_A == globalSettings.activeVfo)
switchVFO(VFO_B); switchVFO(VFO_B);
else else
switchVFO(VFO_A); switchVFO(VFO_A);
@ -366,14 +367,14 @@ void processCATCommand2(byte* cmd) {
case 0xf7: case 0xf7:
{ {
boolean isHighSWR = false; boolean isHighSWR = false;
boolean isSplitOn = false; boolean issplitOn = false;
/* /*
Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib) Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib)
*/ */
response[0] = ((inTx ? 0 : 1) << 7) + response[0] = ((globalSettings.txActive ? 0 : 1) << 7) +
((isHighSWR ? 1 : 0) << 6) + //hi swr off / on ((isHighSWR ? 1 : 0) << 6) + //hi swr off / on
((isSplitOn ? 1 : 0) << 5) + //Split on / off ((issplitOn ? 1 : 0) << 5) + //Split on / off
(0 << 4) + //dummy data (0 << 4) + //dummy data
0x08; //P0 meter data 0x08; //P0 meter data

View File

@ -1,5 +1,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h> #include <Wire.h>
#include "settings.h"
#include "ubitx.h" #include "ubitx.h"
// ************* SI5315 routines - tks Jerry Gaffke, KE7ER *********************** // ************* SI5315 routines - tks Jerry Gaffke, KE7ER ***********************
@ -48,7 +49,6 @@ uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate
uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv) uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv)
uint8_t si5351bx_drive[3] = {3, 3, 3}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2 uint8_t si5351bx_drive[3] = {3, 3, 3}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2
uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off
int32_t calibration = 0;
void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c
Wire.beginTransmission(SI5351BX_ADDR); Wire.beginTransmission(SI5351BX_ADDR);
@ -115,14 +115,13 @@ void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz
void si5351_set_calibration(int32_t cal){ void si5351_set_calibration(int32_t cal){
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor
si5351bx_setfreq(0, usbCarrier); si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
} }
void initOscillators(){ void initOscillators(){
//initialize the SI5351 //initialize the SI5351
si5351bx_init(); si5351bx_init();
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor si5351_set_calibration(globalSettings.oscillatorCal);
si5351bx_setfreq(0, usbCarrier);
} }

View File

@ -1,6 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <EEPROM.h> #include <EEPROM.h>
#include "morse.h" #include "morse.h"
#include "settings.h"
#include "ubitx.h" #include "ubitx.h"
#include "nano_gui.h" #include "nano_gui.h"
@ -147,40 +148,13 @@ boolean getButton(btn_set_e index, Button* button){
return true; return true;
} }
/*
* This formats the frequency given in f
*/
void formatFreq(long f, char *buff) {
// tks Jack Purdum W8TEE
// replaced fsprint commmands by str commands for code size reduction
memset(buff, 0, 10);
memset(b, 0, sizeof(b));
ultoa(f, b, DEC);
//one mhz digit if less than 10 M, two digits if more
if (f < 10000000l){
buff[0] = ' ';
strncat(buff, b, 4);
strcat(buff, ".");
strncat(buff, &b[4], 2);
}
else {
strncat(buff, b, 5);
strcat(buff, ".");
strncat(buff, &b[5], 2);
}
}
inline void drawCommandbar(char* text){ inline void drawCommandbar(char* text){
displayText(text, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(text, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
} }
/** A generic control to read variable values /** A generic control to read variable values
*/ */
int getValueByKnob(int minimum, int maximum, int step_size, int initial, char* prefix, char *postfix) int getValueByKnob(int minimum, int maximum, int step_size, int initial, const __FlashStringHelper* prefix, const __FlashStringHelper* postfix)
{ {
int knob = 0; int knob = 0;
int knob_value; int knob_value;
@ -191,10 +165,10 @@ int getValueByKnob(int minimum, int maximum, int step_size, int initial, char*
active_delay(200); active_delay(200);
knob_value = initial; knob_value = initial;
strcpy(b, prefix); strcpy_P(b,(const char*)prefix);
itoa(knob_value, c, 10); itoa(knob_value, c, 10);
strcat(b, c); strcat(b, c);
strcat(b, postfix); strcat_P(b, (const char*)postfix);
drawCommandbar(b); drawCommandbar(b);
while(!btnDown() && digitalRead(PTT) == HIGH){ while(!btnDown() && digitalRead(PTT) == HIGH){
@ -205,10 +179,10 @@ int getValueByKnob(int minimum, int maximum, int step_size, int initial, char*
if (knob_value < maximum && knob > 0) if (knob_value < maximum && knob > 0)
knob_value += step_size; knob_value += step_size;
strcpy(b, prefix); strcpy_P(b,(const char*)prefix);
itoa(knob_value, c, 10); itoa(knob_value, c, 10);
strcat(b, c); strcat(b, c);
strcat(b, postfix); strcat_P(b,(const char*)postfix);
drawCommandbar(b); drawCommandbar(b);
} }
checkCAT(); checkCAT();
@ -218,13 +192,13 @@ int getValueByKnob(int minimum, int maximum, int step_size, int initial, char*
return knob_value; return knob_value;
} }
void displayVFO(int vfo){ void displayVFO(Vfo_e vfo){
int x, y; int x, y;
int displayColor, displayBackground, displayBorder; int displayColor, displayBackground, displayBorder;
Button button; Button button;
if (splitOn){ if (globalSettings.splitOn){
if (vfoActive == vfo){ if (vfo == globalSettings.activeVfo){
c[0] = 'R'; c[0] = 'R';
} }
else{ else{
@ -245,32 +219,30 @@ void displayVFO(int vfo){
c[1] = ':'; c[1] = ':';
if (vfo == VFO_A){ if (VFO_A == vfo){
getButton(BUTTON_VFOA, &button); getButton(BUTTON_VFOA, &button);
formatFreq(globalSettings.vfoA.frequency, c+2, sizeof(c)-2);
if (vfoActive == VFO_A){ if (VFO_A == globalSettings.activeVfo){
formatFreq(frequency, c+2);
displayColor = COLOR_ACTIVE_VFO_TEXT; displayColor = COLOR_ACTIVE_VFO_TEXT;
displayBackground = COLOR_ACTIVE_VFO_BACKGROUND; displayBackground = COLOR_ACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_ACTIVE_BORDER; displayBorder = COLOR_ACTIVE_BORDER;
}else{ }else{
formatFreq(vfoA, c+2);
displayColor = COLOR_INACTIVE_VFO_TEXT; displayColor = COLOR_INACTIVE_VFO_TEXT;
displayBackground = COLOR_INACTIVE_VFO_BACKGROUND; displayBackground = COLOR_INACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_INACTIVE_BORDER; displayBorder = COLOR_INACTIVE_BORDER;
} }
} }
if (vfo == VFO_B){ if (VFO_B == vfo){
getButton(BUTTON_VFOB, &button); getButton(BUTTON_VFOB, &button);
formatFreq(globalSettings.vfoB.frequency, c+2, sizeof(c)-2);
if (vfoActive == VFO_B){ if (VFO_B == globalSettings.activeVfo){
formatFreq(frequency, c+2);
displayColor = COLOR_ACTIVE_VFO_TEXT; displayColor = COLOR_ACTIVE_VFO_TEXT;
displayBackground = COLOR_ACTIVE_VFO_BACKGROUND; displayBackground = COLOR_ACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_ACTIVE_BORDER; displayBorder = COLOR_ACTIVE_BORDER;
} else { } else {
formatFreq(vfoB, c+2);
displayColor = COLOR_INACTIVE_VFO_TEXT; displayColor = COLOR_INACTIVE_VFO_TEXT;
displayBackground = COLOR_INACTIVE_VFO_BACKGROUND; displayBackground = COLOR_INACTIVE_VFO_BACKGROUND;
displayBorder = COLOR_INACTIVE_BORDER; displayBorder = COLOR_INACTIVE_BORDER;
@ -301,7 +273,7 @@ void btnDraw(struct Button *button){
} }
case BUTTON_RIT: case BUTTON_RIT:
{ {
if(1 == ritOn){ if(globalSettings.ritOn){
btnDrawActive(button); btnDrawActive(button);
} }
else{ else{
@ -311,7 +283,7 @@ void btnDraw(struct Button *button){
} }
case BUTTON_USB: case BUTTON_USB:
{ {
if(1 == isUSB){ if(VFO_MODE_USB == GetActiveVfoMode()){
btnDrawActive(button); btnDrawActive(button);
} }
else{ else{
@ -321,7 +293,7 @@ void btnDraw(struct Button *button){
} }
case BUTTON_LSB: case BUTTON_LSB:
{ {
if(0 == isUSB){ if(VFO_MODE_LSB == GetActiveVfoMode()){
btnDrawActive(button); btnDrawActive(button);
} }
else{ else{
@ -331,7 +303,7 @@ void btnDraw(struct Button *button){
} }
case BUTTON_SPL: case BUTTON_SPL:
{ {
if(1 == splitOn){ if(globalSettings.splitOn){
btnDrawActive(button); btnDrawActive(button);
} }
else{ else{
@ -341,7 +313,7 @@ void btnDraw(struct Button *button){
} }
case BUTTON_CW: case BUTTON_CW:
{ {
if(1 == cwMode){ if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
btnDrawActive(button); btnDrawActive(button);
} }
else{ else{
@ -361,10 +333,10 @@ void btnDraw(struct Button *button){
void displayRIT(){ void displayRIT(){
c[0] = 0; c[0] = 0;
displayFillrect(LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND); displayFillrect(LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND);
if (ritOn){ if(globalSettings.ritOn){
strcpy_P(c,(const char*)F("TX:")); strcpy_P(c,(const char*)F("TX:"));
formatFreq(ritTxFrequency, c+3); formatFreq(globalSettings.ritFrequency, c+3, sizeof(c)-3);
if (vfoActive == VFO_A) if (VFO_A == globalSettings.activeVfo)
displayText(c, LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
else else
displayText(c, LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(c, LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
@ -372,14 +344,13 @@ void displayRIT(){
} }
void fastTune(){ void fastTune(){
int encoder;
//if the btn is down, wait until it is up //if the btn is down, wait until it is up
while(btnDown()) while(btnDown())
active_delay(50); active_delay(50);
active_delay(300); active_delay(300);
displayText("Fast tune", LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); strcpy_P(c,(const char*)F("Fast tune"));
displayText(c, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
while(1){ while(1){
checkCAT(); checkCAT();
@ -394,15 +365,17 @@ void fastTune(){
return; return;
} }
encoder = enc_read(); int encoder = enc_read();
if (encoder != 0){ if (encoder != 0){
uint32_t freq = GetActiveVfoFreq();
if (encoder > 0 && frequency < 30000000l) if (encoder > 0 && freq < 30000000l){
frequency += 50000l; freq += 50000l;
else if (encoder < 0 && frequency > 600000l) }
frequency -= 50000l; else if (encoder < 0 && freq > 600000l){
setFrequency(frequency); freq -= 50000l;
displayVFO(vfoActive); }
setFrequency(freq);
displayVFO(globalSettings.activeVfo);
} }
}// end of the event loop }// end of the event loop
} }
@ -410,7 +383,6 @@ void fastTune(){
void enterFreq(){ void enterFreq(){
//force the display to refresh everything //force the display to refresh everything
//display all the buttons //display all the buttons
int f;
for (int i = 0; i < KEYS_TOTAL; i++){ for (int i = 0; i < KEYS_TOTAL; i++){
Button button; Button button;
@ -420,7 +392,6 @@ void enterFreq(){
int cursor_pos = 0; int cursor_pos = 0;
memset(c, 0, sizeof(c)); memset(c, 0, sizeof(c));
f = frequency / 1000l;
while(1){ while(1){
@ -444,14 +415,16 @@ void enterFreq(){
switch(button.id){ switch(button.id){
case KEYS_OK: case KEYS_OK:
{ {
long f = atol(c); long freq = atol(c);
if(30000 >= f && f > 100){ if((LOWEST_FREQ/1000 <= freq) && (freq <= HIGHEST_FREQ/1000)){
frequency = f * 1000l; freq *= 1000L;
setFrequency(frequency); setFrequency(freq);
if (vfoActive == VFO_A) if (VFO_A == globalSettings.activeVfo){
vfoA = frequency; globalSettings.vfoA.frequency = freq;
else }
vfoB = frequency; else{
globalSettings.vfoB.frequency = freq;
}
saveVFOs(); saveVFOs();
} }
guiUpdate(); guiUpdate();
@ -499,32 +472,35 @@ void enterFreq(){
}//if button hit test }//if button hit test
}// end of the button scanning loop }// end of the button scanning loop
strcpy(b, c); strcpy(b, c);
strcat(b, " KHz"); strcat_P(b,(const char*)F(" KHz"));
displayText(b, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_MODE_TEXT_X, LAYOUT_MODE_TEXT_Y, LAYOUT_MODE_TEXT_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
delay(300); active_delay(300);
while(readTouch()) while(readTouch())
checkCAT(); checkCAT();
} // end of event loop : while(1) } // end of event loop : while(1)
} }
void drawCWStatus(){ void drawCWStatus(){
strcpy(b, " cw: "); strcpy_P(b,(const char*)F(" cw: "));
int wpm = 1200/cwSpeed; int wpm = 1200/globalSettings.cwDitDurationMs;
itoa(wpm,c, 10); itoa(wpm,c, 10);
strcat(b, c); strcat(b, c);
strcat(b, "wpm, "); strcat_P(b,(const char*)F("wpm, "));
itoa(sideTone, c, 10); itoa(globalSettings.cwSideToneFreq, c, 10);
strcat(b, c); strcat(b, c);
strcat(b, "hz"); strcat_P(b,(const char*)F("hz"));
displayText(b, LAYOUT_CW_TEXT_X, LAYOUT_CW_TEXT_Y, LAYOUT_CW_TEXT_WIDTH, LAYOUT_CW_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND); displayText(b, LAYOUT_CW_TEXT_X, LAYOUT_CW_TEXT_Y, LAYOUT_CW_TEXT_WIDTH, LAYOUT_CW_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND);
} }
void drawTx(){ void drawTx(){
if (inTx) if (globalSettings.txActive){
displayText("TX", LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_ACTIVE_TEXT, COLOR_ACTIVE_BACKGROUND, COLOR_BACKGROUND); strcpy_P(b,(const char*)F("TX"));
else displayText(b, LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_ACTIVE_TEXT, COLOR_ACTIVE_BACKGROUND, COLOR_BACKGROUND);
}
else{
displayFillrect(LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_BACKGROUND); displayFillrect(LAYOUT_TX_X, LAYOUT_TX_Y, LAYOUT_TX_WIDTH, LAYOUT_TX_HEIGHT, COLOR_BACKGROUND);
}
} }
void drawStatusbar(){ void drawStatusbar(){
drawCWStatus(); drawCWStatus();
@ -562,7 +538,7 @@ void guiUpdate(){
// this builds up the top line of the display with frequency and mode // this builds up the top line of the display with frequency and mode
void updateDisplay() { void updateDisplay() {
displayVFO(vfoActive); displayVFO(globalSettings.activeVfo);
} }
@ -708,21 +684,19 @@ int enc_read(void) {
void ritToggle(struct Button *button){ void ritToggle(struct Button *button){
if (ritOn == 0){ if(!globalSettings.ritOn){
ritEnable(frequency); ritEnable(GetActiveVfoFreq());
} }
else else{
ritDisable(); ritDisable();
}
btnDraw(button); btnDraw(button);
displayRIT(); displayRIT();
} }
void splitToggle(Button *button){ void splitToggle(Button *button){
if (splitOn) globalSettings.splitOn = !globalSettings.splitOn;
splitOn = 0;
else
splitOn = 1;
btnDraw(button); btnDraw(button);
@ -740,17 +714,17 @@ void splitToggle(Button *button){
void vfoReset(){ void vfoReset(){
Button button; Button button;
if (vfoActive = VFO_A) if (VFO_A == globalSettings.activeVfo)
vfoB = vfoA; globalSettings.vfoB.frequency = globalSettings.vfoA.frequency;
else else
vfoA = vfoB; globalSettings.vfoA.frequency = globalSettings.vfoB.frequency;
if (splitOn){ if(globalSettings.splitOn){
getButton(BUTTON_SPL, &button); getButton(BUTTON_SPL, &button);
splitToggle(&button); splitToggle(&button);
} }
if (ritOn){ if(globalSettings.ritOn){
getButton(BUTTON_RIT, &button); getButton(BUTTON_RIT, &button);
ritToggle(&button); ritToggle(&button);
} }
@ -762,21 +736,22 @@ void vfoReset(){
} }
void cwToggle(struct Button *b){ void cwToggle(struct Button *b){
if (cwMode == 0){ if (TuningMode_e::TUNE_SSB == globalSettings.tuningMode){
cwMode = 1; globalSettings.tuningMode = TuningMode_e::TUNE_CW;
}
else{
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
} }
else
cwMode = 0;
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
btnDraw(b); btnDraw(b);
} }
void sidebandToggle(Button* button){ void sidebandToggle(Button* button){
if(BUTTON_LSB == button->id) if(BUTTON_LSB == button->id)
isUSB = 0; SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
else else
isUSB = 1; SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
struct Button button2; struct Button button2;
getButton(BUTTON_USB, &button2); getButton(BUTTON_USB, &button2);
@ -805,51 +780,57 @@ void redrawVFOs(){
} }
void switchBand(long bandfreq){ void switchBand(uint32_t bandfreq){
long offset;
// Serial.println(frequency); //Serial.println(frequency);
// Serial.println(bandfreq); //Serial.println(bandfreq);
if (3500000l <= frequency && frequency <= 4000000l) uint32_t offset;
offset = frequency - 3500000l; uint32_t freq = GetActiveVfoFreq();
else if (24800000l <= frequency && frequency <= 25000000l) if (3500000L <= freq && freq <= 4000000L)
offset = frequency - 24800000l; offset = freq - 3500000l;
else if (24800000L <= freq && freq <= 25000000L)
offset = freq - 24800000L;
else else
offset = frequency % 1000000l; offset = freq % 1000000L;
// Serial.println(offset); //Serial.println(offset);
setFrequency(bandfreq + offset); setFrequency(bandfreq + offset);
if(bandfreq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
else{
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
updateDisplay(); updateDisplay();
saveVFOs(); saveVFOs();
} }
int setCwSpeed(){ void setCwSpeed()
int knob = 0; {
int wpm; int wpm = 1200/globalSettings.cwDitDurationMs;
wpm = 1200/cwSpeed; wpm = getValueByKnob(1, 100, 1, wpm,F("CW: "),F(" WPM"));
wpm = getValueByKnob(1, 100, 1, wpm, "CW: ", " WPM"); globalSettings.cwDitDurationMs = 1200/wpm;
SaveSettingsToEeprom();
cwSpeed = 1200/wpm; active_delay(500);
drawStatusbar();
EEPROM.put(CW_SPEED, cwSpeed); //printLine2("");
active_delay(500); //updateDisplay();
drawStatusbar();
// printLine2("");
// updateDisplay();
} }
void setCwTone(){ void setCwTone(){
int knob = 0; int knob = 0;
int prev_sideTone; int prev_sideTone;
tone(CW_TONE, sideTone); tone(CW_TONE, globalSettings.cwSideToneFreq);
itoa(sideTone, c, 10); itoa(globalSettings.cwSideToneFreq, c, 10);
strcpy(b, "CW Tone: "); strcpy_P(b,(const char*)F("CW Tone: "));
strcat(b, c); strcat(b, c);
strcat(b, " Hz"); strcat_P(b,(const char*)F(" Hz"));
drawCommandbar(b); drawCommandbar(b);
//disable all clock 1 and clock 2 //disable all clock 1 and clock 2
@ -857,18 +838,18 @@ void setCwTone(){
{ {
knob = enc_read(); knob = enc_read();
if (knob > 0 && sideTone < 2000) if (knob > 0 && globalSettings.cwSideToneFreq < 2000)
sideTone += 10; globalSettings.cwSideToneFreq += 10;
else if (knob < 0 && sideTone > 100 ) else if (knob < 0 && globalSettings.cwSideToneFreq > 100 )
sideTone -= 10; globalSettings.cwSideToneFreq -= 10;
else else
continue; //don't update the frequency or the display continue; //don't update the frequency or the display
tone(CW_TONE, sideTone); tone(CW_TONE, globalSettings.cwSideToneFreq);
itoa(sideTone, c, 10); itoa(globalSettings.cwSideToneFreq, c, 10);
strcpy(b, "CW Tone: "); strcpy_P(b,(const char*)F("CW Tone: "));
strcat(b, c); strcat(b, c);
strcat(b, " Hz"); strcat_P(b,(const char*)F(" Hz"));
drawCommandbar(b); drawCommandbar(b);
//printLine2(b); //printLine2(b);
@ -876,14 +857,14 @@ void setCwTone(){
active_delay(20); active_delay(20);
} }
noTone(CW_TONE); noTone(CW_TONE);
//save the setting
EEPROM.put(CW_SIDETONE, sideTone); SaveSettingsToEeprom();
b[0] = 0; b[0] = 0;
drawCommandbar(b); drawCommandbar(b);
drawStatusbar(); drawStatusbar();
// printLine2(""); //printLine2("");
// updateDisplay(); //updateDisplay();
} }
void doCommand(Button* button){ void doCommand(Button* button){
@ -918,7 +899,7 @@ void doCommand(Button* button){
} }
case BUTTON_VFOA: case BUTTON_VFOA:
{ {
if(VFO_A == vfoActive){ if(VFO_A == globalSettings.activeVfo){
fastTune(); fastTune();
} }
else{ else{
@ -928,7 +909,7 @@ void doCommand(Button* button){
} }
case BUTTON_VFOB: case BUTTON_VFOB:
{ {
if(VFO_B == vfoActive){ if(VFO_B == globalSettings.activeVfo){
fastTune(); fastTune();
} }
else{ else{
@ -1057,7 +1038,7 @@ void doCommands(){
//unfocus the buttons //unfocus the buttons
drawFocus(select, COLOR_INACTIVE_BORDER); drawFocus(select, COLOR_INACTIVE_BORDER);
if (vfoActive == VFO_A) if (VFO_A == globalSettings.activeVfo)
drawFocus(BUTTON_VFOA, COLOR_ACTIVE_BORDER); drawFocus(BUTTON_VFOA, COLOR_ACTIVE_BORDER);
else else
drawFocus(BUTTON_VFOB, COLOR_ACTIVE_BORDER); drawFocus(BUTTON_VFOB, COLOR_ACTIVE_BORDER);

View File

@ -30,21 +30,10 @@
* Si5351 object to control the clocks. * Si5351 object to control the clocks.
*/ */
#include <Wire.h> #include <Wire.h>
#include <EEPROM.h> #include "settings.h"
#include "ubitx.h" #include "ubitx.h"
#include "nano_gui.h" #include "nano_gui.h"
/**
The main chip which generates upto three oscillators of various frequencies in the
Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
We no longer use the standard SI5351 library because of its huge overhead due to many unused
features consuming a lot of program space. Instead of depending on an external library we now use
Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the
code). Here are some defines and declarations used by Jerry's routines:
*/
/** /**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
* We have to be very careful with variables that are declared inside the functions as they are * We have to be very careful with variables that are declared inside the functions as they are
@ -58,102 +47,10 @@
*/ */
char c[30], b[30]; char c[30], b[30];
/**
* These are the indices where these user changable settinngs are stored in the EEPROM
*/
#define MASTER_CAL 0
#define LSB_CAL 4
#define USB_CAL 8
#define SIDE_TONE 12
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
#define VFO_A 16
#define VFO_B 20
#define CW_SIDETONE 24
#define CW_SPEED 28
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
#define SLOPE_X 32
#define SLOPE_Y 36
#define OFFSET_X 40
#define OFFSET_Y 44
#define CW_DELAYTIME 48
//These are defines for the new features back-ported from KD8CEC's software
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
#define VFO_A_MODE 256 // 2: LSB, 3: USB
#define VFO_B_MODE 257
//values that are stroed for the VFO modes
#define VFO_MODE_LSB 2
#define VFO_MODE_USB 3
// handkey, iambic a, iambic b : 0,1,2f
#define CW_KEY_TYPE 358
/**
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
* L-network used on it's either sides.
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
* and USB becomes LSB.
* The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at
* 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives
* at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
* thus keeping the signal's sidebands inverted. The USB will become LSB.
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
* 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
*/
#define INIT_USB_FREQ (11059200l)
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ (100000l)
#define HIGHEST_FREQ (30000000l)
//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes
//these are the parameter passed to startTx
#define TX_SSB 0
#define TX_CW 1
char ritOn = 0;
char vfoActive = VFO_A;
int8_t meter_reading = 0; // a -1 on meter makes it invisible
unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier;
char isUsbVfoA=0, isUsbVfoB=1;
unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
unsigned long firstIF = 45005000L;
// if cwMode is flipped on, the rx frequency is tuned down by sidetone hz instead of being zerobeat
int cwMode = 0;
//these are variables that control the keyer behaviour
int cwSpeed = 100; //this is actuall the dot period in milliseconds
extern int32_t calibration;
int cwDelayTime = 60;
bool Iambic_Key = true;
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
unsigned char keyerControl = IAMBICB;
//during CAT commands, we will freeeze the display until CAT is disengaged //during CAT commands, we will freeeze the display until CAT is disengaged
unsigned char doingCAT = 0; unsigned char doingCAT = 0;
/**
* Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
*/
boolean txCAT = false; //turned on if the transmitting due to a CAT command
char inTx = 0; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
int splitOn = 0; //working split, uses VFO B as the transmit frequency
char keyDown = 0; //in cw mode, denotes the carrier is being transmitted
char isUSB = 0; //upper sideband was selected, this is reset to the default for the
//frequency when it crosses the frequency border of 10 MHz
byte menuOn = 0; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited byte menuOn = 0; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
unsigned long cwTimeout = 0; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
unsigned long dbgCount = 0; //not used now
unsigned char txFilter = 0; //which of the four transmit filters are in use
boolean modeCalibrate = false;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
//beat frequency
/** /**
* Below are the basic functions that control the uBitx. Understanding the functions before * Below are the basic functions that control the uBitx. Understanding the functions before
@ -173,27 +70,9 @@ void active_delay(int delay_by){
} }
} }
void saveVFOs(){ void saveVFOs()
{
if (vfoActive == VFO_A) SaveSettingsToEeprom();
EEPROM.put(VFO_A, frequency);
else
EEPROM.put(VFO_A, vfoA);
if (isUsbVfoA)
EEPROM.put(VFO_A_MODE, VFO_MODE_USB);
else
EEPROM.put(VFO_A_MODE, VFO_MODE_LSB);
if (vfoActive == VFO_B)
EEPROM.put(VFO_B, frequency);
else
EEPROM.put(VFO_B, vfoB);
if (isUsbVfoB)
EEPROM.put(VFO_B_MODE, VFO_MODE_USB);
else
EEPROM.put(VFO_B_MODE, VFO_MODE_LSB);
} }
/** /**
@ -275,38 +154,31 @@ void setTXFilters_v5(unsigned long freq){
* through mixing of the second local oscillator. * through mixing of the second local oscillator.
*/ */
void setFrequency(unsigned long f){ void setFrequency(unsigned long freq){
uint64_t osc_f, firstOscillator, secondOscillator; static const unsigned long firstIF = 45005000L;
setTXFilters(f); setTXFilters(freq);
/* uint32_t local_osc_freq;
if (isUSB){ if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
si5351bx_setfreq(2, firstIF + f); local_osc_freq = firstIF + freq + globalSettings.cwSideToneFreq;
si5351bx_setfreq(1, firstIF + usbCarrier);
} }
else{ else{
si5351bx_setfreq(2, firstIF + f); local_osc_freq = firstIF + freq;
si5351bx_setfreq(1, firstIF - usbCarrier);
}
*/
//alternative to reduce the intermod spur
if (isUSB){
if (cwMode)
si5351bx_setfreq(2, firstIF + f + sideTone);
else
si5351bx_setfreq(2, firstIF + f);
si5351bx_setfreq(1, firstIF + usbCarrier);
}
else{
if (cwMode)
si5351bx_setfreq(2, firstIF + f + sideTone);
else
si5351bx_setfreq(2, firstIF + f);
si5351bx_setfreq(1, firstIF - usbCarrier);
} }
frequency = f; uint32_t ssb_osc_freq;
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
ssb_osc_freq = firstIF + globalSettings.usbCarrierFreq;
}
else{
ssb_osc_freq = firstIF - globalSettings.usbCarrierFreq;
}
si5351bx_setfreq(2, local_osc_freq);
si5351bx_setfreq(1, ssb_osc_freq);
SetActiveVfoFreq(freq);
} }
/** /**
@ -316,81 +188,72 @@ void setFrequency(unsigned long f){
* CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode * CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode
*/ */
void startTx(byte txMode){ void startTx(TuningMode_e tx_mode){
unsigned long tx_freq = 0; globalSettings.tuningMode = tx_mode;
digitalWrite(TX_RX, 1); if (globalSettings.ritOn){
inTx = 1;
if (ritOn){
//save the current as the rx frequency //save the current as the rx frequency
ritRxFrequency = frequency; uint32_t rit_tx_freq = globalSettings.ritFrequency;
setFrequency(ritTxFrequency); globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_tx_freq);
} }
else else{
{ if(globalSettings.splitOn){
if (splitOn == 1) { if(Vfo_e::VFO_B == globalSettings.activeVfo){
if (vfoActive == VFO_B) { globalSettings.activeVfo = Vfo_e::VFO_A;
vfoActive = VFO_A;
isUSB = isUsbVfoA;
frequency = vfoA;
} }
else if (vfoActive == VFO_A){ else{
vfoActive = VFO_B; globalSettings.activeVfo = Vfo_e::VFO_B;
frequency = vfoB;
isUSB = isUsbVfoB;
} }
} }
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
} }
if (txMode == TX_CW){ if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
digitalWrite(TX_RX, 0);
//turn off the second local oscillator and the bfo //turn off the second local oscillator and the bfo
si5351bx_setfreq(0, 0); si5351bx_setfreq(0, 0);
si5351bx_setfreq(1, 0); si5351bx_setfreq(1, 0);
//shif the first oscillator to the tx frequency directly //shift the first oscillator to the tx frequency directly
//the key up and key down will toggle the carrier unbalancing //the key up and key down will toggle the carrier unbalancing
//the exact cw frequency is the tuned frequency + sidetone //the exact cw frequency is the tuned frequency + sidetone
if (isUSB) if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
si5351bx_setfreq(2, frequency + sideTone); si5351bx_setfreq(2, GetActiveVfoFreq() + globalSettings.cwSideToneFreq);
else }
si5351bx_setfreq(2, frequency - sideTone); else{
si5351bx_setfreq(2, GetActiveVfoFreq() - globalSettings.cwSideToneFreq);
}
delay(20); delay(20);
digitalWrite(TX_RX, 1);
} }
digitalWrite(TX_RX, 1);//turn on the tx
globalSettings.txActive = true;
drawTx(); drawTx();
//updateDisplay();
} }
void stopTx(){ void stopTx(){
inTx = 0; digitalWrite(TX_RX, 0);//turn off the tx
globalSettings.txActive = false;
digitalWrite(TX_RX, 0); //turn off the tx //set back the carrier oscillator - cw tx switches it off
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
if (ritOn) if(globalSettings.ritOn){
setFrequency(ritRxFrequency); uint32_t rit_rx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_rx_freq);
}
else{ else{
if (splitOn == 1) { if(globalSettings.splitOn){
//vfo Change if(Vfo_e::VFO_B == globalSettings.activeVfo){
if (vfoActive == VFO_B){ globalSettings.activeVfo = Vfo_e::VFO_A;
vfoActive = VFO_A;
frequency = vfoA;
isUSB = isUsbVfoA;
} }
else if (vfoActive == VFO_A){ else{
vfoActive = VFO_B; globalSettings.activeVfo = Vfo_e::VFO_B;
frequency = vfoB;
isUSB = isUsbVfoB;
} }
} }
setFrequency(frequency); setFrequency(GetActiveVfoFreq());
} }
//updateDisplay();
drawTx(); drawTx();
} }
@ -398,18 +261,18 @@ void stopTx(){
* ritEnable is called with a frequency parameter that determines * ritEnable is called with a frequency parameter that determines
* what the tx frequency will be * what the tx frequency will be
*/ */
void ritEnable(unsigned long f){ void ritEnable(unsigned long freq){
ritOn = 1; globalSettings.ritOn = true;
//save the non-rit frequency back into the VFO memory //save the non-rit frequency back into the VFO memory
//as RIT is a temporary shift, this is not saved to EEPROM //as RIT is a temporary shift, this is not saved to EEPROM
ritTxFrequency = f; globalSettings.ritFrequency = freq;
} }
// this is called by the RIT menu routine // this is called by the RIT menu routine
void ritDisable(){ void ritDisable(){
if (ritOn){ if(globalSettings.ritOn){
ritOn = 0; globalSettings.ritOn = false;
setFrequency(ritTxFrequency); setFrequency(globalSettings.ritFrequency);
updateDisplay(); updateDisplay();
} }
} }
@ -426,15 +289,16 @@ void ritDisable(){
void checkPTT(){ void checkPTT(){
//we don't check for ptt when transmitting cw //we don't check for ptt when transmitting cw
if (cwTimeout > 0) if (globalSettings.cwExpirationTimeMs > 0){
return; return;
}
if (digitalRead(PTT) == 0 && inTx == 0){ if(digitalRead(PTT) == 0 && !globalSettings.txActive){
startTx(TX_SSB); startTx(TuningMode_e::TUNE_SSB);
active_delay(50); //debounce the PTT active_delay(50); //debounce the PTT
} }
if (digitalRead(PTT) == 1 && inTx == 1) if (digitalRead(PTT) == 1 && globalSettings.txActive)
stopTx(); stopTx();
} }
@ -471,41 +335,13 @@ void checkButton(){
active_delay(50);//debounce active_delay(50);//debounce
} }
void switchVFO(int vfoSelect){ void switchVFO(Vfo_e new_vfo){
if (vfoSelect == VFO_A){ ritDisable();//If we are in RIT mode, we need to disable it before setting the active VFO so that the correct VFO gets it's frequency restored
if (vfoActive == VFO_B){
vfoB = frequency;
isUsbVfoB = isUSB;
EEPROM.put(VFO_B, frequency);
if (isUsbVfoB)
EEPROM.put(VFO_B_MODE, VFO_MODE_USB);
else
EEPROM.put(VFO_B_MODE, VFO_MODE_LSB);
}
vfoActive = VFO_A;
// printLine2("Selected VFO A ");
frequency = vfoA;
isUSB = isUsbVfoA;
}
else {
if (vfoActive == VFO_A){
vfoA = frequency;
isUsbVfoA = isUSB;
EEPROM.put(VFO_A, frequency);
if (isUsbVfoA)
EEPROM.put(VFO_A_MODE, VFO_MODE_USB);
else
EEPROM.put(VFO_A_MODE, VFO_MODE_LSB);
}
vfoActive = VFO_B;
// printLine2("Selected VFO B ");
frequency = vfoB;
isUSB = isUsbVfoB;
}
setFrequency(frequency); globalSettings.activeVfo = new_vfo;
redrawVFOs(); setFrequency(GetActiveVfoFreq());
saveVFOs(); redrawVFOs();
saveVFOs();
} }
/** /**
@ -516,50 +352,48 @@ void switchVFO(int vfoSelect){
*/ */
void doTuning(){ void doTuning(){
int s;
static unsigned long prev_freq; static unsigned long prev_freq;
static unsigned long nextFrequencyUpdate = 0; static unsigned long nextFrequencyUpdate = 0;
unsigned long now = millis(); unsigned long now = millis();
if (now >= nextFrequencyUpdate && prev_freq != frequency){ if (now >= nextFrequencyUpdate && prev_freq != GetActiveVfoFreq()){
updateDisplay(); updateDisplay();
nextFrequencyUpdate = now + 100; nextFrequencyUpdate = now + 100;
prev_freq = frequency; prev_freq = GetActiveVfoFreq();
} }
s = enc_read(); int s = enc_read();
if (!s) if (!s)
return; return;
//Serial.println(s); //Serial.println(s);
doingCAT = 0; // go back to manual mode if you were doing CAT doingCAT = 0; // go back to manual mode if you were doing CAT
prev_freq = frequency; prev_freq = GetActiveVfoFreq();
uint32_t new_freq = prev_freq;
// TODO With the new more responsive tuning, 5 and 10 are nearly useless if (s > 10 || s < -10){
// thresholds for acceleration. I rarely see above 2 or 3 even when turning new_freq += 200L * s;
// the knob quickly.
if (s < 5 && s > -5) {
frequency += 50l * s;
//Serial.println(" 5");
} }
else if (s < 10 && s > -10) { else if (s > 5 || s < -5){
frequency += 100l * s; new_freq += 100L * s;
//Serial.println(" 10");
} }
else { // if (s >= 10 || s <= -10) else{
frequency += 200l * s; new_freq += 50L * s;
//Serial.println(" <");
} }
if (prev_freq < 10000000l && frequency > 10000000l) //Transition from below to above the traditional threshold for USB
isUSB = true; if(prev_freq < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
if (prev_freq > 10000000l && frequency < 10000000l) //Transition from aboveo to below the traditional threshold for USB
isUSB = false; if(prev_freq >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
setFrequency(frequency); setFrequency(new_freq);
} }
@ -567,18 +401,17 @@ void doTuning(){
* RIT only steps back and forth by 100 hz at a time * RIT only steps back and forth by 100 hz at a time
*/ */
void doRIT(){ void doRIT(){
unsigned long newFreq;
int knob = enc_read(); int knob = enc_read();
unsigned long old_freq = frequency; uint32_t old_freq = GetActiveVfoFreq();
uint32_t new_freq = old_freq;
if (knob < 0) if (knob < 0)
frequency -= 100l; new_freq -= 100l;
else if (knob > 0) else if (knob > 0)
frequency += 100; new_freq += 100;
if (old_freq != frequency){ if (old_freq != new_freq){
setFrequency(frequency); setFrequency(new_freq);
updateDisplay(); updateDisplay();
} }
} }
@ -589,87 +422,8 @@ void doRIT(){
* variables. * variables.
*/ */
void initSettings(){ void initSettings(){
byte x; LoadDefaultSettings();
//read the settings from the eeprom and restore them LoadSettingsFromEeprom();
//if the readings are off, then set defaults
EEPROM.get(MASTER_CAL, calibration);
EEPROM.get(USB_CAL, usbCarrier);
EEPROM.get(VFO_A, vfoA);
EEPROM.get(VFO_B, vfoB);
EEPROM.get(CW_SIDETONE, sideTone);
EEPROM.get(CW_SPEED, cwSpeed);
EEPROM.get(CW_DELAYTIME, cwDelayTime);
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
if (usbCarrier > 11060000l || usbCarrier < 11048000l)
usbCarrier = 11052000l;
if (vfoA > 35000000l || 3500000l > vfoA)
vfoA = 7150000l;
if (vfoB > 35000000l || 3500000l > vfoB)
vfoB = 14150000l;
if (sideTone < 100 || 2000 < sideTone)
sideTone = 800;
if (cwSpeed < 10 || 1000 < cwSpeed)
cwSpeed = 100;
if (cwDelayTime < 10 || cwDelayTime > 100)
cwDelayTime = 50;
/*
* The VFO modes are read in as either 2 (USB) or 3(LSB), 0, the default
* is taken as 'uninitialized
*/
EEPROM.get(VFO_A_MODE, x);
switch(x){
case VFO_MODE_USB:
isUsbVfoA = 1;
break;
case VFO_MODE_LSB:
isUsbVfoA = 0;
break;
default:
if (vfoA > 10000000l)
isUsbVfoA = 1;
else
isUsbVfoA = 0;
}
EEPROM.get(VFO_B_MODE, x);
switch(x){
case VFO_MODE_USB:
isUsbVfoB = 1;
break;
case VFO_MODE_LSB:
isUsbVfoB = 0;
break;
default:
if (vfoA > 10000000l)
isUsbVfoB = 1;
else
isUsbVfoB = 0;
}
//set the current mode
isUSB = isUsbVfoA;
/*
* The keyer type splits into two variables
*/
EEPROM.get(CW_KEY_TYPE, x);
if (x == 0)
Iambic_Key = false;
else if (x == 1){
Iambic_Key = true;
keyerControl &= ~IAMBICB;
}
else if (x == 2){
Iambic_Key = true;
keyerControl |= IAMBICB;
}
} }
void initPorts(){ void initPorts(){
@ -711,24 +465,24 @@ void setup()
Serial.begin(38400); Serial.begin(38400);
Serial.flush(); Serial.flush();
displayInit();
initSettings(); initSettings();
displayInit();
initPorts(); initPorts();
initOscillators(); initOscillators();
frequency = vfoA; setFrequency(globalSettings.vfoA.frequency);
setFrequency(vfoA);
if (btnDown()){ //Run initial calibration routine if button is pressed during power up
if(btnDown()){
setupTouch(); setupTouch();
isUSB = 1; SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
setFrequency(10000000l); setFrequency(10000000l);
setupFreq(); setupFreq();
isUSB = 0; SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
setFrequency(7100000l); setFrequency(7100000l);
setupBFO(); setupBFO();
} }
guiUpdate();
guiUpdate();
} }
@ -736,23 +490,23 @@ void setup()
* The loop checks for keydown, ptt, function button and tuning. * The loop checks for keydown, ptt, function button and tuning.
*/ */
byte flasher = 0;
boolean wastouched = false;
void loop(){ void loop(){
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
if (cwMode)
cwKeyer(); cwKeyer();
else if (!txCAT) }
else if(!globalSettings.txCatActive){
checkPTT(); checkPTT();
}
checkButton(); checkButton();
//tune only when not tranmsitting //tune only when not tranmsitting
if (!inTx){ if(!globalSettings.txActive){
if (ritOn) if(globalSettings.ritOn){
doRIT(); doRIT();
else }
else{
doTuning(); doTuning();
}
checkTouch(); checkTouch();
} }