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口进行辅助测量,使用起来非常方便。

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

    关注

    6037

    文章

    44557

    浏览量

    635167
  • 示波器
    +关注

    关注

    113

    文章

    6245

    浏览量

    184931
  • GPIO
    +关注

    关注

    16

    文章

    1204

    浏览量

    52089
  • 程序执行时间

    关注

    0

    文章

    2

    浏览量

    6699
收藏 人收藏

    评论

    相关推荐

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

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

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

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

    如何计算执行时间

    嗨,大家好,有没有方法计算程序程序的一部分(在两个断点之间)的执行时间?比如秒表之类的?我希望任何人都能帮上忙!抢劫 以上来自于百度翻译 以下为原文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

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

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

    stm32定时器中断程序

    STM32定时器是个强大的模块,定时器使用的频率也是很高的,定时器可以做一些基本的定时,还可以做PWM输出或者输入捕获功能。 以下是进行
    发表于 10-12 16:59 1.3w次阅读

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

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

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

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

    MCU上的代码执行时间

    编写的,而且开发人员常常被迫对代码进行手工优化,可能会回到汇编语言,以满足性能的需求。测量代码部分的实际执行时间可以帮助找到代码中的热点。本文将说明如何可以方便地测量和显示在基于Cortex-M MCU的...
    发表于 10-28 13:36 12次下载
    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>

    TPT19新特性之最坏情况执行时间的指示

    主机上的执行时间。 指示显示了哪些测试和哪些测试刺激延长了执行时间。因此,您可以使用指示作为代码变更的早期预警系统推导出测量目标板上真
    的头像 发表于 04-27 10:08 485次阅读
    TPT19新特性之最坏情况<b class='flag-5'>执行时间</b>的指示