#include #include #include #include #include #include "iocompat.h" /************************* * AVR SWR METER * * (C) OK1DX 2009-13 * * ok1dx@amsat.org * ************************* version 1.3 - bugfix function correction (tnx UY3QW) version 1.2 - 2 probes (autoswitching) - protection signal version 1.1 - SWR calculated using peak measurement - indication of FWD overload version 1.0 - first release */ // Xtal 4 MHz // fuse bits: // low byte 11111111 = 0xFF - all unprogrammed BODLEVEL=1, BODEN = 1, SUT0,1 = 1, CKSEL0-3 = 1 // high byte 11011001 (default value from manufacturer, it is not programmed by attached SP12 script) // SWR meter with LCD 2x40 chars, 2x24 chars or 2x20 chars #define LCDLINELENGTH 40 //#define LCDLINELENGTH 24 //#define LCDLINELENGTH 20 // uncomment following line if you use internal voltage reference (remove jumper on PCB as well!) //#define INTERNAL_REF // Level tresholds // minimum input level to perform SWR calculation, reset for SWR protection (full scale = 1024) #define SWRCALC 100 // minimum input level necesary for channel switching (full scale = 1024) #define CHSWITCH 10 // SWR to activate protection, multiplied by 100 (here for SWR>3) #define SWRMAX 300 // LCD interface: // PB0 - out, Enable // PB1 - out, R/W // PB2 - out, RS (control/data) // PD4 - in, out D4 // PD5 - in, out D5 // PD6 - in, out D6 // PD7 - in, out D7 #define ENABLE PB0 #define RW PB1 #define RS PB2 // Probe interface: // PC0 - ADC0, channel A, probe select // PC1 - ADC1, channel A, forward voltage // PC2 - ADC2, channel A, return voltage // PC3 - ADC3, channel B, probe select // PC4 - ADC4, channel B, forward voltage // PC5 - ADC5, channel B, return voltage // Programming interface // PC6 - RESET // PB3 - MOSI // PB4 - MISO // PB5 - SCK // USART interface // PD0 - RXD // PD1 - TXD // SWITCH // PD2 - in, pullup // SWR PROTECTION // PD3 - output, 1 = SWR too high #define PORTBPU 0 #define PORTCPU 0 #define PORTDPU (1< #define ADCCOMPLETE 1 //tx buffer empty #define REFRESH 2 //command from timer to perform display data refresh #define SETUPMODE 3 //when set, setup mode is active #define OVERLOAD 4 // FWD ADC value overload #define CHSELECT 5 // 0 = channel A selected, 1 = channel 2 selected #define SWRPROTECT 6 // 1 = SWR protection active volatile uint8_t rxcount; // number of characters in RX buffer uint8_t rxbuf[RXBUFSIZE]; // receive buffer volatile uint8_t txcount; //number of characters in TX buffer uint8_t txbuf[TXBUFSIZE]; // transmit buffer char n2a[5]; // buffer for conversion from number to ASCII. No terminating 0! uint16_t adcValue[6]; // ADC values; 0 = Ch A PROBE, 1 = Ch A forward, 2 = Ch A reverse, 3 = Ch B PROBE, 4 = Ch B forward, 5 = Ch B reverse uint16_t fwdmem[PEAKMEMSIZE]; // forward peak voltage shift register uint16_t revmem[PEAKMEMSIZE]; // reverse peak voltage shift register uint16_t fwdavg; // used for forward voltage floating averaging uint16_t revavg; // used for reverze voltage floating averaging uint16_t fwpwr; // calculated scalled forward power (peak or average) up to 9999 uint16_t revpwr; // calculated scalled reverse power (peak or average) up to 9999 uint8_t memptr; // pointer to last sample in peak memory uint8_t timerctr; uint8_t probe; // probe selector (based on ADC channel 0) uint8_t corrs[PROBES][16] __attribute__((section(".eeprom"))) = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{14,18,16,15,15,16,18,25,30,28,27,15,14,7,6,0}}; // power scaling. HF power for ADC corrected reading = 1000. Value between 1000 and 9999 (4 digits) uint16_t scales[PROBES] __attribute__((section(".eeprom"))) = {1000,2000,3000,4000,5000,6000,7000,8000}; // power range. bit0-3 position of decimal point, bit4-7 displayed unit (pW,nW,uW,mW,W,kW,MW) uint8_t ranges[PROBES] __attribute__((section(".eeprom"))) = {0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,0x42}; uint8_t welcome[] __attribute__((section(".eeprom"))) = "SWR Mega 1.3 OK1DX"; uint8_t txtF[] __attribute__((section(".eeprom"))) = " F="; uint8_t txtR[] __attribute__((section(".eeprom"))) = " R="; uint8_t txtPwr[] __attribute__((section(".eeprom"))) = " Pwr="; uint8_t txtSWR[] __attribute__((section(".eeprom"))) = "W SWR="; uint8_t txtUnknown[] __attribute__((section(".eeprom"))) = " ??? "; uint8_t txtOverload[] __attribute__((section(".eeprom"))) = " !!! "; uint8_t txtHigh[] __attribute__((section(".eeprom"))) = " > 10"; uint8_t txtP[] __attribute__((section(".eeprom"))) = " P="; // generates interrupt every 100 msec; initiates DAC sampling, displays bar indicator // every fifths cycle (500 msec) initiates data processing // performs no direct action, just sets the flags, all work is done in main loop ISR (TIMER1_OVF_vect) { timerctr++; if (timerctr > 4) { timerctr = 0; flagreg |= (1<= 5) // last channel number = 5 { #ifdef INTERNAL_REF ADMUX = 0xc0; // prepare for next startup, internal reference #else ADMUX = 0; // prepare for next startup #endif flagreg |= (1< 0) { UDR = txbuf[0]; // pass character to TX data register txcount--; for(uint8_t i=0; i flag RXCOMPLETE is set. // after processing set rxcount to zero and enable RXC interrupt ISR (USART_RXC_vect) { while (UCSRA & (1< sets flag RX complete, disable further RX { flagreg |= 1< ignored { rxbuf[rxcount] = rxc; rxcount++; } } } } // wait till LCD reports busy (no timeout) void lcdWait(void) { // return;// simulation, to allow debugging PORTB = PORTBPU; DDRD &= 0xF; // set upper 4 pins as input uint8_t data = 0xFF; while(data) // if BF not set { // RS = 0, RW = 1, E = 2 pulses (for both nibles) // requirement: enable cycle >1microsec, enable active >450 nsec PORTB = PORTBPU | (1<='0') && (*bufptr <='9') && ((bufptr-buffer) < len)) { retval = 10*retval + *bufptr - '0'; bufptr++; } return retval; } // append number in n2a buffer to usart TX buffer. Ignore space characters void usartNumber(void) { for(uint8_t i=0; i<5; i++) { if (n2a[i] != ' ') { txbuf[txcount] = n2a[i]; txcount++; if (txcount >= TXBUFSIZE) txcount = TXBUFSIZE-1; // should never happen!!! } } } //periodical sending ADC values when in SETUP mode void setupUsartSend(void) { if (!(UCSRB & (1< txbuf[txcount++] = 10; // UCSRB |= (1< txbuf[txcount++] = 10; // UCSRB |= (1< txbuf[txcount++] = 10; // UCSRB |= (1< input, 1 -> output. PORTxn = 0 -> pullup OFF, 1 -> pullup ON // PORT B: bit 0-2 out, bit 3-5 inp 3state. bit 6,7 XTAL - defined by config bits DDRB = (1< 0x3FF) x = 0x3FF; uint8_t* idx = corrs[probe] + (x >> 6); // x value 10 bits, idx 4 bits int8_t a = (int8_t)eeprom_read_byte(idx); // point above setpoint int8_t b = 0; if ((x >> 6) > 0) b = (int8_t)eeprom_read_byte(idx - 1); // point bellow setpoint int8_t diff = x & 0x3F; // lower 6 bits, range between corr points return x + b + (((a-b) * diff)/64); } uint16_t correction_old(uint16_t x, uint8_t probe) { return x; //todo: use EEPROM table to correct linearity of detector } // Show horizontal level bars for forward/reverse signal on 1st display line void showBar(uint16_t fwd, uint16_t rev) { lcdSendCmd(0x80); // address - first line, display text uint8_t step = 1024/LCDLINELENGTH; fwd *= 2; // we have 2 levels per character rev *= 2; uint16_t level = step/2; for (uint8_t psn = 0; psn < LCDLINELENGTH; psn++) { uint8_t c = 0; if (fwd > level) c=1; if (rev > level) c|=2; level += step; if (fwd > level) c|=4; if (rev > level) c|=8; level += step; if (c == 15) c = 3; else if (c == 11) c = 5; else if (c == 10) c = 7; else if (c == 0) c = 32; // space else c--; lcdSendChar(c); } } // print number from n2a buffer to LCD at given position void lcdNumber(void) { for(uint8_t i=0; i<5; i++) lcdSendChar(n2a[i]); } // multiple 2 12bit numbers, result dividen by 1024 (total 14 bit length) uint16_t dxMult(uint16_t a, uint16_t b) { uint16_t y; uint8_t a1 = (a>>8); // max 4 bit uint16_t a2 = a & 0xFF; uint8_t b1 = (b>>8); // max 4 bit uint16_t b2 = b & 0xFF; y = (((a1 * b1)<<8) + (a1 * b2) + (a2 * b1) + ((a2 * b2)>>8))>>2; return y; } // calculates power based on measured voltage scalled to range // range = power when voltage = 1024. 1000 <= range <= 9999 // retval = volts * volts / (1024 * 1024) * range uint16_t calcPwr(uint16_t volts, uint16_t range) { uint16_t v = dxMult(volts, volts); // total shift 10 bit v = dxMult(v, range); return v; // (Volts * volts * range) >> 20, that means result 0 to range } // send complete set of probe parameters (correction void sendProbePar(uint8_t rprobe) { uint8_t probechar = rprobe + '0'; while (UCSRB & (1< txbuf[txcount++] = 10; // UCSRB |= (1< txbuf[txcount++] = 10; // UCSRB |= (1< txbuf[txcount++] = 10; // UCSRB |= (1< CHSWITCH) && (adcValue[1] > adcValue[4])) { flagreg &= ~(1< CHSWITCH) && (adcValue[4] > adcValue[1])) flagreg |= (1<> 7) & 0x07; uint16_t fwd = correction(ch_fwd, probe); uint16_t rev = correction(ch_rev, probe); fwdavg = (AVERAGEFACTOR * fwdavg + fwd)/(AVERAGEFACTOR + 1); //floating average revavg = (AVERAGEFACTOR * revavg + rev)/(AVERAGEFACTOR + 1); memptr++; if (memptr >= PEAKMEMSIZE) memptr = 0; // rounding fwdmem[memptr] = fwd; // override oldest values with the new ones revmem[memptr] = rev; uint16_t fwdpeak = fwdmem[0]; uint16_t revpeak = revmem[0]; for(uint8_t i = 1; i fwdpeak) fwdpeak = fwdmem[i]; if ( revmem[i] > revpeak) revpeak = revmem[i]; } if (fwdpeak >=1023) flagreg |= (1<= SWRCALC) // excitation for FWD signal needed to start SWR calculation { swr = 10000; // SWR very high (>10) if (revpeak < fwdpeak) { swr = ((fwdpeak+revpeak)*20)/(fwdpeak - revpeak); // calculated value; 20 times real SWR if (swr>=2000) swr = 10000; // too high else swr *= 5; // value = 100 * SWR } if (swr > SWRMAX) flagreg |= (1< rev) fwd = fwd - rev; // forward power minus reverse power else fwd = 0; n2ascii(fwd, range & 0x0F); // lower bits - decimal point position lcdNumber(); if (!(flagreg & (1<> 4) { case 0: pfx='p';break; case 1: pfx='n';break; case 2: pfx=0xe4;break; // micro case 3: pfx='m';break; case 5: pfx='k';break; case 6: pfx='M';break; } lcdSendChar(pfx); if (!(flagreg & (1< txbuf[3] = 10; // txcount=4; UCSRB |= (1< txbuf[txcount++] = 10; // UCSRB |= (1<4) && (rxbuf[1] == 'p')) { uint8_t wprobe = rxbuf[2] - '0'; if (wprobe < PROBES) { uint8_t wpoint = rxbuf[3] - 'a'; if (wpoint < 16) { uint16_t val = ascii2n(rxbuf + 4, rxcount - 4); eeprom_write_byte(corrs[wprobe]+wpoint, val); while ((UCSRB & (1<6) && (rxbuf[1] == 's')) { uint8_t wprobe = rxbuf[2] - '0'; if (wprobe < PROBES) { uint16_t val = ascii2n(rxbuf + 3, rxcount - 3); if ((val < 9999) & (val >= 1000)) { eeprom_write_word(scales + wprobe, val); while ((UCSRB & (1<3) && (rxbuf[1] == 'r')) { uint8_t wprobe = rxbuf[2] - '0'; if (wprobe < PROBES) { uint16_t val = ascii2n(rxbuf + 3, rxcount - 3); if (val < 256) { eeprom_write_byte(ranges + wprobe, val); while ((UCSRB & (1<= 0) && (rprobe < 8)) sendProbePar(rprobe); // ToDo send all probe parameters via USART } } rxcount = 0; flagreg &= ~(1<