Add test-bench (tnx Alan, M0PUB).

pull/11/head
guido 2020-08-09 14:23:27 +02:00
rodzic 23d7d4e522
commit f9b667da8d
1 zmienionych plików z 115 dodań i 18 usunięć

Wyświetl plik

@ -5,16 +5,20 @@
// 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.02e"
#define TESTBENCH 1
// QCX pin defintions
#define LCD_D4 0 //PD0 (pin 2)
#define LCD_D5 1 //PD1 (pin 3)
#define LCD_D6 2 //PD2 (pin 4)
#define LCD_D7 3 //PD3 (pin 5)
#define LCD_EN 4 //PD4 (pin 6)
#ifndef TESTBENCH
#define LCD_D4 0 //PD0 (pin 2)
#define LCD_D5 1 //PD1 (pin 3)
#define LCD_D6 2 //PD2 (pin 4)
#define LCD_D7 3 //PD3 (pin 5)
#define LCD_EN 4 //PD4 (pin 6)
#define LCD_RS 18 //PC4 (pin 27)
#endif
#define FREQCNT 5 //PD5 (pin 11)
#define ROT_A 6 //PD6 (pin 12)
#define ROT_B 7 //PD7 (pin 13)
#define ROT_A 7 //PD6 (pin 12)
#define ROT_B 6 //PD7 (pin 13)
#define RX 8 //PB0 (pin 14)
#define SIDETONE 9 //PB1 (pin 15)
#define KEY_OUT 10 //PB2 (pin 16)
@ -25,7 +29,6 @@
#define AUDIO2 15 //PC1/A1 (pin 24)
#define DVM 16 //PC2/A2 (pin 25)
#define BUTTONS 17 //PC3/A3 (pin 26)
#define LCD_RS 18 //PC4 (pin 27)
#define SDA 18 //PC4 (pin 27)
#define SCL 19 //PC5 (pin 28)
//#define NTX 11 //PB3 (pin 17) - experimental: LOW on TX
@ -64,6 +67,9 @@ experimentally: #define AUTO_ADC_BIAS 1
//FUSES = { .low = 0xFF, .high = 0xD6, .extended = 0xFD }; // Fuse settings should be these at programming.
#ifndef TESTBENCH
class LCD : public Print { // inspired by: http://www.technoblogy.com/show?2BET
public: // LCD1602 display in 4-bit mode, RS is pull-up and kept low when idle to prevent potential display RFI via RS line
#define _dn 0 // PD0 to PD3 connect to D4 to D7 on the display
@ -206,6 +212,25 @@ public: // QCXLiquidCrystal extends LiquidCrystal library for pull-up driven LCD
};
};
#else
//M0PUB: dummy LCD functions
class LCD {
public :
LCD() { ; }
~LCD() { ; }
void begin(uint8_t x = 0, uint8_t y = 0){ ; }
void setCursor(uint8_t x, uint8_t y){ ; }
void print(const __FlashStringHelper* fs) { ; }
void print(const char c) { ; }
void print(const char c[]) { ; }
void cursor(){ ; }
void noCursor(){ ; }
void noDisplay(){ ; }
void createChar(uint8_t l, uint8_t glyph[]){ ; }
};
#endif
// I2C class used by SSD1306 driver; you may connect a SSD1306 (128x32) display on LCD header pins: 1 (GND); 2 (VCC); 13 (SDA); 14 (SCL)
class I2C_ { // direct port I/O (disregards/does-not-need pull-ups)
public:
@ -893,8 +918,8 @@ public:
#define FAST __attribute__((optimize("Ofast")))
#define F_XTAL 27005000 // Crystal freq in Hz, nominal frequency 27004300
//#define F_XTAL 25004000 // Alternate SI clock
//#define F_XTAL 27005000 // Crystal freq in Hz, nominal frequency 27004300
#define F_XTAL 25004000 // Alternate SI clock
//#define F_XTAL 20004000 // A shared-single 20MHz processor/pll clock
volatile uint32_t fxtal = F_XTAL;
@ -1768,6 +1793,7 @@ inline int16_t slow_dsp(int16_t ac)
} // needs: p.12 https://www.veron.nl/wp-content/uploads/2014/01/FmDemodulator.pdf
else { ; } // USB, LSB, CW
if(agc) ac = process_agc(ac);
ac = ac >> (16-volume);
if(nr) ac = process_nr(ac);
@ -1795,6 +1821,47 @@ inline int16_t slow_dsp(int16_t ac)
return ac;
}
// Sine table with 72 entries results in 868Hz sine wave at effective sampling rate of 31250 SPS
// for each of I and Q, since thay are sampled alternately, and hence I (for example) only
// gets 36 samples from this table before looping.
const int8_t sine[] = {
11, 22, 33, 43, 54, 64, 73, 82, 90, 97, 104, 110, 115, 119, 123, 125, 127, 127,
127, 125, 123, 119, 115, 110, 104, 97, 90, 82, 73, 64, 54, 43, 33, 22, 11, 0,
-11, -22, -33, -43, -54, -64, -73, -82, -90, -97, -104, -110, -115, -119, -123, -125, -127, -127,
-127, -125, -123, -119, -115, -110, -104, -97, -90, -82, -73, -64, -54, -43, -33, -22, -11, 0
};
// Short Sine table with 36 entries results in 1736Hz sine wave at effective sampling rate of 62500 SPS.
/* const int8_t sine[] = {
22, 43, 64, 82, 97, 110, 119, 125, 127,
125, 119, 110, 97, 82, 64, 43, 22, 0,
-22, -43, -64, -82, -97, -110, -119, -125, -127,
-125, -119, -110, -97, -82, -64, -43, -22, 0
}; */
uint8_t ncoIdx = 0;
int16_t NCO_Q()
{
ncoIdx++;
if (ncoIdx >= sizeof(sine))
ncoIdx = 0;
return (int16_t(sine[ncoIdx])) << 2;
}
int16_t NCO_I()
{
uint8_t i;
ncoIdx++;
if (ncoIdx >= sizeof(sine))
ncoIdx = 0;
i = ncoIdx + (sizeof(sine) / 4); // Advance by 90 degrees
if (i >= sizeof(sine))
i -= sizeof(sine);
return (int16_t(sine[i])) << 2;
}
typedef void (*func_t)(void);
volatile func_t func_ptr;
#undef R // Decimating 2nd Order CIC filter
@ -1813,9 +1880,13 @@ volatile uint8_t rx_state = 0;
void sdr_rx()
{
// process I for even samples [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
#ifdef TESTBENCH
int16_t adc = NCO_I();
#else
ADMUX = admux[1]; // set MUX for next conversion
ADCSRA |= (1 << ADSC); // start next ADC conversion
int16_t adc = ADC - 511; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
#endif
func_ptr = sdr_rx_q; // processing function for next conversion
sdr_rx_common();
@ -1824,7 +1895,6 @@ void sdr_rx()
int16_t corr_adc = (prev_adc + adc) / 2;
prev_adc = adc;
adc = corr_adc;
//static int16_t dc;
//dc += (adc - dc) / 2; // we lose LSB with this method
//dc = (3*dc + adc)/4;
@ -1852,6 +1922,8 @@ void sdr_rx()
i = v[0]; v[0] = v[1]; v[1] = v[2]; v[2] = v[3]; v[3] = v[4]; v[4] = v[5]; v[5] = v[6]; v[6] = ac2; // Delay to match Hilbert transform on Q branch
int16_t ac = i + qh;
Serial.println(q);
ac = slow_dsp(ac);
// Output stage
@ -1876,9 +1948,13 @@ void sdr_rx()
void sdr_rx_q()
{
// process Q for odd samples [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
#ifdef TESTBENCH
int16_t adc = NCO_Q();
#else
ADMUX = admux[0]; // set MUX for next conversion
ADCSRA |= (1 << ADSC); // start next ADC conversion
int16_t adc = ADC - 511; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
#endif
func_ptr = sdr_rx; // processing function for next conversion
#ifdef SECOND_ORDER_DUC
// sdr_rx_common(); //necessary? YES!... Maybe NOT!
@ -1909,7 +1985,7 @@ void sdr_rx_q()
// Process Q (down-sampled) samples
static int16_t v[14];
q = v[7];
qh = ((v[0] - ac2) * 2 + (v[2] - v[12]) * 8 + (v[4] - v[10]) * 21 + (v[6] - v[8]) * 15) / 128 + (v[6] - v[8]) / 2; // Hilbert transform, 40dB side-band rejection in 400..1900Hz (@4kSPS) when used in image-rejection scenario; (Hilbert transform require 5 additional bits)
qh = ((v[0] - ac2) * 2 + (v[2] - v[12]) * 8 + (v[4] - v[10]) * 21 + (v[6] - v[8]) * 15) / 128 + (v[6] - v[8]) / 2; // transform, 40dB side-band rejection in 400..1900Hz (@4kSPS) when used in image-rejection scenario; (Hilbert transform require 5 additional bits)
for(uint8_t j = 0; j != 13; j++) v[j] = v[j + 1]; v[13] = ac2;
}
rx_state = 0; return;
@ -2622,6 +2698,13 @@ void initPins(){
void setup()
{
// M0PUB: for test bench only, open serial port for diagnostic output
#ifdef TESTBENCH
Serial.begin(115200);
while (!Serial)
;
#endif
digitalWrite(KEY_OUT, LOW); // for safety: to prevent exploding PA MOSFETs, in case there was something still biasing them.
uint8_t mcusr = MCUSR;
@ -2679,6 +2762,7 @@ void setup()
si5351.powerDown(); // Disable all (used) outputs
// Test if QCX has DSP/SDR capability: SIDETONE output disconnected from AUDIO2
#ifndef TESTBENCH
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
delay(1); // settle
@ -2716,7 +2800,11 @@ void setup()
//ssb_cap = 1; dsp_cap = 0; // force SSB and standard QCX-RX capability
//ssb_cap = 1; dsp_cap = 1; // force SSB and DSP capability
//ssb_cap = 1; dsp_cap = 2; // force SSB and SDR capability
#else
dsp_cap = SDR;
ssb_cap = true;
Serial.println(F("Testbench: force SDR cap."));
#endif
show_banner();
lcd.setCursor(7, 0); lcd.print(F(" R")); lcd.print(F(VERSION)); lcd_blanks();
@ -2824,7 +2912,10 @@ void setup()
uint32_t speed = (1000000 * 8 * 7) / (t1 - t0); // speed in kbit/s
if(false)
{
lcd.setCursor(0, 1); lcd.print(F("i2cspeed=")); lcd.print(speed); lcd.print(F("kbps")); lcd_blanks();
lcd.setCursor(0, 1);
lcd.print(F("i2cspeed="));
lcd.print(speed);
lcd.print(F("kbps")); lcd_blanks();
delay(1500); wdt_reset();
}
@ -2840,8 +2931,12 @@ void setup()
for(int j = 3; j != 8; j++) if(si5351.RecvRegister(SI_SYNTH_PLL_B + j) != si5351.pll_regs[j]) i2c_error++;
}
if(i2c_error){
lcd.setCursor(0, 1); lcd.print(F("!!BER_i2c=")); lcd.print(i2c_error); lcd_blanks();
delay(1500); wdt_reset();
lcd.setCursor(0, 1);
lcd.print(F("!!BER_i2c="));
lcd.print(i2c_error);
lcd_blanks();
delay(1500);
wdt_reset();
}
#endif
@ -2899,7 +2994,10 @@ void parse_cat(uint8_t in){ // TS480 CAT protocol: https://www.kenwood.com/i/
if(in == ';'){ // end of cat command -> parse and process
switch(cat_cmd){
case 'I'<<8|'F': Serial.write("IF00010138000 +00000000002000000 ;"); lcd.setCursor(0, 0); lcd.print("IF"); break;
case 'I'<<8|'F': Serial.write("IF00010138000 +00000000002000000 ;");
lcd.setCursor(0, 0);
lcd.print("IF");
break;
}
nc = 0; // reset
} else {
@ -3077,7 +3175,6 @@ void loop()
//int16_t x = 0;
lcd.setCursor(15, 1); lcd.print("V");
for(; !digitalRead(BUTTONS);){ // while in VOX mode
int16_t in = analogSampleMic() - 512;
static int16_t dc;
int16_t i, q;