Added configurable PA-PWM envelope working range. Added description for direct PWM PA biasing solution (removing key shaping circuit).

pull/8/head
guido 2019-12-08 19:43:42 +01:00
rodzic 1823683c59
commit 3c57e3b5b9
2 zmienionych plików z 35 dodań i 26 usunięć

Wyświetl plik

@ -4,29 +4,29 @@
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define VERSION "1.01q"
#define VERSION "1.01r"
/* BACKLOG:
code definitions and re-use for comb, integrator, dc decoupling, arctan
in func_ptr for different mode types
refactor main()
agc based on rms256, agc/smeter after filter
vox by sampling mic port in sdr_rx?
noisefree integrator (rx audio out) in lower range
raised cosine tx amp for cw
auto paddle
cw tx message/cw encoder
32 bin fft
menu back (right button), menu enter (rotary button)
dynamic range cw
att extended agc
profiling nsamples, cpu load idle in menu
configuarable F_CPU
configurable F_CPU
CW-R/CW-L offset
VFO-A/B+split+RIT
OLED display support
VOX integration in main loop
auto-bias for AUDIO1+2 inputs
K2/TS480 CAT
K2/TS480 CAT control
faster RX-TX switch to support CW
*/
// QCX pin defintion
@ -539,7 +539,7 @@ inline int16_t ssb(int16_t in)
if(_amp < 4 ) return 0; //hack: for constant amplitude cases, set drive=1 for good results
//digitalWrite(RX, (_amp < 4)); // fast on-off switching for constant amplitude case
#endif
_amp = ((_amp > 255) || (drive == 8)) ? 255 : _amp; // clip or when drive=8 use max output
_amp = ((_amp > 255) || (drive == 8)) ? lut[255] : _amp; // clip or when drive=8 use max output
amp = (tx) ? lut[_amp] : 0;
static int16_t prev_phase;
@ -602,6 +602,9 @@ void dsp_tx_cw()
int16_t df = ssb(adc >> MIC_ATTEN); // convert analog input into phase-shifts (carrier out by periodic frequency shifts)
si5351.freq_calc_fast(df); // calculate SI5351 registers based on frequency shift and carrier frequency
numSamples++;*/
OCR1BL = lut[255];
//if(mon) OCR1AL = (adc << (mon-1)) + 128; // TX audio monitoring
//process_minsky();
@ -1441,6 +1444,7 @@ void start_rx()
}
void switch_rxtx(uint8_t tx_enable){
tx = tx_enable;
TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt
//delay(1);
noInterrupts();
@ -1454,6 +1458,7 @@ void switch_rxtx(uint8_t tx_enable){
si5351.SendRegister(SI_CLK_OE, 0b11111011); // CLK2_EN=1, CLK1_EN,CLK0_EN=0
digitalWrite(RX, LOW); // TX
} else {
OCR1BL = 0; // make sure PWM (KEY_OUT) is set to 0%
digitalWrite(RX, !(att == 2)); // RX (enable RX when attenuator not on)
si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1
lcd.setCursor(15, 1); lcd.print((vox) ? "V" : "R");
@ -1598,6 +1603,9 @@ template<typename T> void paramAction(uint8_t action, T& value, const __FlashStr
}
}
static uint8_t pwm_min = 0; // PWM value for which PA reaches its minimum: 29 when C31 installed; 0 when C31 removed; x for biasing BS170 directly
static uint8_t pwm_max = 255; // PWM value for which PA reaches its maximum: 96 when C31 installed; 255 when C31 removed; x for biasing BS170 directly
const char* offon_label[2] = {"OFF", "ON"};
const char* mode_label[5] = { "LSB", "USB", "CW ", "AM ", "FM " };
const char* filt_label[7] = { "Full", "4000", "2500", "1700", "200", "100", "50" };
@ -1605,10 +1613,10 @@ const char* band_label[N_BANDS] = { "80m", "60m", "40m", "30m", "20m", "17m", "1
#define _N(a) sizeof(a)/sizeof(a[0])
#define N_PARAMS 19 // number of (visible) parameters
#define N_PARAMS 21 // number of (visible) parameters
#define N_ALL_PARAMS (N_PARAMS+2) // number of parameters
enum params_t {ALL, VOLUME, MODE, FILTER, BAND, STEP, AGC, NR, ATT, ATT2, SMETER, CWDEC, VOX, VOXGAIN, MOX, DRIVE, SIFXTAL, PARAM_A, PARAM_B, PARAM_C, FREQ, VERS};
enum params_t {ALL, VOLUME, MODE, FILTER, BAND, STEP, AGC, NR, ATT, ATT2, SMETER, CWDEC, VOX, VOXGAIN, MOX, DRIVE, SIFXTAL, PWM_MIN, PWM_MAX, PARAM_A, PARAM_B, PARAM_C, FREQ, VERS};
void paramAction(uint8_t action, uint8_t id = ALL) // list of parameters
{
@ -1639,9 +1647,11 @@ void paramAction(uint8_t action, uint8_t id = ALL) // list of parameters
case MOX: paramAction(action, mox, F("3.3"), F("MOX"), NULL, 0, 4, false); break;
case DRIVE: paramAction(action, drive, F("3.4"), F("TX Drive"), NULL, 0, 8, false); break;
case SIFXTAL: paramAction(action, si5351.fxtal, F("9.1"), F("Ref freq"), NULL, 24000000, 28000000, false); break;
case PARAM_A: paramAction(action, param_a, F("9.2"), F("Param A"), NULL, 0, 65535, false); break;
case PARAM_B: paramAction(action, param_b, F("9.3"), F("Param B"), NULL, -32768, 32767, false); break;
case PARAM_C: paramAction(action, param_c, F("9.4"), F("Param C"), NULL, -32768, 32767, false); break;
case PWM_MIN: paramAction(action, pwm_min, F("9.2"), F("PA Bias min"), NULL, 0, 255, false); break;
case PWM_MAX: paramAction(action, pwm_max, F("9.3"), F("PA Bias max"), NULL, 0, 255, false); break;
case PARAM_A: paramAction(action, param_a, F("9.4"), F("Param A"), NULL, 0, 65535, false); break;
case PARAM_B: paramAction(action, param_b, F("9.5"), F("Param B"), NULL, -32768, 32767, false); break;
case PARAM_C: paramAction(action, param_c, F("9.6"), F("Param C"), NULL, -32768, 32767, false); break;
// Invisible parameters
case FREQ: paramAction(action, freq, NULL, NULL, NULL, 0, 0, false); break;
case VERS: paramAction(action, eeprom_version, NULL, NULL, NULL, 0, 0, false); break;
@ -1721,18 +1731,6 @@ void setup()
lcd.createChar(0x01 + i, /*font[i]*/_item);
}
// initialize LUT
//#define C31_IS_INSTALLED 1 // Uncomment this line when C31 is installed (shaping circuit will be driven with analog signal instead of being switched digitally with PWM signal)
#ifdef C31_IS_INSTALLED // In case of analog driven shaping circuit:
#define PWM_MIN 29 // The PWM value where the voltage over L4 is approaching its minimum (~0.6V)
#define PWM_MAX 96 // The PWM value where the voltage over L4 is approaching its maximum (~11V)
#else // In case of digital driven shaping circuit:
#define PWM_MIN 0 // The PWM value where the voltage over L4 is its minimum (0V)
#define PWM_MAX 255 // The PWM value where the voltage over L4 is its maximum (12V)
#endif
for(i = 0; i != 256; i++)
lut[i] = (float)i / ((float)255 / ((float)PWM_MAX - (float)PWM_MIN)) + PWM_MIN;
// Test if QCX has DSP/SDR capability: SIDETONE output disconnected from AUDIO2
si5351.SendRegister(SI_CLK_OE, 0b11111111); // Mute QSD: CLK2_EN=CLK1_EN,CLK0_EN=0
digitalWrite(RX, HIGH); // generate pulse on SIDETONE and test if it can be seen on AUDIO2
@ -1871,6 +1869,9 @@ void setup()
change = true;
prev_bandval = bandval;
for(uint16_t i = 0; i != 256; i++) // refresh LUT based on pwm_min, pwm_max
lut[i] = (float)i / ((float)255 / ((float)pwm_max - (float)pwm_min)) + pwm_min;
show_banner(); // remove release number
start_rx();
@ -2081,6 +2082,10 @@ void loop()
if(menu == SIFXTAL){
change = true;
}
if((menu == PWM_MIN) || (menu == PWM_MAX)){
for(uint16_t i = 0; i != 256; i++) // refresh LUT based on pwm_min, pwm_max
lut[i] = (float)i / ((float)255 / ((float)pwm_max - (float)pwm_min)) + pwm_min;
}
}
}

