Getting started with a vanilla ATmega328p

I’ve spent time over the years playing with various hobbyist electronics platforms, such as Arduino and NodeMCU (ESP 2866). Most of the time it’s just been working on a breadboard but now I am working on a project that needs to be very minimal (low power and cost). For this I’ve decided to skip an actual Arduino and go with a plain ATmega328p. For those that don’t know, many Arduino boards are powered by the (formerly Atmel) Microchip ATmega328p. The Arduino platform makes it easy to prototype but when designing your own circuit, it’s nice to have full control over all the ICs and components used, so you can optimize for whatever your project calls for.

In this post I’m going to go through a simple tutorial of how to make an LED blink with a plain ATmega328p. Also, as I don’t have an official AVR programmer, but I do have an FTDI FT232R based USB cable, we will use the FTDI bit banging support in avrdude.

Getting the chip

First, you’ll need to get the ATmega328p. You can probably find it on Amazon, but I purchased mine from Digi-Key, specifically this unit.

As we will be using avrdude to do the programming with an FTDI cable, it doesn’t really matter if your chip has an Arduino bootloader programmed or not. Also, in this tutorial we will be using the internal 8Mhz RC oscillator, so an external crystal isn’t required.

Installing tools

We are going to skip the Arduino IDE all together and instead just use command line, specifically we need the AVR toolchain (avr-gcc) and avrdude. I’m using a Mac so I’ll give instructions for that, but it should be easy enough to install if you have another platform as well.

Install Homebrew if you don’t already have it, see this page.

$ brew tap osx-cross/avr
$ brew install avr-gcc avrdude

Wire up the programmer

You will need a FT232R based USB cable for this part. Essentially what we are going to do is configure avrdude with a new programmer type that will work with the FT232R and pay atttention that we map the pins on the cable to the ATmega chip.

FT232R pin avrdude ftdi_syncbb pin
TXD 0
RXD 1
RTS 2
CTS 3
DTR 4
DSR 5
DCD 6
RI 7

Open up the file /usr/local/etc/avrdude.conf and add the following block:

programmer
  id    = "ftdi232rl";
  type  = "ftdi_syncbb";
  reset = 0;
  sck   = 1;
  mosi  = 3;
  miso  = 2;
;

If you want, you can tweak the values for reset, sck, mosi, and miso, just know this means you’ll need to wire up your FTDI cable differently to the ATmega chip. The above configuration says the following about how the cable is wired:

  • TXD -> RESET
  • RXD -> SCK
  • CTS -> MOSI
  • RTS -> MISO

Now is where you’ll need to consult the ATmega datasheet and the one for your FTDI USB cable (for me, it is this one).

In addition to wiring the above to your ATmega, you’ll also need to wire up 2 VCC and 2 GND connections. You can use the VCC (5V in my case) and GND supplied by your FTDI USB cable to power the board.

Here is the full schematic:

Verify connectivity

Before starting, if you have previously installed the FTDI serial drivers (which you most likely have) for Mac OS, it will conflict with avrdude accessing the FTDI bit banging functionality. Run the following to check:

$ kextstat | grep FTDIUSB

If you see the FTDIUSB kext loaded, run the following to unload it (you can always manually kextload it back, or after restart it will auto-load):

$ sudo kextunload /Library/Extensions/FTDIUSBSerialDriver.kext

Now, run the following to see if avrdude can connect to your board:

$ avrdude -c ftdi232rl -p atmega328p -P ft0 -b 1200 -v

If all went well, you will see something like:

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.10s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as FF

avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as FF
avrdude: safemode: Fuses OK (E:FF, H:D9, L:E2)

avrdude done.  Thank you.

NOTE: I use the -b 1200 argument above to use a slow baud rate. This is because my ATmega328p from the factory has a fuse set to divide the clock by 8 (CKDIV8).

If all is working, let’s update the low fuse (if necessary) to move up to an 8Mhz clock. (This isn’t necessary, but allows for faster programming). Check the calculator here to figure out the right fuse value, for me it is E2. Make sure to substitude your fuse value below where it says lfuse:w:0xe2:m.

$ avrdude -c ftdifriend -p atmega328p -P ft0 -b 240 -U lfuse:w:0xe2:m

If all goes well, let’s move on to building a program and flashing the MCU!

Blinking LED

The blinking LED is one of the most basic examples, so it’s perfect for this. We will use pin PD5 to switch the LED on and off. Wire up PD5 to an LED and place a resistor in series after to ground. See the picture at the top of this post for an example. The code for this program is:

#include <avr/io.h>
#include <util/delay.h>

void main()
{
    while (1)
    {
        _delay_ms(1000);
        PORTD ^= 1 << PD5;
    }
}

To run this, I’ve uploaded the source code and a Makefile that handles compilation and flashing to your ATmega to GitHub here.

Locally, just run the following and it will compile and flash, and if everything is wired up correctly, you’ll see a blinking LED.

$ git clone https://github.com/chrismoos/avr-blinking-led
$ cd avr-blinking-led
$ make build flash