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);
}