Project tutorial
Custom Timer Functions on Arduino Nano

Custom Timer Functions on Arduino Nano © GPL3+

Use the ATmega328P's 8-bit timer and prescaler to implement custom timed events, rather than relying on millis() or delay().

  • 12 respects

Components and supplies

Apps and online services

About this project

Time-Sensitive Tasks

Nearly all projects that utilize a microcontroller have some kind of time-dependent component, such as a delay or repeating task. Internal timer circuits enable this functionality by counting up on every pulse it gets, either from a prescaler or a clock directly.

By getting the value of this counter, you can determine how much time has elapsed. For example, if an MCU's clock is set to 125KHz, one of its timer is set to use that clock, and its prescaler is set to 1/1024, then each increment of its counter register is equal to about 1/122 seconds, which is derived from:

t = 1 / (CLK / prescaler)

so (1/122) = 1 / (125000 / 1024)

In case you are curious, the job of the prescaler is to divide incoming clock pulses by a certain value, which slows down the counter by that factor. So a timer that has a prescale value of 4 will see a system clock of 8Mhz as 2MHz instead. Arduino's millis(), delay(), and micros() all rely on these timers to operate. But there is an issue: delay() is blocking, and to make it non-blocking, you have to check the millis()'s value in each loop.

Using Interrupts Instead

To avoid this issue, the ATmega328P's timers can be set to trigger interrupts on several different triggers. One of these is the overflow flag, which is set whenever the counter register rolls over to 0 from its max value, such as an 8-bit register going from 255 to 0. Another way to trigger an interrupt is by using compare registers, which store a value that is continually checked against against the counter, and raises an interrupt whenever that value is reached by the counter. Microcontrollers such as the ATmega328P use this kind of function to control PWM on pins, and other, more advanced MCUs, are able to directly toggle pins from the timer without needing the CPU at all.

Setting Up the Hardware

For this example, I created a simple program that sets a compare value for the ATmega328P's Timer/Counter2, fires an interrupt on compare match A, and toggles the value of a pin. All of the details for this can be found in the microcontroller's datasheet. The code starts by calling the hardware_setup() function, in which several registers are set to configure the system, timer, and pin. Digital pin 2 is set as an output by placing the value of (1 << DDD2) into the DDRD register. Next, timer 2's prescaler is set by putting ones into the clock select bit fields for the TCCR2B register, setting the prescaler to 1/1024.

Next, a value of 255 is placed into the compare register A (OCR2A), which means that an event will occur when the counter gets to 255. The TIMSK2 register gets a value of (1 << OCIE2A) placed into it, which lets timer 2 output an interrupt when compare match A is triggered. Finally, TCCR2A gets a value of (1 << COM2A0) that toggles D12 on each compare match as well.

Program Execution and Final Thoughts

Although we set the TIMSK2 register to trigger an interrupt, it still needs to be handled. This is accomplished by creating an ISR (Interrupt Service Routine) that will trigger when the interrupt is raised. In the attached code, the ISR increments a counter and clears the flag. In the while loop in main(), the counter variable is checked to see when it reaches 100, and if it does, then the value for D2 is toggled by performing an XOR operation like so: PORTD ^= (1 << PORTD2);

It is important to make the count variable volatile because it tells the compiler that its value can be changed at any time outside of the program's normal path of execution. The program should be flashed via a programmer to the Nano. Don't use Arduino functions with custom timers, as this can mess up both your timer and the builtin functions.

With this code, an LED attached to pin 2 should blink every 2.5 seconds. Try changing different values or setting up other timers in various modes.


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

volatile uint8_t count = 0;

void hardware_setup(void);

int main(void)
    while (1) 
		if(count == 100){
			PORTD ^= (1 << PORTD2);		// toggle LED on D2
			count = 0;

	TIFR2 &= ~(1 << OCF2A);		// lower flag

void hardware_setup(void)
	CLKPR = (1 << CLKPCE) + (0b111);	// System clk prescaler to 1/128
	DDRD |= (1 << DDD2);	// set pin 2 to output
	TCCR2B = (1 << CS22) + (1 << CS21) + (1 << CS20);	// set to 1/1024 prescaler
	OCR2A = 255;	// output when counter gets to 255
	TIMSK2 = (1 << OCIE2A);		// enable interrupt for match on A
	TCCR2A = (1 << COM2A0);		// also toggle D12
	//ASSR |= (1 << AS2);


Schematic 3uykjrpmzb


Similar projects you might like

Retro View Timer

Project tutorial by yilmazyurdakul

  • 3 respects

Arduino Nano Clock with 4x64 LED Matrix (new version)

Project tutorial by M.V.P.

  • 74 respects

More than an Hour Timer

Project tutorial by Ian Cumming

  • 10 respects

Serial Timer

Project in progress by scardeath0101

  • 2 respects

Arduino Countdown Timer

Project tutorial by tylerpeppy

  • 22 respects

7-Segment Clock with Arduino Nano + DS3231 + LDR

Project tutorial by Ingo Lohs

  • 42 respects
Add projectSign up / Login