16 Commits

Author SHA1 Message Date
Rob French
47840e09dd Forgot to add the Keyer on the previous commit. 2021-02-11 23:56:08 -06:00
Rob French
d2213e34ff Raduino: Disabled CAT in the Raduino main loop. Fixed some split freq setting via I2C. TeensyDSP: Added a Keyer. Works ok, but I have to disable ADC during transmit (or all of CW?) in order to keep the timing good... need to use interrupts and/or continuous ADC at some point. Added the MD CAT command, and fixed other CAT commands. Split seems to work, but don't do split when using the keyer! Halted the Raduino. 2021-02-11 23:55:41 -06:00
Rob French
814fe6c733 Did add some code for updating the RigState architecture. Not ready to swap it out yet, however. 2021-02-11 22:00:24 -06:00
Rob French
c3cc9a7cf7 Got basic 3-way comm (CAT-Teensy-Raduino) working. CAT commands are received via Serial by the Teensy. Data is passed on to the Raduino via I2C. Had to add an intermediate step in the protocol in order for the Raduino to request a byte as a flag for whether or not any changed data was coming, and then if so, request the changed data. There are certainly some optimizations that could be made on this number. Currently, the Raduino code is very clunky. In addition, the Rig and RigState classes have deteriorated somewhat. 2021-02-10 00:10:24 -06:00
Rob French
aeeec69daf Raduino changes are getting to TeensyDSP over I2C. TeensyDSP successfully receiving some CAT. 2021-02-09 22:58:07 -06:00
Rob French
702f370d1b Heavily modified the TS590 class. 2021-02-07 17:12:08 -06:00
Rob French
b9be616361 More scary updates. Implemented some basic CAT control via USB serial, for the TeensyDSP. More fully fleshed out a RigState and Rig types. Compiles. Still MAY need to update the Raduino to match the TeensyDSP (it may actually be okay, because right now only the RIGINF command is being sent. 2021-02-06 23:45:19 -06:00
Rob French
4186fdcdd4 Scary commit. I've taken baby steps toward passing rig status between the Raduino and the TeensyDSP using I2C. Compiles, but has not been tested. Need to create a branch. 2021-02-05 22:59:31 -06:00
Rob French
e62e3ef548 Since integration seems to be proceeding well, started documenting some of the circuits via schematic (KiCad). 2021-02-02 16:29:09 -06:00
Rob French
deb0aca5fe removed some leftover instrumentation 2021-02-02 08:57:37 -06:00
Rob French
04d5f3ba12 Sensors are now functioning more-or-less correctly. Calibration isn't quite right, but the basics are correct. 2021-02-02 08:54:27 -06:00
Rob French
ba744f5b7a Miscellaneous fixes on the integration branch. Next up on hardware: swap the FWD and REV PWR lines (or switch them in software, duh...). Then in software--verify that S-Meter, FWD/REV PWR, and VSWR signals are working correctly. 2021-01-31 22:46:43 -06:00
Rob French
4e818b6a89 Merge branch 'meter-to-teensy' into integration 2021-01-30 07:56:48 -06:00
Rob French
b2cb1a26ba Merge branch 'combined-cw-ptt' into integration 2021-01-30 07:56:29 -06:00
Rob French
6b365beac0 Added Schematics folder and picture of the I/O board (specifically the ADC buffer). 2021-01-27 22:03:08 -06:00
Rob French
88143f57a2 Updated Raduino code with some old code from the ubitx-v5d repository, in order to suppress the key line (prevent inadvertant transmitting when I first start working this). 2021-01-20 23:54:28 -06:00
30 changed files with 4331 additions and 244 deletions

View File

@@ -122,7 +122,8 @@ 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, cwmCarrier;
unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life
unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
unsigned long frequency;
unsigned long ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
unsigned int cwSpeed = 100; //this is actuall the dot period in milliseconds
extern int32_t calibration;
@@ -316,6 +317,17 @@ unsigned long delayBeforeTime = 0;
byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle
delayBeforeTime = millis();
/*
* KC4UPR - IOP review, 2020-05-03
*
* I don't see anything in here that is either important to, or will adversely affect, IOP
* operation. I'm not planning on using the uBITX autokeyer (since all keying will be in the
* IOP), so neither getPaddle() nor autoSendPTTCheck() will be issues. I do need to look into
* overall CAT operation, in general.
*
* UPDATE: Fixed getPaddle() to be compatible.
*/
while (millis() - delayBeforeTime <= delayTime) {
if (fromType == 4)
@@ -327,12 +339,12 @@ byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWK
//Check PTT while auto Sending
autoSendPTTCheck();
Check_Cat(3);
//Check_Cat(3);
}
else
{
//Background Work
Check_Cat(fromType);
//Check_Cat(fromType);
}
}
@@ -678,6 +690,10 @@ void ritDisable(){
*/
void checkPTT(){
/*
* KC4UPR - note that some of this is superfluous now that checkPTT() is only executed
* in SSB mode, and cwKeyer is only executed in CW mode...
*/
//we don't check for ptt when transmitting cw
if (cwTimeout > 0)
return;
@@ -790,7 +806,7 @@ void checkButton(){
//wait for the button to go up again
while(keyStatus == getBtnStatus()) {
delay(10);
Check_Cat(0);
//Check_Cat(0);
}
//delay(50);//debounce
}
@@ -809,7 +825,7 @@ void checkButton(){
//wait for the button to go up again
while(btnDown()) {
delay(10);
Check_Cat(0);
//Check_Cat(0);
}
//delay(50);//debounce
}
@@ -1386,7 +1402,8 @@ void setup()
//printLineF(1, FIRMWARE_VERSION_INFO);
DisplayVersionInfo(FIRMWARE_VERSION_INFO);
Init_Cat(38400, SERIAL_8N1);
//Init_Cat(38400, SERIAL_8N1);
Serial.begin(38400);
initSettings();
initPorts();
@@ -1456,18 +1473,48 @@ void checkAutoSaveFreqMode()
saveCheckTime = 0; //for reduce cpu use rate
}
}
rigState.vfo[0] = vfoA;
rigState.vfo[1] = vfoB;
rigState.rit = ritRxFrequency - frequency;
rigState.flags = 0;
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
}
void loop(){
if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process
if (!txCAT)
/*
* KC4UPR - IOP update, 2020-05-03
*
* Getting rid of the autokeyer code... not planning on using, since any autokeying
* would actually be done by the IOP. We'll check the PTT, but only in SSB mode
* (same line as CW, so it would be caught by cwKeyer() in CW mode).
*
* Only check the CW keyer if we are in one of the CW modes. Why? Because we
* are using the same input for PTT and CW.
*/
// if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process
// if (!txCAT)
// checkPTT();
// checkButton();
// }
// else
// controlAutoCW();
// KC4UPR: Note, implementation below leaves no manual way to abort TX due to CAT. May
// want to add in a way to interrupt CAT transmission with a PTT/CW event.
//if (!txCAT) {
if (cwMode == 0) {
checkPTT();
checkButton();
}
else
controlAutoCW();
} else {
cwKeyer();
}
checkButton();
//}
//cwKeyer();
//tune only when not tranmsitting
if (!inTx){
@@ -1487,7 +1534,7 @@ void loop(){
} //end of check TX Status
//we check CAT after the encoder as it might put the radio into TX
Check_Cat(inTx? 1 : 0);
//Check_Cat(inTx? 1 : 0);
//for SEND SW Serial
#ifdef USE_SW_SERIAL

1
Raduino/RigState.h Symbolic link
View File

@@ -0,0 +1 @@
../TeensyDSP/RigState.h

View File

@@ -19,11 +19,13 @@
#include <Arduino.h> //for Linux, On Linux it is case sensitive.
#include "RigState.h"
//==============================================================================
// Compile Option
//==============================================================================
//Ubitx Board Version
#define UBITX_BOARD_VERSION 2 //v1 ~ v4 : 4, v5: 5
#define UBITX_BOARD_VERSION 5 //v1 ~ v4 : 4, v5: 5
//Depending on the type of LCD mounted on the uBITX, uncomment one of the options below.
//You must select only one.
@@ -48,8 +50,8 @@
//#define USE_CUSTOM_LPF_FILTER //LPF FILTER MOD
//#define ENABLE_FACTORYALIGN
#define FACTORY_RECOVERY_BOOTUP //Whether to enter Factory Recovery mode by pressing FKey and turning on power
#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary.
//#define FACTORY_RECOVERY_BOOTUP //Whether to enter Factory Recovery mode by pressing FKey and turning on power
//#define ENABLE_ADCMONITOR //Starting with Version 1.07, you can read ADC values directly from uBITX Manager. So this function is not necessary.
extern byte I2C_LCD_MASTER_ADDRESS; //0x27 //if Set I2C Address by uBITX Manager, read from EEProm
extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
@@ -253,6 +255,12 @@ extern byte I2C_LCD_SECOND_ADDRESS; //only using Dual LCD Mode
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
// Raduino provides updated data to TeensyDSP
#define I2CMETER_RIGINF 0x50
// Raduino requests any CAT updates from TeensyDSP
#define I2CMETER_REQCAT 0x51
//==============================================================================
// for public, Variable, functions
//==============================================================================
@@ -332,4 +340,10 @@ extern void DisplayVersionInfo(const char* fwVersionInfo);
//I2C Signal Meter, Version 1.097
extern int GetI2CSmeterValue(int valueType); //ubitx_ui.ino
extern void doRaduinoToTeensy(UBitxRigState* r);
extern void updateStateFromRaduino(UBitxRigState& r);
extern void updateRaduinoFromState(UBitxRigState& r);
extern UBitxRigState rigState;
#endif //end of if header define

View File

@@ -39,6 +39,22 @@ char lastPaddle = 0;
//reads the analog keyer pin and reports the paddle
byte getPaddle(){
/*
* KC4UPR - IOP update, 2020-05-03
*
* Modifying this for the uBITX IOP. Big picture:
*
* (1) It uses the PTT input line.
*
* (2) It's always "straight key" mode (the IOP provides the keyer).
*/
if (digitalRead(PTT) == 1) // key/PTT is up
return 0;
else
return PADDLE_STRAIGHT;
/*
int paddle = analogRead(ANALOG_KEYER);
if (paddle > 800) // above 4v is up
@@ -52,6 +68,7 @@ byte getPaddle(){
return PADDLE_BOTH; //both are between 1 and 2v
else
return PADDLE_STRAIGHT; //less than 1v is the straight key
*/
}
/**
@@ -96,6 +113,17 @@ 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) {
/*
* KC4UPR - IOP update, 2020-05-03
*
* Modifying this for the uBITX IOP. Big picture:
*
* No iambic keyer. It's always "straight key" based on the IOP.
*
* It uses the PTT line.
*/
return (digitalRead(PTT) ? 0 : DIT_L);
/*
unsigned char tmpKeyerControl = 0;
int paddle = analogRead(ANALOG_KEYER);
@@ -119,6 +147,7 @@ char update_PaddleLatch(byte isUpdateKeyState) {
keyerControl |= tmpKeyerControl;
return tmpKeyerControl;
*/
}
/*****************************************************************************
@@ -126,106 +155,113 @@ 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) {
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
/*
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state
if (!inTx){
//DelayTime Option
delay_background(delayBeforeCWStartTime * 2, 2);
keyDown = 0;
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
startTx(TX_CW, 1);
}
* KC4UPR - IOP update, 2020-05-03
*
* Modifying this for the uBITX IOP. Big picture:
*
* No iambic keyer. It's always "straight key" based on the IOP.
*/
if (!inTx){
//DelayTime Option
delay_background(delayBeforeCWStartTime * 2, 2);
keyDown = 0;
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
startTx(TX_CW, 1);
}
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;
}
Check_Cat(2);
} //end of while
}
else{
// 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
// /*
// ktimer += millis(); // set ktimer to interval end time
// keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
// keyerState = KEYED; // next state
// if (!inTx){
// //DelayTime Option
// delay_background(delayBeforeCWStartTime * 2, 2);
//
// keyDown = 0;
// cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
// startTx(TX_CW, 1);
// }
// */
// if (!inTx){
// //DelayTime Option
// delay_background(delayBeforeCWStartTime * 2, 2);
//
// keyDown = 0;
// cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
// startTx(TX_CW, 1);
// }
// 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;
// }
//
// Check_Cat(2);
// } //end of while
// }
// else{
while(1){
if (update_PaddleLatch(0) == DIT_L) {
// if we are here, it is only because the key is pressed
@@ -262,7 +298,7 @@ void cwKeyer(void){
Check_Cat(2);
} //end of while
} //end of elese
// } //end of elese
}
@@ -365,5 +401,3 @@ void cwKeyer(){
}
}
*/

