0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用STM32定时器测量程序执行时间的方法详解

CHANBAEK 来源:固件工人 作者:固件工人 2023-01-17 15:11 次阅读

1.1 背景

单片机的固件开发过程中,有的时候需要评估固件代码的执行性能,会对部分关键程序代码的执行时间进行测量。通常会用到的测量程序执行时间的方法是使用示波器进行测量。一般步骤是借助单片机的某一个GPIO口,假设默认情况下GPIO口置1;在需要测量的程序代码开始处将GPIO口清0,然后执行程序代码段,在代码段的终止处将GPIO口重新置1;示波器设置成边沿触发方式,抓取GPIO口从清0到重新置1的这段波形,然后用示波器卡出GPIO口下降沿到上升沿的这段时间,也就是程序代码段的执行时间。

以上方法的不足之处在于需要用到示波器,而且需要借用MCU的一个GPIO进行辅助测量,灵活性也欠佳,实际使用不是太方便。那有没有更简便的测量方法呢?答案是肯定的,那就是使用MCU的定时器进行程序执行时间的测量。当然,为了提高时间的测量精度,MCU需要使用外部晶振来为其提供工作主频。下面就对该方法进行详细讲解。该方法结合下面提到的开发板,可以达到10ns以内的测量分辨率和1us以内的测量精度。

1.2 测试平台

这里使用的开发环境和相关硬件如下。

  • 操作系统:Ubuntu 20.04.2 LTS x86_64(使用uname -a命令查看)
  • 集成开发环境(IDE):Eclipse IDE for Embedded C/C++ Developers,Version: 2021-06 (4.20.0)
  • 硬件开发板:STM32F429I-DISCO
  • 本文对应的例程代码链接如下。

https://download.csdn.net/download/goodrenze/85162425

1.3 使用STM32定时器测量程序执行时间的方法详解

这里就结合开发板STM32F429I-DISCO上的STM32F429ZI单片机来演示使用SysTick系统定时器测量程序代码段执行时间的实现方法。

使用SysTick系统定时器测量程序执行时间之前,必须先确认定时器的以下参数

  • 定时器的时钟源频率。
  • 定时器的定时周期。
  • 定时器的计数方向。

这里的代码基于STM32F429I-DISCO开发板,该开发板的MCU外接8MHz的石英晶振,代码使用该外部晶振经内部PLL倍频后,产生168MHz的主频供MCU使用。这里的SysTick系统定时器的时钟源直接来自168MHz的主频,对该频率进行计数,所以每过1000 / 168 = 5.95238ns时间,定时器计数值就会加1。这里将SysTick定时器的定时周期设置成1ms,即每过1ms,SysTick定时器就会产生一次定时器中断。另外,SysTick定时器是倒计数定时器,即其计数值是递减的,当计数值减到为0时,继续减1时会重新加载重装载值并继续计时,同时产生定时器溢出中断。

确定了以上参数之后,后面的代码实现就非常简单了,只需要实现以下的几个功能函数皆可。

1)SysTick系统定时器初始化函数和中断处理函数。用于配置该定时器的定时周期为1ms,打开定时器中断并启动定时,同时实现对应的中断处理函数使定时器计数值累加。程序代码如下。

// 该函数为STM32的官方代码
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }


  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }


  /* Return function status */
  return HAL_OK;
}


// 该函数为STM32的官方代码,调用的SysTick_Config()函数在“core_cm4.h”头文件中有现成的实现
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);
}


// SysTick系统定时器中断入口函数
void SysTick_Handler(void)
{
  HAL_IncTick();
}


// SysTick系统定时器中断处理函数,对uwTick值进行累加
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

2)获取起始时间的函数。该函数用于获取SysTick系统定时器当前的毫秒计数值,以及当前的定时器计数值。程序代码如下。参数p_pdwStartMs为获取到的起始毫秒计数值,p_pdwStartNsTicks为获取到的起始定时器计数值。

void vGetStartTime(uint32_t* p_pdwStartMs, uint32_t* p_pdwStartNsTicks)
{
  *p_pdwStartMs = HAL_GetTick();
  *p_pdwStartNsTicks = SysTick->VAL;
}

