Im folgenden ist der gesamte Quellcode des LC-Meters dargestellt, wie ich ihn für die einzelnen Experimente und Meßvorschriften im Hauptartikel benutzt habe. Die verwendete CPU ist ein Atmel ATmega163. Wird eine andere CPU aus der ATmega-Reihe verwendet, dann sind gegebenenfalls ein paar Namen (etwa bei den Timern und bei den Interruptvektoren) auszuwechseln. Wird eine völlig andere CPU verwendet, dann sind natürlich alle die Ports, Counter und Interrupts betreffenden Stellen anzupassen. Der Bequemlichkeit halber kann der Qullcode auch hier direkt heruntergeladen werden. Außerdem müssen die Anmerkungen im Artikel Verdrahtung berücksichtigt werden. Dort ist neben der Verkabelung vor allem beschrieben, wie eine notwendige Modifikation durchzuführen ist.
// Firmware for an experimental LC-Meter // (c) 2005-2009 by DL8NCI // Compiled with WinAVR 20071221 // and avr libc 1.6.0 // fuse bits: LOW: 0xda HIGH:0xff // BOOTSZ = 128 word (irrelevant) // BOOTRST = not checked // BODLEVEL = 2,7 V (irrelevant) // BODEN = not checked // SPIEN = checked // CKSEL = Crystal Oscillator slowly raising power // Lockbits: 0xff #include <inttypes.h> #include <avr/io.h> #include <avr/interrupt.h> #include <stdio.h> #include <util/delay.h> #include <avr/pgmspace.h> // for the LC-Display #define LCD_DDR DDRA #define LCD_PORT PORTA #define LCD_PIN PINA #define LCD_PORT_E PORTC #define LCD_DDR_E DDRC #define LCD_E 2 #define LCD_RW 3 #define LCD_RS 4 // some convenient functions #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // global variables for LC-display uint8_t LCD_pos; uint8_t LCD_esc; // counter value and ready status for interrupt volatile uint32_t CNT; volatile uint8_t ready; uint8_t n_CNT; // measuring mode, calibration mode and range uint8_t mmode; uint8_t cmode; uint8_t rmode; // key status uint8_t keys; #define NX_MAX 16 // some fixed values double C13 = 470e-12; double C15 = 330e-12; #define PI 3.1415926535 #define TF 200 // ca. 50 ms double t_gate; // the relays/switches #define SHORT_LX cbi(PORTB,4); #define OPEN_LX sbi(PORTB,4); #define ON_330 cbi(PORTB,3); #define OFF_330 sbi(PORTB,3); #define SHORT_L3 cbi(PORTB,2); #define OPEN_L3 sbi(PORTB,2); #define ON_33k cbi(PORTD, 1); #define OFF_33k sbi(PORTD, 1); #define ON_1M cbi(PORTD, 2); #define OFF_1M sbi(PORTD, 2); #define ON_1k cbi(PORTD, 0); #define OFF_1k sbi(PORTD, 0); // all the LCD stuff - connected to the printf statement void LCD_PulseE(void) { sbi(LCD_PORT_E,LCD_E); _delay_us(2); // 2 us cbi(LCD_PORT_E,LCD_E); } void LCD_WaitBusy(void) { uint8_t h=0x80; LCD_DDR = 0x0c; LCD_PORT = 0x00; while(h!=0) { LCD_PORT = 0x04; sbi(LCD_PORT_E,LCD_E); _delay_us(1); // 1 us h = LCD_PIN & 0x80; cbi(LCD_PORT_E,LCD_E); LCD_PulseE(); } LCD_DDR = 0xfc; } void LCD_SendCmd(char c) { char ch; LCD_WaitBusy(); LCD_PORT = c & 0xf0; LCD_PulseE(); ch = c; asm("swap %0" : "=r" (ch) : "0" (ch)); LCD_PORT = ch & 0xf0; LCD_PulseE(); } void LCD_SendChar_int(char c) { char ch; LCD_WaitBusy(); LCD_PORT = (c & 0xf0) | 0x08; LCD_PulseE(); ch = c; asm("swap %0" : "=r" (ch) : "0" (ch)); LCD_PORT = (ch & 0xf0) | 0x08; LCD_PulseE(); LCD_pos++; } int LCD_SendChar(char c, FILE *stream) { if (LCD_esc==0) { if (c!='\001') { LCD_SendChar_int(c); } else { LCD_esc = 1; } } else { switch(c) { case '\001': LCD_SendCmd(0x80); LCD_pos = 0; LCD_esc = 0; break; case '\002': LCD_SendCmd(0xc0); LCD_pos = 0; LCD_esc = 0; break; case '\003': LCD_SendCmd(0x94); LCD_pos = 0; LCD_esc = 0; break; case '\004': LCD_SendCmd(0xd4); LCD_pos = 0; LCD_esc = 0; break; case '\005': while(LCD_pos<20) LCD_SendChar_int(0x20); LCD_esc = 0; break; } } return 0; } void LCD_Goto(uint8_t line, uint8_t column) { LCD_SendCmd(87 + column + 40*line); LCD_pos = column - 1; } void LCD_Init(void) { uint8_t i; for (i=0;i<20;i++) { _delay_ms(15.0); } // 300 ms sbi(LCD_DDR_E,LCD_E); LCD_DDR = 0xfc; LCD_PORT = 0x30; LCD_PulseE(); _delay_ms(15.0); // 15 ms LCD_PORT = 0x30; LCD_PulseE(); _delay_ms(1.0); // 1 ms LCD_PORT = 0x30; LCD_PulseE(); LCD_PORT = 0x20; LCD_PulseE(); LCD_SendCmd(0x28); LCD_SendCmd(0x0c); LCD_SendCmd(0x06); LCD_SendCmd(0x01); static FILE mystdout = FDEV_SETUP_STREAM(LCD_SendChar,NULL,_FDEV_SETUP_WRITE); stdout = &mystdout; LCD_pos = 0; LCD_esc = 0; } // sometimes we have nothing to do void idle(void) { asm("nop" "\n\t" ::); } // the handlers for key status changes void key0on(void) { } // not used void key0off(void) { // togle light on/off if ((PORTD & 0x80)==0x80) cbi(PORTD,7); else sbi(PORTD,7); } void key1on(void) { } // not used void key1off(void) { // range selection rmode++; cmode = 0; if (rmode==3) rmode=0; } void key2on(void) { } // not used void key2off(void) { // toggle calibration/measurement if (cmode==0) cmode=1; else cmode=0; } void key3on(void) { } // not used void key3off(void) { // switch to next measuring mode mmode++; cmode = 0; rmode = 0; if (mmode==5) mmode=0; } // the timer2-compare interrupt ISR(TIMER2_COMP_vect) { uint16_t l,h; TCCR1B = 0; // stop counter TCCR2 = 0; // stop timer // read counter and ... l = TCNT1L; h = TCNT1H; // ... add to global counter CNT = CNT + l + (h << 8); ready=1; } // the timer1-capture interrupt ISR(TIMER1_CAPT_vect) { uint16_t l,h; TCCR1B = 0; // stop timer // read counter and ... l = TCNT1L; h = TCNT1H; // ... add to global counter CNT = CNT + l + (h << 8); ready = 1; } // some preparation - mainly determine t_gate (done once only) void Calibrate(void) { CNT = 0; // setup timer 1 as counter and timer 2 as timer cli(); TCCR1A = 0x00; TCCR1B = 0x00; TCNT1H = 0; TCNT1L = 0; TCCR2 = 0x00; OCR2 = TF; TCNT2 = 0; sbi(SFIOR,PSR2); // Resest Timer Prescaler //start counter TCCR1B = 0x02; // 0.5 MHz //start timer TCCR2 = 0x07; //sbi(PORTD,5); //some further initializations ready = 0; sbi(TIMSK,OCIE2); // timer2-compare interrupt on sbi(TIFR,OCF2); // terminate active interrupts, if existing sei(); while (ready==0) idle(); t_gate = 8*(double)CNT/(double)F_CPU; } // scan the keyboard void scan_keys(void) { uint8_t h1,h2,h3; h1 = PINC; h2 = h1 & 0x10; // current key0 h3 = keys & 0x10; // previous key0 if ((h3==0x10)&&(h2==0x00)) key0on(); if ((h3==0x00)&&(h2==0x10)) key0off(); h2 = h1 & 0x20; // current key1 h3 = keys & 0x20; // previous key1 if ((h3==0x20)&&(h2==0x00)) key1on(); if ((h3==0x00)&&(h2==0x20)) key1off(); h2 = h1 & 0x40; // current key2 h3 = keys & 0x40; // previous key2 if ((h3==0x40)&&(h2==0x00)) key2on(); if ((h3==0x00)&&(h2==0x40)) key2off(); h2 = h1 & 0x80; // current key3 h3 = keys & 0x80; // previous key3 if ((h3==0x80)&&(h2==0x00)) key3on(); if ((h3==0x00)&&(h2==0x80)) key3off(); keys = h1; } // measure one time the current frequency void Measure_1(void) { // prepare timer 1 as counter and timer 2 as timer cli(); TCCR1A = 0x00; TCCR1B = 0x00; TCNT1H = 0; TCNT1L = 0; TCCR2 = 0x00; OCR2 = TF; TCNT2 = 0; sbi(SFIOR,PSR2); // Resest Timer Prescaler //start counter TCCR1B = 0x06; //start timer TCCR2 = 0x07; //some further initializations ready = 0; sbi(TIMSK,OCIE2); // timer2-compare interrupt on sbi(TIFR,OCF2); // terminate active interrupts, if existing sei(); while (ready==0) idle(); n_CNT++; // ready for next block cbi(TIMSK,OCIE2); // timer2-compare interrupt off scan_keys(); } // measure one time the pulse length of the NE555 void Measure_555_1(void) { cli(); // disable all interrupts TCCR1A = 0x00; // prepare timer 1 and ... TCCR1B = 0; // ... stop timer 1 TCNT1H = 0; // set timer1 count value to 0 TCNT1L = 0; // start measurement ready = 0; // clear the ready status sbi(TIFR,ICF1); // terminate active interrupts, if existing sbi(TIMSK,TICIE1); // timer1-capture Interrupt on sbi(SFIOR,PSR10); // reset prescaler TCCR1B=0x01; // start Timer 1 cbi(PORTD,5); // start NE555 sei(); // enable interrupts sbi(PORTD,5); // return to high level while (ready==0) idle(); // wait until ready n_CNT++; // this was one more measurement cbi(TIMSK,TICIE1); // timer1-capture Interrupt off scan_keys(); // the keys } // execute the frequency measuremnt n times double Measure_M(uint8_t n) { double f; CNT = 0; n_CNT = 0; while (n_CNT<n) Measure_1(); cli(); f=(double)CNT/((double)n*t_gate); return f; } // execute the pulse length measurement n times double Measure_555_N(uint8_t n) { double t; CNT = 0; n_CNT = 0; while (n_CNT<n) Measure_555_1(); cli(); t=(double)CNT/((double)F_CPU*(double)n); return t; } // the main program int main (void) { uint8_t i,lcmode; double f1,f2,f3,f4,Lx,L3,Cy,t1,t2,C13x,Cxa, Cxb, Cx, R; cli(); LCD_Init(); printf_P(PSTR("\001\001LC-Meter Evaluation\001\005\001\002 (c) 2005 by DL8NCI\001\005")); for(i=0; i<100; i++) _delay_ms(10.0); sbi(DDRB,4); // for shorting Lx sbi(DDRB,3); // for 330 pF sbi(DDRB,2); // for shorting L0 sbi(DDRD,0); // PD0..PD2 as output sbi(DDRD,1); sbi(DDRD,2); SHORT_LX ON_330 SHORT_L3 OFF_1k OFF_33k OFF_1M keys = PINC; DDRC = DDRC & 0x0f; // PC4..PC7 as input PORTC = PORTC | 0xf0; // PC4..PC7 with Pull Up cbi(PORTD,7); // PD7 - background light for LCD (LED off) sbi(DDRD,7); // PD7 as output - LED sbi(DDRD,5); // PD5 as output sbi(PORTD,5); t_gate = 0; Calibrate(); // determine t_gate mmode = 0; // the initial measuring mode cmode = 0; // calibrating mode rmode = 0; // range // forever ... while(1) { lcmode = cmode; switch (mmode) { case 0: // normal operation with L0 and f1, f2, f3 OPEN_L3 OPEN_LX OFF_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f1=Measure_M(8); // L0 + Lx; 470 pF + Cx SHORT_LX for(i=0; i<10; i++) { _delay_ms(10.0); } f2=Measure_M(8); // L0; 470 pF + Cx ON_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f3=Measure_M(8); // L0; 470 pF + 330 pF + Cx if (f1>100) { L3 = (f2*f2-f3*f3)/(4*PI*PI*f2*f2*f3*f3*C15); Cy = (C15*f3*f3)/(f2*f2-f3*f3)-C13; Lx = (f2*f2-f1*f1)*(f2*f2-f3*f3)/(4*PI*PI*f1*f1*f2*f2*f3*f3*C15); if (Lx>0.1) { printf_P(PSTR("\001\001%7.4f H %5.1f pF\001\005"),Lx, Cy*1e12); } else { printf_P(PSTR("\001\001%7.2f uH %5.1f pF\001\005"),Lx*1e6, Cy*1e12); } printf_P(PSTR("\001\002%7.2f uH ----- pF\001\005"),L3*1e6); } else { printf_P(PSTR("\001\001------- uH ----- pF\001\005")); printf_P(PSTR("\001\002------- uH ----- pF\001\005")); } printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000); printf_P(PSTR("\001\004%8.3f %8.3f ms\001\005"),f3/1000, t_gate*1e3); break; case 1: // normal operation with L0 and f1, f2, f4 OPEN_L3 SHORT_LX OFF_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f2=Measure_M(8); // L0 + Lx; 470 pF + Cx OPEN_LX for(i=0; i<10; i++) { _delay_ms(10.0); } f1=Measure_M(8); // L0; 470 pF + Cx ON_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f4=Measure_M(8); // L0 + Lx; 470 pF + 330 pF + Cx if (f1>100) { L3 = (f1*f1-f4*f4)/(4*PI*PI*f2*f2*f4*f4*C15); Cy = (C15*f4*f4)/(f1*f1-f4*f4)-C13; Lx = (f2*f2-f1*f1)*(f1*f1-f4*f4)/(4*PI*PI*f1*f1*f2*f2*f4*f4*C15); if (Lx>0.1) { printf_P(PSTR("\001\001%7.4f H %5.1f pF\001\005"),Lx, Cy*1e12); } else { printf_P(PSTR("\001\001%7.2f uH %5.1f pF\001\005"),Lx*1e6, Cy*1e12); } printf_P(PSTR("\001\002%7.2f uH ----- pF\001\005"),L3*1e6); } else { printf_P(PSTR("\001\001------- uH ----- pF\001\005")); printf_P(PSTR("\001\002------- uH ----- pF\001\005")); } printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000); printf_P(PSTR("\001\004%8.3f %8.3f ms\001\005"),f4/1000, t_gate*1e3); break; case 2: // normal operation without L0 SHORT_L3 OPEN_LX OFF_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f1=Measure_M(8); // Lx, C13, Cy ON_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f2=Measure_M(8); // Lx, C13, Cy, C15 if (f1>100) { Lx = (f1*f1-f2*f2)/(4*PI*PI*f1*f1*f2*f2*C15); Cy = ((C13+C15)*f2*f2-C13*f1*f1)/(f1*f1-f2*f2); if (Lx>0.1) { printf_P(PSTR("\001\001%7.4f H %5.1f pF\001\005"),Lx, Cy*1e12); } else { printf_P(PSTR("\001\001%7.2f uH %5.1f pF\001\005"),Lx*1e6, Cy*1e12); } printf_P(PSTR("\001\002\005")); } else { printf_P(PSTR("\001\001------- uH ----- pF\001\005")); printf_P(PSTR("\001\002------- uH ----- pF\001\005")); } printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000); printf_P(PSTR("\001\004-------- %8.3f ms\001\005"), t_gate*1e3); break; case 3: // C-Measurement with LC-Oscillator if (cmode==0) { printf_P(PSTR("\001\001 Reference ")); OPEN_L3 SHORT_LX OFF_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f1=Measure_M(8); // L3, C13 ON_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f2=Measure_M(8); // L3, C13, C15 C13x = C15*f2*f2/(f1*f1-f2*f2); L3 = (f1*f1-f2*f2)/(4*PI*PI*f1*f1*f2*f2*C15); printf_P(PSTR("\001\002\001\005")); printf_P(PSTR("\001\003\001\005")); } else { printf_P(PSTR("\001\001Measuring Mode 6.1")); OPEN_L3 SHORT_LX OFF_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f3=Measure_M(8); // L3, C13 ON_330 for(i=0; i<10; i++) { _delay_ms(10.0); } f4=Measure_M(8); // L3, C13 Cxa = (C15*f2*f2*(f1*f1-f3*f3))/(f3*f3*(f1*f1-f2*f2)); Cxb = (C15*f1*f1*(f2*f2-f4*f4))/(f4*f4*(f1*f1-f2*f2)); printf_P(PSTR("\001\002%9.2f pF (a)"),Cxa*1e12); printf_P(PSTR("\001\003%9.2f pF (b)"),Cxb*1e12); } printf_P(PSTR("\001\004%6.2f uH %7.2f pF"),L3*1e6, C13x*1e12); break; case 4: // C-measurement with NE555 switch (rmode) { case 0: R = 1e6; OFF_1k OFF_33k ON_1M break; case 1: R = 33e3; OFF_1k ON_33k OFF_1M break; case 2: R = 1e3; ON_1k OFF_33k OFF_1M break; } if (cmode==0) { printf_P(PSTR("\001\001 NE555 Reference ")); t1=Measure_555_N(64); printf_P(PSTR("\001\002t1 = %9.2f us\001\005"),t1*1e6); Cy = t1/(1.1*R); printf_P(PSTR("\001\003Cy = %9.2f pF\001\005"),Cy*1e12); } else { printf_P(PSTR("\001\001 NE555 Measurement")); t2=Measure_555_N(64); printf_P(PSTR("\001\002t2 = %9.2f us\001\005"),t2*1e6); Cx = (t2-t1)/(1.1*R); printf_P(PSTR("\001\003Cx = %9.2f pF\001\005"),Cx*1e12); } printf_P(PSTR("\001\004R = %4.0f kOhm\001\005"),R*1e-3); for(i=0; i<30; i++) _delay_ms(10.0); break; } } return (0); }