stm32利用通用定时器实现函数运行时间精确测量

思路:采用TIM3,设置为向上计数模式,每次计数溢出(因为以72Mhz计数,stm32全为16位定时器,0.9ms就会溢出了),则变量加一,如此来测量。核心代码如下,测试过了,非常准确。但是此方法的误差在于,由于stm32没有32位定时器,所以0.9ms就会进入中断一次进行cntPeriod++,这个也会消耗时间的,实际上就是一种误差了,但是一般而言,误差效果可以忽略不计。

extern u32 cntPeriod;

void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	
	DBGMCU_Config(DBGMCU_TIM3_STOP,ENABLE);
	// 使得调试模式下,定时器也跟着暂停
	
	//定时器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
	//这条语句之后,定时器3的CNT计数值就为0了,对的
	
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	//加入这么一句,就不会事先进入一次中断了
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
	// 注意:第二次之后,此条语句执行完后,就马上进入了中断(因为第一次NVIC_Init已经配置完了),
	// 所以后面统计真正的中断次数得减去1
		
	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
	// 注意:第一次配置时候,此条语句执行完后,就马上进入了中断,原因是,上面TIM_ITConfig配置了

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}

void TIM3_Int_Close()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断优先级NVIC设置
	
	TIM_Cmd(TIM3, DISABLE);  //停止TIM3					 

	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; //IRQ通道被关闭
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器

}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
			
			++cntPeriod;
		
		}
}




void MeasureTimeStart()
{
    cntPeriod = 0;
    TIM3_Int_Init(64799,0);//1分频,即72Mhz的计数频率,计数到64800为0.9ms  ,64800/72=900us
}



float MeasureTimeEnd()  //别用double类型返回,用float,因为对keil对double支持不友好,尽量别用,
{
	float runTime;
	u32 cntTmp;
	u32 cntTim3 = TIM3->CNT;//为了尽可能的减小偏移误差,这里尽快拿到这个CNT值,实测函数跳转特别耗时
	
	TIM3_Int_Close();			 
	printf("\r\ncntTim3:%d \r\n",cntTim3);
	cntTmp = cntPeriod*64800 + cntTim3;
	runTime = (float)cntTmp/72.0/1000.0;	//返回多少ms
	
	return runTime;
}





int main(void)
{
	float val;
	
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化

	MeasureTimeStart();
	delay_1ms_my(1000);
	val = MeasureTimeEnd();
	printf("%f",val);
//	test();
	
	
	while(1);
}





注:delay_1ms_my()是我自己写的延时函数,测量后发现非常准确。

void delay_1us_my(void)  //64个nop指令,加上跳转等执行时间,差不多就是72个时钟周期了,stm32f1主频72Mhz,即1us

{  

  __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();  

  __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();  

  __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();  

  __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
    
  __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();

  __nop(); __nop(); __nop(); __nop();

}

void delay_1ms_my(u32 nms)
{
    u32 nus=nms*1000;
    while(--nus)
    {
        delay_1us_my();
    }
}

效果如下所示:

stm32利用通用定时器实现函数运行时间精确测量

此外,测了一下中间不加任何指令的运行时间,如下图所示:

stm32利用通用定时器实现函数运行时间精确测量

 stm32利用通用定时器实现函数运行时间精确测量

stm32利用通用定时器实现函数运行时间精确测量

即两条空语句之间会用掉49个时钟周期,约0.681us(固定的多余消耗,提高测量精度时候可以直接减去这个值),这是因为其实中间有函数跳转等操作,可以看出也还是挺快的了,而且可以看出这个时间测量方法精度非常的高,达到1/72us

 

其中发现一个很奇怪的现象,

            MeasureTimeStart();    // 假如是测空语句,这个放前面和放这里测出来的会有细微的差别,这个没关系的了,知道就好
            delay_1ms_my(1000);
//            _VZ(Smix,numOfStrongTask+numOfHardTask);        
            timeRunVzop = MeasureTimeEnd();
            printf("%f",timeRunVzop);
     while(1)
    {

//            TaskDataClear();   //这句代码有效的话,上面测量出来的时间会偏小3ms,想了很久,没想明白为什么,按道理说这后面的代码不会影响到上面的时间测量,只有一种解释,那就是编译后代码所占空间变大?导致前面的函数地址跳转变多了?我也不知道具体原因,反之就当真粗略使用吧
    }

上一篇:Eclipse环境下使用Maven注意事项


下一篇:rabbitMQ实战(一)---------使用pika库实现hello world