Go Down

Topic: DIY Geiger Counter with low power consumption  (Read 5683 times) previous topic - next topic

LowPowerConsumption

I've read gammon.com.au/forum/?id=11497 and got my ATmega 328P-PU down to ~6.5mA consumption (on breadboard, no external crystal, down to 1MHz, turned off various chip functions), but without sleep mode. The reason why this is without sleep mode is that my first try didn't work and now I'm not sure if sleep in my case does make sense at all: I'm using a Geiger counter to receive and count INT impulses and display the number on a Nokia 5110 LCD. If I would sleep, even the time gets turned off and I don't know how many impulses I receive in a minute (Counts Per Minute, CPM). The number of CPM can be from 20 to a few 100000 (1000s impulses per second) and so I'm not sure if sleep is an option at all to lower power consumption.

Since I'm using a relatively simple setup, maybe there's an alternative chip with similar commands (so I can simply connect and reuse the LCD and Counter for INT impulses) but consumes less power?
Maybe a chip which is especially designed for lower power consumption, a chip from TI MSP430 series?


el_supremo

You could connect the INT pulses to a counter chip and read that every minute. Just as an example, the PCF8593 is an I2C CMOS clock calendar which also has an event counter mode which counts up to 6 digits. As long as you choose an interval (e.g. perhaps 30 seconds) which guarantees that the count won't exceed 6-digits, the Arduino can sleep during that interval, then wake up to read the counter and go back to sleep again.

Pete
Don't send me technical questions via Private Message.

Peter_n

There is a sleep mode that keeps the timers and interrupts running, I think the 'lightest' sleep mode is "IDLE". You have to use a sleep mode to reduce the current, even if the ATmega328P wakes up hundred times a second (or more).
Timer0 is the system timer, I sometimes go to sleep at the end of the loop() and let the Timer0 interrupt wake up the Arduino. My current use was half of what it was before.
Sometimes I also replace all the delay() in my sketch with a delay that uses a sleep mode.



I don't understand what is wrong with the 6mA. Your Geiger circuit also uses current, probably more than 6mA. You don't use the backlight of the Nokia LCD ? Are all the leds in your circuit are off ?

The MSP430 series is very good, but it is a challenge for me to make you use an Arduino ;)

LowPowerConsumption

#3
Dec 06, 2014, 05:45 pm Last Edit: Dec 06, 2014, 06:27 pm by LowPowerConsumption
Quote
You could connect the INT pulses to a counter chip and read that every minute.
Every minute is too slow, it's more like every 10s. Otherwise interesting idea using a PCF8593.

Quote
There is a sleep mode that keeps the timers and interrupts running, I think the 'lightest' sleep mode is "IDLE". You have to use a sleep mode to reduce the current
IDLE uses 15mA XD. Yes, sleep mode is needed.

Quote
I don't understand what is wrong with the 6mA. Your Geiger circuit also uses current, probably more than 6mA. You don't use the backlight of the Nokia LCD ? Are all the leds in your circuit are off ?
6mA is way too much. This is a running time of only several days. LCD without LEDs consumes 0.4mA and Geiger 0.0XmA, so the rest is power efficient, although I'd like to know a LCD with less than 0.4mA. LEDs consume in general relatively much and so are not constantly used (backlight for a few seconds is ok).

Quote
The MSP430 series is very good, but it is a challenge for me to make you use an Arduino
The MSP430 seems indeed very interesting. I don't understand, I'm using the ATmega chip only (although I started with an Arduino), not Arduino, which consumes about 75mA.

el_supremo

Quote
it's more like every 10s.
That isn't a problem. Reading the counter takes a matter of milliseconds so as long as the Arduino is in a low power sleep/idle mode for the rest of the time it won't consume much current. I've used one of these counters on a Nano which had to run unattended for 8 or 9 months while being powered only from a battery that was charged from wind and solar.

Pete
Don't send me technical questions via Private Message.

john1993

#5
Dec 06, 2014, 07:06 pm Last Edit: Dec 06, 2014, 07:09 pm by john1993
Geiger 0.0XmA
if this is indeed a "gieger" counter i find it hard to believe you have a hv supply that low. most draw orders of magnitude more. as mentioned for 0.1hz average pulses an avr chip will draw almost nothing by comparison.

Peter_n

Do you use one of those radiation collecting diodes ? With opamps ?

I didn't know that IDLE was 15mA (and about 6.5mA using PRR and so on). That is a lot for doing almost nothing.
http://www.gammon.com.au/power

The ATmega328P has a 'clock' option, by using 32.768kHz crystal and the microcontroller running code on it's internal oscillator. The datasheet says that the ATmega328P in power-save mode uses 0.75uA, and the 32.768kHz is still running to know the time.

