-
MCU占用率测量
MCU 占用率 = (总时间 - 空闲时间) / 总时间 × 100%原理:通过FreeRTOS中的空闲任务占用时间计算得到MCU占用率,当进入空闲任务时,记下CPU的周期计数C1,当切换出空闲任务后,记下CPU的周期计数C2,从而得到空闲任务占用的CPU周期计数。
1、FreeRTOSConfig.h中的宏定义配置
#define configUSE_IDLE_HOOK 1
#define configUSE_APPLICATION_TASK_TAG 1
#define traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook( pxCurrentTCB, (void*)1 )
#define traceTASK_SWITCHED_IN() xTaskCallApplicationTaskHook( pxCurrentTCB, (void*)0 )
2、app_freertos.c中配置空闲任务钩子函数

extern portBASE_TYPE IdleTaskHook(void* p);
void vApplicationIdleHook( void )
{
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
task. It is essential that code added to this hook function never attempts
to block in any way (for example, call xQueueReceive() with a block time
specified, or call vTaskDelay()). If the application makes use of the
vTaskDelete() API function (as this demo application does) then it is also
important that vApplicationIdleHook() is permitted to return to its calling
function, because it is the responsibility of the idle task to clean up
memory allocated by the kernel to any task that has since been deleted. */
vTaskSetApplicationTaskTag(NULL, IdleTaskHook);
}
3、在TouchGFXHAL.cpp中增加cpu占用率的测量类