3)获取时间间隔的函数。该函数用于获取当前时间相对于起始时间的时间间隔。程序代码如下。参数p_dwStartMs为起始毫秒计数值,p_dwStartNsTicks为起始定时器计数值,p_pdwIntervalMs为当前时间相对于p_dwStartMs的毫秒时间间隔,p_pdwIntervalNsTicks为当前时间相对于p_dwStartNsTicks的定时器计数间隔。

void vGetIntervalTime(uint32_t p_dwStartMs, uint32_t p_dwStartNsTicks, uint32_t* p_pdwIntervalMs, uint32_t* p_pdwIntervalNsTicks)
{
  uint32_t l_dwCurMs = HAL_GetTick();
  uint32_t l_dwCurNsTicks = SysTick->VAL;
  uint32_t l_dwReloadValue = SysTick->LOAD;


  // STM32F429ZI的定时器为倒数定时器。
  // 如果当前的定时器计数值比起始计数值要小,SysTick未发生相对起始时刻不足1ms的定时器中断,所以ms计数无需额外减1
  if(l_dwCurNsTicks <= p_dwStartNsTicks)
  {
    if(l_dwCurMs >= p_dwStartMs)
    {
      *p_pdwIntervalMs = l_dwCurMs - p_dwStartMs;
    }
    else
    {
      *p_pdwIntervalMs = ~(p_dwStartMs - l_dwCurMs) + 1;
    }
    *p_pdwIntervalNsTicks = p_dwStartNsTicks - l_dwCurNsTicks;
  }
  // 如果当前的定时器计数值比起始计数值要大,SysTick发生了相对起始时刻不足1ms的定时器中断,所以ms计数需要额外减1
  else
  {
    if(l_dwCurMs >= p_dwStartMs)
    {
      *p_pdwIntervalMs = l_dwCurMs - p_dwStartMs - 1;
    }
    else
    {
      *p_pdwIntervalMs = ~(p_dwStartMs - l_dwCurMs);
    }
    *p_pdwIntervalNsTicks = p_dwStartNsTicks + (l_dwReloadValue - l_dwCurNsTicks) + 1;
  }
}

4)获取程序代码段执行时间的演示例程。用于演示如何使用以上提到的相关函数来测量程序代码段的执行时间。

int main(void)
{
  uint32_t count = 0;
  uint32_t l_dwStartMs, l_dwIntervalMs;
  uint32_t l_dwStartNsTicks, l_dwIntervalNsTicks;
  float l_fUs;  // 微秒时间


  HAL_Init();


  /* Configure the system clock to 168 MHz */
  SystemClock_Config();


  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);


  vGetStartTime(&l_dwStartMs, &l_dwStartNsTicks);
#if 1
  while (1)
  {
    if (count == 0x3fffff)
    {
      BSP_LED_Toggle(LED3);
      BSP_LED_Toggle(LED4);
      count = 0;
      break;
    }
    count++;
  }
#else
  vDelayUs(1000);
#endif
  vGetIntervalTime(l_dwStartMs, l_dwStartNsTicks, &l_dwIntervalMs, &l_dwIntervalNsTicks);
  l_fUs = l_dwIntervalMs * 1000 + l_dwIntervalNsTicks * NS_PER_SYS_TICK / 1000.0f;


  while(1);
}

图1 以上演示例程代码段的执行时间

1.4 结语

通过以上提到的相关函数,可以很方便地实现程序执行时间的测量,而且可以在几乎任何地方使用(中断内部使用需注意中断优先级的影响)。另外,如果结合串口打印调试信息的功能,可以直接将测量到的执行时间直接打印输出,方便查看。本文提到的执行时间测量方法无需使用示波器,也不需要借用MCU的GPIO口进行辅助测量,使用起来非常方便。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6001

    文章

    43973

    浏览量

    620847
  • 示波器
    +关注

    关注

    111

    文章

    5650

    浏览量

    181775
  • GPIO
    +关注

    关注

    16

    文章

    1133

    浏览量

    50573
  • 程序执行时间

    关注

    0

    文章

    2

    浏览量

    6678
