One of the ways we’ve tried to differentiate some of our recent and upcoming products, like the Krampus and RBG RGB badge, is by using some of the tiniest ATTiny microcontrollers we can get our hands on. For these boards we picked the ATtiny4, which has just 6 pins, 256B (not KB!) of EEPROM and SRAM, and 512B (not MB!) of flash, providing a hearty challenge to hackers who may be accustomed to more spacious resources. But before we dive into hacking with that most minimalist MCU, let’s take a general look at the basics of programming AVR chips without all of the abstraction provided by tools like Arduino – or as it is sometimes called: bare-metal programming.
When you order the Hacker Challenge edition of our Krampus kit, it comes with a USBasp programmer (which we’ve updated for you to be able to handle the TPI protocol – but that’s another blog for another day!), so that’s what we’ll be focusing on here. Plenty of fancier, more expensive options exist, but like an in-circuit Xzibit, the USBasp is basically a simple AVR circuit … for programming your simple AVR circuits! Instead of the ATtiny4, we’re going to luxuriate ourselves with the ATmega168, as found in the original Arduino Diecimila (these instructions will work for the 328 series as well – just change all the instances of “168” to “328”). With a whopping 16 KB of flash – 32 times the amount found on the ATtiny4, the ATmega168 – and its 32K sibling, the ATmega328 (any guesses as to how they came up with these names?) are capable of “full” Arduino compatibility, i.e. running sketches, using libraries etc. – which is why they can be found at the heart of Arduino’s most popular boards.
If you’ve ever taken a close look at an Arduino board before, you may have noticed that it is fairly-well populated beyond the ATmega chip. Most of the components you’ll find on the Uno PCB for example are for supporting a range of power supplies, as well as onboard USB-to-serial, so that all that is required for programming is a standard USB cable. Given the visual complexity of the Arduino, it may surprise you to discover that the ATmega168 chip itself comes with everything you need for basic operation right inside the package! One of the components you’ll see on the board is an external crystal, which allows the 168 to operate at 16mHz, but the 168 is actually capable of running at 8mHz with its own onboard clock. So other than the above-mentioned programmer to flash it, and a 5V power source, none of that other fancy stuff is actually required for basic operation!
So let’s wire up our programmer and DIP ATmega168 on a breadboard and see how easy it is to flash a program on bare metal hardware. In order to complete the age-old ritual of getting to blinky, let’s also add an LED and resistor to our simple circuit. The USBasp has 6 pins: VCC, GND, MOSI, MISO, SCK, and RST. VCC and GND we will connect to the respectively-named pins on the 168 (7 and 8 (or 22) respectively), providing it with 5V and ground. MOSI and MISO are used to provide a Serial Peripheral Interface (SPI) so that our devices can talk. The preferred modern expansion of this initialism in our context is Microcontroller Out/Serial In and Microcontroller In/Serial Out, although you may see other terms used. We’re going to connect them to pins 17 and 18 on our DIP. The SCK pin provides our clock so that our data transmission remains in sync, and on the 168 that’s pin 19. Finally, RST allows us to halt operations on our microcontroller and flash our new program, then restart and run the new code; on the 168, that’s pin 1. Our LED we could connect in a number of places, but let’s hook the positive side to pin 14 at the end, and the negative to ground via a resistor of the appropriate value (220 ohms or so should work, or you can calculate a more precise value using an LED series resistance calculator). When you’ve completed these steps, your breadboard should look like this:
A few things to check on the USBasp before we move on: make sure it is set to 5V power (the curiously labeled "JP21" with the yellow jumper on the ones we stock (it should logically be "JP2"), positioned toward the ISP header/away from the USB end), or if you are powering the board from another source, go ahead and leave that jumper off completely. If you are using our ISP with Power board, make sure the switch is on, meaning the USBasp is providing power, and once everything is plugged in (not yet!), confirm that the LED is lit, indicating successful power delivery. If you want to power your breadboard separately from another 5V source, leave the switch off (common ground is still provided).
That’s it for hardware! But don’t plug the USBasp into your machine yet – let’s get things set up on the software side first! There are plenty of resources online with detailed explanations of how to install the required tools, but the short version is that we’re going to need gcc-avr to compile our code, and AVRDUDE to flash it. If you’re on Ubuntu/Debian, it’s as easy as:
Windows users can use WinAVR, which despite its age, should do everything you’ll need:
or AVRDUDESS – a GUI for AVRDUDE:
macOS users can find everything they need in CrossPack:
With our compiler and Downloader/UploaDEr installed (get it? DUDE??), let’s load some blinky code! This blog post is already rather long, and I can tell you’re anxious to see that LED blink, so let’s just grab some pre-compiled code and not worry too much about how it was built from a .c file or what it’s doing – there are more blog posts coming in future where we will get into compiling and makefiles and ports and pins and bit masking and all of that! We used a nicely commented piece of example code "blinkLED.c" from the repo for Make: AVR Programming (474 pages of raw AVR goodness centered around projects which demonstrate key microcontroller concepts – pick up a copy from the Maker Shed!). We forked their repo and added the compiled hex file to ours here:
Grab the raw version of that, paste it into a file called blinkLED.hex. Then you can use either command line tools or a GUI to load it into your processor! Note that even though this has been compiled for the ATMega168, it still runs on an ATMega328P, so you can try it out on either.
Programming via AVRDUDESS GUI on Windows:
The below screenshot shows settings for the 328P.
The file listed under "Flash" should be the blinkLED.hex file you just saved on your computer. It's good to first verify that your computer can talk to your processor by clicking the "detect" button. You'll get output in the bottom window that looks like this:
If you get the below error, it likely means you don't have the right driver installed for the USBasp. On Windows, we use Zadig to swap drivers to the libusbK, though libusb-win32 may also work.
If you're re-programming a 328P chip, make sure the fuse settings match those above for a 1 MHz default clock, otherwise your LED may blink at a different rate! Click the read button to read your fuse bits. If you need to change them, enter the new values and then click write. You can check out default fuse settings for other chips, and what each fuse setting does via this handy fuse calculator tool.
Note that you may also need to change the bit clock to properly communicate with the chip - 187.5 kHz is used for a chip with a 1 MHz clock, and 1.5 MHz is used for a chip with an 8 MHz clock.
If everything is set up and communicating correctly, mash that "Program!" button! Your output should look like this, and your LED should blink!
Programming via avrdude Command Line on Linux:
This is for you keyboard warriors out there who like to keep things simple and direct! First we're going to confirm that we're able to talk to the chip, and this example is for an ATMega168. Type in the below command. Before you hit enter, go ahead and plug the USB end of the USBasp into your computer. Monitor your circuit to make sure no magic smoke or anything appears (unlikely, but better safe than sorry!)
Then hit enter. You should see something like this:
This confirms that we’re able to talk to the chip, without actually flashing anything yet. If you get an error, read some of our notes above in the GUI section about drivers and fuse settings, and try searching the web for solutions – chances are someone else has run into this before. You can also type "avrdude --help" to see a list of available settings and commands. Lastly, feel free to drop us a note in the comments and we’d be more than happy to try to help diagnose, though it may take us a few days.
If everything went well, you're ready to load the compiled hex file!
You should see output like:
Your LED should start blinking after the reset. If not, try power-cycling it. If your LED is still not blinking after power-cycling, try searching for solutions to any errors, or drop us a comment and we’ll do our best to help figure out what’s up.
SO, you did it! Compiled and flashed code to a bare metal AVR microcontroller without an Arduino IDE or board in sight! How does it feel!? If you’re anything like us, riding this close to the rails provides a new thrill and feeling of mastery that clicking the old Upload button never could. And if you enjoyed this foray into AVR development, let us know, as we’re planning a series of blog posts about it! Let us know your ideas and interests in the comments! 😊