Minimising ATTiny ADC power consumption

Published: April 17, 2017
Tags: adc attiny avr electronics hacking hardware low power

This post is a quick note on making full use of the power saving settings for the analog-to-digital converter in ATTiny chips. This is based on my experience tinkering with the ATTiny84 but I suspect that it applies pretty broadly across the family, including to the more popular ATTiny85.

The ADC is one of the most power hungry peripherals on these chips, and if you are trying to achieve an ultra low power sleep state for a battery- or solar-powered application its important to shut it down when its not in use. Somewhat confusingly, there are two options provided to facilitate this. You can disable the ADC by clearing the ADEN bit in the ADCSRA register (which, to my understanding, stops the ADC clock running but leaves the ADC powered up), but you can also actually power down the ADC by setting the PRADC bit in the PRR power reduction register (AVR libc provides convenience functions for doing this, power_adc_disable() and power_adc_enable()). Disabling the ADC seems to provide a much greater power saving than powering it down (i.e. the ADC consumes very little power when the clock is not running), but if you are really gunning for nanoamp consumption then to get maximum savings you should do both.

Basically all I'm writing this for is to emphasise the fact that when doing both of these things you need to pay attention to the order in which you do them for things to work as expected. The datasheet is almost explicit about this, saying "The ADC must be disabled before shut down" (i.e. write to ADCSRA before you write to PRR). What it doesn't say is that, when bringing the ADC back up, you need to do the reverse and restore power before enabling it (i.e. write to PRR before you write to ADCSRA). Arguably this is obvious, but its easy to overlook, especially if you do what I did: write your "power up" code by copying and pasting your "power down" code and reversing the bit setting/clearing logic without reversing the order of the lines. As far as I can tell, if you try to enable a powered down ADC, your write to ADCSRA will simply be ignored. When you then power it up, ADEN remains cleared, and any reads from ADCH or ADCL will simply return the value of the last successful conversion, making it look like your ADC is "stuck". So be careful!


Using the alarms on the MCP7940N

Published: April 08, 2017
Tags: attiny electronics hacking hardware rtc mcp7940

Lately I've been tinkering with an ATTiny-based microcontroller project, exploring the possibilities for ultra low power operation after being inspired by this wonderful series of blog posts by Heliosoph. Part of the setup is using a real time clock chip to periodically wake the microcontroller from a very deep sleep state. The darling RTCs of the hacker/maker world are unquestionably the DS13XX chips from Maxim, most commonly the DS1307 - these are the chips you'll find for sale at places like Sparkfun, Adafruit, Tayda, etc. However, I prefer to use the MCP7940N from Microchip.

The MCP7940 is considerably cheaper than any of the DS13XX chips, at least from my suppliers, which is my main reason for using it. However, it also has excellent power specs (works down to 1.8V and draws 1.2 microamps at 3.3V, compared to the DS1307 which requires 4.5V and draws 200 microamps - admittedly the DS1337 is much more closely matched) and a lot of nice additional features, like a digital trimmer which lets you correct for the tolerance of the quartz crystal, a multifunction output pin which can generate alarm interrupts or squarewaves at various frequencies, and logging timestamps to battery-backed SRAM recording when the chip switches between its main power supply and the battery backup. Paradoxically, I think it is simultaneously the cheapest and the most featureful DIP-packaged RTC chip on the market. Of course, it has some drawbacks, too, which is presumably the reason it is not widely used. The DS13XX chips have internal capacitors for their oscillator circuit, whereas these need to be external components on the MCP7940. Microchip also recommend (in a guide for those migrating from the DS1307 to the MCP7940) connecting the backup battery to the RTC via a diode, resistor and capacitor network, whereas the DS13XX chis permit a direct connection. The need for these external components offsets the cheaper chip price to some extent, but I still think the MCP7940 has the lower total BOM cost.

However, the biggest drawback is that the programming interface for the MCP7940's alarms is frankly a bit of a pig to work with, which is the motivation for this post. One user on Microchip's forum declared the alarms to be "unusable". The specific complaint was that it's very hard to set an alarm to fire each day at a specific time, say 0730, the way you'd expect a traditional alarm clock for sleeping humans to work (which, I acknowledge, is not really the kind of use case an RTC chip is designed for). To set an alarm on the MCP7940, you specify a value for seconds, minutes, hours, day of the week, date and month, and then select one of the following six matching conditions:

  • seconds match
  • minutes match
  • hours match
  • day of week match
  • date match
  • seconds, minutes, hour, day of week, date and month match

Conspicuously absent is a "minutes and hours match" mode (which the DS1337 does provide, or rather it provides a "seconds, minutes and hours match" mode). This means it's easy to trigger alarms at 0630, 0730, 0830, etc. (by having the minutes match 30) and easy to trigger alarms at 0700, 0701, 0702, etc. (by having the hours match 07), but apparently impossible to trigger an alarm at 0730. The only option is to use the last mode, where everything has to match. This lets you trigger your alarm at 0730 on Sunday the 9th of April 2017, but the drawback is that to reenable that alarm for 0730 on Monday you'll have to increment the day of week and date settings for the alarm - and then check to see if you've rolled over to a new month (requiring you to write "30 days has September..." code), and if you have to check if you've rolled over to a new year. Don't forget about leap years! This is rather too much mucking about for any sane person's taste to achieve as simple a task as triggering an alarm once per day at the same time each day.

