cancel
Showing results for 
Search instead for 
Did you mean: 

Compute cpu load for a STM32L0, a gpio and a voltmeter

SLE G.1
Associate

Hello,

At work we have developed firmware on a stm32l0 based on different time base with systick and counters of elapsed time (10ms, 100ms and 1s). Therefore, some function are executed periodically.

The board is powered with 3.3V.

In order to measure cpu load, we use a gpio and a voltmeter as below :

main()
{
    /* Init phases */
    ...
    ...
    while(1)
    {
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
 
        if(u8_FlagTime10ms == 1u)
        {
            u8_FlagTime10ms = 0u;
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
            /*
             * Do some stuff
             */
        }
        if(u8_FlagTime100ms == 1u)
        {
            u8_FlagTime100ms = 0u;
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
            /*
             * Do some stuff
             */            
        }
        if(u8_FlagTime1000ms == 1u)
        {
            u8_FlagTime1000ms = 0u;
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
            /*
             * Do some stuff
             */            
        }
    }
}

In this way, by using the min, max and average value shown by the voltmeter, we can compute the cpu load with the formula :

Cpu load % min = ( (3.3V - V_min_voltmeter) / 3.3V ) * 100

Cpu load % moy = ( (3.3V - V_moy_voltmeter) / 3.3V ) * 100

Cpu load % max = ( (3.3V - V_max_voltmeter) / 3.3V ) * 100

My question is the following:

Is this method suitable to compute cpu load for an stm32L0 ?

Thank you in advance 😊

Sebastian

7 REPLIES 7
Paul1
Lead

Careful:

  • This won't show CPU load due to interrupt code. Add code to save+set B12 state at entry, and restore B12 state at interrupt exit, do for every enabled interrupt. Option use a different pin so can see on a scope or meter the CPU time used for interrupt versus regular actions.
  • Consider adding sleep with wake on interrupts. It could significantly reduce your power. Possibly put the sleep before the first GPIO write, and have a 10msec interrupt.
  • The counters for 100msec and 1000msec can be in the 10msec actions, don't need to be in interrupt.

I do something similar, but from a magazine article from ages back, and two variants of it (DAC and no DAC).

  • With a DAC set it to distinct levels at various points in the code, including level0 for sleep (or wait loop). Maybe levels like 0x00, 0x40, 0x80, 0xC0, 0xFF (add intermediate levels or higher levels if need more steps or have DAC with more bits). In interrupts save the DAC level before changing it, and restore it upon exit (Similar for select subroutines). On an oscilloscope you will be able to easily see in real time where the code is spending its time. Some ST tools allow a similar graphical output of where code is spending time, though I'm unsure if those run at real time full MCU speed.
  • Without a DAC you need a few MCU pins, basically one pin for each action, similar to your method, but optional alternate pins for various interrupts or states in the code. Again clear the pins before doing a sleep or wait loop.

Paul

TDK
Guru

No, that won't work as an interrupt could happen at any time. You could do the following:

while (1) {
  __disable_irq();
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
  __disable_irq();
 
  // other stuff
}

You would need to take a reading of the voltage at idle first. Then, cpu usage would be a scale between 0V (100%) and the idle reading (0%). Idle reading value will vary based on compiler settings and code.

There are better ways.

If you feel a post has answered your question, please click "Accept as Solution".
MM..1
Chief II

Or simpler What is your LOAD ? Current to MCU or reply to question CPU do nothing or something.

Your idea is ok for nothing vs something, but dont count ISR code. Current load is big and relative constant, for example 3mA . L0 is low power designed then if you properly use for example STOP mode you jump to 3uA and in lower mode 300nA . 10000:1

How would you even know which task you were looking at? Should perhaps be using different GPIO or an else-if

Perhaps be better looking at elapsed time across a WFI

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Piranha
Chief II

Implement a proper task scheduling and sleeping at a single location of code with WFI at idle. Then, as said by Tesla, measure the time spend sleeping with a hardware timer. Typically DWT_CYCCNT is used.

To get the idea:

uint32_t t1, t2;
 
for (;;) {
	__disable_irq();
 
	if (/* Test if there are no tasks to execute */) {
		t1 = DWT->CYCCNT;
		__DSB();
		__WFI();
		t2 = DWT->CYCCNT;
 
		__enable_irq();
		
		// Accumulate the idle time for some period and then calculate the CPU load for that period
	} else {
		// Select the task to execute
		// Remove the active flag for the selected task
 
		__enable_irq();
 
		// Execute the selected task
	}
}

KnarfB
Principal III

If you have implemented the idle loop as suggested by @Piranha​ and still want to use your voltmeter (L0 doesn't have CYCCNT), issue a __SEV() instruction in the idle for loop and configure the pin as EVENTOUT. That loop will consume all remaining CPU cycles and the pin will be high whenever __SEV() is executed and low else.

By using a logic analyzer (starting at $10) you can gain much more insight like execution patterns and interrupt latencies.

There is also instrumentation software which will collect such info for you, but this is, of course, an intrusive method.

hth

KnarfB

Also setting/resetting any pin around the WFI instead of reading t1/t2 should also do the job.