The ATmega328P uses about 1mA at 5V at 1MHz. That is not so bad, but still more than the MPU430 series.

LowPowerConsumption

#7
Dec 06, 2014, 08:17 pm Last Edit: Dec 06, 2014, 08:18 pm by LowPowerConsumption
Quote
Reading the counter takes a matter of milliseconds so as long as the Arduino is in a low power sleep/idle mode for the rest of the time it won't consume much current. I've used one of these counters on a Nano which had to run unattended for 8 or 9 months while being powered only from a battery that was charged from wind and solar.
So if it's in deep sleep mode, isn't the clock turned off?
If you charge it, you don't know anymore the running time (expet if you measure the power consumption and I guess it was rather high if it was a DIY Geiger).

Quote
find it hard to believe you have a hv supply that low
It is that low. Commercial Counters can consume ~10µA at background, so it is possible.

Quote
The datasheet says that the ATmega328P in power-save mode uses 0.75uA, and the 32.768kHz is still running to know the time.
Yes in power mode it is very efficient but what do you mean by 32kHz crystal? I was able to go down as low a 1MHz, and with 128kHz is stopped working.

Quote
That is not so bad, but still more than the MPU430 series.
Yes unfortunately still like 5x as much.

nickgammon

Here's an idea. Timer 2 can run asynchronously, which means it can be clocked by an external pulse (see bit AS2 or EXCLK in ASSR).

Now I haven't tried this, but my theory is that you could use Timer 2 to count pulses, even while the chip is asleep. Then you could wake up periodically and find the count. A problem would be if the timer overflowed, perhaps that interrupt would wake the chip. Another problem is knowing how much time elapsed, possibly an external clock (which itself can be powered by a lithium battery while in standby mode) would achieve that.

Another power saving technique is to simply run at a lower voltage, eg. 2.1V.

According to my power saving page, running at 1.8V used only 0.14 mA if clocked at 128 kHz.

There must be some combination that will work, eg. a clock crystal to clock the processor (thus you know the time) and low voltage.

Here's an example of clocking timer 2 from the 32.768 clock crystal: http://www.gammon.com.au/forum/?id=11504&reply=10#reply10. Doing that tells you the time, and then you just count geiger counter interrupts (waking from sleep to do it).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

#9
Dec 07, 2014, 02:19 am Last Edit: Dec 07, 2014, 03:06 am by Nick Gammon Reason: Added "volatile"
This was an interesting challenge, so I did an experiment. Using the idea above, I connected a 32.768 crystal to pins 9 and 10 of the chip (the XTAL1/XTAL2 pins), set it to run on the 8 MHz oscillator, and uploaded this sketch:

Code: [Select]
#include <avr/sleep.h>
#include <avr/power.h>

const byte TICK = 9;  // PB1

volatile unsigned long seconds = 0;
unsigned long oldSeconds = 0;
volatile unsigned long ticks = 0;

// interrupt on Timer 2 compare "A" completion g
ISR (TIMER2_COMPA_vect)
  {
  seconds++;  
  }

ISR (INT0_vect)
  {
  ticks++;
  }
  
void activateInterrupt0 ()
  {
  EICRA &= ~(bit(ISC00) | bit (ISC01));  // clear existing flags
  EICRA |=  bit (ISC01);    // set wanted flags (falling level interrupt)
  EIFR   =  bit (INTF0);    // clear flag for interrupt 0
  EIMSK |=  bit (INT0);     // enable it
  }  // end of activateInterrupt0
  
  
void setup()
 {
  pinMode (TICK, OUTPUT);

  activateInterrupt0();  
  
  TCCR2A = 0;
  TCCR2B = 0;

  // clock input to timer 2 from XTAL1/XTAL2
  ASSR = bit (AS2);  

  // set up timer 2 to count up to 32 * 1024  (32768)
  TCCR2A = bit (WGM21);                             // CTC
  TCCR2B = bit (CS20) | bit (CS21) | bit (CS22);    // Prescaler of 1024                                  
  OCR2A =  31;              // count to 32 (zero-relative)                  

  // enable timer interrupts
  TIMSK2 |= bit (OCIE2A);

  // stop timers 0 and 1
  TCCR0A = 0;
  TCCR0B = 0;
  TCCR1A = 0;
  TCCR1B = 0;
  
  // disable ADC
  ADCSRA = 0;  
  
  // turn off everything we can
  power_adc_disable ();
  power_spi_disable();
  power_twi_disable();
  power_timer0_disable();
  power_timer1_disable();
  
  // full power-down doesn't respond to Timer 2
  set_sleep_mode (SLEEP_MODE_PWR_SAVE);  

  }  // end of setup

