ATtiny84 NanoFlow Meter

The ATtiny84 NanoFlow Meter is a circuit designed to measure currents at the nano level. We used it to measure the power consumption of some microprocessors in the ATtiny series. Measures current values between 10μA and 30nA at a reasonable accuracy level.

Accurately measuring very small currents is as difficult as anyone knows with normal digital multimeters; either they do not provide a low current range at all, or if they do, they create a voltage drop called "load voltage", which can make the screen faulty.One way to overcome this is to use measuring instruments designed for low currents.

Since we do not care about high accuracy, we will calculate the measurement by referencing the time it takes for the capacitor to empty. The discharge time of the capacitor, which discharges at a resistant load, is exponential, just like radioactive degradation. Within consecutive fixed time intervals, the voltage halves, so if the voltage starts at 5V, after a certain t-time it drops to half of this value, 2.5V.After a while the t drops to 1.25V and this continues, this t is called its halving life.

t To calculate the halving life: It is necessary to receive log 2 of theRC time constant, i.e. log2RC, or 0.693 x RC. Therefore, for a fixed capacitor C, we can calculate the effective resistance of the load and therefore the current consumption R by measuring the time it takes for the voltage to drop to 50% of the initial value.

It is easy to do this using a microprocessor with ADC input, and a few other components are needed besides the capacitor; the disadvantage is that current measurements in nanoampers can take a few seconds while the capacitor is discharged.

Circuit Diagram

Port A: PA0 – PA6 pins are used to drive segments and PB0 – PB2 figures on Port B are used to drive.Because there are no more pins available, PB2 doubles as a drive for the decimal point, which works because there is no need for a decimal place in the third digit.The remaining pin PA7 is used as an analog input to measure the voltage on the capacitor.

We used a DC film capacitor with a 5% tolerance ± for the capacitor.Any quality capacitor will probably be suitable, but try not to use electrolytic capacitors, as they have a wide tolerance.

A button connects to the reset pin, a reset signal is sent to start reading.

We fed the circuit from a correct 5V power source.If you want to operate from the battery or battery, you should add a regulator, since the accuracy of the analog input depends on the feed voltage.

Usage Details

Connect the circuit you want to test between the Test and Gnd terminals and press the reset button.The program first applies 5V to the Test terminal to charge the 1μF capacitor.Then, after a short delay, it cuts the power and leaves the test circuit to draw power from the capacitor.

When the capacitor discharges to the target voltage, the program calculates the discharge current and displays it as nA or μA; μA is indicated by a "u" sing-bye. For example, some possible results are as follows:

7 Segment LEDDescription
LoCurrent < 30nA
45Current = 45nA
750Current = 750nA
2.5uCurrent = 2.5μA
10uCurrent = 10μA
HiCurrent > 10μA

Note that it can take up to 100 seconds for the nanoflow meter to measure currents as low as 30nA.

If you are testing the current consumption of a dormant circuit, power the circuit from the Nano Current Meter's Test terminal and press the reset button when the circuit goes to sleep.

Program Details

As with many of our previous projects, the image is created under interrupt using the contents of buffer[].For example, you can use this code to show the number "123" in 7 segments.

Buffer[0]=1; Buffer[1]=2; Buffer[2]=3;

The program uses Timer/Counter1 to create a interrupt at 200 Hz, which is used to multilase the screen.

void SetupDisplay () {
  TCCR1A = 0<WGM10;
  TCCR1B = 1<WGM12 | 2<CS10;  // 8'e bölme
  OCR1A = 4999;                 //  200Hz kesmesi
  TIMSK1 = 1<OCIE1A;           // kesmeyi aktifleştirmek
}

The interrupt operation routine simply calls DisplayNextDigit() :

ISR(TIM1_COMPA_vect) {
  DisplayNextDigit();
}

This block of code selects the next digit, adjusts the appropriate segment model in the digit, and then activates the common anode of the digit:

void DisplayNextDigit () {
  DDRB = 0;                                    all low
  digit = (digit+1) % ndigits;
  char segs = charArray[Buffer[digit]];
  DDRA = DDRA & 0x80;                          input every pin
  7 segment settings
  if (dp == digit && dp != 2) DDRB = 1<DDB2;
  PORTA = (PORTA & 0x80) | (~segs & 0x7F);     // 1 = düşük
  DDRA = (DDRA & 0x80) | (segs & 0x7F);        // 1 = çıkış
  DDRB = DDRB | 1<digit;                      
  PORTB = 1<digit;                           
}

The upper bit of PORTA is masked to prevent interference with the Test signal.

Timing Capacitor Discharge

