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);
    }
}
相关推荐
浩子智控2 小时前
zynq嵌入式开发(2)—基本开发测试实例
linux·嵌入式硬件·硬件架构
猫猫的小茶馆2 小时前
【Linux 驱动开发】Linux 内核启动过程详解
linux·c语言·arm开发·驱动开发·stm32·单片机·mcu
辰哥单片机设计2 小时前
STM32太阳能光伏板
stm32·单片机·嵌入式硬件
mftang2 小时前
MCU上应用的主流NVM技术: 嵌入式相变存储器(PCM) 和嵌入式磁阻随机存取存储器(MRAM)
单片机·嵌入式硬件·pcm
炸膛坦客2 小时前
Cortex-M3-STM32F1 开发:(五十四)CAN(车企会用),难但很重要
stm32·单片机·嵌入式硬件
willhuo2 小时前
RS485回响程序设计方案
单片机·lua
项目題供诗2 小时前
51单片机入门-LED点阵屏(九)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖2 小时前
51单片机ADC模数转换
单片机·嵌入式硬件·51单片机
项目題供诗2 小时前
51单片机入门-DS1302时钟(十)
单片机·嵌入式硬件·51单片机