View File

@@ -990,9 +990,17 @@ void SWS_Process(void)
char checkCount = 0;
char checkCountSMeter = 0;
UBitxRigState rigState;
UBitxRigState catState;
//execute interval : 0.25sec
void idle_process()
{
// KC4UPR 2021-02-05 added update process for Raduino-TeensyDSP coordination
updateStateFromRaduino(rigState);
doRaduinoToTeensy(&rigState);
updateRaduinoFromState(rigState);
//S-Meter Display
if (((displayOption1 & 0x08) == 0x08 && (sdrModeOn == 0)) && (++checkCountSMeter > SMeterLatency))
{

View File

@@ -18,6 +18,8 @@ const PROGMEM uint8_t meters_bitmap[] = {
};
*/
#include "RigState.h"
//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
#ifdef OPTION_SKINNYBARS //We want skninny bars with more text
@@ -296,4 +298,95 @@ int GetI2CSmeterValue(int valueType)
}
}
//======================================================================
void doRaduinoToTeensy(UBitxRigState* r) {
uint8_t* ptr = (uint8_t*)r;
Wire.beginTransmission(I2CMETER_ADDR);
Wire.write(I2CMETER_RIGINF);
//for (size_t i = 0; i < sizeof(UBitxRigState); i++) {
// Wire.write(ptr[i]);
//}
//Note, I can switch this back...
Wire.write(ptr, sizeof(UBitxRigState));
Wire.endTransmission();
Serial.println("BEFORE:");
Serial.print("VFO A: ");
Serial.print(r->vfo[0]);
Serial.print(", VFO B: ");
Serial.print(r->vfo[1]);
Serial.print(", Data Size: ");
Serial.print(sizeof(UBitxRigState));
Serial.println();
// First we need to see if there's any updated state.
Wire.requestFrom(I2CMETER_ADDR, sizeof(uint8_t));
int len = 0;
int readflag = Wire.read();
if (readflag != 0) {
Wire.requestFrom(I2CMETER_ADDR, sizeof(UBitxRigState));
UBitxRigState tmp;
//int len = 0;
//ptr = (uint8_t*)&tmp;
while (Wire.available() > 0) {
uint8_t b = Wire.read();
if (len < sizeof(UBitxRigState)) {
ptr[len++] = b;
}
}
}
Serial.println("AFTER:");
Serial.print("VFO A: ");
Serial.print(r->vfo[0]);
Serial.print(", VFO B: ");
Serial.print(r->vfo[1]);
Serial.print(", Data Size: ");
Serial.print(len);
Serial.println();
}
void updateStateFromRaduino(UBitxRigState& r) {
// Note, we really need to be checking a dirty flag for this. But, I don't have a dirty flag in this version of the data type...
if (vfoActive == VFO_A) {
rigState.vfo[0] = frequency;
rigState.flags &= ~UBITX_VFOB_FLAG;
} else if (vfoActive == VFO_B) {
rigState.vfo[1] = frequency;
rigState.flags |= UBITX_VFOB_FLAG;
}
rigState.rit = ritRxFrequency - frequency;
rigState.flags = 0;
rigState.flags |= (vfoActive == VFO_B ? UBITX_VFOB_FLAG : 0);
rigState.flags |= (cwMode != 0 ? UBITX_CW_FLAG : 0);
rigState.flags |= (isUSB != 0 ? UBITX_USB_FLAG : 0);
rigState.flags |= (splitOn != 0 ? UBITX_SPLIT_FLAG : 0);
rigState.flags |= (ritOn != 0 ? UBITX_RIT_FLAG : 0);
}
void updateRaduinoFromState(UBitxRigState& r) {
vfoActive = rigState.flags & UBITX_VFOB_FLAG ? VFO_B : VFO_A;
if (vfoActive == VFO_A) {
if (rigState.vfo[0] != frequency) {
setFrequency(rigState.vfo[0]);
}
} else if (vfoActive == VFO_B) {
if (rigState.vfo[1] != frequency) {
setFrequency(rigState.vfo[1]);
}
}
ritRxFrequency = frequency + rigState.rit;
splitOn = rigState.flags & UBITX_SPLIT_FLAG ? 1 : 0;
ritOn = rigState.flags & UBITX_RIT_FLAG ? 1 : 0;
isUSB = rigState.flags & UBITX_USB_FLAG ? 1 : 0;
if (rigState.flags & UBITX_CW_FLAG) {
cwMode = isUSB ? 2 : 1; // 2 = cwu / 1 = cwl
} else {
cwMode = 0;
}
}

Binary file not shown.

View File

