/* //////////////////////////////////////////////////////////////////////// /// /// /// SOLID STATE POWER AMPLIFIER READOUT & PROTECTION CIRCUIT /// /// /// //////////////////////////////////////////////////////////////////////// By ON7EQ 02/2015 Revised V11 on 10-12-2018 for 1.8kW LDMOS & power supply 55/48v Compiled with IDE 0.22 for ARDUINO NANO board Connected with W6PQL SSPA controller ARDUINO NANO CONNECTIONS ________________________ PIN DESCRIPTION TO PIN ANALOG A0 Forward voltage directional coupler Dir coupler FWD A1 Reflected voltage directional coupler Dir coupler REFL A2 NTC 10k NTC (10k to +5v) A3 50v supply - voltage ATTOPILOT Voltage A4 50v supply - current ATTOPILOT Current A5 26v supply - Voltage A6 13.8v supply - voltage A7 PTT - read status DIGITAL I/O 0 I Serial RX & input recover/reset switch (0 = recover/reset) 1 I Serial TX & Input PREAMP status (0 = preamp ON) 2 I "Operate" status read Logic HI = operate 3 O +50v Supply control Output, logic HI = ON 4 O Buzzer Output, Logic HI = sounding 5 O FAN Control (PWM port) Output, Logic HI = 'ON' pulse to Mosfet Gate 6 O LCD : DB7 LCD 01 7 O LCD : DB6 LCD 02 8 O LCD : DB5 LCD 03 9 O LCD : DB4 LCD 04 10 O LCD : En2 LCD 15 11 O LCD : En1 LCD 09 12 O LCD : RS LCD 11 13 O FAULT (PTT BLOCK W6PQL) Logic HI = Kill PTT via open collector output =================================================================== NTC Thermistor Schematic =================================================================== (+5v ) ---- (10k-Resistor) -------|------- (Thermistor) ---- (GND) | Analog Pin 'IN_NTC' */ // include 4x40 LED driver #include // include math functions #include "math.h" uint8_t nRows = 4; //number of rows on LCD uint8_t nColumns =40; //number of columns // Modify the pin number below to meet your board //Analog pins #define IN_FWD A0 // analog input for left channel #define IN_REFL A1 // analog input for right channel #define IN_NTC A2 // analog input for right channel #define IN_50vv A3 // analog input for 50v voltage #define IN_50vI A4 // analog input for 50v current #define IN_26v A5 // analog input for 26v voltage #define IN_12v A6 // analog input for 12v voltage #define IN_PTT A7 // analog input for PTT detect (0 = keyed) // Digital pins #define IN_reset 0 // input for recover/SELFTEST switch detect - momentary pulling to GND (0 = recover/SELFTEST) #define IN_PREAMP 1 // input for Preamp detect (1 = ON) #define IN_OPERATE 2 // input for operate detect (1 = OPERATE) #define supply_50v 3 // output for 50v supply control (1 = 50v ON) #define Buzzer 4 // output for Buzzer control (1 = ON) #define FANcontrol 5 // output for FAN control (PWM !) #define PTT_block 13// output for PTT block control (no alarm = 0, FAULT = 1...n) // Other minor configurable value #define T_REFRESH1 50 // msec bargraph refresh rate #define T_REFRESH2 500 // msec refresh rate other variables #define T_PEAKHOLD 600 // msec peak hold time before return #define T_pepHOLD 600 // msec pep hold time before return #define R1 (10) // from GND to IN_26v, express in 100R (12 = 1200 Ohm) #define R2 (82) // from + power supply to IN_26v, express in 100R (47 = 4700 Ohm) #define R3 (12) // from GND to IN_12v, express in 100R (12 = 1200 Ohm) #define R4 (47) // from + power supply to IN_12v, express in 100R (47 = 4700 Ohm) // FAN speeds (0 ... 255) PWM driven. Adjust according to your fan type ! #define fan_vy_slow (20) // Very slow, idle speed #define fan_slow (30) // Slow #define fan_med_lo (40) // Medium speeds, low #define fan_med_hi (50) // Medium Hi, start of PID regulation till 255 = max ! // Calibration factors int calibrP = 540; // Assume 3.3v = 1.000w in 50R. CalibrP = (3.3 / 5.0 x 1024 + Vdiode)x(3.3 / 5.0 x 1024 + Vdiode) / 1000w int Vdiode = 60; // if we assume FWD voltage diode = 0,3v : Vdiode = 0,3 v / 5.0 v x 1024 // local variable byte fill[6]={ 0x20,0x00,0x01,0x02,0x03,0xFF }; // character used to fill (0=empty 5=full) byte peak[7]={ 0x20,0x00,0x04,0x05,0x06,0x07,0x20 }; // character used to peak indicator int lmax[5]; // level max memory int dly[5]; // delay & speed for peak return long lastT1=0; // update display timer1 long lastT2=0; // update display timer2 long lastTpep = 0; // update PEP display long lastTempTime = 0; // update last temp readout for fan control long LastTXtime = 0; // timestamp when last transmission ended (for FAN control) long HiPowerTime = 0; // timestamp Hig Power - for EME/Digital mode conditions long HiAmp50Time = 0; // timestamp high current value on 50v power supply int anF = 0; // analog read forward power int anR = 0; // analog read reflected power int volt_50 = 0; // 50v volt supply - voltage (in 0,1 volt) int amp_50 = 0; // 50v volt supply - current (in 0,1 Amp) int amp_50_max = 0; // 50v volt supply - max current (in 0,1 Amp) int volt_26 = 0; // 26v volt supply - voltage (in 0,1 volt) int volt_12 = 0; // 13,8 v volt supply - voltage (in 0,1 volt) int temp = 0; // temperature (in 1 °C) int lasttemp = 0; // last temperature for fan control byte PTT = 0; // 0 = RX, 1 = TX byte wasPTT = 0; // former RX/TX status byte : if 1 = was in TX mode byte TestMode = 0; // 0 = normal mode, 1 = TestMode (PA will 'operate' with no +50v). Testmode is // initiated by depressing 'RESET4 button upon startup of SSPA int Operate = 0; // When operate = 0 , PA in stby mode int FAN = (0); // Fan control (PWM) - 255 = full speed byte HiTemp = (0); // Temperature passing above PID start temp byte FAULT = 0; // when FAULT = 0 : NO FAULT // when FAULT = 1 : Overcurrent // when FAULT = 2 : voltage error 50v // when FAULT = 3 : voltage error 26v // when FAULT = 4 : voltage error 12v // when FAULT = 5 : power out error // when FAULT = 6 : SWR error // when FAULT = 7 : NTC sensor read error // when FAULT = 8 : Temperature error // when FAULT = 9 : 50v idle current error // when FAULT = 10: W6PQL control PCB SWR exceed fault // when FAULT = 11: Power supply voltage too high in DIGI mode(= poor efficiency) // when FAULT = 12: PA power output too high in DIGI mode byte FirstLoop = (1); // First loop detect byte SelfTest = (1); // 1 to force selftest byte HiAmp50 = (0); // Flag to detect long lasting current peaks on 50v supply byte DIGImode = (0); // 0 = normal, 1 = EME/digtal mode conditions (no SSB but CW/JT65/FT-8/FSK ....) unsigned long pow_fwd = 0; // power forward (watts) unsigned long pow_ref = 0; // power reflected (watts) unsigned int pow_fwd_max = 0; // power forward max (for peak hold) unsigned int pow_ref_max = 0; // power reflected max (for peak hold) float SWR = 0; // SWR float Pratio = 0; // Power ratio P forward / P refl int SWRDis = 0; // power calculation for showing in display LiquidCrystal lcd(12,255,11,10,9,8,7,6); // (RS,RW,En1,En2,D4,D5,D6,D7) 255 if RW is connected to GND and not controlled by the interface. //=====for a 4x40 LCD with 2 HD44780 type chips and 18 pin interface in 2 rows of 9; // LCD Nano Signal // 18 Gnd Backlight white on blue 4x LED draws 40 mAmps // 17 +5V Backligt + through external resistor 15 Ohm. // 16 NC not used // 15 10 En2 -- enable the 2nd HD44780 chip which controls the bottom 2 rows of the display // 14 +5V supply logic // 13 Gnd logic // 12 Wiper of contrast resistor (22k between +5v and Gnd) // 11 12 RS // 10 Connect to Gnd // 9 11 En1 -- enable the 1st HD44780 which controls the top 2 rows // 5-8 Data 0-3: not used in 4 bit modes // 1-4 09-06 Data 4-7: LCD DB7 to ARDUINO 06, DB6 to 07, DB5 to 08, DB4 to 09 /////////////////// DRAW BAR ////////////////////////// void bar ( int row,int lev ) { lcd.setCursor( 3,row ); lcd.write( row ? ' ' : ' ' ); for( int i=1 ; i<30 ; i++ ) { int f=constrain( lev -i*5,0,5 ); // Level instant int p=constrain( lmax[row]-i*5,0,6 ); // Level maximum (=peak) if( f ) lcd.write( fill[ f ] ); else lcd.write( peak[ p ] ); } if( lev>lmax[row] ) { lmax[row] = lev; dly[row] = -(T_PEAKHOLD)/T_REFRESH1; // Starting delay value. Negative=peak don't move } else { if( dly[row]>0 ) lmax[row] -= dly[row]; if( lmax[row]<0 ) lmax[row]=0; else dly[row]++; } } byte block[8][8]= { { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 }, // define character for fill the bar { 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18 }, { 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C }, { 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E }, { 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08 }, // define character for peak level { 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 }, { 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 }, { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 }, }; ///////////////// PRINT TEMPLATE /////////////////// void printtemplate () { lcd.setCursor( 0,0 ); lcd.print( "FWD 0w"); lcd.setCursor( 0,1 ); lcd.print( "REF 0w"); lcd.setCursor( 6,2 ); lcd.print( "Vd=--.-"); lcd.setCursor( 6,3 ); lcd.print( "Id=--.-"); lcd.setCursor( 16,2 ); lcd.print( "Vr=--.-"); lcd.setCursor( 16,3 ); lcd.print( "Vs=--.-"); lcd.setCursor( 26,2 ); lcd.print( "t--"); lcd.print((char)223); //degree symbol //lcd.print((char)165); lcd.setCursor( 0,2 ); lcd.print( "swr"); lcd.setCursor( 0,3 ); lcd.print( "-.-"); /* PREAMP lcd.setCursor( 31,2 ); lcd.print( "PRE"); lcd.setCursor( 31,3 ); lcd.print( "AMP"); */ lcd.setCursor( 36,2 ); lcd.print( "stby"); lcd.setCursor( 4,2 ); lcd.write( peak[3] ); lcd.setCursor( 4,3 ); lcd.write( peak[3] ); lcd.setCursor( 14,2 ); lcd.write( peak[3] ); lcd.setCursor( 14,3 ); lcd.write( peak[3] ); lcd.setCursor( 24,2 ); lcd.write( peak[3] ); lcd.setCursor( 24,3 ); lcd.write( peak[3] ); lcd.setCursor( 30,2 ); lcd.write( peak[3] ); lcd.setCursor( 30,3 ); lcd.write( peak[3] ); lcd.setCursor( 34,2 ); lcd.write( peak[3] ); lcd.setCursor( 34,3 ); lcd.write( peak[3] ); } /////////// This function will calculate temperature from 10k NTC readout ///////////// double Thermister(int RawADC) { double Temp; Temp = log(((10240000/RawADC) - 10000)); Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp ); Temp = Temp - 273.15; // Convert Kelvin to Celcius // Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit return Temp; } //////////////////////////////////////////////////////////////// /////////////////////////// SETUP ////////////////////// //////////////////////////////////////////////////////////////// void setup() { /// Prepare I/O pins pinMode(IN_PREAMP, INPUT); pinMode(IN_OPERATE, INPUT); pinMode(IN_reset, INPUT); digitalWrite(supply_50v, LOW); // We do not allow +50v yet pinMode(supply_50v, OUTPUT); digitalWrite(Buzzer,0); pinMode(Buzzer, OUTPUT); analogWrite(FANcontrol,FAN); digitalWrite(PTT_block,HIGH); // we force RX mode via W6PQL board till all is OK ! pinMode(PTT_block, OUTPUT); lcd.begin(nColumns,nRows); for( int i=0 ; i<8 ; i++ ) lcd.createChar( i,block[i] ); lcd.clear(); lcd.setCursor(5,0 ); lcd.print ("1 . 2 5 k W V H F S S P A "); lcd.setCursor(5,2 ); lcd.print (" by O N 7 E Q "); lcd.setCursor(5,3 ); lcd.print (" 12/2018 - V1.11 "); delay (2000); lcd.clear(); delay (500); printtemplate(); lastT2 = millis(); // set T2 display refresh } /////////////////////////////////////////////////////////// /////////////////////////// LOOP ////////////////////////// /////////////////////////////////////////////////////////// void loop() { // Check if TESTMODE required. Testmode is initiated by depressing the RESET button upon powering up. // In testmode, no 50v supply is applied to PA pallet, and no 50v is required to go into 'OPERATE' and 'TX' mode // exiting TESTMODE is only possible by powering up again the power amplifier. if (((digitalRead(IN_reset) == LOW) or (TestMode == 1))and (FirstLoop == 1) ) { TestMode = 1; lcd.setCursor(10,0 ); lcd.print ("T E S T M O D E !"); lcd.setCursor( 7,1 ); lcd.print ("(N O + 5 0 v T O P A)"); digitalWrite(Buzzer,HIGH); delay (200); digitalWrite(Buzzer,LOW); lcd.setCursor(10,0 ); lcd.print (" "); lcd.setCursor( 7,1 ); lcd.print (" "); delay (200); digitalWrite(Buzzer,HIGH); lcd.setCursor(10,0 ); lcd.print ("T E S T M O D E !"); lcd.setCursor( 7,1 ); lcd.print ("(N O + 5 0 v T O P A)"); delay (200); digitalWrite(Buzzer,LOW); lcd.setCursor(10,0 ); lcd.print (" "); lcd.setCursor( 7,1 ); lcd.print (" "); delay (200); digitalWrite(Buzzer,HIGH); lcd.setCursor(10,0 ); lcd.print ("T E S T M O D E !"); lcd.setCursor( 7,1 ); lcd.print ("(N O + 5 0 v T O P A)"); delay (200); digitalWrite(Buzzer,LOW); delay (500); printtemplate(); } // update OPERATE / STBY mode if (digitalRead(IN_OPERATE) == HIGH) Operate = 1; else Operate = 0; // update PTT status if (analogRead(IN_PTT) < 512) PTT = 1; else PTT = 0; // Check if no PTT & operate mode at startup of our PA if ((Operate == 1) and (FirstLoop == 1) and (PTT == 1)) { digitalWrite(Buzzer,HIGH); lcd.setCursor(5,0 ); lcd.print ("SET TRANSCEIVER IN RX PLEASE !"); delay (500); digitalWrite(Buzzer,LOW); printtemplate(); delay (500); loop(); } ///////////////////////////////////////////////////////////////////////////////////////// /////// perform EVERY LOOP some measurement we need for critical faults detection /////// ///////////////////////////////////////////////////////////////////////////////////////// // Check Current 50v amp_50 = map (analogRead(IN_50vI),0,1023,0,1680); //90 Amp ATTOPILOT board - convert to 0,1 A if (amp_50 < 31) amp_50 = amp_50 + (amp_50/4) ; // Arduino not linear at very low voltages ? if (amp_50 < 51) amp_50 = amp_50 + (amp_50/7) ; if (amp_50 < 101) amp_50 = amp_50 + (amp_50/10) ; if (amp_50 < 150) amp_50 = amp_50 + (amp_50/20) ; if (amp_50 > amp_50_max) amp_50_max = amp_50; // measure peak current every T1 to display max every T2 //Detect current peak (instantaneous, 'never exceed' value) : if (amp_50 >= 400) { // Overcurrent ! peak with more than 40A ! FAULT = 1; fault(); // Jump to FAULT handling } //Detect current of 'long lasting' peak of at least 100mS (overdrive of PA - protect LDMOS) : if (amp_50 >= 350) { // more than 35A if (HiAmp50 == 0) HiAmp50Time = millis(); // timestamp reading HiAmp50 = 1; } else (HiAmp50 = 0); if ((HiAmp50 == 1) and ((millis() - HiAmp50Time) > 100)) { // we have a peak ! HiAmp50 = 0; FAULT = 1; fault(); // Jump to FAULT handling } // Check Voltage 50v (48 - 55V) volt_50 = map (analogRead(IN_50vv),0,1023,0,788); //90 Amp ATTOPILOT board - convert to 0,1 V if ((volt_50 >= 570) ){ // Overvoltage, always alarm ! FAULT = 2; fault(); // Jump to FAULT handling } if ((volt_50 <= 40) and (Operate == 1) and (TestMode == 0) ){ // Undervoltage while in operate mode! FAULT = 2; fault(); // Jump to FAULT handling } // Check Voltage 26v. Make sure RF relays can be energized ! volt_26 = map(analogRead(IN_26v), 0,1023,0,(50*(R2+R1)/R1)) + 1; if(volt_26 == 1) volt_26 = 0; if ((volt_26 >= 300) or (volt_26 <= 230) ){ // Under or overvoltage, always alarm ! FAULT = 3; fault(); // Jump to FAULT handling } // Check Voltage 12v. volt_12 = map(analogRead(IN_12v), 0,1023,0,(50*(R4+R3)/R3)); if ((volt_12 >= 150) or (volt_12 <= 110) ){ // Under or overvoltage, always alarm ! FAULT = 4; fault(); // Jump to FAULT handling } // All critical parameters checked, if we are in SELFTEST mode on startup we can now release PTT block and allow +50v supply PA if ((SelfTest == 1) ) { DIGImode = (0); // Reset DIGImode condition if (TestMode == 0) digitalWrite(supply_50v,HIGH); // +50v only when not in testmode // Check current 50v, should be 0 ! amp_50 = map (analogRead(IN_50vI),0,1023,0,1680); //90 Amp ATTOPILOT board - convert to 0,1 A if ((amp_50 > 1) and (TestMode == 0) ) { // more than 0.1 A idle current with no bias! Fault in pallet ! digitalWrite(supply_50v,LOW); // immediately switch off 50v ! FAULT = 9; fault(); // Jump to FAULT handling } lcd.setCursor( 13,0 ); lcd.print ("SELFTEST = OK !"); if (FirstLoop == 1) analogWrite (FANcontrol,255); // Fan test // Morse code // Oscar digitalWrite(Buzzer,HIGH); delay (240); digitalWrite(Buzzer,LOW); delay (80); digitalWrite(Buzzer,HIGH); delay (240); digitalWrite(Buzzer,LOW); delay (80); digitalWrite(Buzzer,HIGH); delay (240); digitalWrite(Buzzer,LOW); // space analogWrite (FANcontrol,0); delay (240); // Kilo digitalWrite(Buzzer,HIGH); delay (240); digitalWrite(Buzzer,LOW); delay (80); digitalWrite(Buzzer,HIGH); delay (80); digitalWrite(Buzzer,LOW); delay (80); digitalWrite(Buzzer,HIGH); delay (240); digitalWrite(Buzzer,LOW); delay (500); // end CW tune // NOW PA READY TO GO ! digitalWrite(PTT_block,LOW); SelfTest = (0); LastTXtime = millis() - 35000; // avoid FAN running at startup } ///////////////////////////////////////////////////////////////// ///// we must now update screen for timer T1 = short cycle ////// ///////////////////////////////////////////////////////////////// if (( millis() 5) { // only correct for diode voltage when more than zero pow_fwd = (pow_fwd + Vdiode)*(pow_fwd + Vdiode) / calibrP; } // detect PA overdrive if (pow_fwd > 1400) { // PA overdriven ! (limit for 1k8 version) FAULT = 5; fault(); // Jump to FAULT handling } // detect if SSB or DIGI condition (more than 14 seconds continuous CW/JT65/FT-8 ....) if ((pow_fwd < 500)) { // less than 500w continuous HiPowerTime = millis(); if (millis() - HiPowerTime > 30000) DIGImode = 0; // wait 30s to recover from EME mode } if ((pow_fwd > 500) and (millis() - HiPowerTime > 14000)) { // more than 500W continuous during 14s DIGImode = (1); } if ((volt_50 > 490) and (DIGImode == 1)){ // Limit supply power to 48V for digital modes (= optimal efficiency) FAULT = 11; fault(); // Jump to FAULT handling } if ((pow_fwd > 1100) and (DIGImode == 1)){ // Limit output power for digital modes to 1kW FAULT = 12; fault(); // Jump to FAULT handling } pow_ref = analogRead(IN_REFL); if (pow_ref > 5) { // only correct for diode voltage when more than zero pow_ref = (pow_ref + Vdiode)*(pow_ref + Vdiode) / calibrP; } // detect SWR error / load mismatch Pratio = pow_fwd / pow_ref; // calculate ratio with raw data SWR = abs ((1+sqrt(Pratio)) / (1-sqrt(Pratio))) ; if ((SWR > 2) and (pow_fwd > 100)) { // only when forward power > 100w FAULT = 6; fault(); // Jump to FAULT handling } // Detect if W6PQL is in SWR error if ((pow_ref > 5) and (pow_fwd < 2)) { // When W6PQL trips on SWR fault, REFL power steady approx 20w FAULT = 10; fault(); // Jump to FAULT handling } // bargraph display anF = map( pow_fwd,0,1250,0,150 ); // 150 = 30 x 5 colums, full scale 1250w anR = map( pow_ref,0,125 ,0,150 ); // 150 = 30 x 5 colums, full scale 125 w bar( 0,anF ); bar( 1,anR ); // digital readout of power FWD & REFL pow_fwd = ((pow_fwd+5)/10)*10; // FWD round up to 10w precision if (pow_ref > 10) pow_ref = ((pow_ref+2)/5)*5; // REFL round up to 5 w precision if more than 10W // update PEP¨meter ? if (pow_fwd >= pow_fwd_max) { // we have a peak ! lastTpep = millis(); pow_fwd_max = pow_fwd; pow_ref_max = pow_ref; } if (millis() > (lastTpep + T_pepHOLD)) { // clear the peak after hold time pow_fwd_max = pow_fwd; pow_ref_max = pow_ref; } lcd.setCursor( 34,0 ); // print forward power max if (pow_fwd_max > 999) { lcd.print( "1."); if ((pow_fwd_max - 1000) < 100) lcd.print( "0"); if ((pow_fwd_max - 1000) < 10) lcd.print( "0"); lcd.print( (pow_fwd_max - 1000),DEC); } else { if (pow_fwd_max < 1000) lcd.print( " "); if (pow_fwd_max < 100) lcd.print( " "); if (pow_fwd_max < 10) lcd.print( " "); lcd.print(pow_fwd_max,DEC); } lcd.setCursor( 34,1 ); // print reflected power if (pow_ref_max > 999) { // hopefully not the case lcd.print( "1."); if ((pow_ref_max - 1000) < 100) lcd.print( "0"); if ((pow_ref_max - 1000) < 10) lcd.print( "0"); lcd.print( (pow_ref_max - 1000),DEC); } else { if (pow_ref_max < 1000) lcd.print( " "); if (pow_ref_max < 100) lcd.print( " "); if (pow_ref_max < 10) lcd.print( " "); lcd.print(pow_ref_max,DEC); } // update PREAMP STATUS if (digitalRead(IN_PREAMP) == LOW){ lcd.setCursor( 31,2 ); lcd.print( "PRE"); lcd.setCursor( 31,3 ); lcd.print( "AMP"); } else { lcd.setCursor( 31,2 ); lcd.print( " "); lcd.setCursor( 31,3 ); lcd.print( " "); } // update OPER / STBY STATUS if (Operate == HIGH){ // OPERATE lcd.setCursor( 36,2 ); lcd.print( "OPER"); } else { // STANDBY lcd.setCursor( 36,2 ); lcd.print( "stby"); lcd.setCursor( 36,3 ); lcd.print( " "); // Clear TX / RX indicator LastTXtime = millis() - 31000; // reset FAN timer } // update TX / RX STATUS if (Operate == 1){ lcd.setCursor( 36,3 ); if (PTT == 1){ lcd.print( "-TX-"); wasPTT = 1; } if (PTT == 0){ lcd.print( "-RX-"); if (wasPTT == 1) { // previous state was TX ! wasPTT = 0; LastTXtime = millis(); } } } /////////////////////////////////////////////////////////////////////////// //////////// we must update screen for timer T2 = long cycle ////////////// /////////////////////////////////////////////////////////////////////////// if ((millis()-lastT2)>T_REFRESH2) { lastT2 = millis(); // SWR calculation & display // SWR = abs ((1+sqrt(Pratio)) / (1-sqrt(Pratio))) ; // already calculated ! SWRDis = (SWR * 10) + 0.5; // display SWR one figure after DP, round upwards if (SWRDis < 10){ // SWR cannot be lower than 1.0 SWRDis = 10 ; } lcd.setCursor( 0,3 ); if (SWRDis >= 50) { lcd.print(">5!"); } if (pow_fwd < 5) { lcd.print("-.-"); } else if (SWRDis < 50){ lcd.print((SWRDis/10), DEC); lcd.print("."); lcd.print((SWRDis)%10, DEC); } // Temperature check & display temp = ((Thermister(1023 - analogRead(IN_NTC))) + 0.5); // if (temp >= 0) lcd.print(" "); // If temp positive, print space, else a '-' will show up lcd.setCursor( 27,2 ); if ((temp < 0) or (temp > 99)){ lcd.print("??"); FAULT = 7; // NTC error ! fault(); } else { constrain (temp,0,99); if ((temp)<10) lcd.print("0"); lcd.print(temp, DEC); } if (((temp >= 0) and (temp <= 15)) or (temp >= 65)) { // FAULT check FAULT = 8; // temperature error, too cold or hot ! fault(); } // + 50v SUPPLY Volt & Amp measurements display lcd.setCursor( 9,2 ); // Volts if (volt_50 > 999) volt_50 = 999; // smoke in the shack ! if (volt_50<100) lcd.print(" "); lcd.print((volt_50/10), DEC);lcd.print("."); lcd.print((volt_50)%10, DEC); lcd.setCursor( 9,3 ); // Amps if (amp_50_max > 999) amp_50_max = 999; if (amp_50_max<100) lcd.print(" "); lcd.print((amp_50_max/10), DEC); lcd.print("."); lcd.print((amp_50_max)%10, DEC); amp_50_max = 0; // reset after display // + 28v Relay SUPPLY measurements display //volt_26 = map(analogRead(IN_26v), 0,1023,0,(50*(R2+R1)/R1)); lcd.setCursor( 19,2 ); if (volt_26 < 100) lcd.print(" "); // less than 10v lcd.print((volt_26/10), DEC); lcd.print("."); lcd.print((volt_26)%10, DEC); // + 12v SUPPLY measurements display //volt_12 = map(analogRead(IN_12v), 0,1023,0,(50*(R4+R3)/R3)); lcd.setCursor( 19,3 ); if (volt_12 < 100) lcd.print(" "); // less than 10v lcd.print((volt_12/10), DEC); lcd.print("."); lcd.print((volt_12)%10, DEC); ///////////////////////////////////////////////////////////// ////// Perform some less time related critical controls ///// ///////////////////////////////////////////////////////////// // FAN CONTROL // start of routine condition, FAN not runnning if (temp < 35) FAN = 0; // If SSPA in OPERATE mode, FAN to run very slow if temp between 35 - 40°and in RX mode if ((Operate == 1) and (temp>=35) and (temp < 40) and (PTT == 0)) FAN = fan_vy_slow; // If SSPA in OPERATE mode, FAN to run slow if temp < 40°and in TX mode if ((temp < 40) and (Operate == 1) and (PTT == 1)) FAN = fan_slow; // keep fan running for 30 sec after last TX and operate mode if ((temp < 40) and (Operate == 1) and (abs (millis()- LastTXtime) < 31000)) FAN = fan_slow; if ((temp >= 40) and (temp < 45)) FAN = fan_med_lo; if ((temp >= 45) and (FirstLoop == 1)) FAN = 255; //Temp high at power up, immediately cool down ! if (temp < 45) HiTemp = 0; // Temp at least 45°, now go into PID control ! if (temp >= 45) { if (HiTemp == 0) { // detect temp passing PID temp level to start PID correction with no delay... HiTemp = 1; FAN = fan_med_hi; lastTempTime = millis() - 5100; } if (((millis() - lastTempTime) > 5000)) { // every 5 seconds, update PID lastTempTime = millis(); // recover timer for fan control if (temp > lasttemp + 1) { // rising temp, at least 1° ! FAN = FAN + ((temp - 45)*(temp-45)*3); if (FAN > 255) FAN = 255; // constrain FAN value lasttemp = temp; } if (temp < lasttemp) { // temp going down ! FAN = FAN - ((temp - 45)*10); if (FAN < fan_med_hi) FAN = fan_med_hi; // constrain FAN value lasttemp = temp; } } // End PID update } // end temp > 45° if (FAULT == 7) FAN = 0; // we have a NTC error, stop FAN ! lcd.setCursor (26,3); /* // 000 - 255 indication if ((FAN) < 100) lcd.print("0"); if ((FAN) < 10) lcd.print("0"); lcd.print(FAN, DEC); // end debug */ // if ((DIGImode == 1) or (temp >= 50)) FAN = 255; // Test ! // Now control FAN with proper value : if ((DIGImode == 1) and (temp >= 55)) FAN = 255; // Bypass the PID routine, force maximum cooling immediately ! if (temp >= 60) FAN = 255; // Bypass the PID routine, force maximum cooling because limit is 65° if (FAN == 0) lcd.print("fan"); if (FAN == fan_vy_slow) lcd.print("20%"); if (FAN == fan_slow) lcd.print("30%"); if (FAN == fan_med_lo) lcd.print("40%"); if (FAN >= fan_med_hi) { lcd.print (map(FAN,fan_med_hi,255,50,99),DEC); lcd.print("%"); } analogWrite (FANcontrol,FAN); // end FAN control routine FirstLoop = 0; // first loop completely run } /////////////////// END T2 Refresh ///////////////////////// /* /////////// recover Timer 2 display refresh /////////////// if ((millis()-lastT2)>T_REFRESH2) { lastT2 = millis(); } */ } ////// this is end of loop ///////////////////// FAULT CONDITION ///////////////////// // when FAULT = 0 : NO FAULT // when FAULT = 1 : Overcurrent // when FAULT = 2 : voltage error 50v // when FAULT = 3 : voltage error 26v // when FAULT = 4 : voltage error 12v // when FAULT = 5 : power out error // when FAULT = 6 : SWR error // when FAULT = 7 : NTC read error // when FAULT = 8 : Temperature error // when FAULT = 9 : 50v idle current error // when FAULT = 10: W6PQL control PCB SWR exceed fault // when FAULT = 11: Digital mode, but power supply voltage too high (= poor efficiency) // when FAULT = 12: Digital mode, but PA power output too high void fault() { if (FAULT == 0) return; // No fault ! // take vital actions ! digitalWrite(PTT_block,HIGH); // signal to sequencer that we force RX mode ! digitalWrite(supply_50v, LOW); // remove + 50v supply from pallet // update TX / RX STATUS while in FAULT void if (Operate == 1){ lcd.setCursor( 36,3 ); lcd.print( "-RX-"); } else { lcd.setCursor( 36,3 ); lcd.print( " "); } digitalWrite(Buzzer,HIGH); // Sound buzzer // Signal on display lcd.setCursor( 0,0 ); lcd.print (" F A U L T ! "); lcd.setCursor( 0,1 ); if (FAULT == 1){ lcd.print(" OVERCURRENT 48-55v SUPPLY "); // Show Error value lcd.setCursor( 9,3 ); // Amps if (amp_50 > 999) amp_50 = 999; if (amp_50<100) lcd.print(" "); lcd.print((amp_50/10), DEC); lcd.print("."); lcd.print((amp_50)%10, DEC); } if (FAULT == 2) { lcd.print(" UNDER/OVERVOLTAGE 48-55v SUPPLY "); // Show Volt_50 error value lcd.setCursor( 9,2 ); if (volt_50 > 999) volt_50 = 999; if (volt_50<100) lcd.print(" "); lcd.print((volt_50/10), DEC);lcd.print("."); lcd.print((volt_50)%10, DEC); }; if (FAULT == 3) { lcd.print(" UNDER/OVERVOLTAGE 26v SUPPLY "); // Show Volt_26 error value lcd.setCursor( 19,2 ); if (volt_26 < 100) lcd.print(" "); // less than 10v lcd.print((volt_26/10), DEC); lcd.print("."); lcd.print((volt_26)%10, DEC); }; if (FAULT == 4) { lcd.print(" UNDER/OVERVOLTAGE 13.8v SUPPLY "); // Show Volt_12 error value lcd.setCursor( 19,3 ); if (volt_12 < 100) lcd.print(" "); // less than 10v lcd.print((volt_12/10), DEC); lcd.print("."); lcd.print((volt_12)%10, DEC); }; if (FAULT == 5) lcd.print(" POWER AMPLIFIER OVERDRIVE "); if (FAULT == 6) lcd.print(" LOAD MISMATCH (SWR EXCEED) "); if (FAULT == 7) lcd.print(" NTC TEMP. PROBE CIRCUIT ERROR "); if (FAULT == 8) lcd.print(" UNDER/OVER TEMPERATURE "); if((FAULT == 8) and (temp > 40 )) analogWrite (FANcontrol,255); // cooling down ... if (FAULT == 9) lcd.print(" 50v SUPPLY IDLE CURRENT EXCEED "); if (FAULT ==10) lcd.print(" SWR ERROR - POWER CYCLE SSPA "); if (FAULT ==11) lcd.print("LIMIT SUPPLY VOLTAGE TO 48V IN DIGI MODE"); if (FAULT ==12) lcd.print(" LIMIT PA OUTPUT TO 1kW IN DIGI MODE "); delay (500); digitalWrite(Buzzer,LOW); lcd.setCursor( 0,0 ); lcd.print (" "); delay (500); if (digitalRead(IN_reset) == LOW) ResetFault(); fault(); } /////////////////// END FAULT CONDITION //////////////////// ////////////////// RECOVER PROCEDURE ///////////////////// void ResetFault() { digitalWrite(Buzzer,LOW); lcd.setCursor( 0,1 ); lcd.print (" "); lcd.setCursor( 0,0 ); if (PTT ==0) lcd.print (" R E S E T T I N G "); if (PTT ==1) lcd.print (" R E S E T T I N G B U T "); if (PTT ==0)delay (800); lcd.setCursor( 0,0 ); if (PTT ==0)lcd.print (" R E S E T T I N G . "); delay (800); lcd.setCursor( 0,0 ); if (PTT ==0)lcd.print (" R E S E T T I N G . . "); if (PTT ==0)delay (800); lcd.setCursor( 0,0 ); if (PTT ==0)lcd.print (" R E S E T T I N G . . . "); if (PTT ==0)delay (1500); // update PTT status : restart / recover only in RX mode if (analogRead(IN_PTT) < 512) { // We are still in TX mode ! PTT = 1; digitalWrite(Buzzer,HIGH); lcd.setCursor( 0,0 ); lcd.print (" SET TRANSCEIVER IN RX PLEASE ! "); delay (800); ResetFault(); // stay in loop till RX mode } // Recovery digitalWrite(Buzzer,LOW); digitalWrite(PTT_block,LOW); // signal to sequencer that we allow again PTT mode ! FAULT = 0; //recover fault condition printtemplate (); SelfTest = 1; // Force Selftest }