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

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

3天内不再提示

简析如何获取单片机代码运行时间的方法

GReq_mcu168 来源:博客园 作者:amanlikethis 2021-04-20 11:11 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

前言

单片机编程者需要知道自己的程序需要花费多长时间、while周期是多少、delay延时是否真如函数功能描述那样精确延时。

很多时候,我们想知道这些参数,但是由于懒惰或者没有简单的办法,将这件事推到“明天”。笔者提出了一种简便的测试方法,可以解决这些问题。

测试代码的运行时间的两种方法:

使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。

借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。

以下内容为这两种方案的实例,以STM32为测试平台。如果读者是在另外的硬件平台上测试,实际也不难,思路都是一样的,自己可以编写对应的测试代码。

借助示波器方法的实例

Delay_us函数使用STM32系统滴答定时器实现:

#include “systick.h”

/* SystemFrequency / 1000 1ms中断一次

* SystemFrequency / 100000 10us中断一次

* SystemFrequency / 1000000 1us中断一次

*/

#define SYSTICKPERIOD 0.000001

#define SYSTICKFREQUENCY (1/SYSTICKPERIOD)

/**

* @brief 读取SysTick的状态位COUNTFLAG

* @param

* @retval The new state of USART_FLAG (SET or RESET)。

*/

static FlagStatus SysTick_GetFlagStatus(void)

{

if(SysTick-》CTRL&SysTick_CTRL_COUNTFLAG_Msk)

{

return SET;

}

else

{

return RESET;

}

}

/**

* @brief 配置系统滴答定时器 SysTick

* @param 无

* @retval 1 = failed, 0 = successful

*/

uint32_t SysTick_Init(void)

{

/* 设置定时周期为1us */

if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))

{

/* Capture error */

return (1);

}

/* 关闭滴答定时器且禁止中断 */

SysTick-》CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);

return (0);

}

/**

* @brief us延时程序,10us为一个单位

* @param

* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us

* @retval 无

*/

void Delay_us(__IO uint32_t nTime)

{

/* 清零计数器并使能滴答定时器 */

SysTick-》VAL = 0;

SysTick-》CTRL |= SysTick_CTRL_ENABLE_Msk;

for( ; nTime 》 0 ; nTime--)

{

/* 等待一个延时单位的结束 */

while(SysTick_GetFlagStatus() != SET);

}

/* 关闭滴答定时器 */

SysTick-》CTRL &= ~ SysTick_CTRL_ENABLE_Msk;

}

检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:

#ifndef __GPIO_H

#define __GPIO_H

#include “stm32f10x.h”

#define LOW 0

#define HIGH 1

/* 带参宏,可以像内联函数一样使用 */

#define TX(a) if (a)

GPIO_SetBits(GPIOB,GPIO_Pin_0);

else

GPIO_ResetBits(GPIOB,GPIO_Pin_0)

void GPIO_Config(void);

#endif

#include “gpio.h”

/**

* @brief 初始化GPIO

* @param 无

* @retval 无

*/

void GPIO_Config(void)

{

/*定义一个GPIO_InitTypeDef类型的结构体*/

GPIO_InitTypeDef GPIO_InitStructure;

/*开启LED的外设时钟*/

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

}

在main函数中检验Delay_us的执行时间:

#include “systick.h”

#include “gpio.h”

/**

* @brief 主函数

* @param 无

* @retval 无

*/

int main(void)

{

GPIO_Config();

/* 配置SysTick定时周期为1us */

SysTick_Init();

for(;;)

{

TX(HIGH);

Delay_us(1);

TX(LOW);

Delay_us(100);

}

}

示波器的观察结果

可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。

更改一下main函数的延时参数:

int main(void)

{

/* LED 端口初始化 */

GPIO_Config();

/* 配置SysTick定时周期为1us */

SysTick_Init();

for(;;)

{

TX(HIGH);

Delay_us(10);

TX(LOW);

Delay_us(100);

}

}

