Borrowing an Arduino Timer

TimerIf you want to use a microcontroller timer for your own purposes and you are using Arduino you might find that you need to borrow a timer from the Arduino core and then give it back.

The ATtiny85 (and lower memory ATtiny45, ATtiny25 variants) only have two timers and one is used by Arduino to keep track of time. This post explains how you can borrow that timer from Arduino and give it back.

Microcontroller timer counters

Microcontrollers contain a fixed number of timer-counter modules which can be used for various tasks such as driving a PWM output on a pin, driving a Universal Serial Interface (USI), or calling an interrupt to keep track of time passing. The ATTiny85 has two such timers Timer/Counter0 and Timer/Counter1.

The ATtiny85 Timer/Count0 has two Timer Counter Control Registers called TCCR0A and TCCR0B. These registers control the clock source, the prescaler, the output compare units and the PWM waveform generator.

The counter value for Timer/Counter0 is stored in the Timer Counter Register TCNT0.

Timer counter interrupts

The ATtiny Arduino core is using Timer/Counter0 and the Timer/Counter0 Overflow interrupt, TIMER0_OVF, to keep track of time for millis() and micros(). You can verify this by looking at the wiring.c source code in your Arduino core.

Timer/Counter0 can trigger two other interrupts, Compare Match A and Compare Match B (TIMER0_COMPA and TIMER0_COMPB) which are still available to use for your purposes. These trigger when Timer/Counter0 hits the value specified in Output Compare Registers OCR0A or OCR0B.

You can avoid triggering the TIMER0_OVF interrupt, used by Arduino, from being inadvertently called by disabling the overflow interrupt vector.

TIMSK &= ~(1<<TOIE0);

And enabling it when you are finished.

TIMSK |= 1<<TOIE0;

Or by using a Compare Match and never letting Timer/Counter0 overflow.

When we borrow Timer/Count0 for a little while, say to do accurate timing for a serial or IR input, we will probably need to change the timer counter control registers TCCR0A and TCCR0B.

So we can save these values along with the current counter value, TCNT0, before we borrow Timer/Counter0:

// Save Timer/Counter0 values
oldTCCR0B = TCCR0B;
oldTCCR0A = TCCR0A;
oldTCNT0 = TCNT0;

And restore them when we are finished:

//Restore old Timer/Counter0 values
TCCR0A = oldTCCR0A;
TCCR0B = oldTCCR0B;
TCNT0 = oldTCNT0;

Lost Time

This simple approach is perfectly acceptable, but there will be a small amount of time lost. For example to receive a byte of serial at 9600 baud takes approximately one millisecond, so every time we borrowed Timer/Counter0 to receive a byte, Arduino millis() would lose a millisecond.

If you were using millis() for something like button debouncing, where you want to ensure a button stays down for 50 milliseconds before registering a click, then losing a millisecond here and there isn’t going to make much difference to your application.

However if you really want Arduino millis() and micros() to remain accurate then we will need to fix up Arduino’s internal record of time before returning control to Arduino.

Arduino millis and micros

Looking at wiring.c there are two variables used to record the passage of time that we can gain access to using extern.

extern volatile unsigned long timer0_millis;
extern volatile unsigned long timer0_overflow_count;

The first variable, timer0_millis is the number that millis() returns, it is incremented by MILLIS_INC every time Timer/Counter0 overflows.

MILLIS_INC is defined in wiring.c as follows, you will need to redefine these in your code.

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)

The second variable, timer0_overflow_count, is used in combination with the Timer/Counter0 counter value, TCNT0, to calculate the value returned by micros(), which we can summarize as:

((timer0_overflow_count<<8) + TCNT0) * (64 / clockCyclesPerMicrosecond())

Here timer0_overflow_count*256 + TCNT0 gives us the recorded number of Timer/Counter0 increments.

The Arduino core sets the prescaler to 64 which causes Timer/Counter0 to increment once every 64 clock cycles, so this is multiplied by 64 to get the number of clock cycles and divided by clockCyclesPerMicrosecond() to get the number of microseconds.

Recovering lost time

To adjust the Arduino time we need to adjust Arduino’s record of the number of Timer/Counter increments represented by timer0_overflow_count*256 + TCNT0. We need to increment this by the number of microseconds borrowed, delta, multiplied by clockCyclesPerMicrosecond(), to get the number of clock cycles, divided by 64, to get the number of timer increments.

unsigned int newCounter = oldTCNT0 +
    delta / (64 / clockCyclesPerMicrosecond() );
timer0_overflow_count += (newCounter>>8);
TCNT0 = (byte)newCounter;