void loop()
  {
 
  sleep_mode ();  

  if (seconds != oldSeconds)
    {
    oldSeconds = seconds;
    // we awoke! pulse the clock hand
    digitalWrite (TICK, ! digitalRead (TICK));
    
    power_usart0_enable();
    Serial.begin (115200);
    Serial.println ();
    Serial.print ("ticks = ");
    Serial.println (ticks);
    Serial.print ("seconds = ");
    Serial.println (seconds);
    Serial.flush ();
    Serial.end ();
    power_usart0_disable();
    ticks = 0;
    }
  
  }  // end of loop


The Serial stuff was to see how accurate the count was. Feeding in 100 kHz I got this:

Code: [Select]
ticks = 99361
seconds = 226

ticks = 99355
seconds = 227

ticks = 99356
seconds = 228

ticks = 99355
seconds = 229

ticks = 99355
seconds = 230

ticks = 99355
seconds = 231


So the results seem to be within 1% of the correct reading.

The processor sleeps in a fairly deep sleep between interrupts, which are caused by either the Timer 2 overflowing (and thus a second is up) or an external interrupt on pin 2 (the Geiger counter pin). Clearly the more interrupts the more current is used, as that turns the clock on. I measured:

  • 100 Hz : 700 µA
  • 1 kHz : 800 µA
  • 100 kHz : 5.8 mA



So, under 1 mA for up to 1 kHz of pulses, rising somewhat for 100 kHz, but at that stage I think the processor would be awake most of the time. This was measured running from 4V supply, to keep power consumption down.

I tried to get a more accurate count by fiddling with the fuses. I changed the clock fuse to wake up after 6 clock cycles plus 0 mS (LOW = 0x82) which gave a higher count at 100 kHz:

Code: [Select]
ticks = 99399
seconds = 50

ticks = 99409
seconds = 51

ticks = 99410
seconds = 52

ticks = 99403
seconds = 53

ticks = 99412
seconds = 54


Experimentation shows that you can't count much higher than 100 kHz, which isn't that surprising as that would be one interrupt every 10 µS, which at 8 MHz clock doesn't give it much time. To count higher rates you would need to feed the counter into a timer input, which would then mean the processor clock would have to be running (I think, I might be wrong). You might need to consider alternative approaches if this isn't good enough, for example a larger battery, or maybe a dedicated counting chip.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Peter_n

#10
Dec 07, 2014, 04:10 am Last Edit: Dec 07, 2014, 04:23 am by Peter_n
What happens when you run at 1MHz ? The datasheet shows a big saving. The CLKPR set to divide by 8.

I think it is like this:
Code: [Select]

cli();
CLKPR = 0x80;
CLKPR = 0x03;
sei();


Or is that the CKDIV8 fuse ? Or is that fuse for the 32,768kHz ?

nickgammon

#11
Dec 07, 2014, 04:18 am Last Edit: Dec 07, 2014, 04:40 am by Nick Gammon
Before I answer that, I tried a different approach. It seemed that all this stuff about using the clock crystal was too complex. So I used my frequency counter sketch from http://www.gammon.com.au/forum/?id=11504, adapted to run a 8 MHz, and with sleep mode thrown in (SLEEP_MODE_IDLE). This gave reasonably good results. Although the processor was not really in low-power mode, if you run it at 2.5V (which you can, for 8 MHz) you get quite good power consumption. I measured 670 µA consumption when running this, on average. Because it wasn't interrupt-based the consumption was static. Plus, this one will handle counting up to about 2 MHz which is within the spec of "a few hundred thousand per second".


Code: [Select]
// Timer and Counter example
// Author: Nick Gammon
// Date: 17th January 2012
// Modified: 7th December 2014 to run at 8 MHz with low power consumption

// Input: Pin D5

#include <avr/sleep.h>
#include <avr/power.h>

const byte TICK = 9;  // PB1
const unsigned int TIMING_INTERVAL = 1000;  // mS

// these are checked for in the main program
volatile unsigned long timerCounts;
volatile boolean counterReady;

// internal to counting routine
unsigned long overflowCount;
unsigned int timerTicks;
unsigned int timerPeriod;

void startCounting (unsigned int ms)
  {
  counterReady = false;         // time not up yet
  timerPeriod = ms;             // how many 1 mS counts to do
  timerTicks = 0;               // reset interrupt counter
  overflowCount = 0;            // no overflows yet

  // reset Timer 1 and Timer 2
  TCCR1A = 0;            
  TCCR1B = 0;              
  TCCR2A = 0;
  TCCR2B = 0;

  // Timer 1 - counts events on pin D5
  TIMSK1 = bit (TOIE1);   // interrupt on Timer 1 overflow

  // Timer 2 - gives us our 1 mS counting interval
  // 8 MHz clock (125 nS per tick) - prescaled by 64
  //  counter increments every 8 µS.
  // So we count 125 of them, giving exactly 1000 µS (1 mS)
  TCCR2A = bit (WGM21) ;   // CTC mode
  OCR2A  = 124;            // count up to 125  (zero relative!!!!)

  // Timer 2 - interrupt on match (ie. every 1 mS)
  TIMSK2 = bit (OCIE2A);   // enable Timer2 Interrupt

  TCNT1 = 0;      // Both counters to zero
  TCNT2 = 0;    

  // Reset prescalers
  GTCCR = bit (PSRASY);        // reset prescaler now
  // start Timer 2
  TCCR2B =  bit (CS22) ;  // prescaler of 64
  // start Timer 1
  // External clock source on T1 pin (D5). Clock on rising edge.
  TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12);
  }  // end of startCounting

