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

164
keyer.cpp
View File

@ -1,4 +1,5 @@
#include <Arduino.h>
#include "settings.h"
#include "ubitx.h"
/**
@ -28,72 +29,27 @@
*/
//CW ADC Range
int cwAdcSTFrom = 0;
int cwAdcSTTo = 50;
int cwAdcBothFrom = 51;
int cwAdcBothTo = 300;
int cwAdcDotFrom = 301;
int cwAdcDotTo = 600;
int cwAdcDashFrom = 601;
int cwAdcDashTo = 800;
//byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
static const unsigned int cwAdcSTFrom = 0;
static const unsigned int cwAdcSTTo = 50;
static const unsigned int cwAdcBothFrom = cwAdcSTTo + 1;
static const unsigned int cwAdcBothTo = 300;
static const unsigned int cwAdcDotFrom = cwAdcBothTo + 1;
static const unsigned int cwAdcDotTo = 600;
static const unsigned int cwAdcDashFrom = cwAdcDotTo + 1;
static const unsigned int cwAdcDashTo = 800;
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
* It assumes that we have called cwTxStart and not called cwTxStop
* each time it is called, the cwTimeOut is pushed further into the future
*/
void cwKeydown(){
tone(CW_TONE, globalSettings.cwSideToneFreq);
digitalWrite(CW_KEY, 1);
keyDown = 1; //tracks the CW_KEY
tone(CW_TONE, (int)sideTone);
digitalWrite(CW_KEY, 1);
//Modified by KD8CEC, for CW Delay Time save to eeprom
//cwTimeout = millis() + CW_TIMEOUT;
cwTimeout = millis() + cwDelayTime * 10;
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
}
/**
@ -101,13 +57,10 @@ void cwKeydown(){
* Pushes the cwTimeout further into the future
*/
void cwKeyUp(){
keyDown = 0; //tracks the CW_KEY
noTone(CW_TONE);
digitalWrite(CW_KEY, 0);
digitalWrite(CW_KEY, 0);
//Modified by KD8CEC, for CW Delay Time save to eeprom
//cwTimeout = millis() + CW_TIMEOUT;
cwTimeout = millis() + cwDelayTime * 10;
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
}
//Variables for Ron's new logic
@ -119,16 +72,14 @@ void cwKeyUp(){
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
static unsigned long ktimer;
unsigned char keyerState = IDLE;
uint8_t keyerControl = 0;
//Below is a test to reduce the keying error. do not delete lines
//create by KD8CEC for compatible with new CW Logic
char update_PaddleLatch(byte isUpdateKeyState) {
char update_PaddleLatch(bool isUpdateKeyState) {
unsigned char tmpKeyerControl = 0;
int paddle = analogRead(ANALOG_KEYER);
//diagnostic, VU2ESE
//itoa(paddle, b, 10);
//printLine2(b);
//use the PTT as the key for tune up, quick QSOs
if (digitalRead(PTT) == 0)
@ -138,18 +89,17 @@ char update_PaddleLatch(byte isUpdateKeyState) {
else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo)
tmpKeyerControl |= DIT_L;
else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo)
tmpKeyerControl |= (DAH_L | DIT_L) ;
else
{
if (Iambic_Key)
tmpKeyerControl |= (DAH_L | DIT_L) ;
else{
if (KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode)
tmpKeyerControl = 0 ;
else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo)
tmpKeyerControl = DIT_L ;
else
tmpKeyerControl = 0 ;
tmpKeyerControl = 0 ;
}
if (isUpdateKeyState == 1)
if (isUpdateKeyState)
keyerControl |= tmpKeyerControl;
return tmpKeyerControl;
@ -160,22 +110,24 @@ char update_PaddleLatch(byte isUpdateKeyState) {
// modified by KD8CEC
******************************************************************************/
void cwKeyer(void){
lastPaddle = 0;
bool continue_loop = true;
unsigned tmpKeyControl = 0;
if( Iambic_Key ) {
while(continue_loop) {
switch (keyerState) {
if(KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode){
while(continue_loop){
switch(keyerState){
case IDLE:
tmpKeyControl = update_PaddleLatch(0);
if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L ||
tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) {
update_PaddleLatch(1);
if((tmpKeyControl == DAH_L)//Currently dah
||(tmpKeyControl == DIT_L)//Currently dit
||(tmpKeyControl == (DAH_L | DIT_L))//Currently both
||( keyerControl & (DAH_L | DIT_L))){//Resolving either
update_PaddleLatch(true);
keyerState = CHK_DIT;
}else{
if (0 < cwTimeout && cwTimeout < millis()){
cwTimeout = 0;
}
else{
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
globalSettings.cwExpirationTimeMs = 0;
stopTx();
}
continue_loop = false;
@ -185,7 +137,7 @@ void cwKeyer(void){
case CHK_DIT:
if (keyerControl & DIT_L) {
keyerControl |= DIT_PROC;
ktimer = cwSpeed;
ktimer = globalSettings.cwDitDurationMs;
keyerState = KEYED_PREP;
}else{
keyerState = CHK_DAH;
@ -194,7 +146,7 @@ void cwKeyer(void){
case CHK_DAH:
if (keyerControl & DAH_L) {
ktimer = cwSpeed*3;
ktimer = 3*globalSettings.cwDitDurationMs;
keyerState = KEYED_PREP;
}else{
keyerState = IDLE;
@ -203,13 +155,11 @@ void cwKeyer(void){
case KEYED_PREP:
//modified KD8CEC
if (!inTx){
if (!globalSettings.txActive){
//DelayTime Option
active_delay(delayBeforeCWStartTime * 2);
keyDown = 0;
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
startTx(TX_CW);
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
startTx(TuningMode_e::TUNE_CW);
}
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
@ -220,10 +170,11 @@ void cwKeyer(void){
case KEYED:
if (millis() > ktimer) { // are we at end of key down ?
cwKeyUp();
ktimer = millis() + cwSpeed; // inter-element time
cwKeyUp();
ktimer = millis() + (globalSettings.cwDitDurationMs / 10); // inter-element time
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
}
break;
@ -246,20 +197,19 @@ void cwKeyer(void){
checkCAT();
} //end of while
}
else{
else{//KEYER_STRAIGHT
while(1){
char state = update_PaddleLatch(0);
// Serial.println((int)state);
// Serial.println((int)state);
if (state == DIT_L) {
// if we are here, it is only because the key is pressed
if (!inTx){
startTx(TX_CW);
if (!globalSettings.txActive){
startTx(TuningMode_e::TUNE_CW);
//DelayTime Option
active_delay(delayBeforeCWStartTime * 2);
keyDown = 0;
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwDitDurationMs;
}
cwKeydown();
@ -269,24 +219,16 @@ void cwKeyer(void){
cwKeyUp();
}
else{
if (0 < cwTimeout && cwTimeout < millis()){
cwTimeout = 0;
keyDown = 0;
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
globalSettings.cwExpirationTimeMs = 0;
stopTx();
}
//if (!cwTimeout) //removed by KD8CEC
// 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
return;//Tx stop control by Main Loop
}
checkCAT();
} //end of while
} //end of elese
}//end of else KEYER_STRAIGHT
}

View File

@ -1,19 +1,19 @@
#include <Arduino.h>
#include "ubitx.h"
#include "settings.h"
#include "morse.h"
struct Morse {
char letter;
unsigned char code;
};
/*
* Each byte of the morse table stores one letter.
* The 0 is a dot, a 1 is a dash
* 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
*/
extern int cwSpeed;
struct Morse {
char letter;
unsigned char code;
};
static const PROGMEM struct Morse morse_table[] = {
{'a', 0xf9}, // 11111001
{'b', 0xe8}, // 11101000
@ -51,9 +51,9 @@ static const PROGMEM struct Morse morse_table[] = {
{'8', 0xdc}, // 11011100
{'9', 0xde}, // 11011110
{'0', 0xdf}, // 11011111
{'.', 0xd5}, // 110010101
{',', 0xd3}, // 110110011 //AD7U 20191217
{'?', 0xcc}, // 11001100 //AD7U 20191217 - Added
{'.', 0x95}, // 10010101
{',', 0xb3}, // 10110011
{'?', 0x8c}, // 10001100
};
static void morseLetter(char c){
@ -61,8 +61,8 @@ static void morseLetter(char c){
//handle space character as three dashes
if (c == ' '){
active_delay(cwSpeed * 9);
Serial.print(' ');
active_delay(9 * globalSettings.cwDitDurationMs);
//Serial.print(' ');
return;
}
@ -79,22 +79,23 @@ static void morseLetter(char c){
//now we are at the first zero, skip and carry on
mask = mask >> 1;
while(mask){
tone(CW_TONE, sideTone,10000);
tone(CW_TONE, globalSettings.cwSideToneFreq,10000);
if (mask & code){
delay(3 * (int)cwSpeed);
delay(3 * globalSettings.cwDitDurationMs);
//Serial.print('-');
}
else{
delay((int)cwSpeed);
delay(globalSettings.cwDitDurationMs);
//Serial.print('.');
}
//Serial.print('#');
noTone(CW_TONE);
delay((int)cwSpeed); // space between dots and dashes
delay(globalSettings.cwDitDurationMs); // space between dots and dashes
mask = mask >> 1;
}
//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);
// }
Serial.println(sideTone);
//Serial.println(globalSettings.cwSideToneFreq);
while(*text){
morseLetter(*text++);
}

View File

@ -1,5 +1,5 @@
#include <Arduino.h>
#include <EEPROM.h>
#include "settings.h"
#include "ubitx.h"
#include "nano_gui.h"
@ -8,30 +8,45 @@
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(){
EEPROM.get(SLOPE_X, slope_x);
EEPROM.get(SLOPE_Y, slope_y);
EEPROM.get(OFFSET_X, offset_x);
EEPROM.get(OFFSET_Y, offset_y);
/*
//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(' ');
*/
LoadSettingsFromEeprom();
/* for debugging
Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
//*/
}
void writeTouchCalibration(){
EEPROM.put(SLOPE_X, slope_x);
EEPROM.put(SLOPE_Y, slope_y);
EEPROM.put(OFFSET_X, offset_x);
EEPROM.put(OFFSET_Y, offset_y);
SaveSettingsToEeprom();
}
#define Z_THRESHOLD 400
@ -118,7 +133,7 @@ static void touch_update(){
}
boolean readTouch(){
bool readTouch(){
touch_update();
if (zraw >= Z_THRESHOLD) {
ts_point.x = xraw;
@ -130,8 +145,8 @@ boolean readTouch(){
}
void scaleTouch(struct Point *p){
p->x = ((long)(p->x - offset_x) * 10l)/ (long)slope_x;
p->y = ((long)(p->y - offset_y) * 10l)/ (long)slope_y;
p->x = ((long)(p->x - globalSettings.touchOffsetX) * 10L)/ (long)globalSettings.touchSlopeX;
p->y = ((long)(p->y - globalSettings.touchOffsetY) * 10L)/ (long)globalSettings.touchSlopeY;
//Serial.print(p->x); Serial.print(",");Serial.println(p->y);
}
@ -169,7 +184,6 @@ void displayInit(void){
tft.setRotation(1);
xpt2046_Init();
readTouchCalibration();
}
void displayPixel(unsigned int x, unsigned int y, unsigned int c){
@ -184,7 +198,7 @@ void displayVline(unsigned int x, unsigned int y, unsigned int l, unsigned int c
tft.drawFastVLine(x,y,l,c);
}
void displayClear(unsigned int color){
void displayClear(unsigned int color){
tft.fillRect(0,0,320,240,color);
}
@ -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
//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
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
//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
offset_x = x1 + -((20 * slope_x)/10);
offset_y = y1 + -((20 * slope_y)/10);
globalSettings.touchOffsetX = x1 + -((20 * globalSettings.touchSlopeX)/10);
globalSettings.touchOffsetY = y1 + -((20 * globalSettings.touchSlopeY)/10);
/*
Serial.print(x1);Serial.print(':');Serial.println(y1);
@ -307,10 +321,10 @@ void setupTouch(){
Serial.print(x4);Serial.print(':');Serial.println(y4);
//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(' ');
Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
*/
writeTouchCalibration();
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 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 */
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);

172
setup.cpp
View File

@ -1,6 +1,7 @@
#include <Arduino.h>
#include <EEPROM.h>
#include "morse.h"
#include "settings.h"
#include "ubitx.h"
#include "nano_gui.h"
@ -22,7 +23,6 @@ void setupExit(){
}
//this is used by the si5351 routines in the ubitx_5351 file
extern int32_t calibration;
extern uint32_t si5351bx_vcoa;
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);
}
void printCarrierFreq(unsigned long freq){
memset(c, 0, 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);
void printCarrierFreq(unsigned long freq)
{
formatFreq(freq,c,sizeof(c));
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(){
int knob = 0;
int32_t prev_calibration;
displayDialog(F("Set Frequency"),F("Push TUNE to Save"));
//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"));
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"));
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"));
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"));
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);
//keep clear of any previous button press
while (btnDown())
active_delay(100);
active_delay(100);
prev_calibration = calibration;
calibration = 0;
while (!btnDown())
{
knob = enc_read();
if (knob != 0)
calibration += knob * 875;
/* else if (knob < 0)
calibration -= 875; */
else
int knob = enc_read();
if(knob != 0){
globalSettings.oscillatorCal += knob * 875;
}
else{
continue; //don't update the frequency or the display
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off
si5351_set_calibration(calibration);
setFrequency(frequency);
}
si5351bx_setfreq(0, globalSettings.usbCarrierFreq); //set back the carrier oscillator anyway, cw tx switches it off
si5351_set_calibration(globalSettings.oscillatorCal);
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);
}
EEPROM.put(MASTER_CAL, calibration);
SaveSettingsToEeprom();
initOscillators();
si5351_set_calibration(calibration);
setFrequency(frequency);
si5351_set_calibration(globalSettings.oscillatorCal);
setFrequency(GetActiveVfoFreq());
//debounce and delay
while(btnDown())
@ -148,37 +136,30 @@ void setupFreq(){
}
void setupBFO(){
int knob = 0;
unsigned long prevCarrier;
prevCarrier = usbCarrier;
displayDialog(F("Set BFO"),F("Press TUNE to Save"));
usbCarrier = 11053000l;
si5351bx_setfreq(0, usbCarrier);
printCarrierFreq(usbCarrier);
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
printCarrierFreq(globalSettings.usbCarrierFreq);
while (!btnDown()){
knob = enc_read();
if (knob != 0)
usbCarrier -= 50 * knob;
else
int knob = enc_read();
if(knob != 0){
globalSettings.usbCarrierFreq -= 50 * knob;
}
else{
continue; //don't update the frequency or the display
}
si5351bx_setfreq(0, usbCarrier);
setFrequency(frequency);
printCarrierFreq(usbCarrier);
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
setFrequency(GetActiveVfoFreq());
printCarrierFreq(globalSettings.usbCarrierFreq);
active_delay(100);
}
EEPROM.put(USB_CAL, usbCarrier);
si5351bx_setfreq(0, usbCarrier);
setFrequency(frequency);
updateDisplay();
menuOn = 0;
SaveSettingsToEeprom();
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
setFrequency(GetActiveVfoFreq());
}
void setupCwDelay(){
@ -188,44 +169,41 @@ void setupCwDelay(){
displayDialog(F("Set CW T/R Delay"),F("Press tune to Save"));
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"));
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()){
knob = enc_read();
if (knob < 0 && cwDelayTime > 10)
cwDelayTime -= 10;
else if (knob > 0 && cwDelayTime < 100)
cwDelayTime += 10;
if (knob < 0 && globalSettings.cwActiveTimeoutMs > 100)
globalSettings.cwActiveTimeoutMs -= 100;
else if (knob > 0 && globalSettings.cwActiveTimeoutMs < 1000)
globalSettings.cwActiveTimeoutMs += 100;
else
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"));
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);
menuOn = 0;
setupExit();
}
void setupKeyer(){
int tmp_key, knob;
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 >"));
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 >"));
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);
}
if (!Iambic_Key)
tmp_key = 0; //hand key
else if (keyerControl & IAMBICB)
tmp_key = 2; //Iambic B
else
tmp_key = 1;
int knob = 0;
uint32_t tmp_mode = globalSettings.keyerMode;
while (!btnDown())
{
knob = enc_read();
if (knob == 0){
if(knob == 0){
active_delay(50);
continue;
}
if (knob < 0 && tmp_key > 0)
tmp_key--;
if (knob > 0)
tmp_key++;
if (tmp_key > 2)
tmp_key = 0;
if(knob < 0 && tmp_mode > KeyerMode_e::KEYER_STRAIGHT){
tmp_mode--;
}
if(knob > 0 && tmp_mode < KeyerMode_e::KEYER_IAMBIC_B){
tmp_mode++;
}
if (tmp_key == 0){
if (KeyerMode_e::KEYER_STRAIGHT == tmp_mode){
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);
}
else if (tmp_key == 1){
else if(KeyerMode_e::KEYER_IAMBIC_A == tmp_mode){
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);
}
else if (tmp_key == 2){
else if (KeyerMode_e::KEYER_IAMBIC_B == tmp_mode){
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);
}
}
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;
}
globalSettings.keyerMode = tmp_mode;
SaveSettingsToEeprom();
EEPROM.put(CW_KEY_TYPE, tmp_key);
menuOn = 0;
setupExit();
}
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
*
@ -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.
*/
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.
@ -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 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
#define LOWEST_FREQ (100000l)
#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;
// if cwMode is flipped on, the rx frequency is tuned down by sidetone hz instead of being zerobeat
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
extern uint8_t menuOn;
/* these are functions implemented in the main file named as ubitx_xxx.ino */
void active_delay(int delay_by);
void saveVFOs();
void setFrequency(unsigned long f);
void startTx(byte txMode);
void startTx(TuningMode_e tx_mode);
void stopTx();
void ritEnable(unsigned long f);
void ritDisable();
void checkCAT();
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
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 */
void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
void initOscillators();

View File

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

View File

@ -1,5 +1,6 @@
#include <Arduino.h>
#include <Wire.h>
#include "settings.h"
#include "ubitx.h"
// ************* 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_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
int32_t calibration = 0;
void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c
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){
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor
si5351bx_setfreq(0, usbCarrier);
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
}
void initOscillators(){
//initialize the SI5351
si5351bx_init();
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor
si5351bx_setfreq(0, usbCarrier);
si5351_set_calibration(globalSettings.oscillatorCal);
}

View File

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

View File

@ -30,21 +30,10 @@
* Si5351 object to control the clocks.
*/
#include <Wire.h>
#include <EEPROM.h>
#include "settings.h"
#include "ubitx.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.
* 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];
/**
* 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
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
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
@ -168,32 +65,14 @@ void active_delay(int delay_by){
unsigned long timeStart = millis();
while (millis() - timeStart <= (unsigned long)delay_by) {
delay(10);
//Background Work
//Background Work
checkCAT();
}
}
void saveVFOs(){
if (vfoActive == VFO_A)
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);
void saveVFOs()
{
SaveSettingsToEeprom();
}
/**
@ -227,12 +106,12 @@ void setTXFilters(unsigned long freq){
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 1);
digitalWrite(TX_LPF_C, 1);
}
}
@ -252,12 +131,12 @@ void setTXFilters_v5(unsigned long freq){
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 1);
digitalWrite(TX_LPF_C, 1);
}
}
@ -275,38 +154,31 @@ void setTXFilters_v5(unsigned long freq){
* through mixing of the second local oscillator.
*/
void setFrequency(unsigned long f){
uint64_t osc_f, firstOscillator, secondOscillator;
void setFrequency(unsigned long freq){
static const unsigned long firstIF = 45005000L;
setTXFilters(f);
setTXFilters(freq);
/*
if (isUSB){
si5351bx_setfreq(2, firstIF + f);
si5351bx_setfreq(1, firstIF + usbCarrier);
uint32_t local_osc_freq;
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
local_osc_freq = firstIF + freq + globalSettings.cwSideToneFreq;
}
else{
si5351bx_setfreq(2, firstIF + f);
si5351bx_setfreq(1, firstIF - usbCarrier);
local_osc_freq = firstIF + freq;
}
*/
//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);
uint32_t ssb_osc_freq;
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
ssb_osc_freq = firstIF + globalSettings.usbCarrierFreq;
}
else{
if (cwMode)
si5351bx_setfreq(2, firstIF + f + sideTone);
else
si5351bx_setfreq(2, firstIF + f);
si5351bx_setfreq(1, firstIF - usbCarrier);
ssb_osc_freq = firstIF - globalSettings.usbCarrierFreq;
}
frequency = f;
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
*/
void startTx(byte txMode){
unsigned long tx_freq = 0;
digitalWrite(TX_RX, 1);
inTx = 1;
if (ritOn){
void startTx(TuningMode_e tx_mode){
globalSettings.tuningMode = tx_mode;
if (globalSettings.ritOn){
//save the current as the rx frequency
ritRxFrequency = frequency;
setFrequency(ritTxFrequency);
uint32_t rit_tx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_tx_freq);
}
else
{
if (splitOn == 1) {
if (vfoActive == VFO_B) {
vfoActive = VFO_A;
isUSB = isUsbVfoA;
frequency = vfoA;
else{
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else if (vfoActive == VFO_A){
vfoActive = VFO_B;
frequency = vfoB;
isUSB = isUsbVfoB;
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(frequency);
setFrequency(GetActiveVfoFreq());
}
if (txMode == TX_CW){
digitalWrite(TX_RX, 0);
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
//turn off the second local oscillator and the bfo
si5351bx_setfreq(0, 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 exact cw frequency is the tuned frequency + sidetone
if (isUSB)
si5351bx_setfreq(2, frequency + sideTone);
else
si5351bx_setfreq(2, frequency - sideTone);
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
si5351bx_setfreq(2, GetActiveVfoFreq() + globalSettings.cwSideToneFreq);
}
else{
si5351bx_setfreq(2, GetActiveVfoFreq() - globalSettings.cwSideToneFreq);
}
delay(20);
digitalWrite(TX_RX, 1);
}
digitalWrite(TX_RX, 1);//turn on the tx
globalSettings.txActive = true;
drawTx();
//updateDisplay();
}
void stopTx(){
inTx = 0;
digitalWrite(TX_RX, 0);//turn off the tx
globalSettings.txActive = false;
digitalWrite(TX_RX, 0); //turn off the tx
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off
//set back the carrier oscillator - cw tx switches it off
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
if (ritOn)
setFrequency(ritRxFrequency);
if(globalSettings.ritOn){
uint32_t rit_rx_freq = globalSettings.ritFrequency;
globalSettings.ritFrequency = GetActiveVfoFreq();
setFrequency(rit_rx_freq);
}
else{
if (splitOn == 1) {
//vfo Change
if (vfoActive == VFO_B){
vfoActive = VFO_A;
frequency = vfoA;
isUSB = isUsbVfoA;
if(globalSettings.splitOn){
if(Vfo_e::VFO_B == globalSettings.activeVfo){
globalSettings.activeVfo = Vfo_e::VFO_A;
}
else if (vfoActive == VFO_A){
vfoActive = VFO_B;
frequency = vfoB;
isUSB = isUsbVfoB;
else{
globalSettings.activeVfo = Vfo_e::VFO_B;
}
}
setFrequency(frequency);
setFrequency(GetActiveVfoFreq());
}
//updateDisplay();
drawTx();
}
@ -398,18 +261,18 @@ void stopTx(){
* ritEnable is called with a frequency parameter that determines
* what the tx frequency will be
*/
void ritEnable(unsigned long f){
ritOn = 1;
void ritEnable(unsigned long freq){
globalSettings.ritOn = true;
//save the non-rit frequency back into the VFO memory
//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
void ritDisable(){
if (ritOn){
ritOn = 0;
setFrequency(ritTxFrequency);
if(globalSettings.ritOn){
globalSettings.ritOn = false;
setFrequency(globalSettings.ritFrequency);
updateDisplay();
}
}
@ -424,17 +287,18 @@ void ritDisable(){
* flip the T/R line to T and update the display to denote transmission
*/
void checkPTT(){
void checkPTT(){
//we don't check for ptt when transmitting cw
if (cwTimeout > 0)
if (globalSettings.cwExpirationTimeMs > 0){
return;
}
if (digitalRead(PTT) == 0 && inTx == 0){
startTx(TX_SSB);
if(digitalRead(PTT) == 0 && !globalSettings.txActive){
startTx(TuningMode_e::TUNE_SSB);
active_delay(50); //debounce the PTT
}
if (digitalRead(PTT) == 1 && inTx == 1)
if (digitalRead(PTT) == 1 && globalSettings.txActive)
stopTx();
}
@ -471,41 +335,13 @@ void checkButton(){
active_delay(50);//debounce
}
void switchVFO(int vfoSelect){
if (vfoSelect == VFO_A){
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;
}
void switchVFO(Vfo_e new_vfo){
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
setFrequency(frequency);
redrawVFOs();
saveVFOs();
globalSettings.activeVfo = new_vfo;
setFrequency(GetActiveVfoFreq());
redrawVFOs();
saveVFOs();
}
/**
@ -516,50 +352,48 @@ void switchVFO(int vfoSelect){
*/
void doTuning(){
int s;
static unsigned long prev_freq;
static unsigned long nextFrequencyUpdate = 0;
unsigned long now = millis();
if (now >= nextFrequencyUpdate && prev_freq != frequency){
if (now >= nextFrequencyUpdate && prev_freq != GetActiveVfoFreq()){
updateDisplay();
nextFrequencyUpdate = now + 100;
prev_freq = frequency;
prev_freq = GetActiveVfoFreq();
}
s = enc_read();
int s = enc_read();
if (!s)
return;
//Serial.println(s);
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
// thresholds for acceleration. I rarely see above 2 or 3 even when turning
// the knob quickly.
if (s < 5 && s > -5) {
frequency += 50l * s;
//Serial.println(" 5");
if (s > 10 || s < -10){
new_freq += 200L * s;
}
else if (s < 10 && s > -10) {
frequency += 100l * s;
//Serial.println(" 10");
else if (s > 5 || s < -5){
new_freq += 100L * s;
}
else { // if (s >= 10 || s <= -10)
frequency += 200l * s;
//Serial.println(" <");
else{
new_freq += 50L * s;
}
//Transition from below to above the traditional threshold for USB
if(prev_freq < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
}
//Transition from aboveo to below the traditional threshold for USB
if(prev_freq >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
}
if (prev_freq < 10000000l && frequency > 10000000l)
isUSB = true;
if (prev_freq > 10000000l && frequency < 10000000l)
isUSB = false;
setFrequency(frequency);
setFrequency(new_freq);
}
@ -567,18 +401,17 @@ void doTuning(){
* RIT only steps back and forth by 100 hz at a time
*/
void doRIT(){
unsigned long newFreq;
int knob = enc_read();
unsigned long old_freq = frequency;
uint32_t old_freq = GetActiveVfoFreq();
uint32_t new_freq = old_freq;
if (knob < 0)
frequency -= 100l;
new_freq -= 100l;
else if (knob > 0)
frequency += 100;
new_freq += 100;
if (old_freq != frequency){
setFrequency(frequency);
if (old_freq != new_freq){
setFrequency(new_freq);
updateDisplay();
}
}
@ -589,87 +422,8 @@ void doRIT(){
* variables.
*/
void initSettings(){
byte x;
//read the settings from the eeprom and restore them
//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;
}
LoadDefaultSettings();
LoadSettingsFromEeprom();
}
void initPorts(){
@ -711,24 +465,24 @@ void setup()
Serial.begin(38400);
Serial.flush();
displayInit();
initSettings();
initPorts();
displayInit();
initPorts();
initOscillators();
frequency = vfoA;
setFrequency(vfoA);
setFrequency(globalSettings.vfoA.frequency);
if (btnDown()){
//Run initial calibration routine if button is pressed during power up
if(btnDown()){
setupTouch();
isUSB = 1;
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
setFrequency(10000000l);
setupFreq();
isUSB = 0;
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
setFrequency(7100000l);
setupBFO();
}
guiUpdate();
guiUpdate();
}
@ -736,23 +490,23 @@ void setup()
* The loop checks for keydown, ptt, function button and tuning.
*/
byte flasher = 0;
boolean wastouched = false;
void loop(){
if (cwMode)
cwKeyer();
else if (!txCAT)
void loop(){
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
cwKeyer();
}
else if(!globalSettings.txCatActive){
checkPTT();
}
checkButton();
//tune only when not tranmsitting
if (!inTx){
if (ritOn)
if(!globalSettings.txActive){
if(globalSettings.ritOn){
doRIT();
else
}
else{
doTuning();
}
checkTouch();
}