Related
I'm testing the library ESP32TimerInterrupt for ESP32 using the example ISR_16_Timars_Array. While compiling I get errors of missing declarations about the method timer_isr_callback_add and the parameters TIMER_INTR_T0 and TIMER_INTR_T1. Although all the h files included with the library are available to the example, I assume I miss some file inclusion - which one?
And another question - I'm using the board Heltec WiFILoRa 32(V2). To minimize interference with other HW/SW features I prefer using timer 2 - is it possible?
Best regards
Yons Gy
The error messages:
Arduino: 1.8.19 (Windows 10), Board: "WiFi LoRa 32(V2), Disabled, 240MHz (WiFi/BT), 921600, None, REGION_EU868, None"
In file included from D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ISR_16_Timers_Array.ino:23:0:
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'bool ESP32TimerInterrupt::setFrequency(const float&, bool (* const&)(void*))':
ESP32TimerInterrupt.hpp:329:100: error: 'timer_isr_callback_add' was not declared in this scope
timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::detachInterrupt()':
ESP32TimerInterrupt.hpp:371:66: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:371:82: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::disableTimer()':
ESP32TimerInterrupt.hpp:380:66: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:380:82: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::reattachInterrupt()':
ESP32TimerInterrupt.hpp:390:65: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:390:81: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::enableTimer()':
ESP32TimerInterrupt.hpp:400:65: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:400:81: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
exit status 1
'timer_isr_callback_add' was not declared in this scope
The code (first - ESP32TimerInterrupt.hpp - library file), then the .ino file:
/****************************************************************************************************************************
ESP32TimerInterrupt.hpp
For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
Written by Khoi Hoang
Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
Licensed under MIT license
The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
The timer counters can be configured to count up or down and support automatic reload and software reload.
They can also generate alarms when they reach a specific value, defined by the software.
The value of the counter can be read by the software program.
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
Therefore, their executions are not blocked by bad-behaving functions / tasks.
This important feature is absolutely necessary for mission-critical tasks.
Based on SimpleTimer - A timer library for Arduino.
Author: mromani#ottotecnica.com
Copyright (c) 2010 OTTOTECNICA Italy
Based on BlynkTimer.h
Author: Volodymyr Shymanskyy
Version: 2.0.1
Version Modified By Date Comments
------- ----------- ---------- -----------
1.0.0 K Hoang 23/11/2019 Initial coding
1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
*****************************************************************************************************************************/
#pragma once
#ifndef ESP32TIMERINTERRUPT_HPP
#define ESP32TIMERINTERRUPT_HPP
#if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM )
#define USING_ESP32_S2_TIMERINTERRUPT true
#elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) )
#define USING_ESP32_S3_TIMERINTERRUPT true
#elif ( ARDUINO_ESP32C3_DEV )
#define USING_ESP32_C3_TIMERINTERRUPT true
#elif defined(ESP32)
#define USING_ESP32_TIMERINTERRUPT true
#else
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
#endif
#ifndef ESP32_TIMER_INTERRUPT_VERSION
#define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.0.1"
#define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2
#define ESP32_TIMER_INTERRUPT_VERSION_MINOR 0
#define ESP32_TIMER_INTERRUPT_VERSION_PATCH 1
#define ESP32_TIMER_INTERRUPT_VERSION_INT 2000001
#endif
#ifndef TIMER_INTERRUPT_DEBUG
#define TIMER_INTERRUPT_DEBUG 0
#endif
#if defined(ARDUINO)
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#endif
#include "TimerInterrupt_Generic_Debug.h"
#include <driver/timer.h>
#include "SimpleTimer.h" // https://github.com/jfturcot/SimpleTimer
/*
//ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h:
#define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups
//#brief Selects a Timer-Group out of 2 available groups
typedef enum
{
TIMER_GROUP_0 = 0, //!<Hw timer group 0
TIMER_GROUP_1 = 1, //!<Hw timer group 1
TIMER_GROUP_MAX,
} timer_group_t;
//#brief Select a hardware timer from timer groups
typedef enum
{
TIMER_0 = 0, //!<Select timer0 of GROUPx
TIMER_1 = 1, //!<Select timer1 of GROUPx
TIMER_MAX,
} timer_idx_t;
//#brief Decides the direction of counter
typedef enum
{
TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low
TIMER_COUNT_UP = 1, //Ascending Count from Zero
TIMER_COUNT_MAX
} timer_count_dir_t;
//#brief Decides whether timer is on or paused
typedef enum
{
TIMER_PAUSE = 0, //Pause timer counter
TIMER_START = 1, //Start timer counter
} timer_start_t;
//#brief Decides whether to enable alarm mode
typedef enum
{
TIMER_ALARM_DIS = 0, //Disable timer alarm
TIMER_ALARM_EN = 1, //Enable timer alarm
TIMER_ALARM_MAX
} timer_alarm_t;
//#brief Select interrupt type if running in alarm mode.
typedef enum
{
TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode
//TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now
TIMER_INTR_MAX
} timer_intr_mode_t;
//#brief Select if Alarm needs to be loaded by software or automatically reload by hardware.
typedef enum
{
TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event
TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event
TIMER_AUTORELOAD_MAX,
} timer_autoreload_t;
//#brief Data structure with timer's configuration settings
typedef struct
{
bool alarm_en; //Timer alarm enable
bool counter_en; //Counter enable
timer_intr_mode_t intr_type; //Interrupt mode
timer_count_dir_t counter_dir; //Counter direction
bool auto_reload; //Timer auto-reload
uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536.
} timer_config_t;
*/
class ESP32TimerInterrupt;
typedef ESP32TimerInterrupt ESP32Timer;
#if USING_ESP32_C3_TIMERINTERRUPT
#define MAX_ESP32_NUM_TIMERS 2
#else
#define MAX_ESP32_NUM_TIMERS 4
#endif
#define TIMER_DIVIDER 80 // Hardware timer clock divider
// TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
// In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h
// typedef bool (*timer_isr_t)(void *);
//esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *arg, int intr_alloc_flags);
//esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num);
//timer_deinit(timer_group_t group_num, timer_idx_t timer_num);
//esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask);
//esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask);
typedef bool (*esp32_timer_callback) (void *);
// For ESP32_C3, TIMER_MAX == 1
// For ESP32 and ESP32_S2, TIMER_MAX == 2
typedef struct
{
timer_idx_t timer_idx;
timer_group_t timer_group;
//int alarm_interval;
//timer_autoreload_t auto_reload;
} timer_info_t;
class ESP32TimerInterrupt
{
private:
timer_config_t stdConfig =
{
.alarm_en = TIMER_ALARM_EN, //enable timer alarm
.counter_en = TIMER_START, //starts counting counter once timer_init called
.intr_type = TIMER_INTR_MAX,
.counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value
.auto_reload = TIMER_AUTORELOAD_EN, // reloads counter automatically
.divider = TIMER_DIVIDER
};
timer_idx_t _timerIndex;
timer_group_t _timerGroup;
uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1
uint8_t _timerNo;
esp32_timer_callback _callback; // pointer to the callback function
float _frequency; // Timer frequency
uint64_t _timerCount; // count to activate timer
//xQueueHandle s_timer_queue;
public:
ESP32TimerInterrupt(const uint8_t& timerNo)
{
_callback = NULL;
if (timerNo < MAX_ESP32_NUM_TIMERS)
{
_timerNo = timerNo;
#if USING_ESP32_C3_TIMERINTERRUPT
// Always using TIMER_INTR_T0
_timerIndex = (timer_idx_t) ( (uint32_t) 0 );
// timerNo == 0 => Group 0, timerNo == 1 => Group 1
_timerGroup = (timer_group_t) ( (uint32_t) timerNo);
#else
_timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX);
_timerGroup = (timer_group_t) (_timerNo / TIMER_MAX);
#endif
}
else
{
_timerNo = MAX_ESP32_NUM_TIMERS;
}
};
// frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool setFrequency(const float& frequency, const esp32_timer_callback& callback)
{
if (_timerNo < MAX_ESP32_NUM_TIMERS)
{
// select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now.
// Will use later if very low frequency is needed.
_frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000;
_timerCount = (uint64_t) _frequency / frequency;
// count up
#if USING_ESP32_S2_TIMERINTERRUPT
TISR_LOGWARN3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#elif USING_ESP32_S3_TIMERINTERRUPT
// ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers
// and 54-bit auto-reload-capable up/down-timers
TISR_LOGWARN3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#else
TISR_LOGWARN3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#endif
timer_init(_timerGroup, _timerIndex, &stdConfig);
// Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP
timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency);
// enable interrupts for _timerGroup, _timerIndex
timer_enable_intr(_timerGroup, _timerIndex);
_callback = callback;
// Register the ISR handler
// If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute
// and can only call functions in IRAM or ROM. It cannot call other timer APIs.
//timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL);
timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
timer_start(_timerGroup, _timerIndex);
return true;
}
else
{
#if USING_ESP32_C3_TIMERINTERRUPT
TISR_LOGERROR(F("Error. Timer must be 0-1"));
#else
TISR_LOGERROR(F("Error. Timer must be 0-3"));
#endif
return false;
}
}
// interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool setInterval(const unsigned long& interval, const esp32_timer_callback& callback)
{
return setFrequency((float) (1000000.0f / interval), callback);
}
bool attachInterrupt(const float& frequency, const esp32_timer_callback& callback)
{
return setFrequency(frequency, callback);
}
// interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool attachInterruptInterval(const unsigned long& interval, const esp32_timer_callback& callback)
{
return setFrequency( (float) ( 1000000.0f / interval), callback);
}
void detachInterrupt()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
void disableTimer()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
void reattachInterrupt()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
void enableTimer()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Just stop clock source, clear the count
void stopTimer()
{
timer_pause(_timerGroup, _timerIndex);
}
// Just reconnect clock source, start current count from 0
void restartTimer()
{
timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
timer_start(_timerGroup, _timerIndex);
}
int8_t getTimer() __attribute__((always_inline))
{
return _timerIndex;
};
int8_t getTimerGroup() __attribute__((always_inline))
{
return _timerGroup;
};
}; // class ESP32TimerInterrupt
#include "ESP32_ISR_Timer.hpp"
#endif // ESP32TIMERINTERRUPT_HPP
INO file:
// These define's must be placed at the beginning before #include "ESP32TimerInterrupt.h"
#define _TIMERINTERRUPT_LOGLEVEL_ 4
#define HW_TIMER_INTERVAL_MS 1L
#define NUMBER_ISR_TIMERS 2
#define LED_TOGGLE_INTERVAL_MS 2000L
#define BLOCKING_TIME_MS 10000L
#define LED 25
long blockingTime;
void doingSomething0(){}
void doingSomething1(){}
uint32_t TimerInterval[NUMBER_ISR_TIMERS] = {1000L, 1500L};
typedef void (*irqCallback)();
irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
{
doingSomething0,
doingSomething1
};
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "ESP32TimerInterrupt.hpp"
//#include "ESP32_ISR_Timer.h"
//#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
volatile uint32_t startMillis = 0;
ESP32Timer ITimer(1); // Init ESP32 timer 1
ESP32_ISR_Timer ISR_Timer; // Init ESP32_ISR_Timer
/************************************/
bool IRAM_ATTR TimerHandler(void * timerNo)
{
static int timeRun = 0;
ISR_Timer.run();
// Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS))
{
timeRun = 0;
//timer interrupt toggles pin LED_BUILTIN
digitalWrite(LED, !digitalRead(LED));
}
return true;
}
/************************************/
void setup()
{
Serial.begin(115200);
while (!Serial);
pinMode(LED, OUTPUT);
delay(200);
Serial.print(F("\nStarting ISR_16_Timers_Array on "));
Serial.println(ARDUINO_BOARD);
Serial.print(ESP32_TIMER_INTERRUPT_VERSION);
Serial.print(F(" runninr at CPU Frequency = "));
Serial.print(F_CPU / 1000000);
Serial.println(F(" MHz"));
// Interval in microsecs
if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler))
{
startMillis = millis();
Serial.print(F("Starting ITimer OK, millis() = "));
Serial.println(startMillis);
}
else
Serial.println(F("Can't set ITimer. Select another freq. or timer"));
// You can use up to 16 timer for each ISR_Timer
for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
}
/************************************/
void loop()
{
if ((millis() - blockingTime) < BLOCKING_TIME_MS)
return;
blockingTime = millis();
}
I would like to measure time in C, and I am having a tough time figuring it out, all I want is something like this:
start a timer
run a method
stop the timer
report the time taken (at least to micro accuracy)
Any help would be appreciated.
(I am compiling in windows using mingw)
High resolution timers that provide a resolution of 1 microsecond are system-specific, so you will have to use different methods to achieve this on different OS platforms. You may be interested in checking out the following article, which implements a cross-platform C++ timer class based on the functions described below:
[Song Ho Ahn - High Resolution Timer][1]
Windows
The Windows API provides extremely high resolution timer functions: QueryPerformanceCounter(), which returns the current elapsed ticks, and QueryPerformanceFrequency(), which returns the number of ticks per second.
Example:
#include <stdio.h>
#include <windows.h> // for Windows APIs
int main(void)
{
LARGE_INTEGER frequency; // ticks per second
LARGE_INTEGER t1, t2; // ticks
double elapsedTime;
// get ticks per second
QueryPerformanceFrequency(&frequency);
// start timer
QueryPerformanceCounter(&t1);
// do something
// ...
// stop timer
QueryPerformanceCounter(&t2);
// compute and print the elapsed time in millisec
elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
printf("%f ms.\n", elapsedTime);
}
Linux, Unix, and Mac
For Unix or Linux based system, you can use gettimeofday(). This function is declared in "sys/time.h".
Example:
#include <stdio.h>
#include <sys/time.h> // for gettimeofday()
int main(void)
{
struct timeval t1, t2;
double elapsedTime;
// start timer
gettimeofday(&t1, NULL);
// do something
// ...
// stop timer
gettimeofday(&t2, NULL);
// compute and print the elapsed time in millisec
elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms
elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms
printf("%f ms.\n", elapsedTime);
}
On Linux you can use clock_gettime():
clock_gettime(CLOCK_REALTIME, &start); // get initial time-stamp
// ... do stuff ... //
clock_gettime(CLOCK_REALTIME, &end); // get final time-stamp
double t_ns = (double)(end.tv_sec - start.tv_sec) * 1.0e9 +
(double)(end.tv_nsec - start.tv_nsec);
// subtract time-stamps and
// multiply to get elapsed
// time in ns
Here's a header file I wrote to do some simple performance profiling (using manual timers):
#ifndef __ZENTIMER_H__
#define __ZENTIMER_H__
#ifdef ENABLE_ZENTIMER
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#else
typedef unsigned char uint8_t;
typedef unsigned long int uint32_t;
typedef unsigned long long uint64_t;
#endif
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
#define ZTIME_USEC_PER_SEC 1000000
/* ztime_t represents usec */
typedef uint64_t ztime_t;
#ifdef WIN32
static uint64_t ztimer_freq = 0;
#endif
static void
ztime (ztime_t *ztimep)
{
#ifdef WIN32
QueryPerformanceCounter ((LARGE_INTEGER *) ztimep);
#else
struct timeval tv;
gettimeofday (&tv, NULL);
*ztimep = ((uint64_t) tv.tv_sec * ZTIME_USEC_PER_SEC) + tv.tv_usec;
#endif
}
enum {
ZTIMER_INACTIVE = 0,
ZTIMER_ACTIVE = (1 << 0),
ZTIMER_PAUSED = (1 << 1),
};
typedef struct {
ztime_t start;
ztime_t stop;
int state;
} ztimer_t;
#define ZTIMER_INITIALIZER { 0, 0, 0 }
/* default timer */
static ztimer_t __ztimer = ZTIMER_INITIALIZER;
static void
ZenTimerStart (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztimer->state = ZTIMER_ACTIVE;
ztime (&ztimer->start);
}
static void
ZenTimerStop (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state = ZTIMER_INACTIVE;
}
static void
ZenTimerPause (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state |= ZTIMER_PAUSED;
}
static void
ZenTimerResume (ztimer_t *ztimer)
{
ztime_t now, delta;
ztimer = ztimer ? ztimer : &__ztimer;
/* unpause */
ztimer->state &= ~ZTIMER_PAUSED;
ztime (&now);
/* calculate time since paused */
delta = now - ztimer->stop;
/* adjust start time to account for time elapsed since paused */
ztimer->start += delta;
}
static double
ZenTimerElapsed (ztimer_t *ztimer, uint64_t *usec)
{
#ifdef WIN32
static uint64_t freq = 0;
ztime_t delta, stop;
if (freq == 0)
QueryPerformanceFrequency ((LARGE_INTEGER *) &freq);
#else
#define freq ZTIME_USEC_PER_SEC
ztime_t delta, stop;
#endif
ztimer = ztimer ? ztimer : &__ztimer;
if (ztimer->state != ZTIMER_ACTIVE)
stop = ztimer->stop;
else
ztime (&stop);
delta = stop - ztimer->start;
if (usec != NULL)
*usec = (uint64_t) (delta * ((double) ZTIME_USEC_PER_SEC / (double) freq));
return (double) delta / (double) freq;
}
static void
ZenTimerReport (ztimer_t *ztimer, const char *oper)
{
fprintf (stderr, "ZenTimer: %s took %.6f seconds\n", oper, ZenTimerElapsed (ztimer, NULL));
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#else /* ! ENABLE_ZENTIMER */
#define ZenTimerStart(ztimerp)
#define ZenTimerStop(ztimerp)
#define ZenTimerPause(ztimerp)
#define ZenTimerResume(ztimerp)
#define ZenTimerElapsed(ztimerp, usec)
#define ZenTimerReport(ztimerp, oper)
#endif /* ENABLE_ZENTIMER */
#endif /* __ZENTIMER_H__ */
The ztime() function is the main logic you need — it gets the current time and stores it in a 64bit uint measured in microseconds. You can then later do simple math to find out the elapsed time.
The ZenTimer*() functions are just helper functions to take a pointer to a simple timer struct, ztimer_t, which records the start time and the end time. The ZenTimerPause()/ZenTimerResume() functions allow you to, well, pause and resume the timer in case you want to print out some debugging information that you don't want timed, for example.
You can find a copy of the original header file at http://www.gnome.org/~fejj/code/zentimer.h in the off chance that I messed up the html escaping of <'s or something. It's licensed under MIT/X11 so feel free to copy it into any project you do.
The following is a group of versatile C functions for timer management based on the gettimeofday() system call. All the timer properties are contained in a single ticktimer struct - the interval you want, the total running time since the timer initialization, a pointer to the desired callback you want to call, the number of times the callback was called. A callback function would look like this:
void your_timer_cb (struct ticktimer *t) {
/* do your stuff here */
}
To initialize and start a timer, call ticktimer_init(your_timer, interval, TICKTIMER_RUN, your_timer_cb, 0).
In the main loop of your program call ticktimer_tick(your_timer) and it will decide whether the appropriate amount of time has passed to invoke the callback.
To stop a timer, just call ticktimer_ctl(your_timer, TICKTIMER_STOP).
ticktimer.h:
#ifndef __TICKTIMER_H
#define __TICKTIMER_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define TICKTIMER_STOP 0x00
#define TICKTIMER_UNCOMPENSATE 0x00
#define TICKTIMER_RUN 0x01
#define TICKTIMER_COMPENSATE 0x02
struct ticktimer {
u_int64_t tm_tick_interval;
u_int64_t tm_last_ticked;
u_int64_t tm_total;
unsigned ticks_total;
void (*tick)(struct ticktimer *);
unsigned char flags;
int id;
};
void ticktimer_init (struct ticktimer *, u_int64_t, unsigned char, void (*)(struct ticktimer *), int);
unsigned ticktimer_tick (struct ticktimer *);
void ticktimer_ctl (struct ticktimer *, unsigned char);
struct ticktimer *ticktimer_alloc (void);
void ticktimer_free (struct ticktimer *);
void ticktimer_tick_all (void);
#endif
ticktimer.c:
#include "ticktimer.h"
#define TIMER_COUNT 100
static struct ticktimer timers[TIMER_COUNT];
static struct timeval tm;
/*!
#brief
Initializes/sets the ticktimer struct.
#param timer
Pointer to ticktimer struct.
#param interval
Ticking interval in microseconds.
#param flags
Flag bitmask. Use TICKTIMER_RUN | TICKTIMER_COMPENSATE
to start a compensating timer; TICKTIMER_RUN to start
a normal uncompensating timer.
#param tick
Ticking callback function.
#param id
Timer ID. Useful if you want to distinguish different
timers within the same callback function.
*/
void ticktimer_init (struct ticktimer *timer, u_int64_t interval, unsigned char flags, void (*tick)(struct ticktimer *), int id) {
gettimeofday(&tm, NULL);
timer->tm_tick_interval = interval;
timer->tm_last_ticked = tm.tv_sec * 1000000 + tm.tv_usec;
timer->tm_total = 0;
timer->ticks_total = 0;
timer->tick = tick;
timer->flags = flags;
timer->id = id;
}
/*!
#brief
Checks the status of a ticktimer and performs a tick(s) if
necessary.
#param timer
Pointer to ticktimer struct.
#return
The number of times the timer was ticked.
*/
unsigned ticktimer_tick (struct ticktimer *timer) {
register typeof(timer->tm_tick_interval) now;
register typeof(timer->ticks_total) nticks, i;
if (timer->flags & TICKTIMER_RUN) {
gettimeofday(&tm, NULL);
now = tm.tv_sec * 1000000 + tm.tv_usec;
if (now >= timer->tm_last_ticked + timer->tm_tick_interval) {
timer->tm_total += now - timer->tm_last_ticked;
if (timer->flags & TICKTIMER_COMPENSATE) {
nticks = (now - timer->tm_last_ticked) / timer->tm_tick_interval;
timer->tm_last_ticked = now - ((now - timer->tm_last_ticked) % timer->tm_tick_interval);
for (i = 0; i < nticks; i++) {
timer->tick(timer);
timer->ticks_total++;
if (timer->tick == NULL) {
break;
}
}
return nticks;
} else {
timer->tm_last_ticked = now;
timer->tick(timer);
timer->ticks_total++;
return 1;
}
}
}
return 0;
}
/*!
#brief
Controls the behaviour of a ticktimer.
#param timer
Pointer to ticktimer struct.
#param flags
Flag bitmask.
*/
inline void ticktimer_ctl (struct ticktimer *timer, unsigned char flags) {
timer->flags = flags;
}
/*!
#brief
Allocates a ticktimer struct from an internal
statically allocated list.
#return
Pointer to the newly allocated ticktimer struct
or NULL when no more space is available.
*/
struct ticktimer *ticktimer_alloc (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick == NULL) {
return timers + i;
}
}
return NULL;
}
/*!
#brief
Marks a previously allocated ticktimer struct as free.
#param timer
Pointer to ticktimer struct, usually returned by
ticktimer_alloc().
*/
inline void ticktimer_free (struct ticktimer *timer) {
timer->tick = NULL;
}
/*!
#brief
Checks the status of all allocated timers from the
internal list and performs ticks where necessary.
#note
Should be called in the main loop.
*/
inline void ticktimer_tick_all (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick != NULL) {
ticktimer_tick(timers + i);
}
}
}
Using the time.h library, try something like this:
long start_time, end_time, elapsed;
start_time = clock();
// Do something
end_time = clock();
elapsed = (end_time - start_time) / CLOCKS_PER_SEC * 1000;
If your Linux system supports it, clock_gettime(CLOCK_MONOTONIC) should be a high resolution timer that is unaffected by system date changes (e.g. NTP daemons).
Great answers for GNU environments above and below...
But... what if you're not running on an OS? (or a PC for that matter, or you need to time your timer interrupts themselves?) Here's a solution that uses the x86 CPU timestamp counter directly... Not because this is good practice, or should be done, ever, when running under an OS...
Caveat: Only works on x86, with frequency scaling disabled.
Under Linux, only works on non-tickless kernels
rdtsc.c:
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned long long int64;
static __inline__ int64 getticks(void)
{
unsigned a, d;
asm volatile("rdtsc" : "=a" (a), "=d" (d));
return (((int64)a) | (((int64)d) << 32));
}
int main(){
int64 tick,tick1;
unsigned time=0,mt;
// mt is the divisor to give microseconds
FILE *pf;
int i,r,l,n=0;
char s[100];
// time how long it takes to get the divisors, as a test
tick = getticks();
// get the divisors - todo: for max performance this can
// output a new binary or library with these values hardcoded
// for the relevant CPU - if you use the equivalent assembler for
// that CPU
pf = fopen("/proc/cpuinfo","r");
do {
r=fscanf(pf,"%s",&s[0]);
if (r<0) {
n=5; break;
} else if (n==0) {
if (strcmp("MHz",s)==0) n=1;
} else if (n==1) {
if (strcmp(":",s)==0) n=2;
} else if (n==2) {
n=3;
};
} while (n<3);
fclose(pf);
s[9]=(char)0;
strcpy(&s[4],&s[5]);
mt=atoi(s);
printf("#define mt %u // (%s Hz) hardcode this for your a CPU-specific binary ;-)\n",mt,s);
tick1 = getticks();
time = (unsigned)((tick1-tick)/mt);
printf("%u ms\n",time);
// time the duration of sleep(1) - plus overheads ;-)
tick = getticks();
sleep(1);
tick1 = getticks();
time = (unsigned)((tick1-tick)/mt);
printf("%u ms\n",time);
return 0;
}
compile and run with
$ gcc rdtsc.c -o rdtsc && ./rdtsc
It reads the divisor for your CPU from /proc/cpuinfo and shows how long it took to read that in microseconds, as well as how long it takes to execute sleep(1) in microseconds... Assuming the Mhz rating in /proc/cpuinfo always contains 3 decimal places :-o
I have the following macros that take a statement and multiply it
#define X2(s) do { s; s; } while (0)
#define X3(s) do { s; s; s; } while (0)
#define X4(s) do { s; s; s; s; } while (0)
#define X5(s) do { s; s; s; s; s; } while (0)
#define X6(s) do { s; s; s; s; s; s; } while (0)
etc.
so the pre-processor expands
X2(i++)
to
do { i++; i++; } while (0)
I would like to have a macro
#define CODE_MULTIPLIER(x,s)
which expands a statement s, x times. so
CODE_MULTIPLIER(3,j--)
would be expanded to
do { j--; j--; j--; } while (0)
The ugly idea i came up with is:
#define CODE_MULTIPLIER(x,s) do { \
if ((x) == 1) { s; } \
else if ((x) == 2) { s; s; } \
else if ((x) == 3) { s; s; s; } \
else if ((x) == 4) { s; s; s; s; } \
else if ((x) == 5) { s; s; s; s; s; } \
etc. \
else assert(0); \
} while (0)
hoping the compiler will optimize out the ifs
Why would you ever want macros like this?
One reason (but not the only one) is the need for exact delays in embedded programming. using while/for loops slightly change the timing. Compiler optimization of loops may not preserve delicate timing requirements.
Here is a typical usage example:
#define CLOCK_FREQ_Hz (16000000L)
#define US_TO_NOPS(us) ((us) * CLOCK_FREQ_Hz / 1000000L)
#define OFF_ON_DELAY_US (4) // units: microseconds
#define ON_OFF_DELAY_US (2) // units: microseconds
#define OFF_ON_DELAY_NOPS US_TO_NOPS(OFF_ON_DELAY_US) // units: instructions
#define ON_OFF_DELAY_NOPS US_TO_NOPS(ON_OFF_DELAY_US) // units: instructions
PIN = OFF;
CODE_MULTIPLIER(OFF_ON_DELAY_NOPS,asm("nop")); // 4us delay
PIN = ON;
CODE_MULTIPLIER(ON_OFF_DELAY_NOPS,asm("nop")); // 2us delay
PIN = OFF;
I would appreciate any suggestions as to how to create this macro. High preference to compiler-independent macro wizardry (Us embedded dudes don't always have the luxury of using GCC)
Thanx
Yes such things are possible if you don't stretch things too much. P99 has a whole series of macros to accomplish different sorts of such unrolling. An easy one here would be
#define toto(I) f(I)
P99_UNROLL(toto, 3); // => f(0); f(1); f(2);
But beware that for using P99 you'd need a compiler that implements C99 compliant preprocessor.
As recursion cannot be generally used in macros, and you may not have "extended unrolling features" the only way to have a "always valid solution" is to do some manual work like following one:
#define X1(s) do { s;} while (0)
#define X2(s) do { s; s; } while (0)
#define X3(s) do { s; s; s; } while (0)
#define X4(s) do { s; s; s; s; } while (0)
#define X5(s) do { s; s; s; s; s; } while (0)
#define X6(s) do { s; s; s; s; s; s; } while (0)
#define CODE_MULTIPLIER(s, i) X##i(s)
And you just use CODE_MULTIPLIER(s, i) in your code. In this way you avoid the if/else if conditional expressions. For example you will use CODE_MULTIPLIER(i++,3).
Another trick can be applied if you aren't going to use X1,X2.... but only CODE_MULTIPLIER(s,i):
#define X1(s) s
#define X2(s) s; X1(s)
#define X3(s) s; X2(s)
#define X4(s) s; X3(s)
#define X5(s) s; X4(s)
#define X6(s) s; X5(s)
#define CODE_MULTIPLIER(s, i) do { X##i(s); } while (0)
which has a nicer sintax.
I am searching a nice way to create a "no action" timing (CPU useless operation for timing).
To explain my issue here is the code that I want to change to a macro :
int main (void)
{
int i=0;
printf("Start\r\n");
for(i=0;i<1000;i++); //LINE TO CHANGE TO A MACRO
printf("Delayed trace\r\n");
return 0;
}
Do you have any idea ?
Thanks by advance.
General way for defining such a macro would be:
#define DELAY(amount) \
do { \
/* method of delay */ \
} while (0)
In your case:
#define DELAY(amount) \
do { \
int i; \
for (i = 0; i < amount; ++i); \
} while (0)
If you wish to introduce a delay in terms of some loop iterations, you could employ
#define INTRODUCE_DELAY(count) for(int i = 0; i < count; i++);
You could modify this through a compiler switch to invoke a sleep of similar us as shown below. In case you can't declare i in the for loop then, we can define a small function (which could potentially be inline as shown below
#ifdef WIN32
void mysleep(int count)
{
int i;
for(i = 0; i < count; i++);
}
#define INTRODUCE_DELAY(count) mysleep(count);
#else
#define INTRODUCE_DELAY(count) sleep(count);
#endif
I think you need sleep function, (Work only on unix/linux though)
#include <unistd.h>
#define DELAY(SEC) sleep(SEC)
There must be a similar function in windows.
I would like to measure time in C, and I am having a tough time figuring it out, all I want is something like this:
start a timer
run a method
stop the timer
report the time taken (at least to micro accuracy)
Any help would be appreciated.
(I am compiling in windows using mingw)
High resolution timers that provide a resolution of 1 microsecond are system-specific, so you will have to use different methods to achieve this on different OS platforms. You may be interested in checking out the following article, which implements a cross-platform C++ timer class based on the functions described below:
[Song Ho Ahn - High Resolution Timer][1]
Windows
The Windows API provides extremely high resolution timer functions: QueryPerformanceCounter(), which returns the current elapsed ticks, and QueryPerformanceFrequency(), which returns the number of ticks per second.
Example:
#include <stdio.h>
#include <windows.h> // for Windows APIs
int main(void)
{
LARGE_INTEGER frequency; // ticks per second
LARGE_INTEGER t1, t2; // ticks
double elapsedTime;
// get ticks per second
QueryPerformanceFrequency(&frequency);
// start timer
QueryPerformanceCounter(&t1);
// do something
// ...
// stop timer
QueryPerformanceCounter(&t2);
// compute and print the elapsed time in millisec
elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
printf("%f ms.\n", elapsedTime);
}
Linux, Unix, and Mac
For Unix or Linux based system, you can use gettimeofday(). This function is declared in "sys/time.h".
Example:
#include <stdio.h>
#include <sys/time.h> // for gettimeofday()
int main(void)
{
struct timeval t1, t2;
double elapsedTime;
// start timer
gettimeofday(&t1, NULL);
// do something
// ...
// stop timer
gettimeofday(&t2, NULL);
// compute and print the elapsed time in millisec
elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms
elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms
printf("%f ms.\n", elapsedTime);
}
On Linux you can use clock_gettime():
clock_gettime(CLOCK_REALTIME, &start); // get initial time-stamp
// ... do stuff ... //
clock_gettime(CLOCK_REALTIME, &end); // get final time-stamp
double t_ns = (double)(end.tv_sec - start.tv_sec) * 1.0e9 +
(double)(end.tv_nsec - start.tv_nsec);
// subtract time-stamps and
// multiply to get elapsed
// time in ns
Here's a header file I wrote to do some simple performance profiling (using manual timers):
#ifndef __ZENTIMER_H__
#define __ZENTIMER_H__
#ifdef ENABLE_ZENTIMER
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#else
typedef unsigned char uint8_t;
typedef unsigned long int uint32_t;
typedef unsigned long long uint64_t;
#endif
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
#define ZTIME_USEC_PER_SEC 1000000
/* ztime_t represents usec */
typedef uint64_t ztime_t;
#ifdef WIN32
static uint64_t ztimer_freq = 0;
#endif
static void
ztime (ztime_t *ztimep)
{
#ifdef WIN32
QueryPerformanceCounter ((LARGE_INTEGER *) ztimep);
#else
struct timeval tv;
gettimeofday (&tv, NULL);
*ztimep = ((uint64_t) tv.tv_sec * ZTIME_USEC_PER_SEC) + tv.tv_usec;
#endif
}
enum {
ZTIMER_INACTIVE = 0,
ZTIMER_ACTIVE = (1 << 0),
ZTIMER_PAUSED = (1 << 1),
};
typedef struct {
ztime_t start;
ztime_t stop;
int state;
} ztimer_t;
#define ZTIMER_INITIALIZER { 0, 0, 0 }
/* default timer */
static ztimer_t __ztimer = ZTIMER_INITIALIZER;
static void
ZenTimerStart (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztimer->state = ZTIMER_ACTIVE;
ztime (&ztimer->start);
}
static void
ZenTimerStop (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state = ZTIMER_INACTIVE;
}
static void
ZenTimerPause (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state |= ZTIMER_PAUSED;
}
static void
ZenTimerResume (ztimer_t *ztimer)
{
ztime_t now, delta;
ztimer = ztimer ? ztimer : &__ztimer;
/* unpause */
ztimer->state &= ~ZTIMER_PAUSED;
ztime (&now);
/* calculate time since paused */
delta = now - ztimer->stop;
/* adjust start time to account for time elapsed since paused */
ztimer->start += delta;
}
static double
ZenTimerElapsed (ztimer_t *ztimer, uint64_t *usec)
{
#ifdef WIN32
static uint64_t freq = 0;
ztime_t delta, stop;
if (freq == 0)
QueryPerformanceFrequency ((LARGE_INTEGER *) &freq);
#else
#define freq ZTIME_USEC_PER_SEC
ztime_t delta, stop;
#endif
ztimer = ztimer ? ztimer : &__ztimer;
if (ztimer->state != ZTIMER_ACTIVE)
stop = ztimer->stop;
else
ztime (&stop);
delta = stop - ztimer->start;
if (usec != NULL)
*usec = (uint64_t) (delta * ((double) ZTIME_USEC_PER_SEC / (double) freq));
return (double) delta / (double) freq;
}
static void
ZenTimerReport (ztimer_t *ztimer, const char *oper)
{
fprintf (stderr, "ZenTimer: %s took %.6f seconds\n", oper, ZenTimerElapsed (ztimer, NULL));
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#else /* ! ENABLE_ZENTIMER */
#define ZenTimerStart(ztimerp)
#define ZenTimerStop(ztimerp)
#define ZenTimerPause(ztimerp)
#define ZenTimerResume(ztimerp)
#define ZenTimerElapsed(ztimerp, usec)
#define ZenTimerReport(ztimerp, oper)
#endif /* ENABLE_ZENTIMER */
#endif /* __ZENTIMER_H__ */
The ztime() function is the main logic you need — it gets the current time and stores it in a 64bit uint measured in microseconds. You can then later do simple math to find out the elapsed time.
The ZenTimer*() functions are just helper functions to take a pointer to a simple timer struct, ztimer_t, which records the start time and the end time. The ZenTimerPause()/ZenTimerResume() functions allow you to, well, pause and resume the timer in case you want to print out some debugging information that you don't want timed, for example.
You can find a copy of the original header file at http://www.gnome.org/~fejj/code/zentimer.h in the off chance that I messed up the html escaping of <'s or something. It's licensed under MIT/X11 so feel free to copy it into any project you do.
The following is a group of versatile C functions for timer management based on the gettimeofday() system call. All the timer properties are contained in a single ticktimer struct - the interval you want, the total running time since the timer initialization, a pointer to the desired callback you want to call, the number of times the callback was called. A callback function would look like this:
void your_timer_cb (struct ticktimer *t) {
/* do your stuff here */
}
To initialize and start a timer, call ticktimer_init(your_timer, interval, TICKTIMER_RUN, your_timer_cb, 0).
In the main loop of your program call ticktimer_tick(your_timer) and it will decide whether the appropriate amount of time has passed to invoke the callback.
To stop a timer, just call ticktimer_ctl(your_timer, TICKTIMER_STOP).
ticktimer.h:
#ifndef __TICKTIMER_H
#define __TICKTIMER_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define TICKTIMER_STOP 0x00
#define TICKTIMER_UNCOMPENSATE 0x00
#define TICKTIMER_RUN 0x01
#define TICKTIMER_COMPENSATE 0x02
struct ticktimer {
u_int64_t tm_tick_interval;
u_int64_t tm_last_ticked;
u_int64_t tm_total;
unsigned ticks_total;
void (*tick)(struct ticktimer *);
unsigned char flags;
int id;
};
void ticktimer_init (struct ticktimer *, u_int64_t, unsigned char, void (*)(struct ticktimer *), int);
unsigned ticktimer_tick (struct ticktimer *);
void ticktimer_ctl (struct ticktimer *, unsigned char);
struct ticktimer *ticktimer_alloc (void);
void ticktimer_free (struct ticktimer *);
void ticktimer_tick_all (void);
#endif
ticktimer.c:
#include "ticktimer.h"
#define TIMER_COUNT 100
static struct ticktimer timers[TIMER_COUNT];
static struct timeval tm;
/*!
#brief
Initializes/sets the ticktimer struct.
#param timer
Pointer to ticktimer struct.
#param interval
Ticking interval in microseconds.
#param flags
Flag bitmask. Use TICKTIMER_RUN | TICKTIMER_COMPENSATE
to start a compensating timer; TICKTIMER_RUN to start
a normal uncompensating timer.
#param tick
Ticking callback function.
#param id
Timer ID. Useful if you want to distinguish different
timers within the same callback function.
*/
void ticktimer_init (struct ticktimer *timer, u_int64_t interval, unsigned char flags, void (*tick)(struct ticktimer *), int id) {
gettimeofday(&tm, NULL);
timer->tm_tick_interval = interval;
timer->tm_last_ticked = tm.tv_sec * 1000000 + tm.tv_usec;
timer->tm_total = 0;
timer->ticks_total = 0;
timer->tick = tick;
timer->flags = flags;
timer->id = id;
}
/*!
#brief
Checks the status of a ticktimer and performs a tick(s) if
necessary.
#param timer
Pointer to ticktimer struct.
#return
The number of times the timer was ticked.
*/
unsigned ticktimer_tick (struct ticktimer *timer) {
register typeof(timer->tm_tick_interval) now;
register typeof(timer->ticks_total) nticks, i;
if (timer->flags & TICKTIMER_RUN) {
gettimeofday(&tm, NULL);
now = tm.tv_sec * 1000000 + tm.tv_usec;
if (now >= timer->tm_last_ticked + timer->tm_tick_interval) {
timer->tm_total += now - timer->tm_last_ticked;
if (timer->flags & TICKTIMER_COMPENSATE) {
nticks = (now - timer->tm_last_ticked) / timer->tm_tick_interval;
timer->tm_last_ticked = now - ((now - timer->tm_last_ticked) % timer->tm_tick_interval);
for (i = 0; i < nticks; i++) {
timer->tick(timer);
timer->ticks_total++;
if (timer->tick == NULL) {
break;
}
}
return nticks;
} else {
timer->tm_last_ticked = now;
timer->tick(timer);
timer->ticks_total++;
return 1;
}
}
}
return 0;
}
/*!
#brief
Controls the behaviour of a ticktimer.
#param timer
Pointer to ticktimer struct.
#param flags
Flag bitmask.
*/
inline void ticktimer_ctl (struct ticktimer *timer, unsigned char flags) {
timer->flags = flags;
}
/*!
#brief
Allocates a ticktimer struct from an internal
statically allocated list.
#return
Pointer to the newly allocated ticktimer struct
or NULL when no more space is available.
*/
struct ticktimer *ticktimer_alloc (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick == NULL) {
return timers + i;
}
}
return NULL;
}
/*!
#brief
Marks a previously allocated ticktimer struct as free.
#param timer
Pointer to ticktimer struct, usually returned by
ticktimer_alloc().
*/
inline void ticktimer_free (struct ticktimer *timer) {
timer->tick = NULL;
}
/*!
#brief
Checks the status of all allocated timers from the
internal list and performs ticks where necessary.
#note
Should be called in the main loop.
*/
inline void ticktimer_tick_all (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick != NULL) {
ticktimer_tick(timers + i);
}
}
}
Using the time.h library, try something like this:
long start_time, end_time, elapsed;
start_time = clock();
// Do something
end_time = clock();
elapsed = (end_time - start_time) / CLOCKS_PER_SEC * 1000;
If your Linux system supports it, clock_gettime(CLOCK_MONOTONIC) should be a high resolution timer that is unaffected by system date changes (e.g. NTP daemons).
Great answers for GNU environments above and below...
But... what if you're not running on an OS? (or a PC for that matter, or you need to time your timer interrupts themselves?) Here's a solution that uses the x86 CPU timestamp counter directly... Not because this is good practice, or should be done, ever, when running under an OS...
Caveat: Only works on x86, with frequency scaling disabled.
Under Linux, only works on non-tickless kernels
rdtsc.c:
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned long long int64;
static __inline__ int64 getticks(void)
{
unsigned a, d;
asm volatile("rdtsc" : "=a" (a), "=d" (d));
return (((int64)a) | (((int64)d) << 32));
}
int main(){
int64 tick,tick1;
unsigned time=0,mt;
// mt is the divisor to give microseconds
FILE *pf;
int i,r,l,n=0;
char s[100];
// time how long it takes to get the divisors, as a test
tick = getticks();
// get the divisors - todo: for max performance this can
// output a new binary or library with these values hardcoded
// for the relevant CPU - if you use the equivalent assembler for
// that CPU
pf = fopen("/proc/cpuinfo","r");
do {
r=fscanf(pf,"%s",&s[0]);
if (r<0) {
n=5; break;
} else if (n==0) {
if (strcmp("MHz",s)==0) n=1;
} else if (n==1) {
if (strcmp(":",s)==0) n=2;
} else if (n==2) {
n=3;
};
} while (n<3);
fclose(pf);
s[9]=(char)0;
strcpy(&s[4],&s[5]);
mt=atoi(s);
printf("#define mt %u // (%s Hz) hardcode this for your a CPU-specific binary ;-)\n",mt,s);
tick1 = getticks();
time = (unsigned)((tick1-tick)/mt);
printf("%u ms\n",time);
// time the duration of sleep(1) - plus overheads ;-)
tick = getticks();
sleep(1);
tick1 = getticks();
time = (unsigned)((tick1-tick)/mt);
printf("%u ms\n",time);
return 0;
}
compile and run with
$ gcc rdtsc.c -o rdtsc && ./rdtsc
It reads the divisor for your CPU from /proc/cpuinfo and shows how long it took to read that in microseconds, as well as how long it takes to execute sleep(1) in microseconds... Assuming the Mhz rating in /proc/cpuinfo always contains 3 decimal places :-o