/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : TouchGFXHAL.hpp
******************************************************************************
* This file was created by TouchGFX Generator 4.26.0. This file is only
* generated once! Delete this file from your project and re-generate code
* using STM32CubeMX or change this file manually to update it.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#ifndef TouchGFXHAL_HPP
#define TouchGFXHAL_HPP
/* USER CODE BEGIN TouchGFXHAL.hpp */
#include <TouchGFXGeneratedHAL.hpp>
#include <CortexMMCUInstrumentation.hpp>
/**
* @class TouchGFXHAL
*
* @brief HAL implementation for TouchGFX.
*
* @sa HAL
*/
class TouchGFXHAL : public TouchGFXGeneratedHAL
{
public:
/**
* @fn TouchGFXHAL::TouchGFXHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : TouchGFXGeneratedHAL(dma, display, tc, width, height)
*
* @brief Constructor.
*
* Constructor. Initializes members.
*
* @param [in,out] dma Reference to DMA interface.
* @param [in,out] display Reference to LCD interface.
* @param [in,out] tc Reference to Touch Controller driver.
* @param width Width of the display.
* @param height Height of the display.
*/
TouchGFXHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : TouchGFXGeneratedHAL(dma, display, tc, width, height)
{
}
virtual void initialize();
/**
* @fn virtual void TouchGFXHAL::disableInterrupts();
*
* @brief Disables the DMA, LDC, and GPU2D (if enabled) interrupts.
*
* Disables the DMA, LDC, and GPU2D (if enabled) interrupts.
*/
virtual void disableInterrupts();
/**
* @fn virtual void TouchGFXHAL::enableInterrupts();
*
* @brief Enables the DMA, LCD, and GPU2D (if enabled) interrupts.
*
* Enables the DMA, LCD, and GPU2D (if enabled) interrupts.
*/
virtual void enableInterrupts();
/**
* @fn virtual void TouchGFXHAL::configureInterrupts();
*
* @brief Sets the DMA, LCD, and GPU2D (if enabled) interrupt priorities.
*
* Sets the DMA, LCD, and GPU2D (if enabled) interrupt priorities.
*/
virtual void configureInterrupts();
/**
* @fn virtual void TouchGFXHAL::enableLCDControllerInterrupt();
*
* @brief Configure the LCD controller to fire interrupts at VSYNC.
*
* Configure the LCD controller to fire interrupts at VSYNC. Called automatically
* once TouchGFX initialization has completed.
*/
virtual void enableLCDControllerInterrupt();
virtual bool beginFrame();
virtual void endFrame();
/**
* @fn virtual void TouchGFXHAL::flushFrameBuffer();
*
* @brief This function is called whenever the framework has performed a complete draw.
*
* This specialization is only in place to keep compilers happy. Base impl. will call the
* Rect version.
* @see HAL::flushFrameBuffer
*/
virtual void flushFrameBuffer()
{
TouchGFXGeneratedHAL::flushFrameBuffer();
}
/**
* @fn virtual void TouchGFXHAL::flushFrameBuffer(const Rect& rect);
*
* @brief This function is called whenever the framework has performed a partial draw.
*
* This function is called whenever the framework has performed a partial draw.
*
* @param rect The area of the screen that has been drawn, expressed in absolute coordinates.
*
* @see flushFrameBuffer().
*/
virtual void flushFrameBuffer(const touchgfx::Rect& rect);
/**
* @fn virtual bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
*
* @brief This function performs a platform-specific memcpy.
*
* This function performs a platform-specific memcpy, if supported by the hardware.
*
* @param [out] dest Pointer to destination memory.
* @param [in] src Pointer to source memory.
* @param numBytes Number of bytes to copy.
*
* @return true if the copy succeeded, false if copy was not performed.
*/
virtual bool blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
protected:
/**
* @fn virtual uint16_t* TouchGFXHAL::getTFTFrameBuffer() const;
*
* @brief Gets the frame buffer address used by the TFT controller.
*
* Gets the frame buffer address used by the TFT controller.
*
* @return The address of the frame buffer currently being displayed on the TFT.
*/
virtual uint16_t* getTFTFrameBuffer() const;
/**
* @fn virtual void TouchGFXHAL::setTFTFrameBuffer(uint16_t* adr);
*
* @brief Sets the frame buffer address used by the TFT controller.
*
* Sets the frame buffer address used by the TFT controller.
*
* @param [in,out] adr New frame buffer address.
*/
virtual void setTFTFrameBuffer(uint16_t* adr);
private:
touchgfx::CortexMMCUInstrumentation instrumentation;
};
/* USER CODE END TouchGFXHAL.hpp */
#endif // TouchGFXHAL_HPP
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : TouchGFXHAL.cpp
******************************************************************************
* This file was created by TouchGFX Generator 4.26.0. This file is only
* generated once! Delete this file from your project and re-generate code
* using STM32CubeMX or change this file manually to update it.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#include <TouchGFXHAL.hpp>
#include "FreeRTOS.h"
/* USER CODE BEGIN TouchGFXHAL.cpp */
using namespace touchgfx;
void TouchGFXHAL::initialize()
{
// Calling parent implementation of initialize().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
// Please note, HAL::initialize() must be called to initialize the framework.
TouchGFXGeneratedHAL::initialize();
instrumentation.init();
setMCUInstrumentation(&instrumentation);
enableMCULoadCalculation(true);
}
/**
* Gets the frame buffer address used by the TFT controller.
*
* @return The address of the frame buffer currently being displayed on the TFT.
*/
uint16_t* TouchGFXHAL::getTFTFrameBuffer() const
{
// Calling parent implementation of getTFTFrameBuffer().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
return TouchGFXGeneratedHAL::getTFTFrameBuffer();
}
/**
* Sets the frame buffer address used by the TFT controller.
*
* @param [in] address New frame buffer address.
*/
void TouchGFXHAL::setTFTFrameBuffer(uint16_t* address)
{
// Calling parent implementation of setTFTFrameBuffer(uint16_t* address).
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
TouchGFXGeneratedHAL::setTFTFrameBuffer(address);
}
/**
* This function is called whenever the framework has performed a partial draw.
*
* @param rect The area of the screen that has been drawn, expressed in absolute coordinates.
*
* @see flushFrameBuffer().
*/
void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
// Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
// Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
// be called to notify the touchgfx framework that flush has been performed.
// To calculate the start address of rect,
// use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
// defined in TouchGFXGeneratedHAL.cpp
TouchGFXGeneratedHAL::flushFrameBuffer(rect);
}
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
return TouchGFXGeneratedHAL::blockCopy(dest, src, numBytes);
}
/**
* Configures the interrupts relevant for TouchGFX. This primarily entails setting
* the interrupt priorities for the DMA and LCD interrupts.
*/
void TouchGFXHAL::configureInterrupts()
{
// Calling parent implementation of configureInterrupts().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
TouchGFXGeneratedHAL::configureInterrupts();
}
/**
* Used for enabling interrupts set in configureInterrupts()
*/
void TouchGFXHAL::enableInterrupts()
{
// Calling parent implementation of enableInterrupts().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
TouchGFXGeneratedHAL::enableInterrupts();
}
/**
* Used for disabling interrupts set in configureInterrupts()
*/
void TouchGFXHAL::disableInterrupts()
{
// Calling parent implementation of disableInterrupts().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
TouchGFXGeneratedHAL::disableInterrupts();
}
/**
* Configure the LCD controller to fire interrupts at VSYNC. Called automatically
* once TouchGFX initialization has completed.
*/
void TouchGFXHAL::enableLCDControllerInterrupt()
{
// Calling parent implementation of enableLCDControllerInterrupt().
//
// To overwrite the generated implementation, omit the call to the parent function
// and implement the needed functionality here.
TouchGFXGeneratedHAL::enableLCDControllerInterrupt();
}
bool TouchGFXHAL::beginFrame()
{
return TouchGFXGeneratedHAL::beginFrame();
}
void TouchGFXHAL::endFrame()
{
TouchGFXGeneratedHAL::endFrame();
}
extern "C"
{
portBASE_TYPE IdleTaskHook(void* p)
{
if ((int)p) //idle task sched out
{
touchgfx::HAL::getInstance()->setMCUActive(true);
}
else //idle task sched in
{
touchgfx::HAL::getInstance()->setMCUActive(false);
}
return pdTRUE;
}
}
/* USER CODE END TouchGFXHAL.cpp */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
4、增加mcu测量类实现函数
#ifndef CORTEXMMCUINSTRUMENTATION_HPP
#define CORTEXMMCUINSTRUMENTATION_HPP
#include <platform/core/MCUInstrumentation.hpp>
#include <stdint.h>
namespace touchgfx
{
/**
* @class CortexMMCUInstrumentation CortexMMCUInstrumentation.hpp platform/core/arm/cortex-m/CortexMMCUInstrumentation.hpp
*
* @brief Interface for instrumenting Cortex-M processors to measure MCU load via measured CPU
* cycles.
*
* Interface for instrumenting Cortex-M processors to measure MCU load via measured CPU
* cycles.
*
* @sa MCUInstrumentation
*/
class CortexMMCUInstrumentation : public MCUInstrumentation
{
public:
/**
* @fn virtual void CortexMMCUInstrumentation::init();
*
* @brief Initialization.
*
* Initialization.
*/
virtual void init();
/**
* @fn virtual unsigned int CortexMMCUInstrumentation::getElapsedUS(unsigned int start, unsigned int now, unsigned int clockfrequency);
*
* @brief Gets elapsed microseconds basedon clockfrequency.
*
* Gets elapsed microseconds basedon clockfrequency.
*
* @param start Start time.
* @param now Current time.
* @param clockfrequency Clock frequency of the system.
*
* @return Elapsed microseconds start and now.
*/
virtual unsigned int getElapsedUS(unsigned int start, unsigned int now, unsigned int clockfrequency);
/**
* @fn virtual unsigned int CortexMMCUInstrumentation::getCPUCycles();
*
* @brief Gets CPU cycles from register.
*
* Gets CPU cycles from register.
*
* @return CPU cycles.
*/
virtual unsigned int getCPUCycles();
/**
* @fn virtual void CortexMMCUInstrumentation::setMCUActive(bool active);
*
* @brief Register if MCU is active by measuring cpu cycles.
*
* Register if MCU is active by measuring cpu cycles. If user wishes to track
* MCU load, this method should be called whenever the OS Idle task is scheduled
* in or out. This method makes calls to a concrete implementation of GPIO
* functionality and a concrete implementation of cpu cycles.
*
* @param active If true, MCU is registered as being active, inactive otherwise.
*/
virtual void setMCUActive(bool active);
};
} // namespace touchgfx
#endif // CORTEXMMCUINSTRUMENTATION_HPP
#include <CortexMMCUInstrumentation.hpp>
#include <touchgfx/hal/HAL.hpp>
namespace touchgfx
{
void CortexMMCUInstrumentation::init()
{
// See: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/CEGHJDCF.html
//
// [24] Read/write TRCENA This bit must be set to 1 to enable use of the trace and debug blocks:
// Data Watchpoint and Trace (DWT)
// Instrumentation Trace Macrocell (ITM)
// Embedded Trace Macrocell (ETM)
// Trace Port Interface Unit (TPIU).
// This enables control of power usage unless tracing is required. The application can enable this, for ITM use, or use by a debugger.
// Enable Debug Exception and Monitor Control Register
*((volatile unsigned int*)0xE000EDFC) |= 0x01000000;
// Enable Lock Access Register
*((volatile unsigned int*)0xE0001FB0) |= 0xC5ACCE55;
// Enable Data Watchpoint and Trace Control Register
*((volatile unsigned int*)0xE0001000) |= 1;
}
//Board specific clockfrequency
unsigned int CortexMMCUInstrumentation::getElapsedUS(unsigned int start, unsigned int now, unsigned int clockfrequency)
{
return ((now - start) + (clockfrequency / 2)) / clockfrequency;
}
unsigned int CortexMMCUInstrumentation::getCPUCycles()
{
return *((volatile unsigned int*)0xE0001004);
}
void CortexMMCUInstrumentation::setMCUActive(bool active)
{
if (active) //idle task sched out
{
uint32_t cc_temp = getCPUCycles() - cc_in;
cc_consumed += cc_temp;
}
else //idle task sched in
{
cc_in = getCPUCycles();
}
}
}
5、添加MCU占用率和帧率获取函数