@@ -0,0 +1,342 @@
EESchema-LIBRARY Version 2.4
#encoding utf-8
#
# Amplifier_Operational_LM324A
#
DEF Amplifier_Operational_LM324A U 0 5 Y Y 5 L N
F0 "U" 0 200 50 H V L CNN
F1 "Amplifier_Operational_LM324A" 0 -200 50 H V L CNN
F2 "" -50 100 50 H I C CNN
F3 "" 50 200 50 H I C CNN
ALIAS LM324 TLC274 TLC279 TL074 LM324A MCP6004 TL084 TL064 LMV324 LMC6484 MCP604 MC33079 MC33174 MC33179 OPA1604 OPA1679 OPA4134 OPA4340UA OPA4376 MCP6L94 TSV914 ADA4807-4 TSV994
$FPLIST
SOIC*3.9x8.7mm*P1.27mm*
DIP*W7.62mm*
TSSOP*4.4x5mm*P0.65mm*
SSOP*5.3x6.2mm*P0.65mm*
MSOP*3x3mm*P0.5mm*
$ENDFPLIST
DRAW
P 4 1 1 10 -200 200 200 0 -200 -200 -200 200 f
P 4 2 1 10 -200 200 200 0 -200 -200 -200 200 f
P 4 3 1 10 -200 200 200 0 -200 -200 -200 200 f
P 4 4 1 10 -200 200 200 0 -200 -200 -200 200 f
X ~ 1 300 0 100 L 50 50 1 1 O
X - 2 -300 -100 100 R 50 50 1 1 I
X + 3 -300 100 100 R 50 50 1 1 I
X + 5 -300 100 100 R 50 50 2 1 I
X - 6 -300 -100 100 R 50 50 2 1 I
X ~ 7 300 0 100 L 50 50 2 1 O
X + 10 -300 100 100 R 50 50 3 1 I
X ~ 8 300 0 100 L 50 50 3 1 O
X - 9 -300 -100 100 R 50 50 3 1 I
X + 12 -300 100 100 R 50 50 4 1 I
X - 13 -300 -100 100 R 50 50 4 1 I
X ~ 14 300 0 100 L 50 50 4 1 O
X V- 11 -100 -300 150 U 50 50 5 1 W
X V+ 4 -100 300 150 D 50 50 5 1 W
ENDDRAW
ENDDEF
#
# Amplifier_Operational_LM358
#
DEF Amplifier_Operational_LM358 U 0 5 Y Y 3 L N
F0 "U" 0 200 50 H V L CNN
F1 "Amplifier_Operational_LM358" 0 -200 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
SOIC*3.9x4.9mm*P1.27mm*
DIP*W7.62mm*
TO*99*
OnSemi*Micro8*
TSSOP*3x3mm*P0.65mm*
TSSOP*4.4x3mm*P0.65mm*
MSOP*3x3mm*P0.65mm*
SSOP*3.9x4.9mm*P0.635mm*
LFCSP*2x2mm*P0.5mm*
*SIP*
SOIC*5.3x6.2mm*P1.27mm*
$ENDFPLIST
DRAW
P 4 1 1 10 -200 200 200 0 -200 -200 -200 200 f
P 4 2 1 10 -200 200 200 0 -200 -200 -200 200 f
X ~ 1 300 0 100 L 50 50 1 1 O
X - 2 -300 -100 100 R 50 50 1 1 I
X + 3 -300 100 100 R 50 50 1 1 I
X + 5 -300 100 100 R 50 50 2 1 I
X - 6 -300 -100 100 R 50 50 2 1 I
X ~ 7 300 0 100 L 50 50 2 1 O
X V- 4 -100 -300 150 U 50 50 3 1 W
X V+ 8 -100 300 150 D 50 50 3 1 W
ENDDRAW
ENDDEF
#
# Amplifier_Operational_TL072
#
DEF Amplifier_Operational_TL072 U 0 5 Y Y 3 L N
F0 "U" 0 200 50 H V L CNN
F1 "Amplifier_Operational_TL072" 0 -200 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
ALIAS LM358 AD8620 LMC6062 LMC6082 TL062 TL072 TL082 NE5532 SA5532 RC4558 RC4560 RC4580 LMV358 TS912 TSV912IDT TSV912IST TLC272 TLC277 MCP602 OPA1678 OPA2134 OPA2340 OPA2376xxD OPA2376xxDGK MC33078 MC33178 LM4562 OP249 OP275 ADA4075-2 MCP6002-xP MCP6002-xSN MCP6002-xMS LM7332 OPA2333xxD OPA2333xxDGK LMC6482 LT1492 LTC6081xMS8 LM6172 MCP6L92 NJM2043 NJM2114 NJM4556A NJM4558 NJM4559 NJM4560 NJM4580 NJM5532 ADA4807-2ARM OPA2691 LT6234 OPA2356xxD OPA2356xxDGK OPA1612AxD MC33172 OPA1602 TLV2372 LT6237 OPA2277
$FPLIST
SOIC*3.9x4.9mm*P1.27mm*
DIP*W7.62mm*
TO*99*
OnSemi*Micro8*
TSSOP*3x3mm*P0.65mm*
TSSOP*4.4x3mm*P0.65mm*
MSOP*3x3mm*P0.65mm*
SSOP*3.9x4.9mm*P0.635mm*
LFCSP*2x2mm*P0.5mm*
*SIP*
SOIC*5.3x6.2mm*P1.27mm*
$ENDFPLIST
DRAW
P 4 1 1 10 -200 200 200 0 -200 -200 -200 200 f
P 4 2 1 10 -200 200 200 0 -200 -200 -200 200 f
X ~ 1 300 0 100 L 50 50 1 1 O
X - 2 -300 -100 100 R 50 50 1 1 I
X + 3 -300 100 100 R 50 50 1 1 I
X + 5 -300 100 100 R 50 50 2 1 I
X - 6 -300 -100 100 R 50 50 2 1 I
X ~ 7 300 0 100 L 50 50 2 1 O
X V- 4 -100 -300 150 U 50 50 3 1 W
X V+ 8 -100 300 150 D 50 50 3 1 W
ENDDRAW
ENDDEF
#
# Connector_Conn_01x02_Male
#
DEF Connector_Conn_01x02_Male J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Conn_01x02_Male" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
X Pin_1 1 200 0 150 L 50 50 1 1 P
X Pin_2 2 200 -100 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x03_Male
#
DEF Connector_Conn_01x03_Male J 0 40 Y N 1 F N
F0 "J" 0 200 50 H V C CNN
F1 "Connector_Conn_01x03_Male" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
X Pin_1 1 200 100 150 L 50 50 1 1 P
X Pin_2 2 200 0 150 L 50 50 1 1 P
X Pin_3 3 200 -100 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x06_Male
#
DEF Connector_Conn_01x06_Male J 0 40 Y N 1 F N
F0 "J" 0 300 50 H V C CNN
F1 "Connector_Conn_01x06_Male" 0 -400 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -295 0 -305 1 1 6 F
S 34 -195 0 -205 1 1 6 F
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
S 34 205 0 195 1 1 6 F
P 2 1 1 6 50 -300 34 -300 N
P 2 1 1 6 50 -200 34 -200 N
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
P 2 1 1 6 50 200 34 200 N
X Pin_1 1 200 200 150 L 50 50 1 1 P
X Pin_2 2 200 100 150 L 50 50 1 1 P
X Pin_3 3 200 0 150 L 50 50 1 1 P
X Pin_4 4 200 -100 150 L 50 50 1 1 P
X Pin_5 5 200 -200 150 L 50 50 1 1 P
X Pin_6 6 200 -300 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_C
#
DEF Device_C C 0 10 N Y 1 F N
F0 "C" 25 100 50 H V L CNN
F1 "Device_C" 25 -100 50 H V L CNN
F2 "" 38 -150 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
C_*
$ENDFPLIST
DRAW
P 2 0 1 20 -80 -30 80 -30 N
P 2 0 1 20 -80 30 80 30 N
X ~ 1 0 150 110 D 50 50 1 1 P
X ~ 2 0 -150 110 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_CP1
#
DEF Device_CP1 C 0 10 N N 1 F N
F0 "C" 25 100 50 H V L CNN
F1 "Device_CP1" 25 -100 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
CP_*
$ENDFPLIST
DRAW
A 0 -150 128 1287 513 0 1 20 N -80 -50 80 -50
P 2 0 1 20 -80 30 80 30 N
P 2 0 1 0 -70 90 -30 90 N
P 2 0 1 0 -50 70 -50 110 N
X ~ 1 0 150 110 D 50 50 1 1 P
X ~ 2 0 -150 130 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_D
#
DEF Device_D D 0 40 N N 1 F N
F0 "D" 0 100 50 H V C CNN
F1 "Device_D" 0 -100 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
TO-???*
*_Diode_*
*SingleDiode*
D_*
$ENDFPLIST
DRAW
P 2 0 1 8 -50 50 -50 -50 N
P 2 0 1 0 50 0 -50 0 N
P 4 0 1 8 50 50 50 -50 -50 0 50 50 N
X K 1 -150 0 100 R 50 50 1 1 P
X A 2 150 0 100 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_R_POT_US
#
DEF Device_R_POT_US RV 0 40 Y N 1 F N
F0 "RV" -175 0 50 V V C CNN
F1 "Device_R_POT_US" -100 0 50 V V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Potentiometer*
$ENDFPLIST
DRAW
P 2 0 1 0 0 -90 0 -100 N
P 2 0 1 0 0 100 0 90 N
P 2 0 1 0 100 0 60 0 N
P 4 0 1 0 45 0 90 20 90 -20 45 0 F
P 5 0 1 0 0 -30 40 -45 0 -60 -40 -75 0 -90 N
P 5 0 1 0 0 30 40 15 0 0 -40 -15 0 -30 N
P 5 0 1 0 0 90 40 75 0 60 -40 45 0 30 N
X 1 1 0 150 50 D 50 50 1 1 P
X 2 2 150 0 50 L 50 50 1 1 P
X 3 3 0 -150 50 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_R_US
#
DEF Device_R_US R 0 0 N Y 1 F N
F0 "R" 100 0 50 V V C CNN
F1 "Device_R_US" -100 0 50 V V C CNN
F2 "" 40 -10 50 V I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
R_*
$ENDFPLIST
DRAW
P 2 0 1 0 0 -90 0 -100 N
P 2 0 1 0 0 90 0 100 N
P 5 0 1 0 0 -30 40 -45 0 -60 -40 -75 0 -90 N
P 5 0 1 0 0 30 40 15 0 0 -40 -15 0 -30 N
P 5 0 1 0 0 90 40 75 0 60 -40 45 0 30 N
X ~ 1 0 150 50 D 50 50 1 1 P
X ~ 2 0 -150 50 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Transistor_BJT_PN2222A
#
DEF Transistor_BJT_PN2222A Q 0 0 Y N 1 F N
F0 "Q" 200 75 50 H V L CNN
F1 "Transistor_BJT_PN2222A" 200 0 50 H V L CNN
F2 "Package_TO_SOT_THT:TO-92_Inline" 200 -75 50 H I L CIN
F3 "" 0 0 50 H I L CNN
$FPLIST
TO?92*
$ENDFPLIST
DRAW
C 50 0 111 0 1 10 N
P 2 0 1 0 0 0 25 0 N
P 2 0 1 0 100 -100 25 -25 N
P 2 0 1 0 100 100 25 25 N
P 3 0 1 20 25 75 25 -75 25 -75 N
P 3 0 1 0 95 -95 75 -75 75 -75 N
P 5 0 1 0 45 -65 65 -45 85 -85 45 -65 45 -65 F
X E 1 100 -200 100 U 50 50 1 1 P
X B 2 -200 0 200 R 50 50 1 1 I
X C 3 100 200 100 D 50 50 1 1 P
ENDDRAW
ENDDEF
#
# power_+5V
#
DEF power_+5V #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -150 50 H I C CNN
F1 "power_+5V" 0 140 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 2 0 1 0 -30 50 0 100 N
P 2 0 1 0 0 0 0 100 N
P 2 0 1 0 0 100 30 50 N
X +5V 1 0 0 0 U 50 50 1 1 W N
ENDDRAW
ENDDEF
#
# power_GND
#
DEF power_GND #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -250 50 H I C CNN
F1 "power_GND" 0 -150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N
X GND 1 0 0 0 D 50 50 1 1 W N
ENDDRAW
ENDDEF
#
#End Library

View File

@@ -0,0 +1 @@
(kicad_pcb (version 4) (host kicad "dummy file") )

View File

@@ -0,0 +1,33 @@
update=22/05/2015 07:44:53
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[pcbnew]
version=1
LastNetListRead=
UseCmpFile=1
PadDrill=0.600000000000
PadDrillOvalY=0.600000000000
PadSizeH=1.500000000000
PadSizeV=1.500000000000
PcbTextSizeV=1.500000000000
PcbTextSizeH=1.500000000000
PcbTextThickness=0.300000000000
ModuleTextSizeV=1.000000000000
ModuleTextSizeH=1.000000000000
ModuleTextSizeThickness=0.150000000000
SolderMaskClearance=0.000000000000
SolderMaskMinWidth=0.000000000000
DrawSegmentWidth=0.200000000000
BoardOutlineThickness=0.100000000000
ModuleOutlineThickness=0.150000000000
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
EESchema Schematic File Version 4
EELAYER 30 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 2 2
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$EndSCHEMATC

View File

