I used this simple module for the measurement of CPU load on a STM32F051 that was used on a 100W bench programmable load. I wanted to know how "busy" was the microcontroller executing the program and how much free time did it have.
Before I created this measurement module, I defined CPU load as being the time that the processor did some useful work compared to the total time it had (I chose the reference period of 1000ms in my project), converted to a percent number.
All my program functionality was split on a number of very simple "tasks". There is a very simple timer based check that sets a set of flags when it is the time for a "task" to run. I put quotes to task because they are not really operating system tasks, but very simple code groups that run on flag check in main() basis.
Here are my tasks that I used in my project:
Collection of tasks I had
typedef enum
{
RunningTask_NoTask = (u8)0x00,
RunningTask_10ms = (u8)0x01,
RunningTask_100ms = (u8)0x02,
RunningTask_250ms = (u8)0x04,
RunningTask_500ms = (u8)0x08,
RunningTask_1000ms = (u8)0x10,
RunningTask_Bkg = (u8)0x20,
RunningTask_PID_500us = (u8)0x40
}RunningTask_t;
When the program runs, interrupts also occur and need to be handled by the processor and they can occur anytime, meanwhile a simple "task" is running or in between the "tasks", so I needed to keep track also on the time spent in the interrupt handlers.
In my main() function I had something like this:
Task runtime measurement
if(FLAG_100ms)
{
RTMS_MeasureTaskStart(RunningTask_100ms);
// useful code that needs to run every 100ms
TMS_MeasureTaskEnd(RunningTask_100ms);
}
if(FLAG_10ms)
{
RTMS_MeasureTaskStart(RunningTask_10ms);
// some useful code that need to run every 10ms
RTMS_MeasureTaskEnd(RunningTask_10ms);
}
// and so on for the rest of all the tasks that you have
And for the interrupt handlers:
ISR runtime measurement
void TIM17_IRQHandler(void)
{
RTMS_MeasureIntStart;
// interrupt handler code
RTMS_MeasureIntEnd;
}
void I2C2_IRQHandler(void)
{
RTMS_MeasureIntStart;
// interrupt handler code
RTMS_MeasureIntEnd;
}
// and so on for all your interrupt handlers
At the beginning of the task you call RTMS_MeasureTaskStart(task), which simply sets a flag bit to mark the current running task and get a free running timer timestamp. It is a MACRO so it does not add function call overhead, and it looks like this:
RTMS_MeasureTaskStart(task)
#define RTMS_MeasureTaskStart(task) \
{ \
RunningTask |= task; \
start_task = TIMER_1US_CNT; \
} \
And at the end of the task, you call RTMS_MeasureTaskEnd(task), which resets the flag bit of the task and calculates the time elapsed from the call to RTMS_MeasureTaskStart(task), and this time difference is added to a total.
#define RTMS_MeasureTaskEnd(task)
#define RTMS_MeasureTaskEnd(task) \
{ \
end_task = TIMER_1US_CNT; \
total_task += end_task - start_task; /* contains also interrupt time which has to be substracted */ \
RunningTask &= (u8)(~task); \
} \
And then, something similar is done for measuring the time in the ISRs:
#define RTMS_MeasureIntStart
#define RTMS_MeasureIntStart \
{ \
start_int = TIMER_1US_CNT; \
} \
#define RTMS_MeasureIntEnd \
{ \
end_int = TIMER_1US_CNT; \
total_int += end_int - start_int; \
if(RunningTask != RunningTask_NoTask) { \
total_int_intask += end_int - start_int; \
} \
} \
The only difference is that if the interrupt occurs during a "task" execution, this time is added separately and it will be substracted from the total task time, because this time is already contained in the task time because the task was interrupted.
Finally, RTMS_CpuLoadCalculation() needs to be called cyclically, which calculates the current CPU load that the CPU had in the past 1000ms, based on the total task and interrupt runtimes recorded, it also keeps track of a global minimum and maximum CPU load:
void RTMS_CpuLoadCalculation()
void RTMS_CpuLoadCalculation()
{
/* CPU load calculation */
total_task -= total_int_intask; /* substract from task time, interrupt time that occured during task runtime */
cpuload_task = (total_task * CPULOAD_RESOLUTION) / CPULOAD_MEASUREMENT_CYCLE_US;
cpuload_int = (total_int * CPULOAD_RESOLUTION) / CPULOAD_MEASUREMENT_CYCLE_US;
total_task = 0;
total_int = 0;
total_int_intask = 0;
cpuload_temp = cpuload_task + cpuload_int;
if(cpuload_temp <= 1000) cpuload = (u16)cpuload_temp;
else cpuload = 1000;
if(cpuload_max < cpuload) cpuload_max = cpuload;
if(cpuload_min > cpuload) cpuload_min = cpuload;
}
I called RTMS_CpuLoadCalculation() once every second:
Text Box
if(FLAG_1000ms)
{
RTMS_CpuLoadCalculation();
// other useful code to be done every 1000ms
}
At the bottom of this page you can download the source files, good luck with your projects !