2022-03-17 20:59:02 -04:00
|
|
|
/**
|
|
|
|
* File name ubitx_keyer.cpp
|
|
|
|
* CW Keyer
|
2022-03-18 22:27:45 -04:00
|
|
|
*
|
|
|
|
* The CW keyer handles either a straight key or an iambic / paddle key.
|
2022-03-17 20:59:02 -04:00
|
|
|
* D12 for DOT Paddle and D11 for DASH Paddle and D* for PTT/Handkey
|
2022-03-18 22:27:45 -04:00
|
|
|
*
|
2022-03-17 20:59:02 -04:00
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
#include "ubitx.h"
|
2022-03-18 22:27:45 -04:00
|
|
|
#include <Arduino.h>
|
2022-03-17 20:59:02 -04:00
|
|
|
|
|
|
|
extern void stopTx(void);
|
2022-03-18 22:27:45 -04:00
|
|
|
extern void startTx(byte txMode, byte isDisplayUpdate = 0);
|
2022-03-17 20:59:02 -04:00
|
|
|
|
|
|
|
extern unsigned long sideTone;
|
|
|
|
extern int cwSpeed;
|
2022-03-18 22:27:45 -04:00
|
|
|
// extern long CW_TIMEOUT;
|
|
|
|
extern long cwTimeout;
|
|
|
|
#define CW_TIMEOUT (cwTimeout)
|
2022-03-17 20:59:02 -04:00
|
|
|
extern volatile bool inTx;
|
2022-03-18 22:27:45 -04:00
|
|
|
// extern volatile int ubitx_mode;
|
|
|
|
extern char isUSB;
|
|
|
|
extern char cwMode;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
|
|
|
extern volatile unsigned char keyerControl;
|
2022-03-18 22:27:45 -04:00
|
|
|
// extern volatile unsigned char keyerState;
|
|
|
|
volatile unsigned char keyerState = IDLE;
|
|
|
|
// extern unsigned volatile char IAMBICB;
|
|
|
|
// extern unsigned volatile char PDLSWAP;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
// extern volatile unsigned long Ubitx_Voltage;
|
|
|
|
// extern volatile int Ubitx_Voltage_Timer;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
volatile bool keyDown = false; // in cw mode, denotes the carrier is being transmitted
|
|
|
|
volatile uint8_t Last_Bits = 0xFF;
|
|
|
|
;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
|
|
|
volatile bool Dot_in_Progress = false;
|
2022-03-18 22:27:45 -04:00
|
|
|
volatile unsigned long Dot_Timer_Count = 0;
|
2022-03-17 20:59:02 -04:00
|
|
|
volatile bool Dash_in_Progress = false;
|
2022-03-18 22:27:45 -04:00
|
|
|
volatile unsigned long Dash_Timer_Count = 0;
|
2022-03-17 20:59:02 -04:00
|
|
|
volatile bool Inter_Bit_in_Progress = false;
|
2022-03-18 22:27:45 -04:00
|
|
|
volatile unsigned long Inter_Bit_Timer_Count = 0;
|
2022-03-17 20:59:02 -04:00
|
|
|
volatile bool Turn_Off_Carrier_in_Progress = false;
|
2022-03-18 22:27:45 -04:00
|
|
|
volatile unsigned long Turn_Off_Carrier_Timer_Count = 0;
|
2022-03-17 20:59:02 -04:00
|
|
|
volatile bool Ubitx_Voltage_Act = false;
|
|
|
|
volatile bool PTT_HANDKEY_ACTIVE = false;
|
|
|
|
volatile long last_interrupt_time = 20;
|
2022-03-18 22:27:45 -04:00
|
|
|
// extern bool Cat_Lock;
|
|
|
|
// extern volatile bool TX_In_Progress;
|
|
|
|
extern volatile bool txCAT;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2022-03-18 22:27:45 -04:00
|
|
|
void cwKeydown(void) {
|
|
|
|
keyDown = 1; // tracks the CW_KEY
|
|
|
|
tone(CW_TONE, (int)sideTone);
|
|
|
|
digitalWrite(CW_KEY, 1);
|
|
|
|
#ifdef XMIT_LED
|
|
|
|
digitalWrite(ON_AIR, 0); // extinguish the LED on NANO's pin 13
|
|
|
|
#endif
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Stops the CW carrier transmission along with the sidetone
|
|
|
|
* Pushes the cwTimeout further into the future
|
|
|
|
*/
|
2022-03-18 22:27:45 -04:00
|
|
|
void cwKeyUp(void) {
|
|
|
|
keyDown = 0; // tracks the CW_KEY
|
2022-03-17 20:59:02 -04:00
|
|
|
noTone(CW_TONE);
|
2022-03-18 22:27:45 -04:00
|
|
|
digitalWrite(CW_KEY, 0);
|
|
|
|
#ifdef XMIT_LED
|
|
|
|
digitalWrite(ON_AIR, 1); // extinguish the LED on NANO's pin 13
|
|
|
|
#endif
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void update_PaddleLatch() {
|
2022-03-18 22:27:45 -04:00
|
|
|
// if (!digitalRead(DIGITAL_DOT) ) keyerControl |= DIT_L;
|
|
|
|
// if (!digitalRead(DIGITAL_DASH) ) keyerControl |= DAH_L;
|
2022-03-17 20:59:02 -04:00
|
|
|
if (digitalRead(DIGITAL_DOT) == LOW) {
|
2022-03-18 22:27:45 -04:00
|
|
|
if (keyerControl & PDLSWAP)
|
|
|
|
keyerControl |= DAH_L;
|
|
|
|
else
|
|
|
|
keyerControl |= DIT_L;
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
if (digitalRead(DIGITAL_DASH) == LOW) {
|
2022-03-18 22:27:45 -04:00
|
|
|
if (keyerControl & PDLSWAP)
|
|
|
|
keyerControl |= DIT_L;
|
|
|
|
else
|
|
|
|
keyerControl |= DAH_L;
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// interupt handlers
|
|
|
|
|
|
|
|
//// timers
|
|
|
|
ISR(TIMER1_OVF_vect) {
|
2022-03-18 22:27:45 -04:00
|
|
|
static volatile bool i_am_running = false;
|
|
|
|
bool continue_loop = true;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
if (i_am_running) return;
|
|
|
|
i_am_running = true;
|
2022-03-17 20:59:02 -04:00
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
// process if CW modes
|
|
|
|
// if( (ubitx_mode == MODE_CW)||(ubitx_mode == MODE_CWR)){
|
|
|
|
if (cwMode > 0) {
|
2022-03-17 20:59:02 -04:00
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
// process DOT and DASH timing
|
|
|
|
if ((Dot_in_Progress) && (Dot_Timer_Count > 0)) {
|
|
|
|
if (!inTx) {
|
|
|
|
keyDown = 0;
|
|
|
|
startTx(TX_CW);
|
|
|
|
}
|
|
|
|
if (keyDown == 0)
|
|
|
|
cwKeydown();
|
|
|
|
Dot_Timer_Count = Dot_Timer_Count - 1;
|
|
|
|
if (Dot_Timer_Count <= 0) {
|
|
|
|
Dot_Timer_Count = 0;
|
|
|
|
Dot_in_Progress = false;
|
|
|
|
cwKeyUp();
|
|
|
|
}
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
|
|
|
|
// process Inter Bit Timing
|
|
|
|
if ((Inter_Bit_in_Progress) && (Inter_Bit_Timer_Count > 0)) {
|
|
|
|
Inter_Bit_Timer_Count = Inter_Bit_Timer_Count - 1;
|
|
|
|
if (Inter_Bit_Timer_Count <= 0) {
|
|
|
|
Inter_Bit_Timer_Count = 0;
|
|
|
|
Inter_Bit_in_Progress = false;
|
|
|
|
}
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
// process turning off carrier
|
|
|
|
if ((Turn_Off_Carrier_in_Progress) && (Turn_Off_Carrier_Timer_Count > 0)) {
|
2022-03-17 20:59:02 -04:00
|
|
|
Turn_Off_Carrier_Timer_Count = Turn_Off_Carrier_Timer_Count - 1;
|
2022-03-18 22:27:45 -04:00
|
|
|
if (Turn_Off_Carrier_Timer_Count <= 0) {
|
2022-03-17 20:59:02 -04:00
|
|
|
Turn_Off_Carrier_in_Progress = false;
|
|
|
|
Turn_Off_Carrier_Timer_Count = 0;
|
|
|
|
stopTx();
|
|
|
|
}
|
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
|
|
|
|
// process hand key
|
|
|
|
if (digitalRead(DIGITAL_KEY) == 0) {
|
|
|
|
// If interrupts come faster than 5ms, assume it's a bounce and ignore
|
|
|
|
last_interrupt_time = last_interrupt_time - 1;
|
|
|
|
if (last_interrupt_time <= 0) {
|
|
|
|
last_interrupt_time = 0;
|
|
|
|
if (!inTx) {
|
|
|
|
keyDown = 0;
|
|
|
|
startTx(TX_CW);
|
|
|
|
}
|
|
|
|
if (keyDown == 0)
|
|
|
|
cwKeydown();
|
|
|
|
PTT_HANDKEY_ACTIVE = true;
|
2022-03-17 20:59:02 -04:00
|
|
|
Turn_Off_Carrier_Timer_Count = CW_TIMEOUT;
|
2022-03-18 22:27:45 -04:00
|
|
|
}
|
|
|
|
} else if ((keyDown == 1) && (PTT_HANDKEY_ACTIVE == true)) {
|
|
|
|
cwKeyUp();
|
|
|
|
Turn_Off_Carrier_Timer_Count = CW_TIMEOUT;
|
|
|
|
Turn_Off_Carrier_in_Progress = true;
|
|
|
|
last_interrupt_time = PTT_HNDKEY_DEBOUNCE_CT;
|
|
|
|
PTT_HANDKEY_ACTIVE = false;
|
|
|
|
} else
|
|
|
|
last_interrupt_time = PTT_HNDKEY_DEBOUNCE_CT;
|
|
|
|
|
|
|
|
if (PTT_HANDKEY_ACTIVE == false) {
|
|
|
|
while (continue_loop) {
|
|
|
|
switch (keyerState) {
|
|
|
|
case IDLE:
|
|
|
|
if ((!digitalRead(DIGITAL_DOT)) || (!digitalRead(DIGITAL_DASH)) ||
|
|
|
|
(keyerControl & 0x03)) {
|
|
|
|
update_PaddleLatch();
|
|
|
|
keyerState = CHK_DIT;
|
|
|
|
Dot_in_Progress = false;
|
|
|
|
Dot_Timer_Count = 0;
|
|
|
|
Turn_Off_Carrier_Timer_Count = 0;
|
|
|
|
Turn_Off_Carrier_in_Progress = false;
|
|
|
|
} else {
|
|
|
|
continue_loop = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHK_DIT:
|
|
|
|
if (keyerControl & DIT_L) {
|
|
|
|
keyerControl |= DIT_PROC;
|
|
|
|
keyerState = KEYED_PREP;
|
|
|
|
Dot_Timer_Count = cwSpeed;
|
|
|
|
} else {
|
|
|
|
keyerState = CHK_DAH;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHK_DAH:
|
|
|
|
if (keyerControl & DAH_L) {
|
|
|
|
keyerState = KEYED_PREP;
|
|
|
|
Dot_Timer_Count = cwSpeed * 3;
|
|
|
|
} else {
|
|
|
|
continue_loop = false;
|
|
|
|
keyerState = IDLE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KEYED_PREP:
|
|
|
|
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
|
|
|
|
keyerState = KEYED; // next state
|
|
|
|
Turn_Off_Carrier_Timer_Count = 0;
|
|
|
|
Turn_Off_Carrier_in_Progress = false;
|
|
|
|
Dot_in_Progress = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KEYED:
|
|
|
|
if (Dot_in_Progress == false) { // are we at end of key down ?
|
|
|
|
Inter_Bit_in_Progress = true;
|
|
|
|
Inter_Bit_Timer_Count = cwSpeed;
|
|
|
|
keyerState = INTER_ELEMENT; // next state
|
|
|
|
} else if (keyerControl & IAMBICB) {
|
|
|
|
update_PaddleLatch(); // early paddle latch in Iambic B mode
|
|
|
|
continue_loop = false;
|
|
|
|
} else
|
|
|
|
continue_loop = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case INTER_ELEMENT:
|
|
|
|
// Insert time between dits/dahs
|
|
|
|
update_PaddleLatch(); // latch paddle state
|
|
|
|
if (Inter_Bit_in_Progress == false) { // are we at end of inter-space ?
|
|
|
|
Turn_Off_Carrier_Timer_Count = CW_TIMEOUT;
|
|
|
|
Turn_Off_Carrier_in_Progress = true;
|
|
|
|
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
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
continue_loop = false;
|
|
|
|
break;
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
}
|
|
|
|
}
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// process PTT
|
2022-03-18 22:27:45 -04:00
|
|
|
// if( (ubitx_mode == MODE_USB)|| (ubitx_mode == MODE_LSB)){
|
|
|
|
if (cwMode == 0) {
|
|
|
|
if (digitalRead(PTT) == 0) {
|
2022-03-17 20:59:02 -04:00
|
|
|
// If interrupts come faster than 5ms, assume it's a bounce and ignore
|
|
|
|
last_interrupt_time = last_interrupt_time - 1;
|
2022-03-18 22:27:45 -04:00
|
|
|
if (last_interrupt_time <= 0) {
|
2022-03-17 20:59:02 -04:00
|
|
|
last_interrupt_time = 0;
|
2022-03-18 22:27:45 -04:00
|
|
|
if (!inTx)
|
|
|
|
startTx(TX_SSB);
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
} else if ((inTx) && (txCAT == false)) {
|
2022-03-17 20:59:02 -04:00
|
|
|
last_interrupt_time = PTT_HNDKEY_DEBOUNCE_CT;
|
|
|
|
stopTx();
|
2022-03-18 22:27:45 -04:00
|
|
|
} else
|
|
|
|
last_interrupt_time = PTT_HNDKEY_DEBOUNCE_CT;
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
i_am_running = false;
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
void Connect_Interrupts(void) {
|
2022-03-17 20:59:02 -04:00
|
|
|
keyerControl = 0;
|
|
|
|
cli();
|
2022-03-18 22:27:45 -04:00
|
|
|
TIMSK1 |= (1 << TOIE1);
|
2022-03-17 20:59:02 -04:00
|
|
|
sei();
|
|
|
|
}
|
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
/*
|
|
|
|
#define N_MORSE (sizeof(morsetab)/sizeof(morsetab[0]))
|
2022-03-17 20:59:02 -04:00
|
|
|
// Morse table
|
|
|
|
struct t_mtab { char c, pat; } ;
|
|
|
|
struct t_mtab morsetab[] = {
|
2022-03-18 22:27:45 -04:00
|
|
|
{'.', 106}, {',', 115}, {'?', 76}, {'/', 41}, {'A', 6}, {'B', 17}, {'C', 21}, {'D', 9},
|
|
|
|
{'E', 2}, {'F', 20}, {'G', 11}, {'H', 16}, {'I', 4}, {'J', 30}, {'K', 13}, {'L', 18},
|
|
|
|
{'M', 7}, {'N', 5}, {'O', 15}, {'P', 22}, {'Q', 27}, {'R', 10}, {'S', 8}, {'T', 3},
|
|
|
|
{'U', 12}, {'V', 24}, {'W', 14}, {'X', 25}, {'Y', 29}, {'Z', 19}, {'1', 62}, {'2', 60},
|
2022-03-17 20:59:02 -04:00
|
|
|
{'3', 56}, {'4', 48}, {'5', 32}, {'6', 33}, {'7', 35}, {'8', 39}, {'9', 47}, {'0', 63}
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
// CW generation routines for CQ message
|
2022-03-17 20:59:02 -04:00
|
|
|
void key(int LENGTH){
|
2022-03-18 22:27:45 -04:00
|
|
|
|
2022-03-17 20:59:02 -04:00
|
|
|
if( !inTx ) startTx(TX_CW);
|
|
|
|
cwKeydown();
|
|
|
|
delay(LENGTH*2);
|
|
|
|
cwKeyUp();
|
|
|
|
delay(cwSpeed*2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void send(char c){
|
|
|
|
int i ;
|
|
|
|
|
2022-03-18 22:27:45 -04:00
|
|
|
|
2022-03-17 20:59:02 -04:00
|
|
|
if (c == ' ') {
|
|
|
|
delay(7*cwSpeed) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
for (i=0; i<N_MORSE; i++){
|
|
|
|
if (morsetab[i].c == c){
|
|
|
|
unsigned char p = morsetab[i].pat ;
|
|
|
|
while (p != 1) {
|
|
|
|
if (p & 1) Dot_Timer_Count = cwSpeed*3;
|
|
|
|
else Dot_Timer_Count = cwSpeed;
|
|
|
|
key(Dot_Timer_Count);
|
|
|
|
p = p / 2 ;
|
|
|
|
}
|
|
|
|
delay(cwSpeed*5) ;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void sendmsg(char *str){
|
2022-03-18 22:27:45 -04:00
|
|
|
|
2022-03-17 20:59:02 -04:00
|
|
|
while (*str) send(*str++);
|
|
|
|
delay(650);
|
2022-03-18 22:27:45 -04:00
|
|
|
stopTx();
|
2022-03-17 20:59:02 -04:00
|
|
|
}
|
2022-03-18 22:27:45 -04:00
|
|
|
*/
|