@@ -23,29 +23,27 @@ UBitxDSP DSP;
//static struct {
// GUItool: begin automatically generated code
AudioInputI2S lineIn; //xy=137,220
AudioInputUSB usbIn; //xy=142,326
AudioMixer4 rxAudio; //xy=394,134
AudioMixer4 txAudio; //xy=398,322
AudioOutputAnalog spkrOut; //xy=774,104
AudioOutputUSB usbOut; //xy=774,138
AudioOutputI2S lineOut; //xy=776,236
AudioInputI2S lineIn; //xy=385.9371643066406,1001.9600830078125
AudioInputUSB usbIn; //xy=390.9371643066406,1107.9600830078125
AudioMixer4 rxAudio; //xy=642.9371643066406,915.9600830078125
AudioMixer4 txAudio; //xy=646.9371643066406,1103.9600830078125
AudioOutputUSB usbOut; //xy=1022.9371643066406,919.9600830078125
AudioOutputI2S lineOut; //xy=1024.9371643066406,1017.9600830078125
AudioConnection patchCord1(lineIn, 0, rxAudio, 0);
AudioConnection patchCord2(lineIn, 1, txAudio, 0);
AudioConnection patchCord3(usbIn, 0, txAudio, 1);
AudioConnection patchCord4(usbIn, 1, txAudio, 2);
AudioConnection patchCord5(rxAudio, spkrOut);
AudioConnection patchCord6(rxAudio, 0, lineOut, 0);
AudioConnection patchCord7(rxAudio, 0, usbOut, 0);
AudioConnection patchCord8(rxAudio, 0, usbOut, 1);
AudioConnection patchCord9(txAudio, 0, lineOut, 1);
AudioControlSGTL5000 audioCtrl; //xy=403,463
AudioConnection patchCord5(rxAudio, 0, lineOut, 0);
AudioConnection patchCord6(rxAudio, 0, usbOut, 0);
AudioConnection patchCord7(rxAudio, 0, usbOut, 1);
AudioConnection patchCord8(txAudio, 0, lineOut, 1);
AudioControlSGTL5000 audioCtrl; //xy=651.9371643066406,1244.9600830078125
// GUItool: end automatically generated code
//} audio;
void UBitxDSP::begin() {
AudioMemory(20);
AudioMemory(16);
audioCtrl.enable();
audioCtrl.volume(0.0); // headphone volume...
audioCtrl.muteHeadphone(); // ...not used by UBitxDSP
@@ -66,8 +64,8 @@ void UBitxDSP::begin() {
// Rig (Line) Input (RX)
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
audioCtrl.unmuteLineout();
audioCtrl.lineInLevel(5, 5); // RX, TX
audioCtrl.lineOutLevel(29, 29); //RX, TX
audioCtrl.lineInLevel(9, 5); // RX, TX
audioCtrl.lineOutLevel(29, 31); //RX, TX
// Mic Input (TX)
audioCtrl.micGain(0); // TODO: set value
@@ -78,9 +76,6 @@ void UBitxDSP::begin() {
// SETUP THE AUDIO OUTPUTS
// Speaker Output (RX)
spkrOut.analogReference(INTERNAL);
// Line Output (RX)
// USB Output (RX)
@@ -91,11 +86,16 @@ void UBitxDSP::begin() {
rx();
}
void UBitxDSP::update() {
}
void UBitxDSP::end() {
}
void UBitxDSP::rx() {
// mute all tx audio
audioCtrl.micGain(0);
for (int i = 0; i < 4; i++) {
txAudio.gain(i, 0.0);
}
@@ -110,9 +110,10 @@ void UBitxDSP::txMicIn() {
rxAudio.gain(RX_AUDIO_CH, 0.0);
// restore the tx mic audio
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
audioCtrl.micGain(12);
for (int i = 0; i < 4; i++) {
if (i == TX_MIC_IN_CH)
txAudio.gain(i, 1.0);
txAudio.gain(i, 0.1);
else
txAudio.gain(i, 0.0);
}
@@ -125,7 +126,7 @@ void UBitxDSP::txLineIn() {
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
for (int i = 0; i < 4; i++) {
if (i == TX_LINE_IN_CH)
txAudio.gain(i, 1.0);
txAudio.gain(i, 0.1);
else
txAudio.gain(i, 0.0);
}
@@ -138,7 +139,7 @@ void UBitxDSP::txUSBIn() {
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
for (int i = 0; i < 4; i++) {
if (i == TX_USB_IN_CH1 || i == TX_USB_IN_CH2)
txAudio.gain(i, 1.0);
txAudio.gain(i, 0.1);
else
txAudio.gain(i, 0.0);
}

View File

@@ -26,6 +26,7 @@ class UBitxDSP {
public:
UBitxDSP() {};
void begin();
void update();
void end();
void rx();
void txMicIn();

View File

@@ -7,7 +7,7 @@
#define DBGPRINT(MSG) do { Serial.print("DBG: "); Serial.print(MSG); } while (0)
#define DBGPRINTLN(MSG) do { Serial.print("DBG: "); Serial.println(MSG); } while (0)
#define DBGNEWLINE() do { Serial.println(); } while (0)
#define DBGCMD(CMD) do { Serial.println("DBG: "); Serial.println(#CMD); CMD; } while (0)
#define DBGCMD(CMD) do { Serial.print("DBG: "); Serial.println(#CMD); CMD; } while (0)
#else
#define DBGPRINT(MSG) do {} while (0)
#define DBGPRINTLN(MSG) do {} while (0)

185
TeensyDSP/Keyer.cpp Normal file
View File

@@ -0,0 +1,185 @@
//======================================================================
//
// nanoIO paddle keyer (c) 2018, David Freese, W1HKJ
//
// based on code from Iambic Keyer Code Keyer Sketch
// Copyright (c) 2009 Steven T. Elliott
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details:
//
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
// Boston, MA 02111-1307 USA
//
//======================================================================
#include <Arduino.h>
//#include "TimerOne.h"
//#include "config.h"
#include "Keyer.h"
const uint8_t LP_in = KEYER_LEFT_PADDLE_PIN;
const uint8_t RP_in = KEYER_RIGHT_PADDLE_PIN;
//#define ST_Freq 600 // Set the Sidetone Frequency to 600 Hz
//======================================================================
// keyerControl bit definitions
//
#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
//======================================================================
//
// State Machine Defines
enum KSTYPE { IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
UBitxKeyer::UBitxKeyer(int wpm, float weight):
speed(wpm), symWeight(weight)
{
// Setup outputs
pinMode(LP_in, INPUT_PULLUP); // sets Left Paddle digital pin as input
pinMode(RP_in, INPUT_PULLUP); // sets Right Paddle digital pin as input
keyerState = IDLE;
keyerControl = 0;
keyMode = IAMBICA;
keyDown = false;
calcRatio();
}
// Calculate the length of dot, dash and silence
void UBitxKeyer::calcRatio()
{
float w = (1 + symWeight) / (symWeight -1);
spaceLen = (1200 / speed);
dotLen = spaceLen * (w - 1);
dashLen = (1 + w) * spaceLen;
}
void UBitxKeyer::setWPM(int wpm)
{
speed = wpm;
calcRatio();
}
//======================================================================
// Latch paddle press
//======================================================================
void UBitxKeyer::updatePaddleLatch()
{
if (digitalRead(LP_in) == LOW) {
keyerControl |= DIT_L;
}
if (digitalRead(RP_in) == LOW) {
keyerControl |= DAH_L;
}
}
bool UBitxKeyer::doPaddles()
{
if (keyMode == STRAIGHT) { // Straight Key
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW)) {
keyDown = true;
return true;
} else {
keyDown = false;
}
return false;
}
// keyerControl contains processing flags and keyer mode bits
// Supports Iambic A and B
// State machine based, uses calls to millis() for timing.
switch (keyerState) {
case IDLE: // Wait for direct or latched paddle press
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
updatePaddleLatch();
keyerState = CHK_DIT;
// letting this fall through // return true;
} else {
return false;
}
// break;
case CHK_DIT: // See if the dit paddle was pressed
if (keyerControl & DIT_L) {
keyerControl |= DIT_PROC;
ktimer = dotLen;
keyerState = KEYED_PREP;
return true;
} else { // fall through
keyerState = CHK_DAH;
}
case CHK_DAH: // See if dah paddle was pressed
if (keyerControl & DAH_L) {
ktimer = dashLen;
keyerState = KEYED_PREP;
// letting this fall through // return true;
} else {
keyerState = IDLE;
return false;
}
// break;
case KEYED_PREP: // Assert key down, start timing
// state shared for dit or dah
keyDown = true;
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state
// letting this fall through // return true;
// break;
case KEYED: // Wait for timer to expire
if (millis() > ktimer) { // are we at end of key down ?
keyDown = false;
ktimer = millis() + spaceLen; // inter-element time
keyerState = INTER_ELEMENT; // next state
// letting this fall through // return true;
} else if (keyMode == IAMBICB) { // Iambic B Mode ?
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
} else {
return true;
}
// break;
case INTER_ELEMENT: // Insert time between dits/dahs
updatePaddleLatch(); // 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
return true;
} else {
keyerControl &= ~(DAH_L); // clear dah latch
keyerState = IDLE; // go idle
return false;
}
} else {
return true;
}
// break;
}
return false; // resolve compiler warning; do we ever get here?
}
UBitxKeyer basicKeyer(15, 3.0);
UBitxKeyer& Keyer = basicKeyer;
//======================================================================
// EOF
//======================================================================

79
TeensyDSP/Keyer.h Normal file
View File

@@ -0,0 +1,79 @@
//**********************************************************************
//
// Keyer, a part of nanoIO
//
// nanoIO paddle keyer (c) 2018, David Freese, W1HKJ
//
// based on code from Iambic Keyer Code Keyer Sketch
// Copyright (c) 2009 Steven T. Elliott
//
// nanoIO is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// nanoIO is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
//
//Revisions:
//
//1.0.0: Initial release
//
//**********************************************************************
#ifndef __Keyer_h__
#define __Keyer_h__
#define IAMBICA 0
#define IAMBICB 1
#define STRAIGHT 2
#define KEYER_LEFT_PADDLE_PIN 17
#define KEYER_RIGHT_PADDLE_PIN 16
class UBitxKeyer
{
public:
UBitxKeyer(int wpm, float weight);
//void cw_pin(int pin);
//void ptt_pin(int pin);
void setWPM(int wpm);
inline void setMode(int mode) { keyMode = mode; }
inline int getMode() { return keyMode; }
inline bool isDown() { return keyDown; }
// void setWeight();
bool doPaddles();
private:
void calcRatio();
void updatePaddleLatch();
bool keyDown;
long ktimer;
int speed;
int dashLen; // Length of dash
int dotLen; // Length of dot
int spaceLen; // Length of space
float symWeight;
char keyerControl;
char keyerState;
int keyMode;
};
extern UBitxKeyer& Keyer;
#endif
//======================================================================
// EOF
//======================================================================

View File

@@ -30,10 +30,10 @@ float calcVSWR = 0.0;
float L_calcVSWR = 0.0;
byte scaledVSWR = 0;
byte L_scaledVSWR = 0;
int fwdPower = 0;
int L_fwdPower = 0;
int revPower = 0;
int L_revPower = 0;
float fwdPower = 0;
float L_fwdPower = 0;
float revPower = 0;
float L_revPower = 0;
//Control must have prefix 'v' or 's'

View File

@@ -107,11 +107,11 @@ extern float L_calcVSWR;
extern byte scaledVSWR;
extern byte L_scaledVSWR;
extern int fwdPower;
extern int L_fwdPower;
extern float fwdPower;
extern float L_fwdPower;
extern int revPower;
extern int L_revPower;
extern float revPower;
extern float L_revPower;
void sendHeader(char varType, char varIndex);
void sendCommandUL(char varIndex, unsigned long sendValue);

3
TeensyDSP/Rig.cpp Normal file
View File

@@ -0,0 +1,3 @@
#include "Rig.h"
UBitxRig Rig;

147
TeensyDSP/Rig.h Normal file
View File

@@ -0,0 +1,147 @@
#ifndef __Rig_h__
#define __Rig_h__
#include "RigState.h"
enum UpdateSource {
NoSource,
RaduinoSource,
CATSource
};
class UBitxRig {
public:
inline void begin() {}
inline void update() {}
inline unsigned long getFreqA() const { return state.vfo[0]; }
inline unsigned long getFreqB() const { return state.vfo[1]; }
inline long getRIT() const { return state.rit; }
inline long getXIT() const { return state.xit; }
inline bool isVFOA() const { return (state.flags & UBITX_VFOB_FLAG) != UBITX_VFOB_FLAG; }
inline bool isVFOB() const { return (state.flags & UBITX_VFOB_FLAG) == UBITX_VFOB_FLAG; }
inline bool isSplit() const { return (state.flags & UBITX_SPLIT_FLAG) == UBITX_SPLIT_FLAG; }
inline bool isRITOn() const { return (state.flags & UBITX_RIT_FLAG) == UBITX_RIT_FLAG; }
inline bool isXITOn() const { return (state.flags & UBITX_XIT_FLAG) == UBITX_XIT_FLAG; }
inline bool isCW() const { return (state.flags & UBITX_CW_FLAG) == UBITX_CW_FLAG; }
inline bool isLSB() const { return (state.flags & UBITX_USB_FLAG) != UBITX_USB_FLAG; }
inline bool isUSB() const { return (state.flags & UBITX_USB_FLAG) == UBITX_USB_FLAG; }
inline bool getAI() const { return autoInfo; }
inline bool updatedByCAT() const { return lastUpdatedBy == CATSource; }
inline bool updatedByRaduino() const { return lastUpdatedBy == RaduinoSource; }
inline void clearUpdate() { lastUpdatedBy = NoSource; }
inline void setRaduinoUpdate() { lastUpdatedBy = RaduinoSource; }
inline void setCATUpdate() { lastUpdatedBy = CATSource; }
inline void setFreqA(unsigned long freq, bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.vfo[0] = freq;
}
inline void setFreqB(unsigned long freq, bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.vfo[1] = freq;
}
inline void setRIT(short offset, bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.rit = offset;
}
inline void setXIT(short offset, bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.xit = offset;
}
inline void selectVFOA(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_VFOB_FLAG;
}
inline void selectVFOB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_VFOB_FLAG;
}
inline void toggleVFO(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags ^= UBITX_VFOB_FLAG;
}
inline void splitOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_SPLIT_FLAG;
}
inline void splitOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_SPLIT_FLAG;
}
inline void ritOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_RIT_FLAG;
}
inline void ritOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_RIT_FLAG;
}
inline void xitOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_XIT_FLAG;
}
inline void xitOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_XIT_FLAG;
}
inline void cwOn(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_CW_FLAG;
}
inline void cwOff(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_CW_FLAG;
}
inline void selectLSB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags &= ~UBITX_USB_FLAG;
}
inline void selectUSB(bool isCAT = false) {
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
state.flags |= UBITX_USB_FLAG;
}
inline void aiOn() { autoInfo = true; }
inline void aiOff() { autoInfo = false; }
uint8_t* const stateAsBytes() const { return (uint8_t* const)&state; }
inline void updateState(UBitxRigState& r, bool isCAT = false) {
if ((r.vfo[0] == state.vfo[0]) &&
(r.vfo[1] == state.vfo[1]) &&
(r.rit == state.rit) &&
(r.xit == state.xit) &&
(r.flags == state.flags)) {
return;
} else {
state = r;
lastUpdatedBy = isCAT ? CATSource : RaduinoSource;
}
}
private:
bool autoInfo = false;
UpdateSource lastUpdatedBy = NoSource;
UBitxRigState state;
};
extern UBitxRig Rig;
#endif

