Saturday, January 9, 2021

millis and micros library for AVR microcontrollers (ATmega328P) - milliseconds and microseconds time tracking

Having precise timing in microcontrollers is important in many projects. For this you can use the two libraries presented in this article - millis & micros.

millis library triggers a timer interrupt every 1 millisecond and increments the milliseconds variable. The user can select the size of the milliseconds variable ranging from char (8 bits) to long long (64 bits) with and overflow from 255 milliseconds to 584.9 million years.

micros library is almost the same as millis but for microseconds. The overflow is between 255 microseconds and 584942 years.

For both libraries the user can select which timer to use: Timer0, Timer1 or Timer2.

In the case of millis library it is recommended that all the active ISR must take less than 1 millisecond to complete otherwise the millis timer interrupt will be delayed. For the micros the ISRs must finish in less than 100 microseconds. The faster the CPU clock the better.

For the milliseconds and microseconds variable decide if you really need a 32 or 64 variable (long and long long) because the bigger the variable the longer it takes to increment it. For example on a 1MHz CPU it takes about 77 microseconds to increment a long long variable.

millis & micros library for AVR microcontrollers

 

millis & micros functions

By default the libraries are set to use Timer 0 and unsigned long variable type for time tracking but these can be changed inside the header file as shown below.

Library setup

 
/*************************************************************
	USER SETUP SECTION
**************************************************************/

/*
* Milliseconds data type
* Data type			- Max time span			- Memory used
* unsigned char			- 255 milliseconds		- 1 byte
* unsigned int			- 65.54 seconds			- 2 bytes
* unsigned long			- 49.71 days			- 4 bytes
* unsigned long long	        - 584.9 million years	        - 8 bytes
*/
typedef unsigned long millis_t; // on 1MHz CPU it takes around 77uS to increment long long variable

#define MILLIS_TIMER0 		0 // Use timer0
#define MILLIS_TIMER1 		1 // Use timer1
#define MILLIS_TIMER2 		2 // Use timer2

#define MILLIS_TIMER 		MILLIS_TIMER0 // Which timer to use

/*************************************************************
	--END OF USER SETUP SECTION
**************************************************************/

For "micros.h" file the time resolution can be changed by modifying the MICROS_RESOLUTION constant. For "millis.h" time resolution is 1 millisecond.

Library initialization


millis_init() & micros_init()

After including the library run the initialization function. This will set the timer and OCRA register depending on the F_CPU and will enable the global interrupts.

Library functions

 

millis_get() & micros_get()

Returns the number of milliseconds / microseconds since the init function was executed or since the last overflow of the variable.

Dealing with millis or micros overflows

Depending on the millis_t data type (uint8_t, uint16_t, uint32_t or uint64_t) the global variable milliseconds that holds the time, will eventually reach it's maximum value and then overflow back to 0.

Most of the time you need to use this functions like this:

uint16_t interval = 1000;
uint16_t lastTime = 0;

if(micros() - lastTime >= interval){
    lastTime = micros();
    blink_led(); // 1000ms interval (1s)
}

After the overflow the micros() will return smaller values than lastTime and the subtraction result will be negative and you don't want that. To solve this issue, simply cast the result to an unsigned integer like this:

uint16_t interval = 1000;
uint16_t lastTime = 0;

if((micros_t) micros() - lastTime >= interval){
    lastTime = micros();
    blink_led(); // 1000ms interval (1s)
}

For more details consider this link https://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover/12588#12588.

millis_pause() & micros_pause()

Pause and turn off timer to save power.

millis_resume() & micros_resume()

Turn the timer back on.

millis_reset() & micros_reset()

Reset milliseconds / microseconds count to 0.

millis_add() & micros_add()

Add microseconds / milliseconds.

millis_subtract() & micros_subtract()

Subtract microseconds / milliseconds.

Error, OCRA and prescaler tables based on CPU frequency for 1 millisecond timer interrupt - Timer0, Timer1 and Timer2


Error, OCRA and prescaler tables based on CPU frequency and time resolution in microseconds timer interrupt - Timer0, Timer1 and Timer2

micros spreadsheet error and OCR calculator based on time resolutionmicros spreadsheet error and OCR calculator based on time resolution

Notice the error is 0% for most CPU frequencies however the time accuracy will also depend on how accurate the RC oscillator or crystal will be.


Download

Change log and license can be found at the beginning of the file
v1.0 millis.h
v1.1 micros.h

micros spreadsheet error and OCR calculator based on time resolution
Changelog - micros.h
v1.1 now the time resolution can be changed to any value in microseconds. Previously it was set to only 100us

No comments:

Post a Comment