/* VnArduino de YO6PIR-2021 Soft modificat cu urmatoarele functii: - Din encoder se modifica in ambele sensuri UP/DOWN - La pornire se intra direct in meniul de selectie mod de lucru - Adaugare tensiune baterie in coltul dreapta sus in meniul Antena ANALISER * **************************************************************************** Hardware : dds ad9850(1) + ad8302 http://ra4nal.lanstek.ru/vna.shtml In the Jnva, Calibration frequency menu replace 10737418 by 23860477 in order to calculate a correct DS_FTW dialogue Jna avec la carte http://wiki.oz9aec.net/index.php/MiniVNA_ICD protocole de transfert: MODE $0D DDS_FTW $0D SAMPLES $0D DDS_STEP $0D Mode StartF NumberF StepF exemples : acquisition : 30 0D 32 33 38 36 30 34 38 0D 31 30 30 0D 34 32 0.2386048.100.42 39 32 34 39 39 38 0D 924998 stop 30 0D 30 0D 31 0D 30 0D 0.0.1.0. générator 30 0D 32 33 38 36 30 34 37 37 30 0D 31 0D 30 0D 0.238604770.1.0. 30 0D 32 33 38 36 30 34 37 37 0D 36 32 38 0D 33 0.23860477.628.3 37 39 38 36 0D 7986 missing in the code: bluevna detection disconnection then reset the arduino managing an sd card ad9851 information: DDS_CLCK = 180Mhz (30Mhz * 6) DS_FTW = F_start * 2 ^ 32 / DDS_CLCK F_start = DS_FTW * DDS_CLCK / 2 ^ 32 DDS_STEP = F_step * 2 ^ 32 / DDS_CLCK deltaphase = f * 4294967296.0 / calibFreq; example: find the frequency from DS_FTW 74082466 * 180000000 / 4294967296.0 = 3104760.28 HZ */ #include #include #include #include #include #include //#define MinFrq 23860900 // FTW min freq = 1 Mhz #define MinFrq 34359738 //FTW 1 MHz la un ceas de 125 MHz //#define MaxFrq 1431655765 // FTW max freq = 60 Mhz #define MaxFrq 1374389534 // FTW 40MHz la un ceas de 125 MHz #define freqMin 1000000 #define freqMax 40000000 #define PowerDown 0x04 // Power down AD9851,50 #define Normal 0x00 // AD9851 RFCLK multiplier enable x 6 en mode normal unsigned char Mode; //mode powerdown or normal mode unsigned long StartF; //DS_FTW start frequency unsigned int NumberF; //number of samples unsigned long StepF; //Increment frequency in FTW (not in Hz!) unsigned int intTemp; //loop variable for scanning unsigned int adcmag; //variables of the 2 ADC measurements unsigned int adcphs; unsigned int adcbat; //variabile tensiune baterie boolean check = 0; #define bth 6 //Pin activare modul BTH #define Rele 5 //relais refexion, transmission #define ADC0 A1 //input mag #define ADC1 A0 //input phs #define ADC2 A2 //input bateria #define dpInEncoderA 2 #define dpInEncoderB 3 #define dpInEncoderPress 4 #define adc2Db 60/1024 // full scale slope Db / resolution ADC = 0.0586 #define offsetDb -30 // offset of -30db, for ADC = 512 -> 0 db #define Adc2Angle 180/1024 // full scale slope Angle / resolution ADC = 0.175 #define D2R 3.14159/180 //degrees to radians float calMag; float calPhs; struct vector_reflection { double Freq; float RL; float Phi; float Rho; float Rs; float Xs; float Swr; float Z; }; vector_reflection Point; struct vector_transmission { double Freq; float TL; float TP; }; volatile long freq = 5000000; byte vnaMode = 0; volatile byte menuSwapp = 0; byte menuChoose = 0; byte bandChoose; volatile byte bandSwapp = 5; volatile byte bandSwappPrec = 11; double dds_reg; byte bpState =0; long freq_prec = 0; long freqStep = 0; LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //4 lines *20 columns lcd char void(* resetFunc) (void) = 0; //declare reset function @ address 0 int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } /************************************** Initializare program ***********************************/ void setup() { lcd.begin(4, 20); //4 lines *20 columns lcd char lcd.setBacklight(LOW); digitalWrite(bth, HIGH); //Modul BTH dezactivat DEFAULT //Serial.begin(115200); pinMode(bth, OUTPUT); pinMode(Rele, OUTPUT); DDS.begin(13, 8, 7); // in order of pins W_CLK, FQ_UD and RESET //DDS.calibrate(180000000); //not immediately useful but you never know analogReference(EXTERNAL); //ad8302 outdoor reference voltage 1.8V dds_reg = ((double)freq) * 4294967296.0 / 125000000.0; //125MHz DDS-AD9850; 180MHz AD9851 DDS.vna(dds_reg, Normal); delay(1); DDS.vna(dds_reg, PowerDown); pinMode(dpInEncoderA, INPUT); digitalWrite(dpInEncoderA, HIGH); pinMode(dpInEncoderB, INPUT); digitalWrite(dpInEncoderB, HIGH); pinMode(dpInEncoderPress, INPUT); digitalWrite(dpInEncoderPress, HIGH); adcmag = EEPROM.read(2) * 256 + EEPROM.read(1); adcphs = EEPROM.read(4) * 256 + EEPROM.read(3); calMag = ((float)adcmag * adc2Db) + offsetDb; calPhs = ((float)adcphs * Adc2Angle); /************************* Afisare ANTET **********************************/ lcd.clear(); lcd.print(F("********************")); //intro lcd.setCursor(0, 1); lcd.print(F("* J-VNA Ver.1 *")); //intro lcd.setCursor(0, 2); lcd.print(F("* YO6PIR-2021 *")); lcd.setCursor(0, 3); lcd.print(F("********************")); //intro lcd.setBacklight(HIGH); delay(2000); lcd.clear(); attachInterrupt(0, doEncoder, RISING); // citeste encoderul boot_menu(); //Meniul de boot-are menuChoose = EEPROM.read(0); //citeste meniul salvat switch (menuChoose) { case 0 : menuJvna(0); Serial.begin(115200); break; // J-VNA PC case 1 : menuJvna(1); { check = 1; Serial.begin(9600);break; // BTH-VNA Android } case 2 : bandSelect(); break; // Antena ANALISER } //check=1; //disable String on bluetooth connect } /* BUCLA PRINCIPALA DE PROGRAM *********************************************************************************************/ void loop() { switch (menuChoose) { case 0 : Jnva(); break; //jvna case 1 : Jnva(); break; //bluetooth case 2 : sweep(); break; //standalone } } /******************************************************** Jvna MODE ********************************************************/ void Jnva() { lcd.setBacklight(LOW); //stinge lumina afisaj char Temp; Serial.flush(); //citeste portul serial Temp = DecodeCom(); //acquisition and decoding of a measurement request from the IG_miniVNA software //if (menuChoose == 1) { // StartF = (StartF / 9) * 20; // StepF = (StepF / 9) * 20; // } StartF = StartF * 3.177; //factor de conversie pentru IG_miniVNA software StepF = StepF * 3.177; affiche_freqs(); //pour debug if ((Temp == 0) && (NumberF != 0)) // if the decoding is good and the number of samples is not zero then measure { lcd.setBacklight(HIGH); //stinge lumina afisaj if (Mode == 0) digitalWrite(Rele, LOW); // transmission 0 or refection 1 relay control else digitalWrite(Rele, HIGH); for (intTemp = 0; intTemp < NumberF; intTemp++) //bucla de masurare { if ((StartF >= MinFrq) && (StartF <= MaxFrq)) { Mode = Normal; // to make a measurement F> 0.5 mhz } else { Mode = PowerDown; // F <0.5 mhz, otherwise Power Down add test if F> fmax then power down } DDS.vna(StartF, Mode); //function dedicated to vna for dds 9851 even if the lib is 9850 // if the 9850 is used, Normal must be 0 //delay(1); magPhsADC(); Serial.write((byte)adcphs); // LSB Serial.write((byte)(adcphs >> 8)); // MSB Serial.write((byte)adcmag); // LSB Serial.write((byte)(adcmag >> 8)); // MSB StartF = StartF + StepF; } } lcd.setBacklight(LOW); //stinge lumina afisaj } void magPhsADC() //average 20 ech { unsigned int amp; unsigned int phs; unsigned int bat; amp = 0; phs = 0; bat=0; for (int n = 0; n < 20; n++) { amp = amp + analogRead(ADC0); //mesure 10 bits phs = phs + analogRead(ADC1); bat=bat+analogRead(ADC2); } adcmag = amp / 20; adcphs = phs / 20; adcbat = bat / 5; } // to be fired later void debug_serial(void) { Serial.print(F("Mode :")); Serial.println(Mode); Serial.print(F("Start F :")); Serial.println(StartF); Serial.print(F("Number F :")); Serial.println(NumberF); Serial.print(F("StepF :")); Serial.println(StepF); } // serial chain processing char DecodeCom (void) { char data, i, err = 0, Param[11]; data = getRX(); // recovers a reflection, transmission character switch (data) { case '0': Mode = 0; break; case '1': Mode = 1; break; default: err = 1; } data = getRX(); // ok it's a carriage return if (data != 0x0D) err = 1; if (err != 0) return (err); // or ERROR for (i = 0; i <= 10; i++) // read the info on the DS_FTW word of start { data = getRX(); if (data == 0x0D) break; // exits the loop if carriage return if (isdigit(data) == 1) Param[i] = data; // check if it is a number else err = 1; // if NOT => ERROR } if ((i == 0) | (i > 10)) err = 1; // error if DS_FTW start null or too long Param[i] = 0; // null char before conversion if (err != 0) return (err); // return if error StartF = atol (Param); // convert to unsigned long for (i = 0; i <= 5; i++) // same principle with the number of samples { data = getRX(); if (data == 0x0D) break; if (isdigit(data) == 1) Param[i] = data; else err = 1; } if ((i == 0) | (i > 5)) err = 1; Param[i] = 0; if (err != 0) return (err); NumberF = atoi (Param); for (i = 0; i <= 10; i++) // same principle with the DS_FTW step { data = getRX(); if (data == 0x0D) break; if (isdigit(data) == 1) Param[i] = data; else err = 1; } if ((i == 0) || (i > 10)) err = 1; Param[i] = 0; if (err != 0) return (err); StepF = atol (Param); return (0); } char getRX(void) { char data = 0; do { if (Serial.available()) { data = Serial.read(); if (check == 0) { if (data == 0x0D) { data = 0; while (data != 0x0d) { if (Serial.available()) data = Serial.read(); } while (data != '0') { if (Serial.available()) data = Serial.read(); } } } check = 1; } } while ((data != 0x0D) && (isdigit(data) != 1)); return data; } void affiche_freqs(void) { if ((StartF >= MinFrq) && (StartF <= MaxFrq)) { delete_char(1, 8, 19); lcd.setCursor(8, 1); lcd.print((unsigned long)ticksToFreq(StartF)); lcd.print(" Hz"); delete_char(2, 8, 19); lcd.setCursor(8, 2); lcd.print((unsigned long)ticksToFreq(StepF)); lcd.print(" Hz"); delete_char(3, 8, 19); lcd.setCursor(8, 3); lcd.print(NumberF); } } double ticksToFreq(long f) { return ((double)f) * 125000000.0 / 4294967296.0; } void menuJvna(byte PB) { lcd.clear(); if (PB == 0) { lcd.print(F(" ")); } else { lcd.print(F(" ")); digitalWrite(bth, LOW); //activare alimentare modul BTH } lcd.setCursor(0, 1); lcd.print(F("Fstart:")); lcd.setCursor(0, 2); lcd.print(F("Fstep:")); lcd.setCursor(0, 3); lcd.print(F("Samples:")); } /************** Antena ANALISER MODE **************************/ void sweep() { char tab[10]; int freqLcd; // byte bpState = 0; if (freq != freq_prec) { double dds_reg = ((double)freq) * 4294967296.0 / 125000000.0; DDS.vna(dds_reg, Normal); freq_prec = freq; BCD(freq, tab); lcd.setCursor(5, 0); lcd.write(tab[7]); lcd.write(tab[6]); lcd.write(46); lcd.write(tab[5]); lcd.write(tab[4]); mesure(); } if (digitalRead(dpInEncoderPress)==0) bpState|=1; if (bpState==1) bandSelect(); bpState=(bpState<<1)&3; } /************* Rutina descompunere cifre frecv *******************/ void BCD (unsigned long b, char* o) { for (int i = 10; i; --i) { *o = (b % 10) + 48; b /= 10; o++; } } /******************** Rutina de masurare ************************/ void mesure() { magPhsADC(); calculDut(adcmag, adcphs); vna_print(); delete_char(1, 3, 9); lcd.setCursor(3, 1); lcd.print((int)Point.RL); lcd.print(F("dB")); delete_char(2, 4, 9); lcd.setCursor(4, 2); lcd.print((int)Point.Phi); lcd.write(0xdf); delete_char(1, 13, 19); lcd.setCursor(13, 1); lcd.print((int)Point.Rs); lcd.write(0xf4); delete_char(2, 13, 19); lcd.setCursor(13, 2); lcd.print((int)Point.Xs); lcd.write(0xf4); delete_char(3, 2, 9); lcd.setCursor(2, 3); lcd.print((int)Point.Z); lcd.write(0xf4); delete_char(3, 14, 19); lcd.setCursor(14, 3); if (Point.Swr >= 1){ lcd.print((int)Point.Swr); lcd.write(46); byte tempSwr = (int)((Point.Swr - (int)(Point.Swr)) * 100); if (tempSwr / 10 == 0) lcd.write(48); lcd.print(tempSwr); } // Read and Display Battery Voltage double Vbat = 0.00; float result = adcbat * 1.84; // Vref=1,84V Vbat = result / 1000; //Convert in Volts char strBat[16]; dtostrf(Vbat, 1, 2, strBat); // Format string Vbat lcd.setCursor(15, 0); lcd.print(strBat); lcd.print(F("V")); } /***************** Rutina de stergere caractere afisate ************/ void delete_char(byte line, byte start, byte end) { while (start <= end) { lcd.setCursor(start, line); lcd.write(32); start++; } } /*************** DUT Calculation *********************************/ void calculDut(int adcMag, int adcPhs) { Point.Freq = freq; Point.RL = ((float)adcMag * adc2Db) + offsetDb - calMag; Point.Phi = ((float)adcPhs * Adc2Angle) - calPhs; Point.Rho = pow(10.0, Point.RL / -20.0); float re = Point.Rho * cos(Point.Phi * D2R); float im = Point.Rho * sin(Point.Phi * D2R); float denominator = ((1 - re) * (1 - re) + (im * im)); Point.Rs = fabs((1 - (re * re) - (im * im)) / denominator) * 50.0; Point.Xs = fabs(2.0 * im) / denominator * 50.0; Point.Z = sqrt(Point.Rs * Point.Rs + Point.Xs * Point.Xs); Point.Swr = fabs(1.0 + Point.Rho) / (1.001 - Point.Rho); Point.RL *= -1; } /******************************************************************/ void vna_print() { Serial.print(freq); Serial.write(9); Serial.print(adcmag); Serial.write(9); Serial.print(adcphs); Serial.write(9); Serial.print(Point.RL); Serial.write(9); Serial.print(Point.Phi); Serial.write(9); Serial.print(Point.Rho); Serial.write(9); Serial.print(Point.Rs); Serial.write(9); Serial.print(Point.Xs); Serial.write(9); Serial.print(Point.Z); Serial.write(9); Serial.println(Point.Swr); } /********************** ENCODER Routine ********************************/ void doEncoder() { if (digitalRead(dpInEncoderA) == digitalRead(dpInEncoderB)) { //Rotire dr if (menuSwapp<=3) { menuSwapp = (menuSwapp + 1)%4; } bandSwappPrec = bandSwapp; if (bandSwapp<=11){ bandSwapp = (bandSwapp + 1)%12 ; } freq += freqStep; if (freq > freqMax) freq = freqMax; } else { //Rotire Stg if (menuSwapp >0) { menuSwapp = (menuSwapp - 1); } bandSwappPrec = bandSwapp; if (bandSwapp>0) { bandSwapp = (bandSwapp - 1); } freq -= freqStep; if (freq < freqMin) freq = freqMin; } } /*********************** LCD Menu Antena ANALISER *********************/ void lcd_menu_analyse_refection() { lcd.clear(); lcd.print(F("FREQ:")); lcd.setCursor(11, 0); lcd.print(F("MHz")); lcd.setCursor(0, 1); lcd.print(F("RL:")); lcd.setCursor(10, 1); lcd.print(F("RS:")); lcd.setCursor(0, 2); lcd.print(F("Phi:")); lcd.setCursor(10, 2); lcd.print(F("XS:")); lcd.setCursor(0, 3); lcd.print(F("Z:")); lcd.setCursor(10, 3); lcd.print(F("SWR:")); } /******************** BOOT Menu ***************************************************/ void boot_menu() { // if (digitalRead(dpInEncoderPress) == 1) return; //lcd.setBacklight(HIGH); lcd.clear(); lcd.setCursor(1, 0); lcd.print(F("")); lcd.setCursor(1, 1); lcd.print(F("")); lcd.setCursor(1, 2); lcd.print(F("")); lcd.setCursor(1, 3); lcd.print(F("")); while (digitalRead(dpInEncoderPress) == 0) {} //BP rise down detect while (digitalRead(dpInEncoderPress) == 1) { for (byte n = 0; n <= 3; n++) { lcd.setCursor(0, n); if (menuSwapp == n) lcd.write(42); else lcd.write(32); } } menuChoose = menuSwapp; for (byte n = 0; n <= 3; n++) { if (menuChoose != n) { for (byte m = 0; m < 20; m++) { lcd.setCursor(m, n); lcd.write(32); } } } delay(1000); if (menuChoose < 3) EEPROM.write(0, menuChoose); else calibration(); } /********************************************************************/ void bandSelect() { lcd.clear(); lcd.setCursor(1, 0); lcd.print(F("160m 80m 60m")); lcd.setCursor(1, 1); lcd.print(F(" 40m 30m 20m")); lcd.setCursor(1, 2); lcd.print(F(" 17m 15m 12m")); lcd.setCursor(1, 3); lcd.print(F(" 10m Free EXIT")); delay(100); //pour le bp while (digitalRead(dpInEncoderPress) == 0) {} //BP rise down detect while (digitalRead(dpInEncoderPress) == 1) { byte x, y; x = (bandSwappPrec % 3) * 6; y = (bandSwappPrec / 3); lcd.setCursor(x, y); lcd.write(32); x = (bandSwapp % 3) * 6; y = (bandSwapp / 3); lcd.setCursor(x, y); lcd.write(42); } delay(100); //pour le bp bandChoose = bandSwapp; switch (bandChoose) { case 0 : freq = 1800000; break; case 1 : freq = 3500000; break; case 2 : freq = 5300000; break; case 3 : freq = 7000000; break; case 4 : freq = 10100000; break; case 5 : freq = 14000000; break; case 6 : freq = 18100000; break; case 7 : freq = 21000000; break; case 8 : freq = 24900000; break; case 9 : freq = 28000000; break; case 10 : freq = 14000000; break; default : resetFunc(); //call reset freq = 40000000; break; } if (bandChoose < 9) freqStep = 10000; else freqStep = 100000; lcd_menu_analyse_refection(); freq_prec = 0; vna_print_unites(); } /*******************************************************************/ void vna_print_unites() { Serial.println(F("Freq\tAdcmag\tAdcphs\tRL\tPhiMag\tRS\tXs\tZ\tSWR")); } /**************** SELF Calibration ****************************/ void calibration() { lcd.clear(); lcd.print(F("Leave open DUT")); lcd.setCursor(0, 1); lcd.print(F("and press button")); while (digitalRead(dpInEncoderPress) == 0) {} //BP rise down detect while (digitalRead(dpInEncoderPress) == 1) {} DDS.vna(dds_reg, Normal); delay(10); lcd.clear(); mesure(); DDS.vna(dds_reg, PowerDown); EEPROM.write(1, (byte) adcmag); EEPROM.write(2, (byte) (adcmag >> 8)); EEPROM.write(3, (byte) adcphs); EEPROM.write(4, (byte) (adcphs >> 8)); delay(2000); lcd.clear(); lcd.print(F("Done")); lcd.setCursor(0, 1); lcd.print(F("Press button to")); lcd.setCursor(0, 2); lcd.print(F("Restart")); while (digitalRead(dpInEncoderPress) == 0) {} //BP rise down detect while (digitalRead(dpInEncoderPress) == 1) {} resetFunc(); //call reset }