STM32U5F7VJT6Q (Cortex-M33, 160MHz) TouchGFX统计MCU占用率和FPS

  • 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);
    }
}
相关推荐
Heartache boy7 分钟前
野火STM32_HAL库版课程笔记-TIM通道捕获应用之编码器模式
笔记·stm32·单片机·嵌入式硬件
柔情的菜刀14 分钟前
踩坑实录|RK3588 BT1120 输出调试全解(适配GS2972)
嵌入式硬件
Lugas Luo33 分钟前
Ascend 310B 定制 SDHCI 主机控制器源码深层次劫持与优化解析
linux·嵌入式硬件
恒森宇电子有限公司2 小时前
芯晞微CSM057 线性充电管理芯片 封装SOT23-6
单片机·嵌入式硬件
蓝凌y3 小时前
51单片机之LCD1602
单片机·嵌入式硬件·51单片机
Lugas Luo3 小时前
Kernel 5.10 针对 eMMC 的 Detect、Power、Add 及深度优化解析
linux·嵌入式硬件
charlie1145141913 小时前
嵌入式C++教程实战之Linux下的单片机编程:从零搭建 STM32 开发工具链(4)从零构建 STM32 构建系统
linux·开发语言·c++·stm32·单片机·学习·嵌入式
欢乐熊嵌入式编程4 小时前
用 ESP32 + WiFi 做远程控制插座(从原理到实战)
单片机·wifi·智能路由器·esp32·远程控制插座
Lugas Luo4 小时前
Kernel 5.10 SD卡专属探测、上电与注册流程分析 (Detect -> Power Up -> Add)
linux·嵌入式硬件
weixin_462901974 小时前
esp32测试DH11模块
单片机