mirror of
https://codeberg.org/mclemens/ubitxv6.git
synced 2024-09-10 18:24:15 -04:00
The files are not split into .cpp and .h files. The main file ubitxxxx.cpp will have the main routines to control the radio, initialization and main loop. The user interface is implemented in ubitx_ui.cpp, the code for setup/calibration routines is in setup.cpp. Nano gui, keyer, morse.cpp (morse reader) are all libaries that have minimum dependencies on each other.
293 lines
8.7 KiB
C++
293 lines
8.7 KiB
C++
#include <Arduino.h>
|
|
#include "ubitx.h"
|
|
|
|
/**
|
|
CW Keyer
|
|
CW Key logic change with ron's code (ubitx_keyer.cpp)
|
|
Ron's logic has been modified to work with the original uBITX by KD8CEC
|
|
|
|
Original Comment ----------------------------------------------------------------------------
|
|
* The CW keyer handles either a straight key or an iambic / paddle key.
|
|
* They all use just one analog input line. This is how it works.
|
|
* The analog line has the internal pull-up resistor enabled.
|
|
* When a straight key is connected, it shorts the pull-up resistor, analog input is 0 volts
|
|
* When a paddle is connected, the dot and the dash are connected to the analog pin through
|
|
* a 10K and a 2.2K resistors. These produce a 4v and a 2v input to the analog pins.
|
|
* So, the readings are as follows :
|
|
* 0v - straight key
|
|
* 1-2.5 v - paddle dot
|
|
* 2.5 to 4.5 v - paddle dash
|
|
* 2.0 to 0.5 v - dot and dash pressed
|
|
*
|
|
* The keyer is written to transparently handle all these cases
|
|
*
|
|
* Generating CW
|
|
* The CW is cleanly generated by unbalancing the front-end mixer
|
|
* and putting the local oscillator directly at the CW transmit frequency.
|
|
* The sidetone, generated by the Arduino is injected into the volume control
|
|
*/
|
|
|
|
//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
|
|
|
|
byte 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(){
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Stops the cw carrier transmission along with the sidetone
|
|
* Pushes the cwTimeout further into the future
|
|
*/
|
|
void cwKeyUp(){
|
|
keyDown = 0; //tracks the CW_KEY
|
|
noTone(CW_TONE);
|
|
digitalWrite(CW_KEY, 0);
|
|
|
|
//Modified by KD8CEC, for CW Delay Time save to eeprom
|
|
//cwTimeout = millis() + CW_TIMEOUT;
|
|
cwTimeout = millis() + cwDelayTime * 10;
|
|
}
|
|
|
|
//Variables for Ron's new logic
|
|
#define DIT_L 0x01 // DIT latch
|
|
#define DAH_L 0x02 // DAH latch
|
|
#define DIT_PROC 0x04 // DIT is being processed
|
|
#define PDLSWAP 0x08 // 0 for normal, 1 for swap
|
|
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
|
|
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
|
|
static unsigned long ktimer;
|
|
unsigned char keyerState = IDLE;
|
|
|
|
//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) {
|
|
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)
|
|
tmpKeyerControl |= DIT_L;
|
|
else if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
|
|
tmpKeyerControl |= DAH_L;
|
|
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 = 0 ;
|
|
else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo)
|
|
tmpKeyerControl = DIT_L ;
|
|
else
|
|
tmpKeyerControl = 0 ;
|
|
}
|
|
|
|
if (isUpdateKeyState == 1)
|
|
keyerControl |= tmpKeyerControl;
|
|
|
|
return tmpKeyerControl;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
// New logic, by RON
|
|
// modified by KD8CEC
|
|
******************************************************************************/
|
|
void cwKeyer(void){
|
|
lastPaddle = 0;
|
|
bool continue_loop = true;
|
|
unsigned tmpKeyControl = 0;
|
|
|
|
if( Iambic_Key ) {
|
|
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);
|
|
keyerState = CHK_DIT;
|
|
}else{
|
|
if (0 < cwTimeout && cwTimeout < millis()){
|
|
cwTimeout = 0;
|
|
stopTx();
|
|
}
|
|
continue_loop = false;
|
|
}
|
|
break;
|
|
|
|
case CHK_DIT:
|
|
if (keyerControl & DIT_L) {
|
|
keyerControl |= DIT_PROC;
|
|
ktimer = cwSpeed;
|
|
keyerState = KEYED_PREP;
|
|
}else{
|
|
keyerState = CHK_DAH;
|
|
}
|
|
break;
|
|
|
|
case CHK_DAH:
|
|
if (keyerControl & DAH_L) {
|
|
ktimer = cwSpeed*3;
|
|
keyerState = KEYED_PREP;
|
|
}else{
|
|
keyerState = IDLE;
|
|
}
|
|
break;
|
|
|
|
case KEYED_PREP:
|
|
//modified KD8CEC
|
|
if (!inTx){
|
|
//DelayTime Option
|
|
active_delay(delayBeforeCWStartTime * 2);
|
|
|
|
keyDown = 0;
|
|
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
|
|
startTx(TX_CW);
|
|
}
|
|
ktimer += millis(); // set ktimer to interval end time
|
|
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
|
|
keyerState = KEYED; // next state
|
|
|
|
cwKeydown();
|
|
break;
|
|
|
|
case KEYED:
|
|
if (millis() > ktimer) { // are we at end of key down ?
|
|
cwKeyUp();
|
|
ktimer = millis() + cwSpeed; // inter-element time
|
|
keyerState = INTER_ELEMENT; // next state
|
|
}else if (keyerControl & IAMBICB) {
|
|
update_PaddleLatch(1); // early paddle latch in Iambic B mode
|
|
}
|
|
break;
|
|
|
|
case INTER_ELEMENT:
|
|
// Insert time between dits/dahs
|
|
update_PaddleLatch(1); // latch paddle state
|
|
if (millis() > ktimer) { // are we at end of inter-space ?
|
|
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
|
|
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
|
|
keyerState = CHK_DAH; // dit done, check for dah
|
|
}else{
|
|
keyerControl &= ~(DAH_L); // clear dah latch
|
|
keyerState = IDLE; // go idle
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
checkCAT();
|
|
} //end of while
|
|
}
|
|
else{
|
|
while(1){
|
|
char state = update_PaddleLatch(0);
|
|
// 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);
|
|
|
|
//DelayTime Option
|
|
active_delay(delayBeforeCWStartTime * 2);
|
|
|
|
keyDown = 0;
|
|
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
|
|
}
|
|
cwKeydown();
|
|
|
|
while ( update_PaddleLatch(0) == DIT_L )
|
|
active_delay(1);
|
|
|
|
cwKeyUp();
|
|
}
|
|
else{
|
|
if (0 < cwTimeout && cwTimeout < millis()){
|
|
cwTimeout = 0;
|
|
keyDown = 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
|
|
}
|
|
|
|
checkCAT();
|
|
} //end of while
|
|
} //end of elese
|
|
}
|
|
|
|
|