#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
#include <touchgfx/utils.hpp>
#include<touchgfx/hal/HAL.hpp>
#include "app_ui.h"
#include "lib_comm.h"
#include "comm_cmd.h"
#include "log_printf.h"
Model::Model() : modelListener(0)
{
#ifdef SIMULATOR
main_board_info_collect_req();
#endif
}
static void print_mcu_loader_percent()
{
static uint8_t lastVal = 0;
uint8_t mcu_loader = touchgfx::HAL::getInstance()->getMCULoadPct();
if (mcu_loader != lastVal)
{
lastVal = mcu_loader;
LOG_E("____mcu loader percent: %d\n", mcu_loader);
}
}
static void print_display_fps()
{
static uint16_t frameSkippedCounter = 0;
static uint16_t frames = 0;
static uint16_t fps = 0;
if (touchgfx::HAL::getInstance()->getLCDRefreshCount() > 1)
{
frameSkippedCounter++;
}
else
{
frames++;
}
if (frames + frameSkippedCounter >= 60)
{
if (fps != frames)
{
fps = frames;
LOG_E("____display fps: %d\n", fps);
}
frameSkippedCounter = 0;
frames = 0;
}
}
void Model::tick()
{
comm_data_t msg;
#ifndef SIMULATOR
print_mcu_loader_percent();
print_display_fps();
result_t ret = lib_comm_get_recv_data(&msg);
if ( ret == RES_OK )
{
handle_recv_msg_by_msg_id(&msg);
deal_message_distribute(msg);
}
else
{
// don't deal
}
app_ui_feed_monitor();
#endif
}
void Model::deal_message_distribute(const comm_data_t &msg)
{
if (modelListener)
{
modelListener->dealRespMessageDistribute(msg.comm_id);
}
}