2016 / 22 février

Save power with AVRs

The AVRs from Atmel offer really lots of features. Even a tiny 8-bit ATtiny13, I have just installed in a project that has so many features that I probably only use half. A feature that I wanted to try but have always wanted, are the various sleep modes to save power. And this project invited just doing: An electronic target, which leaves a few LEDs, and a melody plays when you connect them with a laser in the middle. The whole project is powered by a battery, and should of course keep as long as possible.

Most of the time the device is idle and is waiting for which it is illuminated. For this purpose, it checks every 200ms the voltage level at a light sensor. Accordingly I have only times just trying to push the power consumption at idle, while the LEDs flash goes anyway it a large part of the current for the LEDs. (BTW: 6 LEDs, a speaker and a light sensor to 5 pins going 😉)
Starting point was a power consumption of 6 mA. Already pretty neat, but there is indeed determined yet what. The first step was to reduce the clock rate. The ATtiny13 has three internal oscillators: 9,6MHz, 4,5MHz and 128kHz. Without further adjustment runs on the 9,6MHz oscillator. After a moment’s reflection, I came to the conclusion that is actually 128kHz more than enough, eventually the CPU is doing most of the time nothing. so i can select one the clock source on the changing of the fuses. By the way, I also turned off the same again the brown-out detection, holding the CPU if the voltage drops below the required 2.7 volts. While this prevents that makes the CPU error, because the battery is weakening a bit, or the power supply just switch off but costs power. And we finally do not operate nuclear power plant it. All the action was in any case already once successfully: By reducing the clock speed and switching off the brown-out detection I was ever at ~ 0.9mA or about a sixth of the initial value!

there were hardly any problems. Of course you F_CPU must again define correctly (ie to 128000), otherwise the queues of _delay_ms () will be too long. Besides, I had used the timer to generate tones on the speaker, here I had to reduce benefits and calculate the OCRA values ​​again. For that I had any macro defined, so that was not  problem.

It is perhaps also be noted that the ATmega’s have a register, which allows you to change the clock rate to maturity. This is even more practical, because then you can wait while the CPU frequency turn down, and if there is something to be expected, giving fast gas again. One must then watch only the queues …

Well, but there is indeed even more determined. The tiny recognizes three sleep modes: Idle, ADC Noise Reduction and power-down. In the first mode in particular, the clock of the CPU is stopped, but the rest continues to operate. So you can power-saving wait for interrupts. In the second mode a bit more is switched off, here it is all about during an analog-to-digital conversion as little noise in the system to have. And finally, is practically off everything in the third mode. Here, only one external (!) Interrupt wake up the CPU or the watchdog module (which has its own oscillator which always runs).

Next, I have therefore tried to replace the polling while waiting for the end of ADC conversion by sleeping in noise reduction mode.

ADCSRA |= (_BV(ADEN) | _BV(ADIE) |_BV(ADPS2) | _BV(ADPS0)); //turn ADC on, enable interrupt,  ADC clock prescaler = 32
sei(); //we need interrupts for the ADC wakeup
uint8_t readAnalog()
//loop_until_bit_is_clear(ADCSRA, ADSC);
//will wakeup here
//this interrupt will be called after wakeup from ADC sleep,
//and therefore must be defined...
the more Important here is the one of the ADC interrupt: He must be enabled (ADIE bit in ADCSRA set), the global interrupts must to be (let ()), and – most importantly – it must also be defined a handler! Without the handler is suspended in the interrupt table a RJump __bad_vector and the CPU. The C Library defines a macro conveniently EMPTY_INTERRUPT exactly for this purpose.

In the C library is available in the file sleep.h also equal to a few functions to the sleep modes to control, as seen above. It is also important that automatically starts a conversion during the transition to the ADC sleep mode, so you have not even set ADSC before. If the CPU is no longer wakes up to check again whether the interrupt is necessarily really up and running. Without interrupt the CPU does not realize when the conversion is finished.

And what has it now been? Unfortunately, not really much, at least with regard to power consumption. Maybe yes, the accuracy has increased a bit …

A little bit of power you can also save, where you turn off the modules of the CPU, you do not need. In my case, the analog comparator and the GPIO block of pins are provided on the light sensor depends (if that is active, you can pin also read digital data simultaneously, which now yields maybe not so much sense …) For larger models there of course many more things that you can turn off, and there are also functions in the C library for it.

I’ve done with this code:

DIDR0 = _BV(ADC3D); //disable ADC3 (PB3) as digital input
ACSR = _BV(ACD); //disable the analog comperator
This has once again took about 100μA.
i’ll write the rest of my results when i’ll finish dealing with this first.
to be continued …

No comments so far.