历史上的今天
返回首页

历史上的今天

今天是:2026年01月09日(星期五)

正在发生

2023年01月09日 | 获取STM32代码运行时间的技巧

2023-01-09 来源:zhihu

前言

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

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

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

借助示波器方法的实例

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。

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

使用定时器方法的实例

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



END


推荐阅读

史海拾趣

Fremont_Micro_Devices_USA公司的发展小趣事

Fremont Micro Devices USA公司的发展故事

故事一:初创与成长

Fremont Micro Devices USA(简称FMD或弗里蒙特微)的故事始于对高性能模拟及数模混合信号集成电路设计的执着追求。公司于多年前在加利福尼亚成立,起初是一家小型但充满活力的初创企业,专注于非易失性存储芯片、MCU芯片和电源管理芯片的研发。凭借其创始团队在半导体行业的深厚背景和技术专长,FMD迅速在消费电子、便携设备和通讯/网络领域崭露头角。通过持续的技术创新和产品研发,FMD逐渐积累了市场声誉,吸引了多家国际知名风险投资公司的关注和支持,为公司的快速发展奠定了坚实的基础。

故事二:技术突破与市场拓展

在FMD的发展历程中,技术突破是推动其不断前进的重要动力。公司研发团队拥有国内外顶尖半导体企业的工作经验,他们致力于全定制和半定制的数模混合芯片设计,不断提升产品的性能和可靠性。通过不断的技术积累和创新,FMD在Memory、MCU和PMIC等领域取得了显著的技术突破,拥有了业界领先的技术基础和IP积累。这些技术成果不仅提升了公司的市场竞争力,也为其在消费类、智能硬件、IOT和工控安防等应用领域的市场拓展提供了有力支持。

故事三:全球化布局与供应链管理

随着业务的不断扩张,FMD开始布局全球化市场。公司在美国本土建立了完善的研发和生产体系,同时在中国深圳设立了总部,并在香港设立了办事处,以便更好地服务亚太地区的客户。此外,FMD还积极与全球知名的电子元器件分销商合作,建立了稳定的供应链体系。通过优化供应链管理,FMD确保了产品的质量和交付速度,进一步提升了客户满意度和市场竞争力。

故事四:应对行业挑战与危机

在电子行业的发展过程中,FMD也面临着诸多挑战和危机。例如,全球半导体市场的周期性波动、国际贸易环境的变化以及新冠疫情的爆发等都对公司的运营和发展产生了一定的影响。然而,FMD凭借其强大的技术实力和市场敏锐度,积极应对这些挑战和危机。公司通过加强研发创新、优化产品结构、拓展新兴市场等方式,保持了业绩的稳定增长,并在逆境中实现了新的发展。

故事五:上市规划与未来发展

近年来,FMD在资本市场也取得了重要进展。公司已完成与中信证券的上市辅导协议签署,并在深圳证监局备案,拟在A股市场挂牌上市。这一举措不仅将为FMD带来更多的资金支持和发展动力,也将进一步提升公司的品牌影响力和市场竞争力。展望未来,FMD将继续秉承高性能、高可靠性和低成本的理念,加大在研发创新和市场拓展方面的投入力度,为客户提供更加优质的产品和服务,推动公司向更高层次的发展迈进。

德力西(DELIXI)公司的发展小趣事

德力西非常重视品牌建设,通过全方位的品牌建设及宣传,不断提高品牌知名度和美誉度。同时,公司也加快了国际化步伐。2007年,德力西低压电器业务迈向企业国际化的新阶段,在全国范围内进行资本大重组、产品大联合、市场大拓展和技术大提高。这些努力使德力西在国际市场上也取得了一席之地。

Gustav Klauke GmbH公司的发展小趣事

1984年,胡成中偕其弟胡成国等人在浙江省乐清市创立了“乐清县求精开关厂”,这便是德力西的前身。当时,企业仅有3位股东,5万元的资本,8名员工,以及单一的热继电器产品。凭借“以质取胜”的经营理念和灵活的机制,德力西在温州市电器行业中逐渐崭露头角。

Exar [Exar Corporation]公司的发展小趣事