It turns out that with a more careful reading of the datasheet, there is an easier way than this, but it's a little odd. The MCP7940 has an ALMPOL bit in the ALM0WKDAY register (at address 0x0D) which specifies the polarity of the alarm output pin (MFP). When only one of the two alarms is used, this functions sensibly. If ALMPOL is set, then the alarm output is active high (i.e. the output is low while waiting for the alarm to trigger, and goes high once triggered), and if ALMPOL is cleared, then the alarm output is active low. However, if both alarms are used at once, things get interesting. If ALMPOL is set, then the output on the MFP pin is low by default and goes high when either of the alarms fires. You get two independent alarms with this configuration. However, If ALMPOL is cleared, then the output on MFP is high by default and goes low only when both of the alarms fire. So if you set alarm 0 to fire whenever the hour matches 07, and alarm 1 to fire whenever the minutes match 30, then you essentially end up with a single active low alarm which fires at 0730. It's one alarm for the price of two, but it's better than worrying about all that date incrementing nonsense.

An alternative solution raised in the forum thread is to implement some of the alarm matching in software. You can set an alarm to trigger whenever the minutes match 30, say, and in the interrupt service routine which is triggered by this alarm you can read the current hour out of the RTC and check for yourself whether its 0730 or half past some other hour. If its 0730, do what you have to do, and if its not put the microcontroller back to sleep. Unlike the "native" solution using both alarms described above, this approach lets you use both alarms independently, and also lets you retain the choice between an active high and active low alarm. You can also generalise this quite far: set an alarm to fire whenever the seconds match zero and your ISR will be called once per minute, then you can check any other condition in software. In this fashion you can have as many alarms as you like, and they can be as unusual as you like, like trigger at 0730 on the second Tuesday of the month but only if the date is divisible by three. The sky is the limit!

That's not the end of the story, though, as both this solution and the native solution suffer from a common additional problem, which in my opinion is the real pitfall of the MCP7940's alarm system. This is that the alarms trigger repeatedly as long as the matching condition holds true. If you set an alarm to trigger when the seconds match 00 and in response your microcontroller wakes up, does some work for 10 milliseconds and then goes back to sleep, it will be immediately awoken again because the seconds still match 00. This will in fact repeat one hundred times until the seconds value finally ticks over from 00 to 01 and only then will the microcontroller finally stay asleep. The DS1337 does not suffer from this problem at all, since it tests for a match only once per second, when the time and date registers are updated. The problem is even worse if you are matching on a particular value of the minutes. Instead of your ISR firing constantly for a whole second, it will fire constantly for an entire minute! Of course, your ISR can simply disable the alarm the first time it is called, in which case it will only fire the once, but then your alarm won't fire at all when your condition matches again one minute or one hour later.

So, what do we do if we want to use the MCP7940 to, say, trigger an alarm exactly once each hour when the minutes match 00? The solution is to use both of the alarms in a "leapfrog" configuration:

  • Configure alarm 0 to fire when the minutes match 00 and enable the alarm.
  • Configure alarm 1 to fire when the minutes match 01 and disable the alarm.
  • Put the microcontroller to sleep, where it will remain until alarm 0 fires at the start of a new hour.
  • When the microcontroller awakes due to alarm 0, perform your hourly task, then do the following in this order:
    • Disable alarm 0
    • Clear the ALM0IF interrupt flag
    • Enable alarm 1
    • Go back to sleep
  • The microcontroller will then stay asleep for the remainder of the first minute of the hour.
  • When the microcontroller awakes due to alarm 1, do the following in this order:
    • Disable alarm 1
    • Clear the ALM1IF interrupt flag
    • Enable alarm 0
    • Go back to sleep
  • The microcontroller will then stay asleep until the start of the next hour, whereupon it will be awoken due to alarm 0 and we rinse and repeat.

Triggering an alarm exactly once each minute when the seconds match 00 can, of course, be done in the same way. Its very important that you disable an alarm before clearing its interrupt flag. If you clear the interrupt flag with the alarm still enabled, the flag will immediately be set again and your ISR will unexpectedly fire a second time. This approach is, once again, one alarm for the price of two, but because it is well behaved you can easily implement a software alarm on top of it and have an arbitrary number of alarms which occur every day at particular times specified by an hour and minute. Note that if your repeating task takes longer than two seconds (if it happens every minute) or two minutes (if it happens every hour), then you may miss alarm 1, and then alarm 0 will never be reenabled and your controller will fall into an endless slumber! In this case adjust the match value for alarm 1 accordingly.


Shortwave DXing

Published: January 18, 2016
Tags: dx r9012 shortwave tecsun radio

