记录一下,方便以后翻阅~
本人参考了热心网友分享的一些案例,并增加了一些个人认为比较好的想法,重新整合了一下代码。
硬件:某宝网上购买的STM32F103C8T6工控板,价格50¥左右;
思路:
1)利用通用定时器(选择定时器2)计算某段代码的运行时间;
2)顾名思义,会基于定时器2创建两个函数(TIM2_Clock_Start和TIM2_Clock_End)分别控制定时器2开始计时和结束计时,被测代码放在这两个代码的中间;
3)考虑计时的精度和最大计时长度,创建变量 u8 OverflowNum_cnt用来计算定时器2溢出中断的次数,创建变量u16 cnt_value用来计算最后一次定时器2的cnt值;
4)利用USART1实时读取某段代码的运行时间,并上传至电脑,便于查看。
这个工程较为简单,主要代码如下:
- 定时器2的初始化配置代码,注意:初始化后,不使能TIM2!!
/*****************************************************************
函数名称:TIM2_Init(u16 arr, u16 psc)
函数功能:定时器2 初始化函数
入口参数:u16 arr:自动重装载值, u16 psc:时钟预分频数
返回参数:无
开发作者:闲人Ne
******************************************************************/
void TIM2_Init(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruct;
TIM_DeInit(TIM2); // 定时器 2 复位
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // 使能TIM 2 时钟
// 定时器 2 参数初始化配置
TIM_TimeBaseInitTypeStruct.TIM_Period=arr;
TIM_TimeBaseInitTypeStruct.TIM_Prescaler=psc;
TIM_TimeBaseInitTypeStruct.TIM_CounterMode=TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInitTypeStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitTypeStruct);
// 定时器 3 中断优先级配置
NVIC_Configuration();
}
- 定时器2开始和结束相关代码,注意:引用了2个放在main.c文件里的变量!!
/*****************************************************************
函数名称:TIM2_Clock_Start()
函数功能:使能定时器 2 函数,开始计时
入口参数:无
返回参数:无
开发作者:闲人Ne
******************************************************************/
extern u8 OverflowNum_cnt; // u8 计数器,用来计算溢出中断的次数,初始化值为0000 0000
void TIM2_Clock_Start()
{
OverflowNum_cnt = 0;
TIM2->CNT=0x00; // 将定时器 2 的计数器至零
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 针对TIM2_DIER寄存器,[0]位,UIE至1,允许更新中断
TIM_Cmd(TIM2,ENABLE);
}
/*****************************************************************
函数名称:u16 TIM2_Clock_End()
函数功能:停止定时器 2 函数,读取CNT数值
入口参数:无
返回参数:u16 cnt_value,返回当前TIM2_CNT的值
开发作者:闲人Ne
******************************************************************/
extern u16 cnt_value;
u16 TIM2_Clock_End()
{
cnt_value = TIM2->CNT;
TIM_Cmd(TIM2,DISABLE);
return cnt_value;
}
- 定时器2的中断服务函数,主要目的是每发生一次溢出中断,OverflowNum_cnt值+1,如果OverflowNum_cnt值溢出,就报错。
/************************************************
函数名称:TIM2_IRQHandler()
函数功能:定时器2中断服务函数
入口参数:无
返回参数:无
开发作者:闲人Ne
*************************************************/
extern u8 OverflowNum_cnt; // u8 计数器,用来计算溢出中断的次数,初始化值为0000 0000
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) // 判断是否发生更新中断
{
if(OverflowNum_cnt==0XFF) // 判断OverflowNum_cnt是否溢出
{
printf("错误:超出最大计算时间!");
OverflowNum_cnt = 0; // OverflowNum_cnt置零
}
else
{
OverflowNum_cnt++;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); // 清除定时器2中断标志位
}
}
- 主函数,这里注意,当TIM2_arr = 7199,TIM2_psc = 0,那么TIM2 中断时间间隔为0.0001s,又因为u8 OverflowNum_cnt最大值为255,所以所创建的定时器2计算某段代码的运行时间上限是0.0001 X 255 = 0.0255s,即25.5ms。如果被测代码的实际运行时间大于25.5ms,那么就不适用了。
/************************************************
函数名称:int main()
函数功能:主函数入口
入口参数:无
返回参数:int
开发作者:闲人Ne
*************************************************/
u8 OverflowNum_cnt = 0; // 溢出的次数
u16 cnt_value = 0; // 最后一次cnt的数值
u16 TIM2_arr = 7199;
u16 TIM2_psc = 0;
float TIM2_looptime = 0.0001; // TIM2 中断时间间隔,TIM2_looptime=((TIM2_psc+1)/72000000)*(TIM2_arr+1)
float TIM2_clock = 0.0000000138888888888; // TIM2 cnt更新周期,TIM2_clock=1/72000000
u16 delay_time_ms=10; // 模拟某段代码的运行时间
int main(void)
{
float CodeRunTime=0; // 计算某段代码的运行时间
u8 t=0; // while循环计数用
delay_init();
LED_Init();
My_USART1_Init();
TIM2_Init(TIM2_arr, TIM2_psc);
NVIC_Configuration();
while(1)
{
TIM2_Clock_Start();
delay_ms(delay_time_ms); // 某段代码的运行时间
cnt_value=TIM2_Clock_End();
CodeRunTime = (float)cnt_value*TIM2_clock+(float)OverflowNum_cnt*TIM2_looptime;
t++;
if(t==100)
{
printf("设定某段代码的运行时间为:%d毫秒\r\n",delay_time_ms);
printf("实际某段代码的运行时间为:%f秒\r\n\r\n",CodeRunTime);
D1=!D1; //提示系统正在运行//
t=0;
}
delay_time_ms=delay_time_ms+1;
if(delay_time_ms>20)
delay_time_ms=10;
}
}
实验结果
在串口调试助手上显示结果如下:
经验分享
为什么要测试代码的运算时间?因为有时候,程序没必要运算的非常快,适当的降低运算时间,可以降低功耗。此外,有些功能需要实时的、快速的给与反馈,比如自动驾驶、无人机、主动控制这类工作,如果算法很牛逼,但是运算一次周期长,那也没什么实际用处。