ATmega328P and the Arduino UNO R3

Date: — Topic: — by Slatian

Pinout of the ATmega328P and how it maps to the Arduino UNO R3.

Note: According to the Datasheet most of this Information also applies to the ATmega328, ATmega168(P)A, ATmega88(P)A and ATmega48(P)A.

Pinout Table

The following Table applies to the 28 Pin DIP Packaged ATmega328. The Arduino Pin mapping is based on the Arduino UNO R3.

Pin Name Ardu­ino Pin PWM PC­INT Bus Extra
1 PC6 Reset 14 Reset
2 PD0 0 16 Serial RX
3 PD1 1 17 Serial TX
4 PD2 2 18 Ext. Interrupt 0
5 PD3 3 OC2B 19 Ext. Interrupt 1
6 PD4 4 20 Serial XCK Timer 0
9 PB6 6 XTAL1, TOSC1
10 PB7 7 XTAL2, TOSC2
11 PD5 5 OC0B 21 Timer 1
12 PD6 6 OC0A 22 AIN0
13 PD7 7 23 AIN1
14 PB0 8 0 CLKO, ICP1
15 PB1 9 OC1A 1
16 PB2 10 OC1B 2 SPI SS
17 PB3 11 OC2A 3 SPI MOSI
18 PB4 12 4 SPI MISO
19 PB5 13 5 SPI SCK Builtin LED (Arduino)
20 AVCC Analog VCC
21 AREF Analog Reference
22 GND
23 PC0 14 (A0) 8 ADC0
24 PC1 15 (A1) 9 ADC1
25 PC2 16 (A2) 10 ADC2
26 PC3 17 (A3) 11 ADC3
27 PC4 18 (A4) 12 I²C SDA ADC4
28 PC5 19 (A5) 13 I²C SCL ADC5
Physical Pin on the ATmega328P DIP Package.
The Pin name in the ATmega328 Datasheet.
Arduino Pin
The Pin number/name assigned by Arduino.
Which oscillator the Pin is connected to. If it is connected to one it supports PWM.
The Pin Change Interrupt number assigned to the pin.
Bus protocol Hardware the pin is connected to.
Any extra features the Pin might have or a short explanation.

Note on SPI Nomenclature: Instead of the old "Master" and "Slave" terminology on SPI you can use Main and Secondary while keeping all the old Acronyms. 😉

Barebones Arduino UNO R3 compatible

Maybe you've built something awesome and now you want to get it off that bulky Arduino board and for some reason the individual parts are cheaper than a tiny Arduino compatible board (they used to be, guess who has a stash of ATmega328P chips).

Needed Parts

To build a barebones Arduino UNO R3 compatible you need:

Building the circuit

Schematic: Barebones ATmega328P Circuit Schematic: Barebones ATmega328P Circuit Slatian 2024-04-06
Schematic of the ATmega328P hooked up to power, an external crystal, and a reset circuit as described below. The Serial interface for programming is illustrated as an FTDI compatible layout.

For the power Connect VCC and AVCC to 5V and the two GND to Ground. Leave AREF floating unless you need it.

If you want that extra bit of reliability connect a 100nF Capacitor between VCC and GND and between AVCC and GND. The official UNO has a 10μH inductor between AVCC and the 5V to stabilise it even more.

Note on AREF: The official Arduino boards connect AREF through 100nF Capacitor to ground. I assume this is to initially pull it to Ground while allowing you to override that setting by simply applying a voltage.

For the Clock connect the 16MHz crystal between XTAL1 and XTAL2, and each of XTAL1 and XTAL2 through a 22pf Capacitor to Ground. Try to no make the circuit paths too long here.

For the Reset Circuit: Connect the Reset pin through a 10KΩ Resistor to 5V, put the button between Reset and Ground.

