The Atmel ATMEGA168 was the first MCU I programmed completely by myself, and
it’s true what they say: when, after hours of fiddling and reviewing the circuit
and every possible connection, the LED finally starts blinking, showing you’ve
just programmed your first MCU, you feel so great. It’s really worth the pain.
The megaAVR 8-bit Family
The ATMEGA168 is a member of the megaAVR 8-bit
family ↗. This
means that it contains an 8-bit processor. It also contains 16 KB flash memory.
Other family members are ATMEGA8 and the ATMEGA328, which all behave exactly
the same, but carry different flash sizes.
To program (m)any MCU, we need to connect it to a programmer. This is a
hardware part that allows a computer to interface with the MCU. There are
typically a few ways to connect both: serial,
ISP ↗ (In System
Programming), JTAG ↗ (Joint Test Action
Group),…
Which one to use depends on you MCU. In my case the ATMEGA168 supports ISP. I
also own a JTAG ICE mkII, which supports ISP as a variant
of the JTAG protocol.
ISP requires 6 connections to the MCU: for power VCC (5V) and GND (GrouND), for
Input/Output MISO (Master In Slave Out, output) and MOSI (Master Out Slave In,
input), control SCK (Serial ClocK) and RST (ReSeT).
ISP in fact connects the programmer to the MCU using
SPI ↗ (Serial
Peripheral Bus) - which makes it a bit confusing maybe. MISO, MOSI and SCK are
the three wires that are used from the SPI, with the programmer being the
master and the MCU being the slave. The power-related connections, along with
the reset functionality allow the programmer to control the SPI communication.
Although it basically boils down to connecting 6 wires to your MCU, we better
add some more functionality and protection to smoothen the ride.
We start of with the regulated voltage power supply
as the base for the programming circuit and prepare a ISP using SPI connection
to our programmer (e.g. to the JTAGICE mkII.
Next we add the ATMEGA168. There are two parts to this: connecting the
remaining SPI wires and powering the CMU.
With SPI wired to the power supply, we now have to connect four more wires:
MISO, MOSI, SCK and RST. To do this, we consult the MCU’s data sheet for the
pin configuration.
The four pins we need to connect are pin 19 for SCK, pin 18 for MISO, pin 17
for MOSI and pin 1 for RESET. Connecting these pins would in fact be enough to
continue and fire up the software to push the image through these pipes.
But a few additional wirings can make your life a lot better.
First we need to make sure that, even without the programmer connected, the MCU
would get out of its reset state. The reset pin is an active low pin, meaning
that without 5V connected to it, it will trigger the MCU’s reset functionality
and the MCU will be constantly resetting and not doing anything.
So connecting it to a 5V source is enough. But is can be handy to have a switch
to reset the MCU. So we extend the setup with a momentary switch, by default
off, and of course a resistor to allow the current to flow from the 5V source
to ground, thus bringing pin 1 low and active.
MCU programming requires a pretty stable 5V power supply. To ease out even more
ripples the MCU’s VCC and GND connections are each bridged by a 0.1μF
capacitor.
The final circuit on a breadboard looks something like this …
Mind that on the picture, an additional (red) LED is connected through a
resistor to pin 23. This is one of the GPIO (General Purpose Input/Output)
pins. The software we’re going to upload to the MCU in a minute will make this
LED blink.
The Programming Process
Let’s create and upload the Hello World for embedded systems: make a LED
blink.
The source code is not too big and is interesting to take a look at. I also
modified it a bit, because I wanted the blinking to be a bit more … complex
;-)
Convert blink.c to blink.hex
First we need a compiler. This compiler isn’t your standard compiler maybe
already available on your system. You need avr-gcc a Gnu C Compiler
specially modified to produce AVR RISC binary images.
On my Mac this was … a breeze ;-)
Next we need to compile it. From the great tutorial on
Sparkfun ↗, I happily reused their
Makefile for now - until I have time to clean it up and extract what I need.
For now, I edited it to reflect my setup with a a modified blink.c.
Let’s make this…
Whoops, we’re missing something ;-)
Let’s try that again …
Much better.
Uploading blink.hex
Now we have our blink.hex image, we want to upload it to the MCU’s
flash memory and have it run.
You can use the make program target, but using avrdude
is not that hard and I think it’s better to keep this well under control
Holy crap, why doesn’t this work ? …
About fuses
Because you don’t know what link in the chain is failing, you have to go
through everything, over and over again. At this point I wasn’t even sure if I
could trust the sources I was using, etc.
After a while, digging through Google results, I was introduced to fuses, as
they could be a cause for my problems. It was possible that a bit in the high
fuse wasn’t set, resulting in a disabled SPI. Which means I wouldn’t be able to
program my MCU.
After getting more and more familiar with avrdude, I learned how to
read out the fuse bytes. I had two MCU’s: an old one from a kit I bought @
HAR2009 and a brand new one, I bought 24 hours earlier, just to be sure I had
one working.
Their high fuse bytes were respectively: DD and DF.
Translating this to a bit-string representation gives 1101 1101 and
1101 1111. We need to read these bit-strings from right to left,
starting with index 0. The fifth bit then indicates whether SPI is enabled. In
our case this is the third bit from the right, a zero. No worries, zero is a
good thing here, it means that SPI is enabled.
At this point I knew that I should be able to access the MCU using SPI, so I
should also be able to burn the fuses. I looked up the defaults, hoping this
might reset some other settings that might be faulty.
I used the default fuse settings found on the Embedded AVR Fuse
Calculator ↗. According to this source, the
high fuse byte should be DF, the low fuse byte should be 62
and tried to burn these to the MCU.
It seemed that I could burn the high and low fuse, but not the extended. I
tried it some more times, but the result stayed the same. For some reason, I
tried to upload the image …
One more time …
It worked. Apparantly there actually was something wrong with the fuses and
after resetting them to their default values (at least the high and low fuse
bytes), the upload process did work.
And yes … the red LED was blinking
Understanding the fuses problem
With a working setup, I first wanted to get this documented. That has been done
now :-) The next step is to understand what was wrong. What setting in the
fuses prohibited me from uploading my image.
Coming soon … (I hope)
The Need for Speed
The ATMEGA168 by default is configured to use its internal oscillator, which
operates at 1Mhz (kind of). The ATMEGA168 supports up to 20MHz, which allows it to
process 20 MIPS (Million Instructions Per Second).
8MHz
By default, the ATMEGA168 does run at 1MHz, but this achieved by a fuse
setting, dividing the speed by 8. If we look at the definition of the low fuse
byte (by default 0x62 or 0110 0010), we see that the 7th
bit (the left-most one) is 0, which means that it’s programmed. The definition
describes this bit as name: CKDIV8(4), bit: 7, description: Divide clock by 8,
default: 0 (programmed). So is we change it to 1, the low byte fuse needs to be
1110 0010 or 0xE2.
And immediately our red LED is blinking 8 times faster.
This also shows that we need to take this into account when programming,
especially when dealing with timings. Remember the wait_ms() function?
This is now no longer calibrated to the speed of our MCU. And if we want to
write portable code, we even don’t want to bother. The Sparkfun Makefile
implements a way to abstract this.
But the blink code isn’t using this feature. It turns out that the AVR LibC
library has its own support through _delay_ms() from
util/delay.h, which takes into account F_CPU and allows us to
clean up the code further and make it CPU frequency invariant.
That’s the fastest the MCU can go without external help. But, adding an
external oscillating crystal is easy. It needs to be connected to pins 9 and
To make sure that the crystal starts oscillating, we need to add two 22pF
capacitors before connecting the crystal to GND.
But that’s not all. The MCU also needs to be configured to use this external
crystal. For an external full swing crystal oscillator we need 4 first bits
(the right-most or CKSEL3..0) to be set to 0111 or 0110.
The first (right-most) bit, as well as the 3 of the 4 remaining bits depend on
the way the oscillator behaves at start-up. Based on what I can read I think I
have to go for Crystal Oscillator, slowly rising power, which requires
SUT1..0 = 11 and CKSEL0 = 1, resulting in 1111 0111 or 0xF7.
Fingers crossed …
The LED is blinking like crazy, of course we need to adjust the CPU frequency
parameter in the Makefile, rebuild and upload.
And then the LED is blinking again at the expected rates, while the CPU is
running at 18MHz.
Herfst is soepseizoen, en elke dinsdag sowieso ten Huize VG. De meesten bij ons houden van de gegronde smaak van rode bietjes. Deze versie is met de groene z...
When I tell people that I am technology agnostic, they open their eyes a little bit more. Usually something along the lines of: “But Christophe, you’ve been ...
My son, like many sons (and daughters) loves Minecraft. He especially likes the Enderman . When he wanted to use the character as a base for a school project...