187 lines
5.8 KiB
C++
187 lines
5.8 KiB
C++
/**
|
|
* CW Keyer
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
|
|
// 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);
|
|
|
|
if (paddle > 800) // above 4v is up
|
|
return 0;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* The keyer handles the straight key as well as the iambic key
|
|
* This module keeps looping until the user stops sending cw
|
|
* if the cwTimeout is set to 0, then it means, we have to exit the keyer loop
|
|
* Each time the key is hit the cwTimeout is pushed to a time in the future by cwKeyDown()
|
|
*/
|
|
|
|
void cwKeyer(){
|
|
byte paddle;
|
|
lastPaddle = 0;
|
|
|
|
while(1){
|
|
paddle = getPaddle();
|
|
|
|
// do nothing if the paddle has not been touched, unless
|
|
// we are in the cw mode and we have timed out
|
|
if (!paddle){
|
|
//modifed by KD8CEC for auto CW Send
|
|
if (isCWAutoMode > 1) //if while auto cw sending, dont stop tx by paddle position
|
|
return;
|
|
|
|
if (0 < cwTimeout && cwTimeout < millis()){
|
|
cwTimeout = 0;
|
|
keyDown = 0;
|
|
stopTx();
|
|
}
|
|
|
|
if (!cwTimeout)
|
|
return;
|
|
|
|
//if a paddle was used (not a straight key) we should extend the space to be a full dash
|
|
//by adding two more dots long space (one has already been added at the end of the dot or dash)
|
|
/*
|
|
if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT)
|
|
delay_background(cwSpeed * 2, 3);
|
|
//delay(cwSpeed * 2);
|
|
|
|
// got back to the begining of the loop, if no further activity happens on the paddle or the straight key
|
|
// we will time out, and return out of this routine
|
|
delay(5);
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
//if while auto cw send, stop auto cw
|
|
//but isAutoCWHold for Manual Keying with cwAutoSend
|
|
if (isCWAutoMode > 1 && isAutoCWHold == 0)
|
|
isCWAutoMode = 1; //read status
|
|
|
|
//Remoark Debug code / Serial Use by CAT Protocol
|
|
//Serial.print("paddle:");Serial.println(paddle);
|
|
// if we are here, it is only because the key or the paddle is pressed
|
|
if (!inTx){
|
|
keyDown = 0;
|
|
//Modified by KD8CEC, for CW Delay Time save to eeprom
|
|
//cwTimeout = millis() + CW_TIMEOUT;
|
|
cwTimeout = millis() + cwDelayTime * 10;
|
|
|
|
startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time
|
|
updateDisplay();
|
|
|
|
//DelayTime Option
|
|
delay_background(delayBeforeCWStartTime * 2, 2);
|
|
}
|
|
|
|
// star the transmission)
|
|
// we store the transmitted character in the lastPaddle
|
|
cwKeydown();
|
|
if (paddle == PADDLE_DOT){
|
|
//delay(cwSpeed);
|
|
delay_background(cwSpeed, 3);
|
|
lastPaddle = PADDLE_DOT;
|
|
}
|
|
else if (paddle == PADDLE_DASH){
|
|
//delay(cwSpeed * 3);
|
|
delay_background(cwSpeed * 3, 3);
|
|
lastPaddle = PADDLE_DASH;
|
|
}
|
|
else if (paddle == PADDLE_BOTH){ //both paddles down
|
|
//depending upon what was sent last, send the other
|
|
if (lastPaddle == PADDLE_DOT) {
|
|
//delay(cwSpeed * 3);
|
|
delay_background(cwSpeed * 3, 3);
|
|
lastPaddle = PADDLE_DASH;
|
|
}else{
|
|
//delay(cwSpeed);
|
|
delay_background(cwSpeed, 3);
|
|
lastPaddle = PADDLE_DOT;
|
|
}
|
|
}
|
|
else if (paddle == PADDLE_STRAIGHT){
|
|
while (getPaddle() == PADDLE_STRAIGHT) {
|
|
delay(1);
|
|
Check_Cat(2);
|
|
}
|
|
lastPaddle = PADDLE_STRAIGHT;
|
|
}
|
|
cwKeyUp();
|
|
//introduce a dot long gap between characters if the keyer was used
|
|
if (lastPaddle != PADDLE_STRAIGHT)
|
|
delay(cwSpeed);
|
|
}
|
|
}
|