zoukankan      html  css  js  c++  java
  • 获取单片机代码运行时间的方法

      单片机编程者需要知道自己的程序需要花费多长时间、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;
    }
    View Code

    检验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
    View Code
    #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);    
    }
    View Code

    在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之系统滴答定时器。笔者已经将检查软件的使用封装成库,使用方法在链接文章中也有介绍。我们这里只做一下简要的实践活动。

    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);
    }
    View Code

    在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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

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

  • 相关阅读:
    HDU 1025 Constructing Roads In JGShining's Kingdom (DP+二分)
    HDU 1158 Employment Planning
    HDU 2059 龟兔赛跑
    Csharp 简单操作Word模板文件
    Csharp windowform datagridview Clipboard TO EXCEL OR FROM EXCEL DATA 保存datagridview所有數據
    Csharp 讀寫文件內容搜索自動彈出 AutoCompleteMode
    Csharp windowform controls clear
    CSS DIV大图片右上角叠加小图片
    Csharp DataGridView自定义添加DateTimePicker控件日期列
    Csharp 打印Word文件默認打印機或選擇打印機設置代碼
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3990826.html
Copyright © 2011-2022 走看看