The main program works when we press the Reset button to start a read.To charge the capacitor, it first receives the PA7 output high.It then redefines it as an input and calls analogRead()to monitor the decreasing voltage at the input.

When the voltage drops to the value defined by Target, the program calculates the resistance of the load based on the RC time constant corresponding to the discharge rate using the following equation:

half-life = log(2) RC

To minimize the effect of the changing voltage on the monitored circuit, I chose to set the target to half the half-life, that is, when the voltage dropped to 1/√2 or approximately 3.5V.We used 29/41 in the program, which is a good approach to 1/√2.We need to double that time to get real half-life.

The program then converts it into a discharge current of 5V, which is given as follows:

discharge current = (5 x log(2) x 10 -6 ) / t

where the discharge current is in amps, it is the supply voltage in 5 volts, it is the capacitance of the discharge capacitor of 10 -6, it is half life in 1μF and t seconds.

  nA = 1732868 / Time;

where nA is the discharge current in nA, and Time is in milliseconds.

Compiling the Program

We compiled the program using ATTiny Core developed by Spence Konde. Choose ATtiny24/44/84 under ATTinyCore from the Board menu.Then check if the next options are set as follows (ignore other options):

Chip: "ATtiny84"
Clock: "8 MHz (internal)"
B.O.D: "BOD Disabled"

Pin Mapping: "Clockwise (such as damellis core)"

ATtiny84 runs at 1MHz by default.To do this, you need to set the fuses to work at 8MHz, choose to install the bootstrapper. Then install the program on ATtiny84 with any ISP programmer.

ATtiny84 NanoFlow Meter

Program ID

const int charArrayLen = 17;
char charArray[] = {
// ABCDEFG Segments0b1111110, // 00 b0110000, // 1 0b1101101, // 20b111001, // 30b0110011, // 40 b1011011, // 5 0b10111111, // 60b1110000, // 70b11111111, // 80b1111011, // 90b00000000, // 10 Space0b0000001, // 11 '-'  0b0001110, // 12 'L'0b0011101, // 13 'o'0b01101111, // 14 'H'0b00000100, // 15 'i'0b0011100 // 16 'u'
};

const int Space = 10;
const int Dash = 11;
const int Lo = 12;
const int Hi = 14;
const int uA = 16;
const int ndigits = 3;
volatile int Buffer[] = {Dash, Dash, Dash};
char dp = 2;  // Decimal point position 2 (off) or 0 to 1int digit = 0;

// Display multiplexer *******void DisplayNextDigit () {
  DDRB = 0;                                    // All low
  digit = (digit+1) % ndigits;
  char segs = charArray[Buffer[digit]];
  DDRA = DDRA & 0x80;                          // All inputs// Set display segmentsif (dp == digit && dp != 2) DDRB = 1 <DDB2;
  PORTA = (PORTA &  0x80) | (~segs & 0x7F);     // 1 = low
  DDRA = (DDRA &0x80) | (segs &0x7F);        // 1 = output
  DDRB = DDRB | 1 to 1 <digit;                       // Current digit output
  PORTB = 1 <digit;                             // Current digit high
}

// Display a three digit decimal numbervoid Display (int i) {
  for (int d=2; d>=0 ; d--) {
    Buffer[d]=i 10%;
    i = i / 10;
  }
}

// Set up Timer/Counter1 to multiplex the displayvoid SetupDisplay () {
  TCCR1A = 0 <WGM10;
  TCCR1B =  1 <WGM12 |  2 <CS10;   // Divide by 8
  OCR1A = 4999;                 // Compare match at 200Hz
  TIMSK1 = 1 <OCIE1A;            // Enable compare match interrupt
}

// Timer interrupt - multiplexes display
ISR(TIM1_COMPA_vect) {
  DisplayNextDigit();
}

// Setup *******
  SetupDisplay();
}

void loop() {
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  delay(500);
  pinMode(7, INPUT);
  unsigned long Start = millis(), Time, nA;
  unsigned int Initial = analogRead(7);
  unsigned int Target = (Initial * 29) / 41;
  do {
    Time = millis() - Start;
  } while (analogRead(7) > Target && time <  100000);
  nA = 1732868 / Time;
  dp = 2;
  if (Time >= 100000) { Buffer[0] = Lo; Buffer[1] = Lo+1; Buffer[2] = Space; }
  else if (nA <  1000) { dp = 2; Display(nA); }
  else if (nA <  10000) { dp = 0; Display(nA/10); Buffer[2] = uA; }
  else if (nA <  100000) { dp = 2; Display(nA/100); Buffer[2] = uA; }
  else { Buffer[0] = Hi; Buffer[1] = Hi+1; Buffer[2] = Space; }
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  for (;;);
}