When using a Serial interface (Make sure it uses 5V logic levels, otherwise you'll destroy something), put the 100nF Capacitor between the Ready to Send (RTS or DTR) and the Reset pin, this way the Chip will automatically reset when the Serial line becomes active, which is the exact behaviour you want when programming using the Arduino bootloader.

Note on perfboard Layout: On a perfboard I usually try to go as compact as possible and have a male header right up to the microcontrollers pins that gives access to GND, Reset, RX and TX (with VCC being part of a male GND, VCC, GND header). The DTR capacitor is on a little adaptor board along with an indicator LED that then plugs into my FTDI breakout. The reset button is near the Reset pin and the pull-up resistor goes below the IC-Socket.

Flashing the Bootloader

To flash the Arduino bootloader onto the ATmega328P you have to hook up the SPI interface to a programmer. See the official Arduino tutorial for this step or my tutorial for doing the same using arduino-cli.

PWM and Timer/Counters

The ATmega328P has 3 so called Timer/Counter units, called Timer/Counter0, Timer/Counter1 and Timer/Counter2 each can drive 2 of 6 PWM channels.

I'll abbreviate them as t/c in the following.

Oversimplified they are counters hooked up to a configurable Clock source. Every time the clock ticks the counter increments by one. Each counter has two comperators to trigger actions when the counter has reached a configured point.

The actions that can be triggered may be:

t/c0 and t/c2 have an 8-bit counter while t/c1 has a 16-bit counter (it can be put in 8-bit and 12-bit modes too).

The clock source itself is too complicated to explain as there are many options, best explained by the official Datasheet. Usually the system clock is used. Each t/c also has a prescaler that can be used to make it run slower than the system by a certain factor.

There are a few modes the counters can be in:

Simply counts up and does something when the timer overflows and wraps back to 0
Clear Timer on Compare Match (CTC)
Counts up, but wraps to 0 and does something when the value in ine of the control registers matches
Fast PWM
Counts up, on a first match the PWM signal is turned off, on a second or overflow it is turned back on.
Phase Correct PWM Mode, Timing Diagram
Counts up and instead of wrapping down again, the PWM signal is toggled at a comparison point.

For The Phase Correct PWM Mode, the center of the wave always stays in the same place, which is good for driving motors. It also effectively halfes the frequency of the t/c in addition to whatever the prescaler does.

PWM and Arduino

The Arduino has 6 PWM capable pins, each connected to one comparator channel of the three timers.

By default all of the t/cs are in 8-bit resolution Phase Correct PWM with clock division by 64 in the prescaler, wrapping at the 8-bit overflow (after 256 cycles), which reults in a PWM frequency of ~490Hz.

The exception here is t/c0 driving the counter behind the millis() function (code) is in Fast PWM Mode. This is why Arduino Pins 5 and 6 have double the frequency at ~980Hz.

Calculation of how to get from the System frequency to the PWM frequency.
16MHz / 64 / 2 / 256 = ~488Hz

Tone generation

The tone() function uses t/c2 which is why it messes up the PWM on Arduino Pins 3 and 11.

The underlying code adjusts the clock divider and Compare Registers to select an appropriate one for the desired frequency, and puts the timer into Clear Timer on Compare Match Mode to generate generate an unmodulated square wave.

Assumption: Based on how the code looks it is now capable of using any PWM pin for the tone generating and mess up the the corresponding other PWM pin. I've not bothered to prove or disprove this yet, do your own research.

Avoiding PWM flickering

When connecting an RGB LED to a combination of t/c0 and one of the other timers to generate pretty colors you might notice an annoying flicker which comes from you seeing some interference pattern that stems from the different modes.

Since you really shouldn't change the configuration of t/c0 you can put one of the other timers in Fast PWM mode (the LEDs don't care).

Example that put's t/c1 into Fast PWM mode given the Arduino default configuration.
// Set timer/counter 1 from 8-bit phase correct to 8-bit fast
bitWrite(TCCR1B, WGM12, 1);

See the blogpost on fixing PWM flicker for an explanation why it flickers.

Useful Resources