And for each time that Timer/Counter0 would have overflowed, we need to increment timer0_millis by MILLIS_INC.

unsigned long m = timer0_millis;
m += MILLIS_INC * (newCounter>>8);
timer0_millis = m;

This approach will allow you to fix up Ardunio time to the nearest Timer/Counter0 tick which is 8 microseconds for an ATTiny85 running at 8Mhz.

Arduino PWM MOSFET Gate Resistor

N-channel_mosfet

If you are driving a logic level MOSFET directly from an Arduino, or another Atmel AVR such as an ATTiny85, you may have wondered what value resistor should be placed between the output pin and the MOSFET Gate.

In the tutorials referenced by Adafruit and Sparkfun they connect an Arduino output pin directly to the MOSFET gate. But others insist that you need a current limiting resistor to protect the microcontroller output pin.

Voltage controlled device

A MOSFET is normally thought of as a voltage controlled device, sufficient voltage at the gate opens the MOSFET and allows a large current to flow through the drain/source. In this model very little current is required at the gate. So with this simplistic model of MOSFET operation a current limiting resistor is not required.

Charging the gate

However during switching a MOSFET behaves more like a capacitor that needs to be charged in order to open and discharged in order to close. The datasheet for a STP16NF06L 16A 60V logic level  MOSFET shows a 345nF input capacitance and a 7.3 nC total gate charge. Coulombs are a measure of capacity, current time in units of amp seconds. The datasheet quotes a 47nS on/rise time with a 4.7 ohm gate resistor, so it took an average of 7.3nC/47nS = 155mA average current to charge the gate – that’s far more than the 40mA maximum current for an Atmel AVR output pin. So with this in mind it’s important to protect the microcontroller with a current limiting resistor.

Charging speed

A MOSFET is an efficient switch when completely on or off,  but it is inefficient when switching and dissipates those losses as heat. The more resistance we put between the microcontroller output pin and the MOSFET gate, the more slowly the gate will charge and the hotter the MOSFET will run. This becomes particularly important if you are switching the MOSFET on and off quickly with a PWM output.

PWM stands for Pulse Width Modulation and is a method of controlling the average voltage output of a microcontroller pin by turning the output on (5V) and off (0V) very quickly. The duty load on an Arduino PWM pin can be set with analogWrite and ranges from zero, always off, to 255, always on. Set to 63 the pin would be on a quarter of the time so the average voltage would be 5V/4 = 1.25V.

LEDs have very fast switching times, approximately 20 nanoseconds for white LEDs, so when a PWM output is used to control the brightness of a LED the duty load determines what percentage of time the LED is turned on. As long the PWM frequency is greater than 200Hz the human eye cannot detect any flicker.

Microcontroller source resistance

If you look at an Atmel datasheet you will find a graph showing IO PIN OUPUT VOLTAGE vs. SOURCE CURRENT.

At 25 Celsius this this shows a nice straight line from 5V at 0mA to 4.5V at 20mA.

IOPinOutputVoltage

What this tells us is that the output pins have an internal resistance within the microcontroller,  (5V – 4.5V)/20 mA, which is 25 ohms. So the maximum theoretical current we can attempt to source with the microcontroller running at 5V is 5V/25ohms which is 200mA. This is considerably higher than the 40mA absolute maximum in the data sheet.

To get down to 40mA we need to add a 100 ohm resistor, 5V/125ohms = 40mA. To get down to the 20mA level, recommended by Arduino, would require 225 ohms, 5V/250ohms = 20mA, the nearest standard resistor value of 220 ohms is close enough.

A real world test

To see what really happens I hooked an ATTiny85 8kHz PWM output pin to STP16NF06L logic level MOSFET through a 100ohm resistor. I used an oscilloscope to measure the voltage with respect to ground on both sides of the resistor.

An oscilloscope can only measure voltage directly, but by measuring the voltage drop across the resistor we can calculate the current flowing. I used the math function of the oscilloscope to display the difference between the two signals.

scope_diff

This showed a maximum 2.5V voltage drop across the resistor during turn on and a maximum 3V voltage drop during turn off. So the current flowing was 2.5V/100ohms = 25mA during turn on and 3V/100ohms = 30mA during turn off, both under the 40mA absolute maximum. It’s a good idea to drive components below their absolute maximum ratings, so it’s nice to see that we are below 40mA.

So in summary a 100 ohm resistor between an Arduino or similar Atmel AVR output pin and a logic level MOSFET looks like a good balance between switching speed and protecting the output pin from damage.