A while ago I had the displeasure of fixing a bug in an embedded application. Every 11hours and 55mins the micro-controller would halt. It wasn’t a hard fault, it would simply freeze. Just to fill in some background information:
The target MCU was an STM32F415 and the application had made use of ST’s Cube software to generate a substantial portion of the hardware abstraction layer. Cube, as it happens, uses an unsigned 32bit integer to count system ticks. This application required a clock that was accurate to +/- 10 microseconds.
Doing the maths, 11hrs and 55mins works out to be 42900 seconds. Multiplied by 100,000 (systicks per second) gives 4,290,000,000. The largest number that could be held by an unsigned 32bit int is 4,294,967,296. Coincidence??? I think not!
Somewhere in the murky depths of all that code lay a sys tick roll-over bug which only reared it’s ugly head every 11.9 hours. Over a period of several days I tried and failed to find the bug. In the end I replaced every single time related function with etk::Time.
etk::Time counts both clock ticks and seconds. It cannot roll over unless it runs for over 120yrs straight. It’s quite straightforward I think. In the systick interrupt handler you would write etk::tick(). During initialisation, you must set the tick rate (systick interrupts per second) using etk::set_tick_rate(). From then on, you can use etk::now() to return the system time. Here’s a quick example
auto then = etk::now();
float time_difference = etk::now().diff_time(then);
//time_difference is now 1.0
etk::Time On The Desktop
It’s often necessary to test and develop code on a real computer before porting it to a micro-controller. etk::now(), etk::sleep_ms() and etk::sleep_us() are annotated as ‘weak’. This means you can easily re-implement these functions without linker errors, and override their behaviour. For example, adding the following code to anywhere in your project will replace the default implementations
using namespace std;
auto start_time = std::chrono::high_resolution_clock::now();
auto end_time = std::chrono::high_resolution_clock::now();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time).count();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
_now.seconds() = seconds;
_now.micros() = (micros%1000000);
void sleep_ms(uint32_t ms)
void sleep_us(uint32_t us)
etk::Time now = etk::now();
cout << "start..." << endl;
cout << etk::now().seconds() << " " << etk::now().micros() << endl;
cout << "done" << endl;