Hi all, need some advice to get project off on the right foot.
I'm wanting to use a tiny84 as an i2c slave, and i would like to keep its current usage down to a few tens of uA on average. So the chip should spend most of its time in sleep mode of some description. I need to use a couple of external interrputs (pin change is fine), plus regular a wake-up of around 5 to 10 times per second to monitor other inputs. Of course it must also wake in response to requests from the i2c master. I am writing the i2c master sketch also.
I have been reading up on many sites including Nick Gammon's excellent pages on the subject.
Can forum members please suggest the best tiny core to use, the best libraries or example code for i2c slave, pin change interrupts and low power sleep modes, that will all work together?
I suspect all the cores are going to get in the way more than they will help. They all derive from the original Arduino core. The developers assumed the processor would be full power forever. For example: The ADC clock is spun up whether or not analogRead is ever called and the timers are spun up whether or not analogWrite is ever called.
I have a research tiny core meant specifically for low-power applications. For example: all the peripherals are switched at compile-time so things like the ADC clock can be just left turned off. Unfortunately, it is a colossal mess.
Delegating sleep state management to a first class object helped significantly for me. Sleep Manager fights to power down the processor. Everything else fights to keep the lights on. Sleep Manager is notified at the top of loop. It starts the sleep state at power down. During loop anything can request a less aggressive sleep state. Sleep Manager is notified again at the bottom of loop. At that point it takes the appropriate action.
The details of putting the processor to sleep are extremely important. One small mistake can create a race condition that, if triggered, leaves the processor forever asleep. I believe Nick Gammon has the correct code on his website.
The last time I searched for such things (which was ... well ... more than a year ago) BroHogan had the best quality I2C library for ATtiny processors...
I have been trialling use ofthis library. At first, it was not working for '84, even without any attempt at low power/sleep, but I have worked with the author and he got that fixed. Here's a code snippet demonstrating how to work with the library:
I'm not wedded to this library and will continue to explore any other avenues!
I have been reading the atmel attinyx4 data sheet. Bits of its 250 pages are beginning to make some sense to me. Sounds like, for example, I can use power down sleep mode, the lowest power mode. Any one of pin change, USI and watchdog interrupts can wake the cpu from power down sleep. I just have to figure out which bits in which registers I need to set to get it all working together! And which parts of the chip I can power down because they are not needed.
For example, ADC. I need to use only one for my project. I need to read it several times per second, and noise/accuracy are not terribly important, because the voltage being read can be only one of 16 known values. I am using a lookup table containing ADC values that are mid-way between the expected values, so a little noise shouldn't be a problem. I think this means I can leave the ADC powered off and just switch it on briefly when I need to take a reading.
PaulRB:
I think this means I can leave the ADC powered off and just switch it on briefly when I need to take a reading.
That's what I do. The first read after powering-on (and enabling) takes 25 clocks instead of 13. A small price to pay for saving energy.
It also helps significantly to use ADC Noise Reduction. The processor uses less power during the conversion and the results are much better. Especially on ATtiny processors.
I am now down to 650-700uA by running at 1MHz and switching on ADC with bitSet(ADCSRA, ADEN); and off again after use with bitClear(ADCSRA, ADEN);
That's a sleep mode, yes?
I am now noticing the current draw caused by the internal pull-up resistors - around 80uA per pin, which would correspond to around 40K as expected I suppose.
PaulRB:
switching on ADC with bitSet(ADCSRA, ADEN); and off again after use with bitClear(ADCSRA, ADEN);
I believe you also have to turn it off (PRR) otherwise the comparator draws power.
That's a sleep mode, yes?
Yes.
And a technique. You have to loop putting the processor back to sleep if it wakes early from something like a pin-change interrupt.
Alternatively, you could use ADC Conversion Complete Interrupt instead of polling but I found that to be awkward.
I am now noticing the current draw caused by the internal pull-up resistors - around 80uA per pin, which would correspond to around 40K as expected I suppose.
That should only be true if / when the pin has a path to ground.
Yes, absolutely. One of the sensors I am going to use is an anemometer. A small magnet rotates driven by the cups. A reed switch inside the body of the anemometer is opened and closed by the magnet. Half the time is is closed and a current flows. On a still day there is a 50% chance it will stop in the closed position.
I could abandon the internal pull-up and use a higher value external pull-up resistor, but if I go too high, sensing the state of the reed switch will become unreliable.
PaulRB:
On a still day there is a 50% chance it will stop in the closed position.
I could abandon the internal pull-up and use a higher value external pull-up resistor, but if I go too high, sensing the state of the reed switch will become unreliable.
@Udo Klein had a great idea for handling that situation. I have used it and can confirm it works great.
• When the switch state changes to closed...
• Disable the internal pull-up.
• Wait half the expected switch rate. (If you can poll at a watchdog rate, the watchdog works great for this because you can sleep the processor during the wait.)
• Enable the pull-up.
• Wait two clock cycles.
• Test the input. If it is LOW, the state is still closed. Continue polling.
• When the switch state changes to open...
• Enable the pull-up.
• Sleep.
Basically, you poll when the switch is closed only enabling the pull-up long enough to probe the input.
Please explain further!
First, using the DIDR0 register to disable the pin driver for the analog input(s) is helpful. Otherwise the digital switching consumes power. Just set the appropriate bit(s) to one for the analog input pins.
So basically the technique involves part-time polling. That would preclude longer sleeps, and kind-of defeats the object of using interrupts, but i will have a think about it.
Thanks for the further tip on adc power reduction, i will try that. Please explain how/why/what about the adc clock!
PaulRB:
So basically the technique involves part-time polling.
Yes.
That would preclude longer sleeps, and kind-of defeats the object of using interrupts, but i will have a think about it.
The math is fairly simple. A few microseconds awake to check the pin followed by ~16 ms power down to save ~80 µA.
Thanks for the further tip on adc power reduction, i will try that.
You are welcome.
Please explain how/why/what about the adc clock!
The analog-to-digital converter has its own clock. Whatever core you are using should initialize it to an appropriate speed (the datasheet has the valid range). That clock consumes power. However, I believe when the ADC is disabled the clock is stopped. Typically you will not need to worry about it.