174
TeensyDSP/RigState.h Normal file
View File

@@ -0,0 +1,174 @@
#ifndef __RigState_h__
#define __RigState_h__
#include <Arduino.h>
#define UBITX_VFOA_UPDATE 0x00000001
#define UBITX_VFOB_UPDATE 0x00000002
#define UBITX_RIT_UPDATE 0x00000004
#define UBITX_XIT_UPDATE 0x00000008
#define UBITX_FLAGS_UPDATE 0x00000010
#define UBITX_VFOB_FLAG 0x00000001
#define UBITX_SPLIT_FLAG 0x00000002
#define UBITX_RIT_FLAG 0x00000004
#define UBITX_XIT_FLAG 0x00000008
#define UBITX_CW_FLAG 0x00000010
#define UBITX_USB_FLAG 0x00000020
#define UBITX_TX_FLAG 0x00000040
struct UBitxRigState {
uint32_t header = 0;
uint32_t vfo[2];
int32_t rit;
int32_t xit;
uint32_t flags = 0;
};
/**********************************************************************/
template<typename T, int ID>
struct Field {
byte id = ID;
bool dirty;
T data;
inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; }
template<typename STREAM> void writeChanges() {
if (dirty) {
STREAM().write(id);
STREAM().write((byte*)&data, sizeof(T));
}
}
template<typename STREAM> int read() {
size_t len = 0;
byte* ptr = (byte*)&data;
while (STREAM().available() && len < sizeof(T)) {
ptr[len++] = STREAM().read();
}
return len;
}
inline void merge(Field<T,ID>& f) {
if (dirty) {
f.data = data;
f.dirty = true;
} else if (f.dirty) {
data = f.data;
dirty = true;
}
}
inline void markClean() { dirty = false; }
};
struct RigState {
Field<uint32_t, 0> vfoA;
Field<uint32_t, 1> vfoB;
Field<int32_t, 2> rit;
Field<int32_t, 3> xit;
Field<uint32_t, 4> flags;
inline size_t sizeOfWrite() {
size_t size = 0;
size += vfoA.sizeOfWrite();
size += vfoB.sizeOfWrite();
size += rit.sizeOfWrite();
size += xit.sizeOfWrite();
size += flags.sizeOfWrite();
return size;
}
template<typename STREAM> void writeChanges() {
vfoA.writeChanges<STREAM>();
vfoB.writeChanges<STREAM>();
rit.writeChanges<STREAM>();
xit.writeChanges<STREAM>();
flags.writeChanges<STREAM>();
}
template<typename STREAM> void readChanges(size_t size) {
size_t len = 0;
while (STREAM().available() && len < size) {
switch(STREAM().read()) {
case 0:
len += vfoA.read<STREAM>();
break;
case 1:
len += vfoB.read<STREAM>();
break;
case 2:
len += rit.read<STREAM>();
break;
case 3:
len += xit.read<STREAM>();
break;
case 4:
len += flags.read<STREAM>();
break;
default:
;
}
}
}
inline void merge(RigState& r) {
vfoA.merge(r.vfoA);
vfoB.merge(r.vfoB);
rit.merge(r.rit);
xit.merge(r.xit);
flags.merge(r.flags);
}
inline void markClean(RigState& r) {
vfoA.markClean();
vfoB.markClean();
rit.markClean();
xit.markClean();
flags.markClean();
}
};
/*
Protocol discussion:
- I2C master: Raduino
- I2C slave: TeensyDSP
Raduino state:
- Baseline uBITX variables
- I2C buffer
- On I2C transmit: make updates based on current variables
- On I2C receive:
- Update based on received I2C responses
- Update associated variables
TeensyDSP state:
- CAT buffer
- Used to receive command from CAT (when commands arrive via Serial)
- Used to transmit state to Raduino (when requested via Wire1)
- Raduino buffer
- Used to receive state from Raduino (when received via Wire1)
- Used to transmit responses to CAT (over Serial)
- Questions
- How can these be synchronized?
- At the tail end of an I2C request handler. Before sending the response to the Raduino via I2C:
- Copy updated CAT buffer items to the Raduino buffer.
- Copy updated Raduino buffer items to the CAT buffer.
- In the case of conflicts, CAT wins.
- Transmit the CAT buffer state to the Raduino.
- TeensyDSP updates 'outgoing' state based on CAT inputs.
- Make change to data.
- Mark data as dirty, if different than incoming state.
- When requested, Teensy DSP sends 'outgoing' state to Raduino.
- Send dirty data over I2C.
- Mark data as clean.
*/
#endif

View File