示波器的观察结果:

可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。

结论:此延时函数基本上还是可靠的。

使用定时器方法的实例

至于使用定时器方法,软件检测程序段的执行时间,程序实现思路见STM32之系统滴答定时器:

http://www.cnblogs.com/amanlikethis/p/3730205.html

笔者已经将检查软件的使用封装成库,使用方法在链接文章中也有介绍。我们这里只做一下简要的实践活动。

Delay_us函数使用STM32定时器2实现:

#include “timer.h”

/* SystemFrequency / 1000 1ms中断一次

* SystemFrequency / 100000 10us中断一次

* SystemFrequency / 1000000 1us中断一次

*/

#define SYSTICKPERIOD 0.000001

#define SYSTICKFREQUENCY (1/SYSTICKPERIOD)

/**

* @brief 定时器2的初始化,,定时周期1uS

* @param 无

* @retval 无

*/

void TIM2_Init(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

/*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

/* Time base configuration */

TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_ARRPreloadConfig(TIM2, ENABLE);

/* 设置更新请求源只在计数器上溢或下溢时产生中断 */

TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);

}

/**

* @brief us延时程序,10us为一个单位

* @param

* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us

* @retval 无

*/

void Delay_us(__IO uint32_t nTime)

{

/* 清零计数器并使能滴答定时器 */

TIM2-》CNT = 0;

TIM_Cmd(TIM2, ENABLE);

for( ; nTime 》 0 ; nTime--)

{

/* 等待一个延时单位的结束 */

while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);

}

TIM_Cmd(TIM2, DISABLE);

}

在main函数中检验Delay_us的执行时间:

#include “stm32f10x.h”

#include “Timer_Drive.h”

#include “gpio.h”

#include “systick.h”

TimingVarTypeDef Time;

int main(void)

{

TIM2_Init();

SysTick_Init();

SysTick_Time_Init(&Time);

for(;;)

{

SysTick_Time_Start();

Delay_us(1000);

SysTick_Time_Stop();

}

}

怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。

可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。

备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。

两种方法对比

软件测试方法:

操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。

示波器方法

操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

综合对比,推荐使用示波器方法。

编辑:jq

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

    关注

    6074

    文章

    45340

    浏览量

    663536
  • 定时器
    +关注

    关注

    23

    文章

    3360

    浏览量

    121743
  • 函数
    +关注

    关注

    3

    文章

    4406

    浏览量

    66838