收藏 人收藏

    评论

    相关推荐

    基于KEIL软件的C语言编程,如何计算一段程序执行时间呢?

    如题,一段程序执行时间怎样确定,假如是把每条语句的执行时间累加,那么每条语句的时间如何确定???有别的方法可以确定
    发表于 01-18 06:10

    如何使用STM模块测量函数的执行时间

    我想问你如何使用 STM 模块测量函数的执行时间。 是否有可能通过BSP.H库的now () 函数为两个类型为ifx_tickTimer的变量(分别为起始变量和停止变量分配分笔数)来通过BSP默认
    发表于 01-22 06:38

    LabVIEW 程序执行架构——程序执行时间控制

    程序执行时间控制介绍在介绍时间控制元件之前,我们先来了解为什么程序需要控制时间。举例来说,若我们想要做到每秒显示出一张不一样的图片,那我们就
    发表于 12-26 11:38

    【创龙TMS320C6748开发板试用】+代码执行时间测量及代码调试

    所用的时钟周期 。第二种方法就是通过寄存的使用来测量代码执行时间,并将代码执行时间打印出来。
    发表于 11-13 13:28

    如何计算执行时间

    嗨,大家好,有没有方法计算程序程序的一部分(在两个断点之间)的执行时间?比如秒表之类的?我希望任何人都能帮上忙!抢劫 以上来自于百度翻译 以下为原文Hi all, Is there
    发表于 07-29 08:07

    如何在microblaze上测量C代码的执行时间

    如何在microblaze上测量C代码的执行时间?没有使用OS,所以我不能在time.h中使用桌面C函数,我是否必须使用xps计时或axi计时?任何人都可以使用xps计时
    发表于 10-30 09:36

    如何使用CYCLECOUNTER快速的测量执行时间

    系统基于该寄存实现了一个64位的循环周期计数CYCLECOUNTER,能够精确的测量程序执行时间,并且可以与C-SPY宏结合,成为开发人员非常有用的一个工具。CYCLECOUNT
    发表于 01-08 13:40

    如何在MCU上测量代码执行时间

    期限(deadline)要求。测量部分代码的实际执行时间可以帮助我们找到代码中的时间关键点。本文将展示如何轻松测量和显示基于Cortex-M MCU的代码片段的
    发表于 07-16 09:59

    STM32定时器的功能

    。(我这里配置了两个定时器 TIM3 和tim4这里不说cube的使用方法了)tim3 我配置了10ms的定时。注意点一:定时器配置时间的时
    发表于 08-12 06:31

    STM32定时器详解

    结合起来使用的话可以实现非常丰富的功能,可以测量输入信号的脉冲宽度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普遍方法,这方面知识非常有必要深入了解。STM32F4
    发表于 08-18 06:17

    介绍几种测量单片机程序行时间方法

      有时我们需要知道自己的单片机程序需要花费多长时间,delay延时的精确时间等。今天来介绍几种测量程序行时间
    发表于 03-23 14:55

    MPC5744p如何优化程序执行时间?

    作为 MPC5744p 的新手,我遇到了一个关于如何优化程序执行时间的问题。 这是我的中断服务例程中代码行的图片。 ↓这张图片显示了执行我测试过的代码行之前的定时器值 ↓这张图片显示
    发表于 06-05 12:50

    如何用SysTick实现测量程序行时间

    在实际的项目开发过程中,常常遇到需要得到一段代码的运行时间,通常的方法是用示波器来测量,这篇博文将用 SysTick 来实现 精确测量 程序
    的头像 发表于 05-09 14:07 5640次阅读
    如何用SysTick实现<b class='flag-5'>测量程序</b>运<b class='flag-5'>行时间</b>

    如何测量ARM Cortex-M MCU代码的执行时间

    期限(deadline)要求。测量部分代码的实际执行时间可以帮助我们找到代码中的时间关键点。 本文将展示如何轻松测量和显示基于Cortex-M MCU的代码片段的
    的头像 发表于 08-26 09:20 3093次阅读
    如何<b class='flag-5'>测量</b>ARM Cortex-M MCU代码的<b class='flag-5'>执行时间</b>

    STM32F407+CubeMX - 使用GPIO翻转+示波器测量函数的执行时间

    + 断点的方式可以测量某个函数的运行时间。所以,测量的前提是你用Keil软件作为嵌入式开发的IDE,其他IDE就不适用了。这里使用硬件的方式来测量某个函数的运
    发表于 12-05 12:36 9次下载
    <b class='flag-5'>STM32</b>F407+CubeMX - 使用GPIO翻转+示波器<b class='flag-5'>测量</b>函数的<b class='flag-5'>执行时间</b>