ISR (TIMER1_OVF_vect)
  {
  ++overflowCount;               // count number of Counter1 overflows  
  }  // end of TIMER1_OVF_vect


//******************************************************************
//  Timer2 Interrupt Service is invoked by hardware Timer 2 every 1ms = 1000 Hz
//  16Mhz / 128 / 125 = 1000 Hz

ISR (TIMER2_COMPA_vect)
  {
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = TCNT1;  // see datasheet, page 117 (accessing 16-bit registers)
  unsigned long overflowCopy = overflowCount;

  // see if we have reached timing period
  if (++timerTicks < timerPeriod)
    return;  // not yet

  // if just missed an overflow
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    overflowCopy++;

  // end of gate time, measurement ready

  TCCR1A = 0;    // stop timer 1
  TCCR1B = 0;    

  TCCR2A = 0;    // stop timer 2
  TCCR2B = 0;    

  TIMSK1 = 0;    // disable Timer1 Interrupt
  TIMSK2 = 0;    // disable Timer2 Interrupt
    
  // calculate total count
  timerCounts = (overflowCopy << 16) + timer1CounterValue;  // each overflow is 65536 more
  counterReady = true;              // set global flag for end count period
  }  // end of TIMER2_COMPA_vect

void setup ()
  {
  pinMode (TICK, OUTPUT);
    
  // disable ADC
  ADCSRA = 0;  
  
  // turn off everything we can
  power_adc_disable ();
  power_spi_disable();
  power_twi_disable();
  power_timer0_disable();
  
  set_sleep_mode (SLEEP_MODE_IDLE);  
  startCounting (TIMING_INTERVAL);  // how many mS to count for

  } // end of setup

void loop ()
  {

  sleep_mode ();  

  if (counterReady)
    {
    // we awoke! pulse the clock hand
    digitalWrite (TICK, ! digitalRead (TICK));
      
    // adjust counts by counting interval to give frequency in Hz
    float frq = (timerCounts *  1000.0) / timerPeriod;
    power_usart0_enable();
    Serial.begin (115200);
    Serial.println ();
    Serial.print ("Frequency: ");
    Serial.print ((unsigned long) frq);
    Serial.println (" Hz.");
    Serial.flush ();
    Serial.end ();
    power_usart0_disable();
    startCounting (TIMING_INTERVAL);  // how many mS to count for
    }
    
  }   // end of loop
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

#12
Dec 07, 2014, 04:30 am Last Edit: Dec 07, 2014, 04:30 am by Nick Gammon
What happens when you run at 1MHz ? The datasheet shows a big saving. The CLKPR set to divide by 8.

I think it is like this:
Code: [Select]

cli();
CLKPR = 0x80;
CLKPR = 0x03;
sei();


Or is that the CKDIV8 fuse ? Or is that fuse for the 32,768kHz ?
That code certainly made a difference. Running at 2.5V it reduced consumption to 290 µA. Of course, the timings are out by 8 (including the baud rate). Also your maximum frequency you detect would be an eighth, so you are down to being able to measure about 250 kHz.

You are right about the technique. I must add that to my power usage page, that is a useful tip. You have quite a few factors you can play with (eg. divide by 2 rather than 8).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

Of course, a beneficial side-effect of running at 1 MHz is that you can drop the input voltage down to 1.8V, which I measured as letting the sketch run at 188 µA.

If you were going to do that, I would program the "divide clock by 8" fuse, so that the sketch did not fail to start before it reached the code that reduced the frequency.

Quote
Maybe a chip which is especially designed for lower power consumption, ...
The Atmega328P is in fact designed for low power consumption, as I think these tests demonstrate.

If I were you I would be looking at turning off the LCD, backlight, etc. until user input indicated they were required (eg. switch press).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

JimboZA

Why would anybody want to count geigers anyway  :P
Johannesburg hams call me: ZS6JMB on Highveld rep 145.7875 (-600 & 88.5 tone)
Dr Perry Cox: "Help me to help you, help me to help you...."
Your answer may already be here: https://forum.arduino.cc/index.php?topic=384198.0

Go Up