That was quite a lot of material! Surely there is nothing left to learn about working with the AVR microcontroller or any of its peripherals, right? Wrong!
By now you should have the base knowledge to read the datasheet or scour around the interwebs to find what you need. In that spirit, here is a list of all the topics that I didn’t have room for, but that I feel like you should know about anyway.
The watchdog system is a very slow timer that can be configured to run from 16 milliseconds all the way up to 8 seconds. It is special in that when it reaches the end of its time, it can fire an interrupt or reset the chip. Why would you want to reset the chip every second?
The primary use of the watchdog timer is as a safeguard against runaway code. If your event loop normally takes, say, 20 milliseconds, you can configure the watchdog timer for 40 milliseconds. At the bottom of your event loop, you zero the watchdog timer out again. When your code is running normally, the watchdog timer will never fire.
But if anything goes horribly wrong and your chip freezes up—say you’re doing a blocking wait for peripheral input that never arrives—the watchdog will reset your AVR at least once in a while. You can very cleverly structure your code to take advantage of this and provide nearly foolproof functionality of the first part of your routine.
Another use of the watchdog is as a source of sleep-mode interrupts. If you are putting the chip to sleep for long periods of time, and you configure the watchdog to use a normal interrupt rather than the system reset, it can be a handy wakeup source because it runs so slowly. You can put your chip to sleep for up to eight seconds at a time.
For more on the watchdog system, see the chapter in the datasheet on “System Control and Reset.”
I hardly scratched the surface of power saving and sleep
modes. You saw how to enter sleep mode and vary the CPU prescaler, but there’s
even more you can do. For instance, if you really need to minimize power use, you can turn
off all of the peripherals that you don’t need. Read up on these in the
“Power Management” section of the datasheet, but then go straight to the
avr/power.h
library for implementation because it’s much more readable in
your code.
My first choice for lowering power consumption is just to stay in sleep mode
as much as possible. Following that, I’ll shut down whatever peripherals I
can, but this gets tricky if you’re actually using some of them.
A no-code-change method to reduce your power usage is just to
use a lower voltage for VCC
—all of the A
series chips run on 3.3–3.6 V
just fine.
Here, we always used the internal calibrated RC oscillator as a timebase, but you can also supply an external crystal and set some fuses to use it. This gives you the advantage of a super-accurate timebase. I’ve only really needed a crystal for high-speed USART serial communications and USB emulation. Other people swear by them.
Another option, if you’ve got an accurate external timebase, is to calibrate
the internal oscillator to the external source by varying the byte in the
OSCCAL
register to change the speed. The problem is that the OSCCAL
register is a normal, volatile, register and gets lost on reset.
A solution, once you’ve found a good value, is to
write the calibration byte to a safe place in EEPROM and then read the value
back out and store it back in the OSCCAL
register as part of your
initialization routine at the top of main()
.
With a number of the ATmega chips, notably the mega88 and mega168, you have the option to reserve some space at the end of the program memory for a bootloader program that gets automatically called on reset, if the right fuses are enabled. This bootloader code is able to write to program flash memory, so that you can program the chip with only a serial cable. The old-school Arduinos used to do this, for instance.
I’m not a big fan of bootloaders because the serial port depends so much on the CPU speed that it can get messed up if you play around with the CPU speed fuse settings. That said, you can’t set the fuses from within a bootloader either, so it makes for a nice safe playground. You should have an ISP on hand as a fallback.
The AVR not only has an ADC, but also an analog comparator built in. In
short, it can trigger interrupts when the analog voltage on one pin (AIN0
)
exceeds that on another pin (AIN1
). With a fixed voltage threshold, you
could just use the ADC and compare it to a number in code. But the ADC
allows you to figure out the difference between two signals.
The comparator can also share the ADC multiplexer, so you can use same ADC
pins in place of AIN1
. You do have to turn off the ADC to use its
multiplexer for the comparator, however.
If there’s anything I like about microcontrollers, it’s putting together a project quickly and having it work. Of course, it’s sometimes fun and sometimes frustrating to debug the darn thing for a few hours, but I usually learn something along the way.
Advice on debugging is usually the same old chestnuts, and I’m afraid I’m going to dish them up as well:
volatile
?
Now get out there and build stuff! Take photos, post project writeups, and post your code for all to see.
3.145.38.117