原文标题:实用 | 获取单片机代码运行时间的方法

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    FLASH中的代码是如何得到运行的呢

    以ARM为例: ARM-cortex-M3/4的单片机(比如STM32 等):该类单片机代码在nor flash中,cortex内核可以直接运行,不需要将
    发表于 12-04 08:06

    单片机程序的执行

    ,还是差着几个数量级,所以拷贝到DDRRAM中。这时,一个程序的代码和数据是连续存放的,其中代码段是只读区域,数据段是可读写区域(这是由操作系统的内存管理机制决定的)。 运行时,再将它们拷贝到速度
    发表于 12-04 06:20

    使用Nuclei Studio IDE计算程序运行时间

    和结束处调用两次_gettimeofday()函数,获取两个tv_sec/tv_usec值,然后printf输出起止tv值的差,即可获得程序运行时间
    发表于 10-28 08:25

    rt-thread线程调用rt_thread_mdelay延时后再恢复运行时thread会变,导致mutex释放失败,怎么解决?

    代码片段如下: 运行结果: 可以看到,在线程延时后,再恢复运行时,用rt_thread_self函数获取到的值与没有延时时获取到的值不一
    发表于 09-09 08:06

    3档定时2档调速!纳祥科技以单片机为核心的小型涡轮洗衣器方案

    吸附在固定设备上,单片机接收按键指令,控制纯铜电机离心高速运转,带动仿手洗螺旋涡轮正反转,配合精准的洗涤转速、运行时间,实现强劲去渍。方案集合了单片机、纯铜电机、3
    的头像 发表于 08-25 10:58 467次阅读
    3档定时2档调速!纳祥科技以<b class='flag-5'>单片机</b>为核心的小型涡轮洗衣器方案

    单片机原理及应用详解

    的基本原理 核心组成 : CPU :负责执行指令(如算术运算、逻辑控制)。 存储器 : ROM/Flash :存储程序代码(如固件)。 RAM :临时存储运行时的数据。 I/O端口 :连接外部设备(如传感器、显示屏、按键)。 定时器/计数器 :用于
    的头像 发表于 08-11 13:57 1545次阅读

    怎么测单片机系统频率

    单片机系统频率是指单片机工作时的时钟频率,它直接影响单片机运行速度和处理能力,准确测量系统频率对单片机应用开发、程序调试和性能优化具有重要
    的头像 发表于 07-25 11:39 476次阅读

    单片机怎么驱动电机?

    、高效的控制。       单片机本身的输出电流和电压较小,通常无法直接驱动功率较大的电机,这是因为电机在启动和运行时需要较大的电流,若直接由单片机供电,可能会因过载损坏单片机。因此,
    的头像 发表于 07-25 09:31 439次阅读

    PLC设备运行时间数据采集到运维管理平台如何实现

    工业智能网关通过接入PLC,能够实时采集设备运行时间并通过5G/4G/WIFI/以太网等方式对接到云平台或上位中,从而全面统计各个设备的运行时间,管理人员能够快速定位处于高负荷或闲置状态的设备,也能了解设备故障率、OEE等数据
    的头像 发表于 06-20 16:02 575次阅读
    PLC设备<b class='flag-5'>运行时间</b>数据采集到运维管理平台如何实现

    基于RK3576的时间参数系统操作

    本文介绍了在EASYEAIorin-nano评估套件中进行时间操作的方法,包括查看和设置系统时间、时区设置、系统运行时长等。通过date和hwclock命令可以查看和设置系统
    的头像 发表于 05-12 14:32 728次阅读
    基于RK3576的<b class='flag-5'>时间</b>参数系统操作

    获取具有三个输出的自定义模型的输出张量,运行时错误是怎么回事?

    () infer_request.start_async() infer_request.wait() output = infer_request.get_output_tensor() 打印(输出) 收到的错误: 运行时错误:必须对只有一个参数的函数调用 get_output_tensor()。
    发表于 03-05 09:44

    在Windows上安装OpenVINO™运行时遇到错误怎么解决?

    已在 Windows* 上安装OpenVINO™运行时。 配置环境变量。 遇到错误: Location is not available.
    发表于 03-05 09:13

    运行时OpenVINO™找不到模型优化器,为什么?

    运行时OpenVINO™找不到模型优化器,运行模型下载程序命令并收到错误消息: omz_downloader:找不到命令
    发表于 03-05 08:16

    为了防止dlpc350在运行时中断,如何去校验是否还在和电脑进行连接?

    大家好, 我有2个问题想请教各位大佬 1:为了防止dlpc350在运行时中断,如何去校验是否还在和电脑进行连接? 我想做一个线程隔一段时间就去访问以下它的连接状态,求大佬指点 2:如果重连复选usb已被占用,该如何处理?使用什么方法
    发表于 02-20 08:40

    智能电机控制装置如何最大限度地提高恢复能力和正常运行时间

    机器恢复能力和正常运行时间的智能电机控制装置。 这些应用中的电机控制装置必须能够控制并保护 75 马力 (HP) 至 700 马力的电机。为实现弹性运行,需要对机器进行全方位的保护,包括过载保护、接地故障保护和相位不平衡保护等。 同时还应包括触点磨损自诊断和线圈过
    的头像 发表于 01-25 16:40 1274次阅读
    智能电机控制装置如何最大限度地提高恢复能力和正常<b class='flag-5'>运行时间</b>