Microchip AVR Hacking
This project describes how to interface with and program Microchip (formerly ATMEL) AVR RISC microcontrollers with a specific focus on ATmega328P.
There are many excellent references on the web for working with an ATMega. I have relied heavily on the Application Notes that come with each particular chip (-), All About Circuits , and AVR Freaks  as the primary sources.
AVR CPU Block Diagram
The figure below shows the architecture of the AVR CPU core. The core is a model of simplicity and can serve as an execellent example in a teaching setting. It is an example of a "Harvard Architecture" in which programs and data memory within the CPU are separated. It is also an example of a RISC (reduced instruction set computer) in which the CPU implements a set of simple instructions whose aim is to be executed in one instruction cycle. By contrast, most commodity CPUs today implement a "Von Neumann" architecture (program and data memory are shared) and complex instruction sets (CISC).
Unless you are planning on programming in assembly language, the CPU architecture is probably not worth studying in any great depth.
System Block Diagram
The figure below summarizes the ATMega328 system block diagram.
Programming Considerations - Fuse Bits
There are two passes involved in programming an ATMega chip. In the first pass, you program "fuses" that set the chip up for its physical environment. The "fuses" aren't permanently burned or blown - they can be changed back to factory values with another programming pass. In the second pass, your application code is downloaded into memory and the chip begins to do its tasks.
Programming will not always involve setting the fuses. In fact, the fuses are likely to be set once followed by a long period of application code programming. However, it's important to know that the fuses have to be adjusted in order to set up the peripherals properly.
In order to know which fuses to set, you really need to have a good idea of how you want to configure the clock, peripherals, and internal devices for your target application. In this section, we look very quickly at the fuse bits, their purpose, and how to program. Note that this information is not substantially different from the manual . A good description of the fuse bits and their purpose can be found here. I'm going to repeat some of what was written there.
There are 19 fuse bits separated into three bytes as shown in the figure below. Yellow indicates that the fuse bit is unprogrammed out of the factory (a logic 1) and green indicates that the fuse is programmed (a logic 0). That's worth repeating: you enable the function specified by a fuse bit by setting it to zero (0).
The fuse bits are all latched when the ATmega328P powers-up in normal operating mode and when entering Programming mode. Except for EEPROM memory save bit, changes to the fuse bits after they have been latched won't take effect until they are re-latched (i.e. exiting programming mode, restart in normal mode of operation).
Out of the factory, the Fuse Low Byte has CKSEL to 0010. This selects the clock source to be an internal RC Oscillator that runs at 8MHz. But bit 7 is set (i.e. 0) which means that this clock is divided by 8. So the out-of-the-box operating frequency is 1MHz.
Minimal ATMega Circuit
The figure below shows the pinout for the 28-pin Dual Inline Package (DIP) version of the 328p.
A "minimal" configuration of the ATMega (i.e. the minimum number of external parts needed to get it running) varies a bit with intended use. At a minimum, the device needs power, ground, and a way to connect to a programmer. The figure below shows a minimal circuit for connecting to the chip using a Sparkfun Pocket AVR Programmer.
And here is the implemented circuit on a breadboard with the Pocket AVR Programmer connected.
It isn't hard to find a good, step-by-step build of a minimal circuit on a proto board. Other examples can be found here and here.
Technically, an external crystal isn't needed to run the chip since there is an internal RC oscillator that can provide a clock signal. The circuit shown above does not have a crystal attached.
Note that if the analog-to-digital capabilities are going to be used, then filtering capacitors should be added to the circuit. Here, we are just getting the chip into operation so no extraneous passive components are necessary. We can consult an application note specifically on the use of the A2D when the time comes.
The 328 can be programmed through it's Serial Peripheral Interaface (SPI). This is the interface that is shown connected to the AVR Pocket Programmer in the figure above.
Programming the ATMega 328 Using Atmel Studio 7
The 328 (and many other devices) can be programmed using Microchip's Atmel Studio 7. Studio 7 is free, provides an integrated development environment and simulator, and has hooks for an online app store for extending the environment. Studio 7 is only available for Windows computers.
Download the Adafruit Driver Installer or the USBtinyISP signed driver from Adafruit. I used the installer and selected USBTiny during the installation process.
When Studio 7 generates a binary for the target device, it can use one of several hardware programmers to move the binary to the device. The programmer selection can be made when you select a target device. However, the AVR Pocket Programmer won't be found in that list so we need to set up as a "custom" programmer selection. For this purpose, we use "AVRDude" - a popular command-line utility that is used to download from and upload to Atmel microcontrollers.
AVRDude is available here in the Download Area. Get whatever version is the latest for windows (avrdude-6.3-mingw32.zip as of this note). Unzip the utility to someplace accessible - preferably without spaces in the filename or path. You will need to remember where you unzipped it for the next step.
Ensure that you set up your profile in Studio 7 as "Advanced". This can be checked by clicking on Tools->Select Profile. You will be shown a window similar to the one below. If you are not the "Advanced" profile, make the switch now. It is necessary so that you can see the full "Tools" dialogue.
Click on Tools->External Tools and fill in the dialogue in a manner similar to what's shown below:
- On the title line, type "AVRPocket" (or whatever name you prefer).
- On the command line, click on the "..." button and navigate to the executable for avrdude. Here, the executable is found in the D: drive.
- On the arguments line, enter the following: -F -c usbtiny -p m328p -v -U flash:w:$(TargetDir)$(TargetName).hex:i
- On the initial directory line, just copy the directory for the avrdude command.
- Click on Apply and then OK.
Now let's create a short "Hello World" program for the minimal circuit shown above. Click on File->New Project. You will get a window similar to the one shown below. Select "GCC Executable Project". Give it an appropriate name and location.
You will be presented with a "target architecture" selection similar to the dialogue below. Select the ATmega328P.
Finally, you will be given a starting code framework similar to one shown below.
Edit the code to include the following:
Now click on Build->Build Solution as shown below. When you do this, you should see a number of progress messages in the Output window.
If everything compiled correctly, click on Tools->AVRPocket and the code will be downloaded into your ATMega. If it works, it will blink fast. Note here that the tool name (AVRPocket) is the name we gave AVRDude earlier. If you gave it a different name, you should see this name in the list.
Programming the ATMega Without Studio 7
If you'd rather do all your programming without the assistance of Studio 7 IDE, then you will want to download and install some avr tools. I would recommend doing this on a Linux box.
I used this reference as a starting point for setting up my handraulic avr programming environment. The steps were as follows:
- Issue the following command in a terminal window: sudo apt-get install binutils gcc-avr avr-libc uisp avrdude flex byacc bison. Note that the comments for this reference indicated that binutils-avr would have been a better choice as they more directly support the avr environment. However, I did not encounter any problems using the generic binutils.
- Develop an application for the avr... let's call it main.c
- Issue the following commands to compile and burn it:
- Create an object file: avr-gcc -Os -mmcu=atmega328p -c main.c
- And: avr-gcc -g -mmcu=atmega328p -o main.elf main.o
- Copy the relevant sections into a hex file: avr-objcopy -j .text -j .data -O ihex main.elf main.hex
- Adjust the target size to the hardware: avr-size --format=avr --mcu=atmega32 main.elf
- Create a debug file if required: avr-objdump -d main.elf > main.dbg
- Send the hex file to the chip: avrdude -c usbtiny -p atmega328p -U flash:w:main.hex:i
- Burn fuses if required: avrdude -c usbtiny -p atmega328p -U lfuse:w:0x62:m -U hfuse:w:0xd9:m -U efuse:w:0xff:m (this was an example).
Burning Back a Bootloader (if you blew one away)
If you happen to have used your Arduino board for its ATMega328 and then turned around and needed it for something else, then it would be nice to return the bootloader back to its original state so it can talk to the Arduino software again. Here's what I did to achieve this (after spending several hours trying to figure out why the device wouldn't work before remembering I blew up the bootloader).
- In your Arduino distro, find the "bootloaders" directory. In my installation, I found it at "/home/arduino-18.8.5/hardware/arduino/avr/bootloaders".
- Take note of the hex file associated with your device. In my case, it was the one containing 328 in the name. I renamed the hex file to make it shorter (renamed to "atmega328.hex").
- Issue the following command at the prompt: sudo avrdude -c usbtiny -p atmega328p -U flash:w:"atmega328.hex”. This command assumes you are in the bootloaders directory. Otherwise, you have to issue absolute paths to the hex file.
- AVRDude will report that it has burned and verified the flash.
- You may also want to reset the fuses if you made changes to those: sudo avrdude -c usbtiny -p atmega328p -U lfuse:w:0xFF:m -U hfuse:w:0xd8:m -U efuse:w:0xCB:m.
Atmel ATmega328P Datasheet Summary (11/20/16).
Atmel ATmega328P Datasheet.
Atmel ATmega328P Application Notes (Under Documents tab).
Professor Hill's Course "Mircroprocessor Principles and Applications (California State University).
All About Circuits.