Recently I decided I'd like to try my hand at shortwave DXing - receiving and, hopefully, identifying shortwave radio broadcasts from distant lands which have propagated very long distances by bouncing off the ionosphere. I have fond memories of listening to our shortwave radio as a child; I don't think I had any interest or even awareness of the geopolitical aspects of shortwave listening, I just really enjoyed all the weird and wonderful science-fictiony sounds that you could find in between stations.

To dip my toe in without risking a lot of wasted money if I quickly got bored of the hobby, I purchased the Tecsun R9012 off eBay. Tecsun seems to be the preferred brand of the "ultralight DXing" community (DXing using small, portable and affordable radios instead of big, heavy, professional grade desktop receivers), and the R9012 seems to be their current "top end of the bottom end" (Tecsun produce a baffling array of radios - I found this guide useful for getting my bearings). If I really get into this, my planned upgrade route is to first get the Tecsun PL-380 and then, if I'm still not sated, the PL-880, which as far as I can tell is their current top of the line.

Radio propagation is best at night, and I took the R9012 out on my deck both evenings this past weekend. Everything I've found so far has been quite faint and with quite a bit of static. Apparently this may be, at least in part, due to interference from household electronic appliances (I live in a block of four units, so I'm in close proximity to quite a lot of these). Fortunately, I happen to live right next door to an almost 200m high dormant volcanic cone with easy pedestrian access. Between the elevation and the fact that I'll be some distance from anybody's home I imagine the DXing prospects up there at night might be quite impressive, and I'm keen to try this out when I get a chance.

Despite the sub-par reception on my deck, I have already identified with confidence WWVH, a US NIST time signal broadcast from Hawaii (just over 7,000km away!), and I suspect but can't be completely sure that I've also picked up Radio France Internationale and the Voice of the Islamic Republic of Iran. I'm looking forward to seeing what else I can find as I get used to the radio and get more experience identifying stations. There's a lot of interesting SW content in Asia which I figure should be an easy catch for me, including a Japanese government broadcast aimed at abducted Japanese citizens held in North Korea, and China's "Firedrake" jamming signal, which drowns out Radio Free Asia and similar stations with an hour-long loop of folk music. And, of course, I'd just love to catch a numbers station.


Farewell to PrettyTable

Published: January 15, 2016
Tags: software prettytable

Almost seven years ago (as hard as that is for me to believe!), I announced the release of PrettyTable, a Python library I wrote almost on a whim to make it easy to produce nice looking ASCII tables of the kind I first saw in the command line shell for PostgreSQL. It was much better received than I had ever anticipated; people blogged about it, it spawned a mailing list, it was packaged for Debian and other people wrote things which depended on it. It remains to this day the most successful piece of software I've ever written.

At first I was, of course, elated at this turn out, and worked diligently to add features people wanted and fix bugs. But, truth be told, it got old, and it got boring. It's not a terribly exciting concept to begin with, and it didn't take long before 90% of the work became related to fiddly details surrounding obscure edge cases. For the past several years, I've been a pretty awful maintainer of PrettyTable. I did occasionally make changes in the trunk branch of the svn repository when people poked me, but I never actually made a new release for PyPi or anything like that, so most users have never seen these bug fixes or new features.

I read The Cathedral and the Bazaar, on physical paper, something like thirteen years ago. I don't think I've reread it since, but nevertheless, ESR's "lesson 9" has always remained clear in my mind: "when you lose interest in a program, your last duty to it is to hand it off to a competent successor". I tried to do this with PrettyTable, although not terribly hard, and I always felt bad about letting it stay in the state it did for as long as it had.

Recently, one of those people who wrote something which depends upon PrettyTable got in touch with me. Or rather, Flavio Peroco got in touch with me as a representative of the OpenStack project, which is what an actual successful free software project looks like. It has its own conference! It also uses PrettyTable. With the official PrettyTable repository now frozen thanks to the inevitable death of Google Code, there was some concern about the ability for OpenStack to get needed changes pushed upstream in the future. It turns out that OpenStack has previously adopted other projects that it relies upon, and there was interest in doing this for PrettyTable. After a brief exchange of emails, I was quickly convinced that PrettyTable had an infinitely brighter future under the OpenStack umbrella than I was ever likely to provide it myself, so a handover is now in progress. Once PrettyTable has an official new homepage, I will update this post to link to it.

Even though it didn't really work out in the end, I don't regret the time I spent on PrettyTable. Over the years, on the order of ten people sent me emails for no other reason than to say that they had enjoyed using PrettyTable and to thank me for writing it. One person even sent me a US$5 PayPal donation! Every one of those emails made my day. And I've learned an important, if retrospectively obvious, lesson: it's important that your projects really do scratch your own itches, and it's best if that itch is a strong one. I've actually made very little use PrettyTable myself, which probably went a long way toward me being able to develop the indifference that I ended up showing the project.


Still alive

Published: January 12, 2016
Tags: meta

I'm hoping to write here more in 2016. We'll see what happens.

For older posts, see the archive.
Top tags