diff --git a/code/code.ino b/code/code.ino index 022a5fb..45df5fb 100644 --- a/code/code.ino +++ b/code/code.ino @@ -54,6 +54,10 @@ #include "radio/fm.h" #include "radio/cat.h" #include "radio/rx.h" +#include "radio/common2.h" +#include "radio/calibration.h" + +#include "ui.h" //FUSES = { .low = 0xFF, .high = 0xD6, .extended = 0xFD }; // Fuse settings should be these at programming. @@ -65,170 +69,6 @@ ISR(TIMER2_COMPA_vect) // Timer2 COMPA interrupt #endif } -void dummy() -{ -} - -enum dsp_cap_t { ANALOG, DSP, SDR }; -uint8_t dsp_cap = 0; -uint8_t ssb_cap = 0; - -uint16_t analogSampleMic() -{ - uint16_t adc; - noInterrupts(); - if(dsp_cap == SDR) digitalWrite(RX, LOW); // disable RF input, only for SDR mod - //si5351.SendRegister(SI_CLK_OE, 0b11111111); // CLK2_EN=0, CLK1_EN,CLK0_EN=0 - ADMUX = admux[2]; // set MUX for next conversion - ADCSRA |= (1 << ADSC); // start next ADC conversion - for(;!(ADCSRA & (1 << ADIF));); // wait until ADC conversion is completed - if(dsp_cap == SDR) digitalWrite(RX, HIGH); // enable RF input, only for SDR mod - //si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 - adc = ADC; - interrupts(); - return adc; -} - -volatile bool change = true; -volatile int32_t freq = 7074000; - -int8_t smode = 1; - -float dbm_max; -float smeter(float ref = 5) //= 10*log(8000/2400)=5 ref to 2.4kHz BW. plus some other calibration factor -{ - if(smode == 0){ // none, no s-meter - return 0; - } - float rms = _absavg256 / 256.0; //sqrt(256.0); - //if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (1024.0 * (float)R * 4.0 * 100.0 * 40.0); // 2 rx gain stages: rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain] - if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (1024.0 * (float)R * 4.0 * 820.0 * 3.0/*??*/); // 1 rx gain stage: rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain] - else rms = (float)rms * 5.0 * (float)(1 << att2) / (1024.0 * (float)R * 2.0 * 100.0 * 120.0 / 1.750); - float dbm = (10.0 * log10((rms * rms) / 50.0) + 30.0) - ref; //from rmsV to dBm at 50R - dbm_max = max(dbm_max, dbm); - static uint8_t cnt; - cnt++; - if((cnt % 8) == 0){ - if(smode == 1){ // dBm meter - lcd.noCursor(); lcd.setCursor(9, 0); lcd.print((int16_t)dbm_max); lcd.print(F("dBm ")); - } - if(smode == 2){ // S-meter - uint8_t s = (dbm_max < -63) ? ((dbm_max - -127) / 6) : (uint8_t)(dbm_max - -63 + 10) % 10; // dBm to S - lcd.noCursor(); lcd.setCursor(14, 0); if(s < 10){ lcd.print('S'); } lcd.print(s); - } - dbm_max = -174.0 + 34.0; - } - if(smode == 3){ // S-bar - int8_t s = (dbm < -63) ? ((dbm - -127) / 6) : (uint8_t)(dbm - -63 + 10) % 10; // dBm to S - lcd.noCursor(); lcd.setCursor(12, 0); - char tmp[5]; - for(uint8_t i = 0; i != 4; i++){ tmp[i] = max(2, min(5, s + 1)); s = s - 3; } tmp[4] = 0; - lcd.print(tmp); - } - return dbm; -} - -void start_rx() -{ - _init = 1; - rx_state = 0; - func_ptr = sdr_rx; //enable RX DSP/SDR - adc_start(2, true, F_ADC_CONV); admux[2] = ADMUX; - if(dsp_cap == SDR){ - adc_start(0, !(att == 1)/*true*/, F_ADC_CONV); admux[0] = ADMUX; - adc_start(1, !(att == 1)/*true*/, F_ADC_CONV); admux[1] = ADMUX; - } else { // ANALOG, DSP - adc_start(0, false, F_ADC_CONV); admux[0] = ADMUX; admux[1] = ADMUX; - } - timer1_start(F_SAMP_PWM); - timer2_start(F_SAMP_RX); - TCCR1A &= ~(1 << COM1B1); digitalWrite(KEY_OUT, LOW); // disable KEY_OUT PWM -} - -void switch_rxtx(uint8_t tx_enable){ - tx = tx_enable; - TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt - //delay(1); - noInterrupts(); - if(tx_enable){ - switch(mode){ - case USB: - case LSB: func_ptr = dsp_tx; break; - case CW: func_ptr = dsp_tx_cw; break; - case AM: func_ptr = dsp_tx_am; break; - case FM: func_ptr = dsp_tx_fm; break; - } - } else func_ptr = sdr_rx; - if((!dsp_cap) && (!tx_enable) && vox) func_ptr = dummy; //hack: for SSB mode, disable dsp_rx during vox mode enabled as it slows down the vox loop too much! - interrupts(); - if(tx_enable) ADMUX = admux[2]; - else _init = 1; - rx_state = 0; - if(tx_enable){ - digitalWrite(RX, LOW); // TX (disable RX) -#ifdef NTX - digitalWrite(NTX, LOW); // TX (enable TX) -#endif - lcd.setCursor(15, 1); lcd.print("T"); - si5351.SendRegister(SI_CLK_OE, 0b11111011); // CLK2_EN=1, CLK1_EN,CLK0_EN=0 - //if(!mox) TCCR1A &= ~(1 << COM1A1); // disable SIDETONE, prevent interference during TX - OCR1AL = 0; // make sure SIDETONE is set to 0% - TCCR1A |= (1 << COM1B1); // enable KEY_OUT PWM - } else { - //TCCR1A |= (1 << COM1A1); // enable SIDETONE - TCCR1A &= ~(1 << COM1B1); digitalWrite(KEY_OUT, LOW); // disable KEY_OUT PWM, prevents interference during RX - OCR1BL = 0; // make sure PWM (KEY_OUT) is set to 0% - digitalWrite(RX, !(att == 2)); // RX (enable RX when attenuator not on) -#ifdef NTX - digitalWrite(NTX, HIGH); // RX (disable TX) -#endif - si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 - lcd.setCursor(15, 1); lcd.print((vox) ? "V" : "R"); - } - OCR2A = (((float)F_CPU / (float)64) / (float)((tx_enable) ? F_SAMP_TX : F_SAMP_RX) + 0.5) - 1; - TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt TIMER2_COMPA_vect -} - -#ifdef QCX -#define CAL_IQ 1 -#ifdef CAL_IQ -int16_t cal_iq_dummy = 0; -// RX I/Q calibration procedure: terminate with 50 ohm, enable CW filter, adjust R27, R24, R17 subsequently to its minimum side-band rejection value in dB -void calibrate_iq() -{ - smode = 1; - lcd.setCursor(0, 0); lcd.print(blanks); lcd.print(blanks); - digitalWrite(SIG_OUT, true); // loopback on - si5351.freq(freq, 0, 90); // RX in USB - si5351.SendRegister(SI_CLK_OE, 0b11111000); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 - float dbc; - si5351.freq_calc_fast(+700); si5351.SendPLLBRegisterBulk(); delay(100); - dbc = smeter(); - si5351.freq_calc_fast(-700); si5351.SendPLLBRegisterBulk(); delay(100); - lcd.setCursor(0, 1); lcd.print("I-Q bal. 700Hz"); lcd.print(blanks); - for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); - si5351.freq_calc_fast(+600); si5351.SendPLLBRegisterBulk(); delay(100); - dbc = smeter(); - si5351.freq_calc_fast(-600); si5351.SendPLLBRegisterBulk(); delay(100); - lcd.setCursor(0, 1); lcd.print("Phase Lo 600Hz"); lcd.print(blanks); - for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); - si5351.freq_calc_fast(+800); si5351.SendPLLBRegisterBulk(); delay(100); - dbc = smeter(); - si5351.freq_calc_fast(-800); si5351.SendPLLBRegisterBulk(); delay(100); - lcd.setCursor(0, 1); lcd.print("Phase Hi 800Hz"); lcd.print(blanks); - for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); - - lcd.setCursor(9, 0); lcd.print(blanks); // cleanup dbmeter - digitalWrite(SIG_OUT, false); // loopback off - si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 - change = true; //restore original frequency setting -} -#endif -#endif //QCX - - -#include "ui.h" // Need to seperate this out - void setup() { digitalWrite(KEY_OUT, LOW); // for safety: to prevent exploding PA MOSFETs, in case there was something still biasing them. diff --git a/code/radio/calibration.h b/code/radio/calibration.h new file mode 100644 index 0000000..22225f0 --- /dev/null +++ b/code/radio/calibration.h @@ -0,0 +1,36 @@ +#ifdef QCX +#define CAL_IQ 1 +#ifdef CAL_IQ +int16_t cal_iq_dummy = 0; +// RX I/Q calibration procedure: terminate with 50 ohm, enable CW filter, adjust R27, R24, R17 subsequently to its minimum side-band rejection value in dB +void calibrate_iq() +{ + smode = 1; + lcd.setCursor(0, 0); lcd.print(blanks); lcd.print(blanks); + digitalWrite(SIG_OUT, true); // loopback on + si5351.freq(freq, 0, 90); // RX in USB + si5351.SendRegister(SI_CLK_OE, 0b11111000); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 + float dbc; + si5351.freq_calc_fast(+700); si5351.SendPLLBRegisterBulk(); delay(100); + dbc = smeter(); + si5351.freq_calc_fast(-700); si5351.SendPLLBRegisterBulk(); delay(100); + lcd.setCursor(0, 1); lcd.print("I-Q bal. 700Hz"); lcd.print(blanks); + for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); + si5351.freq_calc_fast(+600); si5351.SendPLLBRegisterBulk(); delay(100); + dbc = smeter(); + si5351.freq_calc_fast(-600); si5351.SendPLLBRegisterBulk(); delay(100); + lcd.setCursor(0, 1); lcd.print("Phase Lo 600Hz"); lcd.print(blanks); + for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); + si5351.freq_calc_fast(+800); si5351.SendPLLBRegisterBulk(); delay(100); + dbc = smeter(); + si5351.freq_calc_fast(-800); si5351.SendPLLBRegisterBulk(); delay(100); + lcd.setCursor(0, 1); lcd.print("Phase Hi 800Hz"); lcd.print(blanks); + for(; !digitalRead(BUTTONS);){ wdt_reset(); smeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset(); + + lcd.setCursor(9, 0); lcd.print(blanks); // cleanup dbmeter + digitalWrite(SIG_OUT, false); // loopback off + si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 + change = true; //restore original frequency setting +} +#endif +#endif //QCX diff --git a/code/radio/common2.h b/code/radio/common2.h new file mode 100644 index 0000000..98854ca --- /dev/null +++ b/code/radio/common2.h @@ -0,0 +1,124 @@ +enum dsp_cap_t { ANALOG, DSP, SDR }; +uint8_t dsp_cap = 0; +uint8_t ssb_cap = 0; + +void dummy() +{ +} + +void start_rx() +{ + _init = 1; + rx_state = 0; + func_ptr = sdr_rx; //enable RX DSP/SDR + adc_start(2, true, F_ADC_CONV); admux[2] = ADMUX; + if(dsp_cap == SDR){ + adc_start(0, !(att == 1)/*true*/, F_ADC_CONV); admux[0] = ADMUX; + adc_start(1, !(att == 1)/*true*/, F_ADC_CONV); admux[1] = ADMUX; + } else { // ANALOG, DSP + adc_start(0, false, F_ADC_CONV); admux[0] = ADMUX; admux[1] = ADMUX; + } + timer1_start(F_SAMP_PWM); + timer2_start(F_SAMP_RX); + TCCR1A &= ~(1 << COM1B1); digitalWrite(KEY_OUT, LOW); // disable KEY_OUT PWM +} + +uint16_t analogSampleMic() +{ + uint16_t adc; + noInterrupts(); + if(dsp_cap == SDR) digitalWrite(RX, LOW); // disable RF input, only for SDR mod + //si5351.SendRegister(SI_CLK_OE, 0b11111111); // CLK2_EN=0, CLK1_EN,CLK0_EN=0 + ADMUX = admux[2]; // set MUX for next conversion + ADCSRA |= (1 << ADSC); // start next ADC conversion + for(;!(ADCSRA & (1 << ADIF));); // wait until ADC conversion is completed + if(dsp_cap == SDR) digitalWrite(RX, HIGH); // enable RF input, only for SDR mod + //si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 + adc = ADC; + interrupts(); + return adc; +} + + +void switch_rxtx(uint8_t tx_enable){ + tx = tx_enable; + TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt + //delay(1); + noInterrupts(); + if(tx_enable){ + switch(mode){ + case USB: + case LSB: func_ptr = dsp_tx; break; + case CW: func_ptr = dsp_tx_cw; break; + case AM: func_ptr = dsp_tx_am; break; + case FM: func_ptr = dsp_tx_fm; break; + } + } else func_ptr = sdr_rx; + if((!dsp_cap) && (!tx_enable) && vox) func_ptr = dummy; //hack: for SSB mode, disable dsp_rx during vox mode enabled as it slows down the vox loop too much! + interrupts(); + if(tx_enable) ADMUX = admux[2]; + else _init = 1; + rx_state = 0; + if(tx_enable){ + digitalWrite(RX, LOW); // TX (disable RX) +#ifdef NTX + digitalWrite(NTX, LOW); // TX (enable TX) +#endif + lcd.setCursor(15, 1); lcd.print("T"); + si5351.SendRegister(SI_CLK_OE, 0b11111011); // CLK2_EN=1, CLK1_EN,CLK0_EN=0 + //if(!mox) TCCR1A &= ~(1 << COM1A1); // disable SIDETONE, prevent interference during TX + OCR1AL = 0; // make sure SIDETONE is set to 0% + TCCR1A |= (1 << COM1B1); // enable KEY_OUT PWM + } else { + //TCCR1A |= (1 << COM1A1); // enable SIDETONE + TCCR1A &= ~(1 << COM1B1); digitalWrite(KEY_OUT, LOW); // disable KEY_OUT PWM, prevents interference during RX + OCR1BL = 0; // make sure PWM (KEY_OUT) is set to 0% + digitalWrite(RX, !(att == 2)); // RX (enable RX when attenuator not on) +#ifdef NTX + digitalWrite(NTX, HIGH); // RX (disable TX) +#endif + si5351.SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1 + lcd.setCursor(15, 1); lcd.print((vox) ? "V" : "R"); + } + OCR2A = (((float)F_CPU / (float)64) / (float)((tx_enable) ? F_SAMP_TX : F_SAMP_RX) + 0.5) - 1; + TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt TIMER2_COMPA_vect +} + +volatile bool change = true; +volatile int32_t freq = 7074000; + +int8_t smode = 1; + +float dbm_max; +float smeter(float ref = 5) //= 10*log(8000/2400)=5 ref to 2.4kHz BW. plus some other calibration factor +{ + if(smode == 0){ // none, no s-meter + return 0; + } + float rms = _absavg256 / 256.0; //sqrt(256.0); + //if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (1024.0 * (float)R * 4.0 * 100.0 * 40.0); // 2 rx gain stages: rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain] + if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (1024.0 * (float)R * 4.0 * 820.0 * 3.0/*??*/); // 1 rx gain stage: rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain] + else rms = (float)rms * 5.0 * (float)(1 << att2) / (1024.0 * (float)R * 2.0 * 100.0 * 120.0 / 1.750); + float dbm = (10.0 * log10((rms * rms) / 50.0) + 30.0) - ref; //from rmsV to dBm at 50R + dbm_max = max(dbm_max, dbm); + static uint8_t cnt; + cnt++; + if((cnt % 8) == 0){ + if(smode == 1){ // dBm meter + lcd.noCursor(); lcd.setCursor(9, 0); lcd.print((int16_t)dbm_max); lcd.print(F("dBm ")); + } + if(smode == 2){ // S-meter + uint8_t s = (dbm_max < -63) ? ((dbm_max - -127) / 6) : (uint8_t)(dbm_max - -63 + 10) % 10; // dBm to S + lcd.noCursor(); lcd.setCursor(14, 0); if(s < 10){ lcd.print('S'); } lcd.print(s); + } + dbm_max = -174.0 + 34.0; + } + if(smode == 3){ // S-bar + int8_t s = (dbm < -63) ? ((dbm - -127) / 6) : (uint8_t)(dbm - -63 + 10) % 10; // dBm to S + lcd.noCursor(); lcd.setCursor(12, 0); + char tmp[5]; + for(uint8_t i = 0; i != 4; i++){ tmp[i] = max(2, min(5, s + 1)); s = s - 3; } tmp[4] = 0; + lcd.print(tmp); + } + return dbm; +}