► Games

► Sound & Music

► Watches & Clocks


► Power Supplies

► Computers

► Graphics

► Thermometers

► Wearables

► Test Equipment

► Tutorials

► Libraries

► PCB-Based Projects

By processor

AVR ATtiny

► ATtiny10

► ATtiny2313

► ATtiny84

► ATtiny841

► ATtiny85

► ATtiny861

► ATtiny88

AVR ATmega

► ATmega328

► ATmega1284

AVR 0-series and 1-series

► ATmega4809

► ATtiny1604

► ATtiny1614

► ATtiny3216

► ATtiny3227

► ATtiny402

► ATtiny404

► ATtiny414

► ATtiny814

AVR DA/DB-series

► AVR128DA28

► AVR128DA32

► AVR128DA48

► AVR128DB28



► RP2040

► RA4M1

About me

  • About me
  • Twitter
  • Mastodon


RSS feed

Programming the ATtiny10 [Updated]

11th November 2017

This article describes how to program the ATtiny10, Microchip's diminuitive 6-pin processor, using the Arduino IDE. It's a great chip for building small gadgets and wearables, or designing interface logic for other projects, and it really lives up to its "tiny" name:


The following sections explain how to program the ATtiny10 in C, and how to download programs using a low-cost ISP programmer. It also illustrates some simple applications with example programs.


For full projects based on the ATtiny10 see the following examples:


If, like me, you like using the simplest possible chip for each application, the ATtiny10 will appeal to you [1]; it's a 6-pin processor, about the same size as an 0805 SMD resistor, and it costs about 35p/35¢. It packs in the following features:

  • Internal 8MHz clock, by default prescaled to 1MHz.
  • Three I/O lines.
  • Two 16-bit PWM analogue outputs.
  • Three 8-bit analogue inputs.
  • An analogue comparator.
  • A 16-bit timer with input capture and an event counter.
  • A watchdog timer.
  • 1024 bytes of program memory, 32 bytes of RAM, and no EEPROM.

All of these features will be familiar to users of the larger AVR chips. Here's the pinout (using Spence Konde's design conventions):


The internal oscillator is accurate to within 10%, but you can calibrate it in software to within 1%. You can configure RESET as a fourth I/O line, which prevents further programming, but I don't cover that in this article.

To work with the ATtiny10 on a breadboard you can mount it on a SOT23 breakout board, such as the one available from Sparkfun [2].

Programming the ATtiny10

Unlike the SPI protocol used to program the larger AVR chips, such as the ATmega328 in the Arduino Uno, the ATtiny10 uses a programming protocol called TPI (Tiny Programming Interface) which needs only five wires. Fortunately Thomas Fischl's excellent USBasp programmer supports this protocol [3]; you can build your own, order one from his site, or they are widely available on eBay [4], Banggood [5], etc. I recommend getting one with a 10-pin to 6-pin adapter for ISP programming. The current versions of the Arduino IDE support the ATtiny10, so you can program it in C and upload programs as easily as with the other AVR chips. Since an Arduino core would use up almost half of the available program memory the best way to program it is to access the registers directly, and I give an overview of how to do this in the section Alternatives to core functions below.

Here are instructions for programming the ATtiny10.

  • Install the ATtiny10Core by following the instructions on my GitHub repository ATtiny10Core.

This should add an ATtiny10Core heading to the Board menu.

  • Enter your program into the Arduino IDE editor.

For example, try the Blink example program given below.

  • Connect the USBasp to the ATtiny10 as shown in the following diagram:


Connecting the USBasp programmer to an ATtiny10.

  • Choose Board from the Tools menu, and select the ATtiny10/9/5/4 option under the ATtiny10Core heading; it's the only option.
  • Choose the chip you want from the Chip menu; for example ATtiny10.
  • Choose USBasp from the Programmer option on the Tools menu.
  • Choose Upload to upload the program.

The LED should blink at 0.5Hz.

Here's my test setup on a mini breadboard:


Testing the ATtiny10 Blink program on a mini breadboard, using the USBasp programmer.


Here are a couple of examples using the ATtiny10:


This is the ubiquitous Blink program:

void setup () {
  DDRB = 1;                       // PB0 as an output
  TCCR0A = 1<<COM0A0 | 0<<WGM00;  // Toggle OC0A, CTC mode
  TCCR0B = 1<<WGM02 | 3<<CS00;    // CTC mode; use OCR0A; /64
  OCR0A = 15624;                  // 1 second; ie 0.5Hz

void loop () {

To run it connect an LED to PB0 as follows:


Circuit using an ATtiny10 to blink an LED.

It uses Timer/Counter0 to divide the 1MHz system clock by a prescaler value of 64, and then by 15625, toggling the output PB0 with a period of 1 second.

Analogue frequency generator

The following program reads the voltage from a potentiometer on analogue input ADC1 (PB1), and then uses this to set the compare match register OCR0A of Timer/Counter0, to generate a square wave on PB0 whose frequency you can control with the potentiometer:

void setup () {
  DDRB = 1;                       // PB0 as an output
  // Set up ADC on PB2
  ADMUX = 1<<MUX0;                // ADC1 (PB1)
  ADCSRA = 1<<ADEN | 3<<ADPS0;    // Enable ADC, 125kHz clock
  // Set up waveform on PB0
  TCCR0A = 1<<COM0A0 | 3<<WGM00;  // Toggle OC0A, Fast PWM
  TCCR0B = 3<<WGM02 | 4<<CS00;    // Fast PWM with OCR0A as TOP; /256

void loop () {
  ADCSRA = ADCSRA | 1<<ADSC;      // Start
  while (ADCSRA & 1<<ADSC);       // Wait while conversion in progress
  OCR0A = ADCL;                   // Copy result to frequency output

Note that because we're changing the compare match value, we need to use Fast PWM mode in this application, because it double-buffers the compare match value. Here's the circuit:


Circuit using a potentiometer to adjust the frequency of a square wave generated by an ATtiny10.

It generates a frequency between 1MHz/256/256, or about 15Hz, and 1MHz/256/1, or 3.9kHz.

Alternatives to core functions

The following sections give some tips on programming the ATtiny10 to achieve some of the things provided by the Arduino core functions.


To specify whether pins are inputs or outputs you set the corresponding bits in the DDRB register to 0 or 1 respectively. For example, to define pins 1 and 3 as outputs (and leave the other pins as inputs):

DDRB = 0b0101;         // Equivalent to pinMode(1, OUTPUT); pinMode(3, OUTPUT);

Input pullups

Unlike the older AVR chips, such as the ATmega328 and ATtiny85, the ATtiny10 enables pullup resistors using a separate pullup register, PUEB. To set pullups on input pins you set the corresponding bits in this register. For example, to set a pullup resistor on input pin 2:

PUEB = 0b0010;         // Equivalent to pinMode(2, INPUT_PULLUP);

Note that it doesn't make sense to set a pullup on an output.


To set the state of an output you set the corresponding bits in the PORTB register. For example, to set bit 1 low and bit 3 high (assuming they have been defined as outputs):

PORTB = 0b0100;        // Equivalent to digitalWrite(1, LOW); digitalWrite(3, HIGH);

Changing the state of an input has no effect.


To read the state of the I/O pins you read the PINB register:

int temp = PINB;


You can use OC0A (PB0) and OC0B (PB1) for analogue output using PWM. You first need to configure the Timer/Counter into PWM mode for that pin; for example, using PB0:

TCCR0A = 2<<COM0A0 | 3<<WGM00; // 10-bit PWM on OC0A (PB0), non-inverting mode
TCCR0B = 0<<WGM02 | 1<<CS00;   // Divide clock by 1
DDRB = 0b0001;                 // Make PB0 an output

To write an analogue value we then need to write the value to the appropriate output compare register, OCR0A:

OCR0A = 1000;                  // Equivalent to analogWrite(0, 1000)

With a 5V supply this will set PB0 to 1000/1024 * 5V, or 4.88V.


To use an I/O pin for analogue input you first need to configure the Analogue-to-Digital Converter. For example, to use ADC0:

ADMUX = 0<<MUX0;               // ADC0 (PB0)
ADCSRA = 1<<ADEN | 3<<ADPS0;   // Enable ADC, 125kHz clock

To read an analogue value from the pin we then need to start a conversion, and when the conversion is ready read the ADC register:

ADCSRA = ADCSRA | 1<<ADSC;     // Start
while (ADCSRA & 1<<ADSC);      // Wait while conversion in progress
int temp = ADCL;               // Copy result to temp


For a simple substitute for delay() you can use a loop adjusted to give roughly the right timing:

void delay (int millis) {
  for (volatile unsigned int i = 34*millis; i>0; i--);

This would provide an alternative way of writing the Blink program. Note that the counter variable i must be defined as volatile or the compiler will optimise it out of the loop, eliminating the delay.

For more accurate delays, and to implement timers like millis(), you could set up Timer/Counter0 as a timer, or use the Watchdog Timer.


6th March 2021: Updated the description to refer to the new ATtiny10Core and Boards Manager installation, designed to solve problems using the ATtiny10Core on recent versions of the Arduino IDE. I've removed comments relating to this from the discussion below to avoid confusion.

11th January 2022: I've updated the link to the ATtiny10 datasheet to point to the latest 2018 version. The previous 2016 version described the operation of I/O port pullups incorrectly.

  1. ^ ATtiny10 Datasheet on Microchip.
  2. ^ Sparkfun SOT23 to DIP Adapter on Sparkfun.
  3. ^ USBasp - USP programmer for Atmel AVE controllers on
  4. ^ USBASP ISP Programmer Cable Adapter from Boos Bits on eBay.
  5. ^ USBASP 3.3 5V AVR Downloader Programmer on Banggood.

blog comments powered by Disqus