Getting Started With the ATtiny85: The Little Microcontroller That Could

The ATtiny85 is a cute little AVR microcontroller that might surprise you with what it can do. We will be considering the 8 pin PDIP version shown above, since we can easily stick it in a breadboard and prototype away. In this project, we will get our toolchain up and running for the first time, and flash some code to blink an LED!

From the datasheet we see that we have 8k of flash for our program and 512 bytes of SRAM for our dynamic memory. We won’t be able to implement huge systems with these memory resources, but this will work nicely for smaller projects that would leave many wasted pins and resources on a larger chip such as an ATmega. We get two 8 bit timer/counters, one high speed, with 4 PWM outputs total. For analog signals we have a 4 channel 10 bit ADC to use. There are some key features left out, such as dedicated USART, SPI, and I2C hardware, which would need to be implemented in software if necessary. This may limit the scope of what this little chip can do, but for a less demanding project, it can do the business.

While there are many options for programming this chip, I will use the avr-gcc compiler, which can be easily installed on a Windows machine with the Win-AVR software. I will be using SparkFun’s AVR Pocket Programmer, a USB Tiny clone, to flash the chip. You should be able to use any USB Tiny programmer and follow along at home with this tutorial.

We can see from the pinout above how the resources are spread across the limited 8 pins. Our first program will, as it should, blink an LED.

Above is the wiring diagram for our project, with the AVR Programmer output on the left, and the ATtiny85 with LED and series resistor on the right.

Here is a picture of the setup, note that the color scheme for the wiring matches the wiring diagram.

Lets look at the code:

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h> 

int main(void) 
{
  // inits
  DDRB |= (1 << PB3);   // set data direction register for PB3 as output  
  
  // event loop
  while (1)
 {
    PORTB |= (1 << PB3);   // turn on led
    _delay_ms(250);      // delay 250 ms

    PORTB &= ~(1 << PB3);  // turn off led
    _delay_ms(250);      // delay 250 ms
  }                                                
  return (0);                           
}

The ATtiny85 has an 8 MHz internal RC oscillator that can be used as the default clock. We will be setting the fuses for the ATtiny85 in the makefile (later on) to the default setting of divide clock by 8. This means that the actual clock reference for our system will be 1 MHz. We begin our code with a #define that sets F_CPU to be this actual clock speed, which will be referenced by delay.h for our system’s delay timing.

We include io.h, which contains macros we will use to refer to the registers in the chip, such as DDRB, PORTB, and PB3. We also include delay.h in order to use the _delay_ms() function to delay a certain number of milliseconds.

Moving into the main function, we see first set the bit in the data direction register b (DDRB) for PB3 to 1. This sets up pin PB3 of the ATtiny as an output that we can use to drive our LED. Let’s take a moment to understand the |= and << operators and how we use them to set the bit in DDRB.

The DDRB is an 8 bit register, so we can think of its state as a byte containing  1’s and 0’s. Where there is a 1 corresponds to setting the pin on the register PORTB as an output. We connected our LED to PB3, or pin 3, on PORTB, so we need to set bit 3 in the DDRB byte as a 1. The io.h include has the convenience macro ‘PB3″ which changes instances of ‘PB3’ in the code to 3 when compiled. The << operator is the bitwise left shift operator. The code (1 << 3) takes the bit 1 (00000001) and shifts it to the left 3 times (00001000), yielding a byte with 1 in the 3rd bit and 0’s elsewhere. Next we logical OR this byte to the DDRB byte, which sets only the third bit in DDRB as 1, while leaving the other bits alone (xxxxxxxx | 00001000 = xxxx1xxxx , where x represents an untouched DDRB bit). By using the |= operator we do the logical OR of the bytes and set the result to DDRB. This is equivalent to just using the statement DDRB = 0b00001000;.

Next we have an infinite while loop, which is where our event code will be contained and looped indefinitely. Inside the loop, we first set the LED on, delay for 250 ms, set the LED off, delay for 250 ms, and then loop back to the beginning of the while loop.

We turn on the LED by setting the third bit in PORTB to 1, outputting a high voltage from PB3. Setting the bit on PORTB is done in the same manner as we explained for setting a bit in DDRB.

Next we call our convenient _delay_ms(250), which halts the code until 250 ms have elapsed.

Next we turn off the LED by clearing the third bit on PORTB to 0. By taking the complement of 1 << 3 = 00001000, we get 11110111, which we logical AND with PORTB to clear only bit 3, while leaving the other bits unchanged (xxxx1xxx &  xxxx0xxx = xxxx0xxx , where x represents an untouched DDRB bit).

After turning off the LED we call the delay function again to delay for 250 ms and loop back to the beginning of the while loop.

As a habit, I have included the return(0) at the end of the code to quell any possible compiler warnings or errors, though this part of the code is never actually reached.

To compile and flash our code to the ATtiny85 we will use a makefile. There are many resources online to generate a makefile, or to just borrow one for a specific device and toolchain. You can find makefile generators online for free and also fuse calculators such as engbedded.com.

DEVICE      = attiny85
CLOCK       = 8000000
PROGRAMMER  = -c usbtiny 
OBJECTS     = blink.o
FUSES       = -U lfuse:w:0x62:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m 
AVRDUDE     = avrdude $(PROGRAMMER) -p $(DEVICE)
COMPILE     = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)

all:	blink.hex

.c.o:
	$(COMPILE) -c $< -o $@

.S.o:
	$(COMPILE) -x assembler-with-cpp -c $< -o $@

.c.s:
	$(COMPILE) -S $< -o $@

flash:	all
	$(AVRDUDE) -U flash:w:blink.hex:i

fuse:
	$(AVRDUDE) $(FUSES)

make: flash fuse

load: all
	bootloadHID blink.hex

clean:
	rm -f blink.hex blink.elf $(OBJECTS)

blink.elf: $(OBJECTS)
	$(COMPILE) -o blink.elf $(OBJECTS)

blink.hex: blink.elf
	rm -f blink.hex
	avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
	avr-size --format=avr --mcu=$(DEVICE) blink.elf

The fuse settings were generated to set the divide by 8 setting, such that the system clock  will be running at 1 MHz.

With our programmer plugged in, we can use the command line to go to the directory containing the c source code and make file, and simply type in ‘make flash’ and press enter. AVRDUDE will show you the compiling and flashing details, and when finished will leave your LED blinking away.

I didn’t have a logic analyzer or oscilloscope with me, but I did have my Basys2 FPGA with a period counter circuit on it to measure the period of the blinking waveform. We can see that we get a period of 1E4 (hex) ms or 484 ms. In our code we intend to delay 500 ms total, so 484 ms is a little off, but is within the 10% (quite bad) spec of the factory set internal RC oscillator. If we needed a more accurate blinking frequency we could use an external crystal oscillator.

I hope this helped you to learn about how to get an ATtiny85 up and running for the first time.

2 thoughts on “Getting Started With the ATtiny85: The Little Microcontroller That Could

Leave a comment