进入21世纪后,数字视频应用的兴起为Exar提供了新的发展机遇。为了抓住这一机遇,公司在2000年推出了视频处理器和编解码器产品。这些产品不仅满足了市场对高质量视频处理的需求,也进一步巩固了Exar在通信和视频处理领域的地位。

American Micro Products Inc公司的发展小趣事

为了保持技术的领先地位,AMP高度重视研发团队的建设和人才培养。公司投入大量资源用于引进和培养高端人才,为研发团队提供了一流的工作环境和研发设备。这些人才为AMP的技术创新和产品升级提供了强有力的支持。

AIM公司的发展小趣事

AIM公司自创立以来,便以其前瞻性的视野和独特的技术理念,在电子行业中崭露头角。创始团队凭借对市场的敏锐洞察和对技术的深刻理解,将公司定位为提供创新解决方案的引领者。在创立初期,AIM便投入大量资源进行技术研发,逐渐在行业中建立了自己的技术优势和品牌形象。

问答坊 | AI 解惑

求一份数控直流电压源设计... 急!!!!!!高手进

本帖最后由 paulhyde 于 2014-9-15 04:14 编辑 设计出有一定输出电压范围和功能的数控直流电源,要求输出电压的范围为0V~9.9V,纹波不大于10mV,输出电流大于500mA,能预制置输出初值,输出电压值由数码管显示。 类似以上要求的也行.. ...…

查看全部问答>

[转帖]应聘数字电路笔试习题

1、同步电路和异步电路的区别是什么?(仕兰微电子) 2、什么是同步逻辑和异步逻辑?(汉王笔试) 同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系。 電路設計可分類為同步電路和非同步電路設計。同步電路利用時鐘 ...…

查看全部问答>

请教关于串口程序调试

请问没有ARM硬件平台的条件下,可以在pc机上只通过SDK编译调试串口程序,实现和另一台pc的串口数据收发么? 环境:pc机1+vs2005,WinCE6.0,  pc机2+通用串口精灵 注:我在WinCE中映射了串口,但是运行程序时提示无法打开串口,请问可 ...…

查看全部问答>

本人想实现wince6.0 + usb + 手机 上网

如标题所述,本人想通过usb链接手机的方式实现wince上网,在这里请教各位大侠,大家可以给我提供资料或者建议,小弟在这不胜感激!…

查看全部问答>

求助:背景图片挡住了文字

大家好,我使用函数ExtTextOut 输出文字,但是由于我的程序有一个全屏的背景图,所以加载图片后文字被遮挡住了。 我大概知道可能是透明、背景色之类的问题,但是改了参数效果依然不对,麻烦大家给指点下,最好能有个例子详细些,多谢啦…

查看全部问答>

MPLAB ICD2無法工作,請教

我用MPLAB V7.52 連接ICD2時,報錯,錯誤代碼是 Connecting to MPLAB ICD 2 ...Connected ICD0133: Firmware does not support command (0x7). ICD0082: Failed MPLAB ICD 2 operation MPLAB ICD 2 Ready 請問該怎麼弄,上一次用還是好好的呀 ...…

查看全部问答>

嵌入式的必备知识,请给个方向!

嵌入式需要软件和硬件方面的知识,软件方面有操作系统,硬件方面有微型计算机原理方面的。还需要掌握一些什么,望大家给出一个方向,以后准备朝这个方向发展。谢谢了!!…

查看全部问答>

PC机通过9针RS232给单片机数据

PC机通过9针RS232给单片机数据,单片机由串口中断获取数据并执行判断      为了方便调试,在p0/p2口接了灯。         可是实际在板上调试时,不论在PC端发任何数据,发现该LED一直不变。偶 ...…

查看全部问答>

3键控制电机,1键开关电机,2键加速,3键减速

#include <pic.h>__CONFIG(0X1832);// 877A,硬件已经调试通过#define keyO3  RB5    //接上拉做键盘输入#define keyO4  RB4    // #define keyI2  RB2    //做输出与RB4\\RB5\\RB ...…

查看全部问答>

如何在Hiwave中看到PORTB的端口状态

如何在Hiwave中看到PORTB的端口状态,是在Memory窗口中吗?PORTB的地址是多少?我用的是mc9212dg128的例子。谢谢! …

查看全部问答>