//================================================================= void GetVloss() { #if FLASHEND > 0x1fff // measure voltage drop after load pulse unsigned int tmpint; unsigned int adcv[4]; union t_combi{ unsigned long dw; // capacity value in 100nF units uint16_t w[2]; } lval; uint8_t ii; uint8_t HiPinR_L; uint8_t LoADC; if (cap.v_loss > 0) { return; // Voltage loss is already known (big Capacitor) } #if (((PIN_RL1 + 1) != PIN_RH1) || ((PIN_RL2 + 1) != PIN_RH2) || ((PIN_RL3 + 1) != PIN_RH3)) LoADC = pgm_read_byte((&PinRLRHADCtab[6])+cap.ca-TP_MIN) | TXD_MSK; #else LoADC = pgm_read_byte((&PinRLRHADCtab[3])+cap.ca-TP_MIN) | TXD_MSK; #endif HiPinR_L = pgm_read_byte(&PinRLRHADCtab[cap.cb-TP_MIN]); //R_L mask for HighPin R_L load EntladePins(); // discharge capacitor ADC_PORT = TXD_VAL; // switch ADC-Port to GND R_PORT = 0; // switch R-Port to GND ADC_DDR = LoADC; // switch Low-Pin to output (GND) R_DDR = HiPinR_L; // switch R_L port for HighPin to output (GND) adcv[0] = ReadADC(cap.cb); // voltage before any load // ******** should adcv[0] be measured without current??? if ((cap.cpre_max > -9) || (cap.cpre_max < -12)) return; // too much or too less capacity lval.dw = cap.cval_max; for (ii=cap.cpre_max+15;ii<7;ii++) { lval.dw = (lval.dw + 5) / 10; } if (lval.dw > 5000) { /* capacity more than 50uF, Voltage loss is already measured */ return; } if (lval.w[0] < 5) return; // Capacity below 5nF R_PORT = HiPinR_L; //R_L to 1 (VCC) R_DDR = HiPinR_L; //switch Pin to output, across R to GND or VCC for (tmpint=0;tmpint<lval.w[0];tmpint+=2) { // wait50us(); // wait exactly 50us wait5us(); // wait exactly 5us } R_DDR = 0; // switch back to input R_PORT = 0; // no Pull up // wait10us(); //wait a little time wdt_reset(); // read voltage without current ADCconfig.Samples = 5; // set ADC to only 5 samples adcv[2] = ReadADC(cap.cb); if (adcv[2] > adcv[0]) { adcv[2] -= adcv[0]; //difference to beginning voltage } else { adcv[2] = 0; // voltage is lower or same as beginning voltage } // wait 2x the time which was required for loading for (tmpint=0;tmpint<lval.w[0];tmpint++) { // wait50us(); wait5us(); } adcv[3] = ReadADC(cap.cb); // read voltage again, is discharged only a little bit ? ADCconfig.Samples = ANZ_MESS; // set ADC back to configured No. of samples wdt_reset(); if (adcv[3] > adcv[0]) { adcv[3] -= adcv[0]; // difference to beginning voltage } else { adcv[3] = 0; // voltage is lower or same as beginning voltage } if (adcv[2] > adcv[3]) { // build difference to load voltage adcv[1] = adcv[2] - adcv[3]; // lost voltage during load time wait } else { adcv[1] = 0; // no lost voltage } // compute voltage drop as part from loaded voltage if (adcv[1] > 0) { // there is any voltage drop (adcv[1]) ! // adcv[2] is the loaded voltage. cap.v_loss = (unsigned long)(adcv[1] * 500UL) / adcv[2]; } #if 0 lcd_line3(); DisplayValue16(adcv[2],0,' ',4); DisplayValue16(adcv[1],0,' ',4); lcd_line4(); DisplayValue16(lval.w[0],0,'x',4); #endif // discharge capacitor again EntladePins(); // discharge capacitors //ready // switch all ports to input ADC_DDR = TXD_MSK; // switch all ADC ports to input ADC_PORT = TXD_VAL; // switch all ADC outputs to GND, no pull up R_DDR = 0; // switch all resistor ports to input R_PORT = 0; // switch all resistor outputs to GND, no pull up #endif return; } // end GetVloss()
uint16_t MeasureESR(Capacitor_Type *Cap) { uint16_t ESR = 0; /* return value */ uint16_t U_1; /* voltage at probe 1 with pos. pulse unloaded */ uint16_t U_2; /* voltage at probe 2 with pos. pulse loaded */ uint16_t U_3; /* voltage at probe 2 with neg. pulse unloaded */ uint16_t U_4; /* voltage at probe 1 with neg. pulse loaded */ uint8_t Probe1; /* probe #1 */ uint8_t Probe2; /* probe #2 */ uint8_t ADC_Mask; /* bit mask for ADC */ uint8_t n; /* counter */ uint8_t muCycles; /* MCU cycles per µs */ uint8_t PulseCycles; /* MCU cycles for a half pulse */ uint32_t Sum_1; /* sum #1 */ uint32_t Sum_2; /* sum #2 */ uint32_t Value; #define LOOP_RUNS 255 /* check for a capacitor >= 0.18µF */ if ((Cap == NULL) || (CmpValue(Cap->Value, Cap->Scale, 180, -9) < 0)) return ESR; /* * init stuff */ DischargeProbes(); /* try to discharge probes */ if (Check.Found == COMP_ERROR) return ESR; /* skip on error */ Probe1 = Cap->A; /* probe facing Gnd */ Probe2 = Cap->B; /* probe facing Vcc */ UpdateProbes(Probe1, Probe2, 0); /* update probes */ /* init variables */ Sum_1 = 1; /* 1 to prevent division by zero */ Sum_2 = 1; /* 1 to prevent division by zero */ Probe1 |= (1 << REFS1) | (1 << REFS0); /* select bandgap reference */ Probe2 |= (1 << REFS1) | (1 << REFS0); /* select bandgap reference */ /* bitmask to enable and start ADC (ADC clock: 125kHz / 8µs) */ ADC_Mask = (1 << ADSC) | (1 << ADEN) | (1 << ADIF) | ADC_CLOCK_DIV; /* delay for pulse */ muCycles = (CPU_FREQ / 1000000); /* MCU cycles per µs */ /* * We have to create a delay to shift the middle of the puls to the ADC's * S&H. S&H happens at 1.5 ADC clock cycles after starting the conversion. * We synchronize to a dummy conversion done directly before, so we'll got * 2.5 ADC clock cycles to S&H. The time between the completed dummy * conversion and S&H of the next conversion is: * 2.5 ADC clock cycles (2.5 * 1/125kHz = 20µs) * - MCU cycles for waiting loop for completion of dummy conversion (4) * - MCU cycles for starting next conversion (2) * - 5µs delay * - MCU cycles for enabling pulse (4) * * That time is the first half of the puls. So we have to double the time * for a full pulse. Half pulse for 8MHz MCU clock is about 13.5µs. */ PulseCycles = muCycles * 15; /* MCU cycles for 15µs (20µs S/H - 5µs delay) */ PulseCycles -= 10; /* substract other cycles */ /* setup delay timer */ if (SetupDelayTimer(PulseCycles) == 0) return ESR; /* skip on error */ /* * charge capacitor with a negative pulse of half length * pulse: GND -- probe 2 / probe 1 -- Rl -- 5V */ ADC_PORT = 0; /* set ADC port to low */ ADMUX = Probe1; /* set input channel to probe 1 & set bandgap ref */ wait10ms(); /* time for voltage stabilization */ ADC_DDR = Probes.ADC_2; /* pull down probe 2 directly */ R_PORT = Probes.Rl_1; /* pull up probe 1 via Rl */ R_DDR = Probes.Rl_1; /* enable resistor */ DelayTimer(); /* wait 1/2 pulse */ R_PORT = 0; /* set resistor port to low */ R_DDR = 0; /* set resistor port to HiZ */ /* * measurement loop: * - simulate AC by positive and negative pulses * - measure start voltage (no load) * - measure pulse voltage (with load) */ n = LOOP_RUNS; while (n > 0) { /* * forward mode, probe 1 only (probe 2 in HiZ mode) * get voltage at probe 1 (facing Gnd) * set probes: GND -- probe 1 -- Rl -- 5V / probe 2 -- HiZ */ ADC_DDR = Probes.ADC_1; /* pull down probe 1 directly to GND */ R_PORT = Probes.Rl_1; /* pull up probe 1 via Rl */ R_DDR = Probes.Rl_1; /* enable resistor */ ADMUX = Probe1; /* set input channel to probe 1 & set bandgap ref */ wdt_reset(); /* reset watchdog */ /* run dummy conversion for ADMUX change */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ /* real conversion */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ U_1 = ADCW; /* save ADC value */ /* * forward mode, positive charging pulse * get voltage at probe 2 (facing Vcc) * set probes: GND -- probe 1 / probe 2 -- Rl -- 5V */ ADMUX = Probe2; /* set input channel to probe 2 & set bandgap ref */ /* run dummy conversion for ADMUX change */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ /* read ADC in the mid of a positive charging pulse */ ADCSRA = ADC_Mask; /* start conversion with next ADC clock cycle */ wait5us(); R_PORT = Probes.Rl_2; /* pull up probe 2 via Rl */ R_DDR = Probes.Rl_2; /* enable resistor */ DelayTimer(); /* wait 1/2 pulse */ DelayTimer(); /* wait another 1/2 pulse */ R_PORT = 0; /* set resistor port to low */ R_DDR = 0; /* set resistor port to HiZ */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ U_2 = ADCW; /* save ADC value */ /* * reverse mode, probe 2 only (probe 1 in HiZ mode) * get voltage at probe 2 (facing Gnd) * set probes: GND -- probe 2 -- Rl -- 5V / probe 1 -- HiZ */ ADC_DDR = Probes.ADC_2; /* pull down probe 2 directly */ R_PORT = Probes.Rl_2; /* pull up probe 2 via Rl */ R_DDR = Probes.Rl_2; /* enable resistor */ ADMUX = Probe2; /* set input channel to probe 2 & set bandgap ref */ wdt_reset(); /* reset watchdog */ /* run dummy conversion for ADMUX change */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ /* real conversion */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ U_3 = ADCW; /* save ADC value */ /* * reverse mode, negative charging pulse * get voltage at probe 1 (facing Vcc) * set probes: GND -- probe 2 / probe 1 -- Rl -- 5V */ ADMUX = Probe1; /* set input channel to probe 1 & set bandgap ref */ /* run dummy conversion for ADMUX change */ ADCSRA = ADC_Mask; /* start conversion */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ /* read ADC in the mid of a negatve charging pulse */ ADCSRA = ADC_Mask; /* start conversion with next ADC clock cycle */ wait5us(); R_PORT = Probes.Rl_1; /* pull up probe 1 via Rl */ R_DDR = Probes.Rl_1; /* enable resistor */ DelayTimer(); /* wait 1/2 pulse */ DelayTimer(); /* wait another 1/2 pulse */ R_PORT = 0; /* set resistor port to low */ R_DDR = 0; /* set resistor port to HiZ */ while (ADCSRA & (1 << ADSC)); /* wait until conversion is done */ U_4 = ADCW; /* save ADC value */ /* * manage measured values */ U_1 += U_3; /* sum of both measurements without pulses/load */ Sum_1 += U_1; /* add to total no-load sum */ U_2 += U_4; /* sum of both measurements with pulses/load */ Sum_2 += U_2; /* add to total with-load sum */ /* * prevent runaway of cap's charge */ if (U_4 <= 100) /* <= 107mV */ { /* charge cap a little bit more (negative pulse) */ /* set probes: GND -- probe 2 / probe 1 -- Rl -- 5V */ /* probe 2 is still pulled down directly */ R_PORT = Probes.Rl_1; /* pull up probe 1 via Rl */ R_DDR = Probes.Rl_1; /* enable pull up */ wait2us(); R_DDR = 0; /* disable any pull up */ R_PORT = 0; /* reset probe resistors */ } if (U_2 <= 100) { /* charge cap a little bit more (positive pulse) */ /* set probes: GND -- probe 1 / probe 2 -- Rl -- 5V */ ADC_DDR = Probes.ADC_1; /* pull down probe 1 directly */ R_PORT = Probes.Rl_2; /* pull up probe 2 via Rl */ R_DDR = Probes.Rl_2; /* enable pull up */ wait2us(); DelayTimer(); /* wait 1/2 pulse */ DelayTimer(); /* wait another 1/2 pulse */ R_DDR = 0; /* disable any pull up */ R_PORT = 0; /* reset probe resistors */ } n--; /* next loop run */ } /* * process measurements */ /* calculate voltage across the DUT */ if (Sum_2 > Sum_1) /* valid measurement */ { Sum_2 -= Sum_1; /* subtract voltage at DUT's low side (RiL) */ } else /* invalid measurement */ { Sum_2 = 0; } /* * calculate ESR * - ESR = U_ESR / I_ESR * with U_ESR = (U2 or U4) and I_ESR = (U1 or U3)/RiL * ESR = (U2 or U4) * RiL / (U1 or U3) * - since we devide (U2 or U4) by (U1 or U3) we don't need to convert * the ADC value into a voltage and desample the sums. * - so ESR = Sum_2 * RiL / Sum_1 * - for a resolution of 0.01 Ohms we have to scale RiL to 0.01 Ohms */ Value = (uint32_t)(NV.RiL * 10); /* RiL in 0.01 Ohms */ Value *= Sum_2; Value /= Sum_1; U_1 = (uint16_t)Value; /* consider probe resistance */ if (U_1 > NV.RZero) { U_1 -= NV.RZero; /* subtract offset */ ESR = U_1; /* we got a valid result */ } /* update Uref flag for next ADC run */ Config.RefFlag = (1 << REFS1); /* set REFS1 bit flag */ return ESR; #undef LOOP_RUNS }