Wyświetl plik

@ -1,7 +1,7 @@
# **ⓆⒸⓍ-ⓈⓈⒷ**
# QCX-SSB: SSB + SDR with your QCX transceiver
This is a simple and experimental modification that transforms a [QCX] into a (Class-E driven) SSB transceiver. It can be used to make QRP SSB contacts, or (in combination with a PC) used for the digital modes such as FT8. It can be fully-continuous tuned through bands 160m-10m in the LSB/USB-modes with a 2400Hz bandwidth has up to 5W PEP SSB output and features a software-based full Break-In VOX for fast RX/TX switching in voice and digital operations.
This is a simple and experimental modification that transforms a [QCX] into a (Class-E driven) SSB transceiver. It can be used to make QRP SSB contacts, or (in combination with a PC) used for the digital modes such as FT8, JS8, FT4. It can be fully-continuous tuned through bands 160m-10m in the LSB/USB-modes with a 2400Hz bandwidth has up to 5W PEP SSB output and features a software-based full Break-In VOX for fast RX/TX switching in voice and digital operations.
The SSB transmit and receiver stages are implemented completely in a digital and software-based manner. At transmit the ATMEGA328P samples the input-audio and reconstructing a SSB-signal by controlling the SI5351 PLL phase (through tiny frequency changes over 800kbit/s I2C) and controlling the PA Power (through PWM on the key-shaping circuit). In this way a highly power-efficient class-E driven SSB-signal can be realized; a PWM driven class-E design keeps the SSB transceiver simple, tiny, cool, power-efficient and low-cost (ie. no need for power-inefficient and complex linear amplifier with bulky heat-sink as often is seen in SSB transceivers). At receive the ATMEGA328P over-samples the I/Q at 62.5kHz, implements a down-sampling phasing receiver in the digital domain via Hilbert transformers and digital filters, and sends the resulting signal via PWM shaping to the headphone/speaker output.
@ -32,7 +32,7 @@ pe1nnz@amsat.org
- An **pre-distorion** algorithm that cancels out the amplitude errors of non-linearities in the PA voltage regulated PWM supply; a lookup table is used that can be calibrated with an internal PA amplitude measurement
- Possibility to extend the QCX analog phasing stage with a **DSP stage**
- Could replace the QCX analog phasing stage completely with a **digital SDR receiver stage**, taking away the need for the manual side-band rejection adjustment procedure and delivering DSP features such as the joy of having a **AGC, adjustable CW/SSB filters**
- **Receiver MDS of -118dBm @2.4kHz BW; dynamic range of 78dB**. Blocking (after reducing gain IC6A/B-stage to 10dB!!): -48dBm@5kHz, -8dBm@15kHz
- Receiver Noise floor **(MDS): –130 dBm** at 24MHz (in 500Hz BW); Blocking dynamic range: 148dB (200kHz offset or greater), 118dB (20kHz offset), 78dB (2kHz offset). Blocking: 18dBm (200kHz offset or greater), -12dBm (20kHz offset), -52dBm (2kHz offset) (IC6A/B-gain must reduce with -10dB!!); LO Phase noise: -138dBc/Hz; Front-end selectivity: Octave
- Digitally switchable RF front-end **attenuators (0dB, -13dB, -20dB, -33dB, -53dB, -60dB, -73dB)**
- This firmware is compatible with an unmodified QCX, partial modified QCX (e.g. SSB mod, or DSP mod), or fully modified QCX (SSB + SDR mod, as described below)
- SDR implementation simplifies the receiver heaviliy; **the original QCX PCB is now half empty** by moving the analog processing into the microcontroller and adding new and improving existing features. On a new QCX build: 46 components less to be installed, 8 component design changes, 9 additional wires.
@ -144,15 +144,18 @@ The following performance measurements were made with QCX-SSB R1.01, a modified
- Alternatively, in case you have an ATMEGA328P chip with Arduino bootloader, you can place the chip in an Arduino UNO board and upload directly (without the need for a ISP cable and QCX) by specifying 'arduino' programmer and baudrate 115200.
- Alternatively, in case you have an [Arduino] environment installed, you can upload the [QCX-SSB Sketch] directly from the Arduino environment (without using AVRDudess and firmware file); make sure "Tools > Board > Arduino/Genuino Uno", "Tools > Port > /dev/ttyUSB0 or ttyACM0", and then "Sketch > Upload" is selected, while the ATMEGA328P chip is placed in the Arduino UNO socket. It is also possible to use [Arduino as ISP] method: upload this variation of [ArduinoISP] to the Arduino board and select "Tools > Programmer > Arduino as ISP", and "Sketch > Upload Using Programmer".
2. <a name="note2"/>The occupied SSB bandwidth can be further reduced by restricting the maximum phase change (set MAX_DP to half a unit-circle _UA/2 (equivalent to 180 degrees)). The sensitivity of the VOX switching can be set with parameter VOX_THRESHOLD. Audio-input can be attenuated by increasing parameter MIC_ATTEN (6dB per step).
3. Alternatively, the PA MOSFETs can be directly biased by the PWM envelope signal, basically making the key-shaping circuit redundant. To do so, Q6,Q4,R41,R42,C32,C31 can be removed entirely, whereby C-E pads of Q6 are wired, and where a 100nF capacitor is inserted at IC3A-pin3 and G of Q1-3, and where a 10k resistor is placed at G-D pads of Q4, a 10nF capacitor between S-D pads of Q4, and where a 10k resistor is placed between D of Q4 and G of Q1-3.
### Credits:
[QCX] (QRP Labs CW Xcvr) is a kit designed by _Hans Summers (G0UPL)_, originally built for RSGB's YOTA summer camp 2017, a high performance, image rejecting DC transceiver; basically a simplified implementation of the [NorCal 2030] by _Dan Tayloe (N7VE)_ designed in 2004 combined with a [Hi-Per-Mite] Active Audio CW Filter by _David Cripe (NMØS)_, [Low Pass Filters] from _Ed (W3NQN)_ 1983 Articles, a key-shaping circuit by _Donald Huff (W6JL)_, a BS170 switched [CMOS driven MOSFET PA] architecture as used in the [ATS] designs by _Steven Weber (KD1JV)_ (originating from the [Power MOSFET revolution] in the mid 70s), and combined with popular components such as Atmel [ATMEGA328P] microprocessor, a Hitachi [HD44780] LCD display and a Silicon Labs [SI5351] Clock Generator (and using a [phase shift in the SI5351 clocks]). The [QCX-SSB] hardware modification and its Arduino [QCX-SSB Sketch] is designed by _Guido (PE1NNZ)_; the software-based SSB transmit stage is a derivate of earlier experiments with a [digital SSB generation technique] on a Raspberry Pi.
[QCX] (QRP Labs CW Xcvr) is a kit designed by _Hans Summers (G0UPL)_, originally built for RSGB's YOTA summer camp 2017, a high performance, image rejecting DC transceiver; basically a simplified implementation of the [NorCal 2030] by _Dan Tayloe (N7VE)_ designed in 2004 combined with a [Hi-Per-Mite] Active Audio CW Filter by _David Cripe (NMØS)_, [Low Pass Filters] from _Ed (W3NQN)_ 1983 Articles, a key-shaping circuit by _Donald Huff (W6JL)_, a BS170 switched [CMOS driven MOSFET PA] architecture as used in the [ATS] designs by _Steven Weber (KD1JV)_ (originating from the [Power MOSFET revolution] in the mid 70s), and combined with popular components such as Atmel [ATMEGA328P] microprocessor, a Hitachi [HD44780] LCD display and a Silicon Labs [SI5351] Clock Generator (and using a [phase shift in the SI5351 clocks]). The [QCX-SSB] transmitter and QCX-SDR receiver stage both running on a ATMEGA328P, including its multiband front-end and direct PA biasing/envelope-generation technique; its concept, circuit, code and modification to run on a QCX are a design by _Guido (PE1NNZ)_; the software-based SSB transmit stage is a derivate of earlier experiments with a [digital SSB generation technique] on a Raspberry Pi.
<!---
### References
- VERON association interviewed me in the [PI4AA June issue] about this project (in Dutch, starting at timestamp 15:50).
- Rüdiger Möller, HPSDR presentation by [DJ1MR], 2018. Transmitter architectures for high efficiency amplification
- [Arduino PWM]
- [Serial interface]
--->
[original schematic]: https://qrp-labs.com/images/qcx/HiRes.png
@ -231,4 +234,5 @@ The following performance measurements were made with QCX-SSB R1.01, a modified
[Arduino PWM]: http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-dds-sinewave-generator/
[Serial interface]: https://groups.io/g/QRPLabs/attachment/40706/0/connections.png