Topics

► Games

► Sound & Music

► Clocks

► GPS

► Power Supplies

► Tools

► Tutorials

By processor

► ATtiny10

► ATtiny85

► ATtiny84

► ATtiny841

► ATtiny2313

► ATtiny861

► ATmega328

► ATmega1284

About me

  • About me

Donate

Feeds

RSS feed

Programming the ATtiny10

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:

ATtiny10.jpg

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.

Introduction

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):

ATtiny10Pinout.gif

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 step-by-step instructions for programming the ATtiny10; it will work with the Arduino IDE versions 1.8 and upwards:

  • Download the ATtiny10Core hardware configuration from my repository on GitHub ATtiny10Core.
  • Copy it to the hardware folder in your Arduino folder in your Documents folder. If there isn't already a hardware folder there, create one first.
  • Restart the Arduino IDE.

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:

ATtiny10USBasp.gif

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:

USBasp.jpg

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

Examples

Here are a couple of examples using the ATtiny10:

Blink

This is the ubiquitous Blink program:

#include <avr/io.h>
#include <stdint.h>

int main (void) {
  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
  while (1);
}

To run it connect an LED to PB0 as follows:

ATtiny10USBasp2.gif

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:

#include <avr/io.h>
#include <stdint.h>

int main (void) {
  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
  // Main loop
  for (;;) {
    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:

ATtiny10USBasp3.gif

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.

includes

You need to add these includes at the start of your program to include the AVR register definitions and standard C++ routines:

#include <avr/io.h>
#include <stdint.h>

setup and loop

Arduino programs are normally written with the initialization in setup() and the main program in loop(), rather than the standard int main() function required by C. If you want to keep to this convention you'll need to add the following definition at the end of your program:

int main() {
  setup();
  for(;;) loop();
}

pinMode

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.

digitalWrite

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.

digitalRead

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

int temp = PINB;

analogWrite

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.

analogRead

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

delay

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.


  1. ^ ATtiny10 Datasheet on Microchip.
  2. ^ Sparkfun SOT23 to DIP Adapter on Sparkfun.
  3. ^ USBasp - USP programmer for Atmel AVE controllers on www.fischl.de.
  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