@@ -8,31 +8,39 @@
/**********************************************************************/
#ifndef UBITX_SENSORS_S_METER_PIN
#define UBITX_SENSORS_S_METER_PIN 27
#define UBITX_SENSORS_S_METER_PIN 28
#endif
#ifndef UBITX_SENSORS_FWD_PWR_PIN
#define UBITX_SENSORS_FWD_PWR_PIN 20
#define UBITX_SENSORS_FWD_PWR_PIN 26
#endif
#ifndef UBITX_SENSORS_REV_PWR_PIN
#define UBITX_SENSORS_REV_PWR_PIN 28
#define UBITX_SENSORS_REV_PWR_PIN 20
#endif
#ifndef UBITX_SENSORS_SUPPLY_PIN
#define UBITX_SENSORS_SUPPLY_PIN 21
#endif
#ifndef UBITX_SENSORS_SPARE1_PIN
#define UBITX_SENSORS_SPARE1_PIN 27
#endif
#ifndef UBITX_SENSORS_SPARE2_PIN
#define UBITX_SENSORS_SPARE2_PIN 31
#endif
#ifndef UBITX_SENSORS_AVG_SAMPLES
#define UBITX_SENSORS_AVG_SAMPLES 16
#endif
#ifndef UBITX_SENSORS_S_METER_R1
#define UBITX_SENSORS_S_METER_R1 22000.0
#define UBITX_SENSORS_S_METER_R1 0.0
#endif
#ifndef UBITX_SENSORS_S_METER_R2
#define UBITX_SENSORS_S_METER_R2 33000.0
#define UBITX_SENSORS_S_METER_R2 1.0
#endif
#ifndef UBITX_SENSORS_FWD_PWR_R1
@@ -101,6 +109,8 @@ const int uBitxSensorsSMeterPin = UBITX_SENSORS_S_METER_PIN;
const int uBitxSensorsFwdPwrPin = UBITX_SENSORS_FWD_PWR_PIN;
const int uBitxSensorsRevPwrPin = UBITX_SENSORS_REV_PWR_PIN;
const int uBitxSensorsSupplyPin = UBITX_SENSORS_SUPPLY_PIN;
const int uBitxSensorsSpare1Pin = UBITX_SENSORS_SPARE1_PIN;
const int uBitxSensorsSpare2Pin = UBITX_SENSORS_SPARE2_PIN;
const int uBitxSensorsAvgSamples = UBITX_SENSORS_AVG_SAMPLES;
const float uBitxSensorsSMeterR1 = UBITX_SENSORS_S_METER_R1;
const float uBitxSensorsSMeterR2 = UBITX_SENSORS_S_METER_R2;
@@ -162,10 +172,15 @@ class TrailingAverage {
* The new element/value to incorporate into the average.
*/
inline void add(T val) {
int last = (current - 1) % N;
average -= data[last];
//int last = (current - 1) % N;
//average -= data[last];
//current = (current + 1) % N;
//data[current] = val / divisor;
//average += data[current];
average -= data[current];
data[current] = val / divisor;
average += data[current];
current = (current + 1) % N;
average += data[current] / divisor;
}
/*!
@@ -200,12 +215,16 @@ class UBitxSensors {
sMeterPin(uBitxSensorsSMeterPin),
fwdPwrPin(uBitxSensorsFwdPwrPin),
revPwrPin(uBitxSensorsRevPwrPin),
supplyPin(uBitxSensorsSupplyPin)
supplyPin(uBitxSensorsSupplyPin),
spare1Pin(uBitxSensorsSpare1Pin),
spare2Pin(uBitxSensorsSpare2Pin)
{
pinMode(sMeterPin, INPUT); // analog
pinMode(fwdPwrPin, INPUT); // analog
pinMode(revPwrPin, INPUT); // analog
pinMode(supplyPin, INPUT); // analog
pinMode(spare1Pin, INPUT); // analog
pinMode(spare2Pin, INPUT); // analog
}
/*!
@@ -222,7 +241,7 @@ class UBitxSensors {
* measurements by reading the associated ADC pin.
*/
void updatePower() {
ADC::Sync_result value = adc.analogSyncRead(fwdPwrPin, revPwrPin);
ADC::Sync_result value = adc.analogSyncRead(revPwrPin, fwdPwrPin);
float fwdV = HF::adcIn(value.result_adc0);
float revV = HF::adcIn(value.result_adc1);
@@ -263,7 +282,7 @@ class UBitxSensors {
* @return Scaled S-Meter reading.
*/
int sMeterScaled() {
float sig = sMeter.read();
int sig = sMeter.read() >> 2;
// small number of elements; just doing a linear search
for (int i = uBitxSensorsSMeterLevels; i > 0; i--) {
if (sig > uBitxSensorsSMeterValues[i - 1]) {
@@ -328,6 +347,8 @@ class UBitxSensors {
int fwdPwrPin;
int revPwrPin;
int supplyPin;
int spare1Pin;
int spare2Pin;
// Buffers for averages
TrailingAverage<int, uBitxSensorsAvgSamples> sMeter;

View File

@@ -7,32 +7,42 @@
UBitxTR TR(DSP);
void UBitxTR::update(bool cw) {
void UBitxTR::update(bool cw, bool extKey) {
updateKey();
if (cw) {
if ((keyEnable && keyDown) || extKey) {
setTX();
} else {
setRX();
}
return;
}
updatePTT();
updateVOX();
updateKey();
if (isTX) {
// If we are currently transmitting, then ANY T/R release (key
// release) will result in exitting transmit... except for VOX
// and CAT which can only function as a release if it was enabled.
if (pttReleased() || keyReleased() ||
(voxEnabled && voxDeactivated()) ||
(catEnabled && catDeactivated())) {
(voxEnable && voxDeactivated()) ||
(catEnable && catDeactivated())) {
// first, stop transmitting; then, setup RX audio
DBGCMD( setRX() );
DBGCMD( dsp.rx() );
}
} else {
if ((pttEnabled && pttPressed()) || (voxEnabled && voxActivated())) {
if ((pttEnable && pttPressed()) || (voxEnable && voxActivated())) {
// first, setup TX audio; then, start transmitting (from Mic)
DBGCMD( dsp.txMicIn() );
DBGCMD( setTX() );
} else if (keyEnabled && keyPressed()) {
} else if (keyEnable && keyPressed()) {
// first, setup TX audio; then, start transmitting (from Line In)
DBGCMD( dsp.txLineIn() );
DBGCMD( setTX() );
} else if (catEnabled && catActivated()) {
} else if (catEnable && catActivated()) {
// first, setup TX audio; then, start transmitting (USB)
DBGCMD( dsp.txUSBIn() );
DBGCMD( setTX() );

View File

@@ -36,17 +36,32 @@ class UBitxTR {
DBGCMD( disableVOX() );
DBGCMD( enableKey() );
DBGCMD( enableCAT() );
DBGCMD( setRX() );
}
inline void enablePTT() { pttEnabled = true; }
inline void enableVOX() { voxEnabled = true; }
inline void enableKey() { keyEnabled = true; }
inline void enableCAT() { catEnabled = true; }
inline void disablePTT() { pttEnabled = false; }
inline void disableVOX() { voxEnabled = false; }
inline void disableKey() { keyEnabled = false; }
inline void disableCAT() { catEnabled = false; }
inline void enablePTT() { pttEnable = true; }
inline void enableVOX() { voxEnable = true; }
inline void enableKey() { keyEnable = true; }
inline void enableCAT() { catEnable = true; }
inline void disablePTT() { pttEnable = false; }
inline void disableVOX() { voxEnable = false; }
inline void disableKey() { keyEnable = false; }
inline void disableCAT() { catEnable = false; }
inline bool pttEnabled() { return pttEnable; }
inline bool voxEnabled() { return voxEnable; }
inline bool keyEnabled() { return keyEnable; }
inline bool catEnabled() { return catEnable; }
inline bool pttPressed() { return ptt.fell(); }
inline bool pttReleased() { return ptt.rose(); }
inline bool voxActivated() { return (L_voxActive != voxActive) && L_voxActive; }
inline bool voxDeactivated() { return (L_voxActive != voxActive) && voxActive; }
inline bool keyPressed() { return (L_keyDown != keyDown) && L_keyDown; }
inline bool keyReleased() { return (L_keyDown != keyDown) && keyDown; }
inline bool catActivated() { return (L_catActive != catActive) && L_catActive; }
inline bool catDeactivated() { return (L_catActive != catActive) && catActive; }
inline void catTX() {
L_catActive = catActive;
@@ -72,8 +87,12 @@ class UBitxTR {
* @param cw
* True if CW mode is currently active; false otherwise.
* Different/faster logic is used in CW mode.
*
* @param extKey
* True if an external keying signal (ie. CW keyer) is
* currently active (ie. key down).
*/
void update(bool cw = false);
void update(bool cw = false, bool extKey = false);
void end() {
}
@@ -94,48 +113,16 @@ class UBitxTR {
ptt.update();
}
inline bool pttPressed() {
return ptt.fell();
}
inline bool pttReleased() {
return ptt.rose();
}
inline void updateVOX() {
L_voxActive = voxActive;
voxActive = (digitalRead(voxPin) == LOW);
}
inline bool voxActivated() {
return (L_voxActive != voxActive) && L_voxActive;
}
inline bool voxDeactivated() {
return (L_voxActive != voxActive) && voxActive;
}
inline void updateKey() {
L_keyDown = keyDown;
keyDown = (digitalRead(keyPin) == LOW);
}
inline bool keyPressed() {
return (L_keyDown != keyDown) && L_keyDown;
}
inline bool keyReleased() {
return (L_keyDown != keyDown) && keyDown;
}
inline bool catActivated() {
return (L_catActive != catActive) && L_catActive;
}
inline bool catDeactivated() {
return (L_catActive != catActive) && catActive;
}
UBitxDSP& dsp;
Bounce ptt;
@@ -147,10 +134,10 @@ class UBitxTR {
bool isTX = false;
bool pttEnabled = false;
bool voxEnabled = false;
bool keyEnabled = false;
bool catEnabled = false;
bool pttEnable = false;
bool voxEnable = false;
bool keyEnable = false;
bool catEnable = false;
bool voxActive = false;
bool L_voxActive = false;

358
TeensyDSP/TS590.cpp Normal file
View File

@@ -0,0 +1,358 @@
#include <Arduino.h>
#include "TS590.h"
#include "Debug.h"
/**********************************************************************/
/*!
* @brief Send a command to the PC via CAT. Note that the command
* should not include the trailing terminator (;). That will
* be automatically added.
* @param format
* A printf-style format string.
* @param args
* Zero or more arguments to include in the command.
*/
void ts590SendCommand(const char* format, ...) {
static char outBuf[ts590CommandMaxLength];
va_list args;
va_start(args, format);
vsprintf(outBuf, format, args);
va_end(args);
Serial.print(outBuf);
Serial.print(";");
}
/**********************************************************************/
/*!
* @brief Create a new CAT command. It should be initialized with
* a 2-character command prefix.
* @param pre
* A 2-character command prefix. If more than 2 characters
* are supplied, only the first two will be used. If less
* than two are supplied, then the command will be
* initialized with a null prefix.
*/
TS590Command::TS590Command(const char* pre) {
if (strlen(pre) >= 2) {
myPrefix[0] = pre[0];
myPrefix[1] = pre[1];
}
}
TS590Command::~TS590Command() {}
/*!
* @brief Determine whether this is a Read command or not. by
* default, if it's a 2-letter command, it's a Read.
* @return True if a Read command; false otherwise.
*/
bool TS590Command::isReadCommand(const char* cmd) const {
if (strlen(cmd) == 2) {
return true;
} else {
return false;
}
}
/*!
* @brief Process the provided command. If the command is a Set
* command, it calls handleCommand(). If Auto Information
* is eet (by the rig), sendResponse() is called at the end.
* If the command is a Read command, it also calls
* sendResponse(). Finally, if necessary, it will return
* any error codes to the PC.
* @param cmd
* The current command string received from the PC via CAT.
* It should be null-terminated, and should no longer have
* the terminator (;).
*/
void TS590Command::process(const char* cmd) {
theError = NoError;
if (isReadCommand(cmd)) {
DBGCMD( sendResponse(cmd) );
} else {
DBGCMD( handleCommand(cmd) );
switch(theError) {
case NoError:
if (theRig->getAI()) {
DBGCMD( sendResponse(cmd) );
}
break;
case SyntaxError:
DBGCMD( ts590SyntaxError() );
break;
case CommError:
DBGCMD( ts590CommError() );
break;
case ProcessError:
DBGCMD( ts590ProcessError() );
break;
}
}
}
/*!
* @brief Set the syntax error flag. This is cleared at the
* beginning of each call to process().
*/
void TS590Command::setSyntaxError() {
theError = SyntaxError;
}
/*!
* @brief Set the comms error flag. This is cleared at the
* beginning of each call to process().
*/
void TS590Command::setCommError() {
theError = CommError;
}
/*!
* @brief Set the process error flag. This is cleared at the
* beginning of each call to process().
*/
void TS590Command::setProcessError() {
theError = ProcessError;
}
/*!
* @brief Set the rig that will be used to process commands.
* @param r
* Pointer to the UBitxRig object.
*/
void TS590Command::setRig(UBitxRig* r) {
theRig = r;
}
UBitxRig* TS590Command::theRig = &Rig;
TS590Error TS590Command::theError = NoError;
/**********************************************************************/
void TS590_FR::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) {
switch (cmd[2]) {
case '0':
rig()->selectVFOA(true);
rig()->splitOff(true);
break;
case '1':
rig()->selectVFOB(true);
rig()->splitOff(true);
break;
case '2':
// TODO: Need to add something for channel mode.
break;
default:
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_FR::sendResponse(const char* cmd) {
if (rig()->isVFOA()) {
ts590SendCommand("FR0");
} else if (rig()->isVFOB()) {
ts590SendCommand("FR1");
} else {
ts590SendCommand("FR2");
}
}
/**********************************************************************/
void TS590_FT::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) {
switch (cmd[2]) {
case '0':
if (rig()->isVFOA()) {
rig()->splitOff(true);
} else if (rig()->isVFOB()) {
rig()->splitOn(true);
} else {
setSyntaxError();
}
break;
case '1':
if (rig()->isVFOA()) {
rig()->splitOn(true);
} else if (rig()->isVFOB()) {
rig()->splitOff(true);
} else {
setSyntaxError();
}
break;
default:
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_FT::sendResponse(const char* cmd) {
if (rig()->isVFOA()) {
ts590SendCommand(rig()->isSplit() ? "FT1" : "FT0");
} else if (rig()->isVFOB()) {
ts590SendCommand(rig()->isSplit() ? "FT0" : "FT1");
} else {
ts590SendCommand("FT2");
}
}
/**********************************************************************/
void TS590_MD::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) {
switch (cmd[2]) {
case '0': // None (setting failure)
case '4': // FM - not supported
case '5': // AM - not supported
case '6': // FSK - not supported
case '8': // None (setting failure)
case '9': // FSK-R - not supported
setProcessError();
break;
case '1': // LSB
rig()->selectLSB(true);
rig()->cwOff(true);
break;
case '2': // USB
rig()->selectUSB(true);
rig()->cwOff(true);
break;
case '3': // CW
rig()->selectUSB(true);
rig()->cwOn(true);
break;
case '7': // CW-R
rig()->selectLSB(true);
rig()->cwOn(true);
break;
default:
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_MD::sendResponse(const char* cmd) {
if (rig()->isCW()) {
if (rig()->isUSB()) {
ts590SendCommand("MD3");
} else {
ts590SendCommand("MD7");
}
} else {
if (rig()->isUSB()) {
ts590SendCommand("MD2");
} else {
ts590SendCommand("MD1");
}
}
}
/**********************************************************************/
TS590_FA cmdFA;
TS590_FB cmdFB;
TS590_FR cmdFR;
TS590_FT cmdFT;
TS590_MD cmdMD;
TS590Command* catCommands[] = {
&cmdFA,
&cmdFB,
&cmdFR,
&cmdFT,
&cmdMD
};
int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]);
/**********************************************************************/
void UBitxTS590::begin() {
Serial.begin(9600); // USB is always 12 Mbit/sec
#ifdef DEBUG
delay(500);
Serial.print("DBG: Number of CAT commands: ");
Serial.println(numCommands);
for (int i = 0; i < numCommands; i++) {
Serial.print(" ");
Serial.println(commands[i]->prefix());
}
#endif
}
void UBitxTS590::update() {
char incomingChar;
while (Serial.available()) {
if (bufLen < ts590CommandMaxLength) {
incomingChar = Serial.read();
if (incomingChar == ';') {
buf[bufLen++] = '\0';
strupr(buf);
processCommand();
} else if (incomingChar == '\n' && bufLen == 0) {
;
} else {
buf[bufLen++] = incomingChar;
}
} else {
// too long... we're going to bail on this.
ts590SyntaxError();
bufLen = 0;
}
}
}
typedef class TS590Command* PCmd;
int compareCATCommands(const void* a, const void* b) {
TS590Command const *B = *(TS590Command const **)b;
int cmp = strncmp((char*)a, (char*)B->prefix(), 2);
#ifdef DEBUG
Serial.print("Comparison: ");
Serial.print((char*)a);
Serial.print(" ? ");
Serial.print((char*)B->prefix());
Serial.print(" --> ");
Serial.println(cmp);
#endif
return cmp;
}
void UBitxTS590::processCommand() {
TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
if (cmd == NULL) {
ts590SyntaxError();
} else {
(*cmd)->process(buf);
}
bufLen = 0;
}
UBitxTS590 TS590(catCommands, numCatCommands);
/**********************************************************************/

183
TeensyDSP/TS590.h Normal file
View File

@@ -0,0 +1,183 @@
#ifndef __TS590_h__
#define __TS590_h__
#include <Arduino.h>
#include "Rig.h"
/**********************************************************************/
#define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null)
const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;
void ts590SendCommand(const char*, ...);
/*!
* @brief Send a syntax error response to the PC via CAT.
*/
inline void ts590SyntaxError() { ts590SendCommand("?"); }
/*!
* @brief Send a communications error response to the PC via CAT.
*/
inline void ts590CommError() { ts590SendCommand("E"); }
/*!
* @brief Send a processing error response to the PC via CAT.
*/
inline void ts590ProcessError() { ts590SendCommand("O"); }
enum TS590Error {
NoError,
SyntaxError,
CommError,
ProcessError
};
/**********************************************************************/
/*!
* @brief A TS590S/SG "CAT" command. This is the base class for all
* CAT commands.
*/
class TS590Command {
public:
TS590Command(const char* pre);
virtual ~TS590Command() = 0;
/*!
* @brief Return the 2-character prefix for the command.
* @return The 2-character prefix for the command.
*/
inline const char* prefix() const { return &myPrefix[0]; }
/*!
* @brief Return the rig that this command will be used to control.
*/
inline UBitxRig* rig() const { return theRig; }
/*!
* @brief Handle the provided Set command. If the Set command
* results in an error, then set the appropriate flag with
* setSyntaxError(), setCommError(), or setProcessError().
* @param cmd
* The current command string received from the PC via CAT.
* It should be null-terminated, and should no longer have
* the terminator (;).
*/
virtual void handleCommand(const char* cmd) = 0;
/*!
* @brief Send a response back to the PC. This assumes a
* successful command (no errors).
*/
virtual void sendResponse(const char* cmd) = 0;
virtual bool isReadCommand(const char* cmd) const;
void process(const char* cmd);
static void setSyntaxError();
static void setCommError();
static void setProcessError();
static void setRig(UBitxRig* r);
private:
char myPrefix[3] = "\0\0";
static TS590Error theError;
static UBitxRig* theRig;
};
/**********************************************************************/
/*!
* @brief CAT command for setting or reading the VFO A/B frequency.
*/
template<bool VFOA>
class TS590_FAB : public TS590Command {
public:
TS590_FAB(): TS590Command(VFOA ? "FA" : "FB") {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 13) {
unsigned long freq = strtoul(&cmd[2], NULL, 10);
if (VFOA) {
rig()->setFreqA(freq, true);
} else {
rig()->setFreqB(freq, true);
}
} else {
setSyntaxError();
}
}
virtual void sendResponse(const char* cmd) {
ts590SendCommand(VFOA ? "FA%011u" : "FB%011u", VFOA ? rig()->getFreqA() : rig()->getFreqB());
}
};
typedef TS590_FAB<true> TS590_FA;
typedef TS590_FAB<false> TS590_FB;
/**********************************************************************/
/*!
* @brief CAT command for setting the receiver VFO. This will always
* disable split mode, if it was previously enabled.
*/
class TS590_FR : public TS590Command {
public:
TS590_FR(): TS590Command("FR") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
};
/**********************************************************************/
/*!
* @brief CAT command for setting the transmitter VFO. If it is
* different than the receiver VFO, then split mode will be
* automatically enabled.
*/
class TS590_FT : public TS590Command {
public:
TS590_FT(): TS590Command("FT") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
};
/**********************************************************************/
/*!
* @brief CAT command for setting the mode.
*/
class TS590_MD : public TS590Command {
public:
TS590_MD(): TS590Command("MD") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
};
/**********************************************************************/
class UBitxTS590 {
public:
UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}
void begin();
void update();
private:
void processCommand();
char buf[ts590CommandMaxLength] = {0};
int bufLen = 0;
TS590Command** commands;
int numCommands;
};
extern UBitxTS590 TS590;
#endif
/**********************************************************************/

View File

@@ -10,9 +10,13 @@ KD8CEC, Ian Lee
#include <Arduino.h>
#include "Debug.h"
#include "DSP.h"
#include "Keyer.h"
#include "Nextion.h"
#include "Rig.h"
#include "RigState.h"
#include "Sensors.h"
#include "TR.h"
#include "TS590.h"
//================================================================
//COMMUNICATION SECTION
@@ -63,3 +67,9 @@ extern int magnitudelimit_low;
//SWR
#define I2CMETER_CALCR 0x55 //Calculated SWR Meter
#define I2CMETER_UNCALCR 0x54 //Uncalculated SWR Meter
// Raduino provides updated data to TeensyDSP
#define I2CMETER_RIGINF 0x50
// Raduino requests any CAT updates from TeensyDSP
#define I2CMETER_REQCAT 0x51

View File

@@ -16,6 +16,11 @@
//const uint8_t responseFooter[4]={'"', 0xFF, 0xFF, 0xFF};
//const char hexCodes[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', };
#ifdef DEBUG
int i2cCmdCounter[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int i2cRespCounter[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#endif
unsigned long SAMPLE_INTERVAL = 0;
int i2cCommand = 0;
@@ -308,6 +313,12 @@ void sendMeterData(uint8_t isSend)
void setup()
{
// Startup each of the subsystems, beginning with CAT.
DBGCMD( TS590.begin() );
DBGCMD( TR.begin() );
DBGCMD( Rig.begin() );
DBGCMD( DSP.begin() );
// load configuration
EEPROM.get(EEPROM_DSPTYPE, DSPType);
if (DSPType > 5)
@@ -350,11 +361,10 @@ void setup()
SAMPLE_INTERVAL = round(1000000 * (1.0 / SAMPLE_FREQUENCY));
//calculateCoeff(cwDecodeHz); //Set 750Hz //9 * 50 + 300 = 750Hz
//Serial1.println("Start...");
DBGCMD( DSP.begin() );
DBGCMD( TR.begin() );
}
bool sentRigInfFlag = false;
/*!
@brief Receive a command via I2C. The most recent command will be received, which will
indicate which data the DSP should be preparing to return.
@@ -364,16 +374,37 @@ void setup()
void i2cReceiveEvent(size_t numBytes)
{
int readCommand = 0;
bool exitLoop = false;
UBitxRigState tmpState;
while (Wire1.available() > 0) // for Last command
{
while (Wire1.available() > 0 && !exitLoop) {
readCommand = Wire1.read();
// KC4UPR: Note that this looks to be only reading the last command, i.e.
// if multiple commands have been queued up, only the last will get executed.
if (readCommand == I2CMETER_RIGINF) {
size_t len = 0;
uint8_t* const ptr = (uint8_t* const)&tmpState;
while ((Wire1.available() > 0) && (len < sizeof(UBitxRigState))) {
ptr[len++] = Wire1.read();
}
if (!Rig.updatedByCAT()) {
Rig.updateState(tmpState);
}
sentRigInfFlag = false; // so we know that we need to send the flag first
exitLoop = true;
}
}
// while (Wire1.available() > 0) // for Last command
// {
// readCommand = Wire1.read();
// // KC4UPR: Note that this looks to be only reading the last command, i.e.
// // if multiple commands have been queued up, only the last will get executed.
// }
if (0x50 <= readCommand && readCommand <= 0x59)
{
#ifdef DEBUG
i2cCmdCounter[readCommand - 0x50]++;
#endif
i2cCommand = readCommand;
}
}
@@ -393,21 +424,60 @@ void i2cRequestEvent(void)
case I2CMETER_CALCS:
// Returns an already-calculated S-meter value.
Wire1.write(scaledSMeter);
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break;
case I2CMETER_UNCALCS:
// Returns a raw signal strength value.
Wire1.write(Sensors.sMeterUnscaled() >> 2); // divided by 4... do we want this?
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break;
case I2CMETER_CALCP:
// Returns a raw forward power value.
Wire1.write(fwdPower);
Wire1.write(int(fwdPower * 100.0));
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break;
case I2CMETER_CALCR:
// Returns a raw reverse power value.
Wire1.write(revPower);
Wire1.write(int(revPower * 100.0));
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break;
case I2CMETER_RIGINF:
// Receive current rig state; transmit any CAT updates, if required.
//Wire1.write(catState.header); // temporary - just writing a single, null byte
//break;
case I2CMETER_REQCAT:
// Provide latest CAT updates, if any.
//Wire1.write(catState.header); // temporary - just writing a single, null byte
if (Rig.updatedByCAT()) {
if (sentRigInfFlag) {
DBGPRINTLN("I2CMETER_REQCAT -- updated by CAT");
Wire1.write(Rig.stateAsBytes(), sizeof(UBitxRigState));
Rig.clearUpdate();
} else {
Wire1.write(1);
sentRigInfFlag = true;
}
} else {
DBGPRINTLN("I2CMETER_REQCAT -- NOT updated by CAT");
//Wire1.write(Rig.stateAsBytes(), sizeof(uint8_t));
Wire1.write(0);
}
#ifdef DEBUG
i2cRespCounter[i2cCommand - 0x50]++;
#endif
break;
default:
@@ -449,16 +519,18 @@ const int adcIntervalMillis = ADC_INTERVAL_MS;
int frameCounter = 0;
#endif
/**********************************************************************/
void loop()
{
//char isProcess = 0; // 0: init, 1: complete ADC sampling, 2: complete FFT
//isProcess = 0;
forwardData();
// One-shot delay to ensure everything is booted up (primarily, the
// Nextion, and secondarily the Raduino.
if (isBooted < 100)
{
//Delay 20msec
// delay 20msec
for (int i = 0; i < 20; i++)
{
forwardData();
@@ -468,15 +540,35 @@ void loop()
return;
}
// If CW mode, we need to update keying a lot...
if (Rig.isCW()) {
if (Rig.isCW()) Keyer.doPaddles();
TR.update(Rig.isCW(), Keyer.isDown());
//if (TR.transmitting()) return;
}
// Start out by forwarding any data sitting in the RX buffer. We will
// do this as often as possible.
forwardData();
if (sinceFrameMillis > frameIntervalMillis) {
// Do stuff that we do once per frame--I/O.
// TODO: debug output (frame skipping / utilization).
sinceFrameMillis = 0;
// Update each of the subsystems, beginning with CAT control.
TS590.update();
TR.update(Rig.isCW(), Keyer.isDown());
Rig.update();
DSP.update();
//if (Rig.isCW()) return;
#ifdef DEBUG
// For debugging, output some debug info every 1.0" (40 frames @ 40 Hz).
frameCounter++;
if (frameCounter % 40 == 0) {
Serial.println("======================================================================");
Serial.print("DBG: Frame: ");
Serial.print(frameCounter);
if (isTX) {
@@ -489,6 +581,14 @@ void loop()
} else {
Serial.println(", TR State: RX");
}
Serial.print("VFO A: ");
Serial.print(Rig.getFreqA());
Serial.print(", VFO B: ");
Serial.print(Rig.getFreqB());
Serial.print(", Data Size: ");
Serial.print(sizeof(UBitxRigState));
Serial.println();
Serial.println("----------------------------------------------------------------------");
Serial.print("DBG: S-Meter Raw: ");
Serial.print(Sensors.sMeterUnscaled());
Serial.print(", S-Meter Scaled: ");
@@ -501,11 +601,30 @@ void loop()
Serial.print(fwdPower, 2);
Serial.print(", REV PWR: ");
Serial.println(revPower, 2);
Serial.print("Audio Memory: ");
Serial.print(AudioMemoryUsage());
Serial.print(",");
Serial.println(AudioMemoryUsageMax());
Serial.println("----------------------------------------------------------------------");
Serial.print("Enabled/Active: PTT: ");
Serial.print(TR.pttEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.pttPressed() ? "Y" : "N");
Serial.print(", VOX: ");
Serial.print(TR.voxEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.voxActivated() ? "Y" : "N");
Serial.print(", Key: ");
Serial.print(TR.keyEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.keyPressed() ? "Y" : "N");
Serial.print(", CAT: ");
Serial.print(TR.catEnabled() ? "Y" : "N"); Serial.print("/"); Serial.print(TR.catActivated() ? "Y" : "N");
Serial.println();
Serial.print("I2C Command/Response: ");
for (int i = 0x50; i <= 0x59; i++) {
Serial.print(i, HEX); Serial.print(": ");
Serial.print(i2cCmdCounter[i - 0x50]); Serial.print("/");
Serial.print(i2cRespCounter[i - 0x50]); Serial.print(", ");
}
Serial.println();
}
#endif
TR.update();
if (isTX) {
calcVSWR = Sensors.VSWR();
scaledVSWR = byte(Sensors.scaledVSWR());
@@ -521,14 +640,14 @@ void loop()
// Send forward power.
if (L_fwdPower != fwdPower) {
L_fwdPower = fwdPower;
sendCommandL('m', fwdPower * 100); // watts x 100?
sendCommandL('m', int(fwdPower * 100.0)); // watts x 100?
sendCommand1Num('m', 2);
}
// Send reverse power.
//if (L_revPower != revPower) {
// L_revPower = revPower;
// sendCommandL('m', revPower * 100); // watts x 100?
// sendCommandL('m', int(revPower * 100.0)); // watts x 100?
// sendCommand1Num('m', 2);
//}
@@ -566,6 +685,8 @@ void loop()
forwardData();
}
if (Rig.isCW()) return; // In CW, the ADC measurement messes with the timing. So need to use interrupts on the Keyer, and/or continuous ADC.
if (sinceADCMillis > adcIntervalMillis) {
// Do stuff that we do once per ADC interval--ADC colllection.
// TODO: debug output (frame skipping / utilization).
@@ -582,6 +703,8 @@ void loop()
//forwardData();
}
//if (Rig.isCW()) return;
// Check Response Command
if (responseCommand > 0 && sinceForward > LAST_TIME_INTERVAL)
{