一、概述
在我测量mian函数大循环的周期时,发生了怪事:我一点也没有更改main函数的代码,只在初始化时添加了一些无用的语句,结果测量时间发生了巨大的差异。
二、事件详情
1、测试代码
main函数
/** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { LED_GPIO_Config(); /* USART1 config 115200 8-N-1 */ USART1_Config(); /** * @brief 通过注释掉程序,可以更改代码的长度 * @param 当注释的只剩下一个赋值等式的时候,计时长度值是0x116638 * @retval 当注释的剩下三个赋值等式的时候,计时长度值是0xEA726 */ DMA_USART1_Config(); NVIC_Configuration(); SysTick_Init(); SysTick_Time_Init(&UseDMATime); for(;;) { SysTick_Time_Start(); LED1(ON);LED2(OFF);LED3(OFF); Delay(); LED1(OFF);LED2(ON);LED3(OFF); Delay(); LED1(OFF);LED2(OFF);LED3(ON); Delay(); SysTick_Time_Stop(); } }
Delay函数
static void Delay(void) //简单的延时函数 { uint16_t i; /*填充将要发送的数据*/ for(i = 0;i < SENDBUFF_SIZE ; i++) { SendBuff[i] = (uint8_t)i; } }
说明:
(1)测量代码段
LED1(ON);LED2(OFF);LED3(OFF);
Delay();
LED1(OFF);LED2(ON);LED3(OFF);
Delay();
LED1(OFF);LED2(OFF);LED3(ON);
Delay();
(2)无效代码段
之所以称之为无效,实际上指的是“这些代码在程序中起到的是很次要的作用,主要是给CPU一些空指令,当然也会起到改变代码量的作用”。
LED_GPIO_Config(); /* USART1 config 115200 8-N-1 */ USART1_Config(); /** * @brief 通过注释掉程序,可以更改代码的长度 * @param 当注释的只剩下一个赋值等式的时候,计时长度值是0x116638 * @retval 当注释的剩下三个赋值等式的时候,计时长度值是0xEA726 */ DMA_USART1_Config();
(3)与测量计时有关的代码
SysTick_Init(); SysTick_Time_Init(&UseDMATime); SysTick_Time_Start(); SysTick_Time_Stop();
关于测量计时代码的实现在随笔STM32之系统滴答定时器中进行了详细讲解。
2、奇怪现象
DMA_USART1_Config是在大循环之前调用的一个无用的函数,实际上可以称之为空函数或者延时函数。通过注释掉里边的语句可以改变排在DMA_USART1_Config代码段之后代码在Flash上的位置,也可以改变整个代码段的量(尽管很小)。
void DMA_USART1_Config(void) { DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/ DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE; /*外设地址不增*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; }
笔者发现,当这个函数完全保留三行语句的时候,while循环测出来的滴答计时器脉冲数是0xEA726(0d960294);而当注释掉只剩下一行语句时,脉冲数变为0x116638(0d1140280)。这中间的差距是0d179986,占0xEA726的百分比是18.7%。也就是说注释语句后的代码执行时间,比没有注释时的时间多了将近1/5,这值得考虑其中的原因。
三、分析与结论
1、分析
测量代码段中有关LED操作的代码执行时间几乎可以忽略,而占主要运行时间的是Delay函数,只要分析Delay函数,就能找到其中的问题。
Delay函数
static void Delay(void) //简单的延时函数 { uint16_t i; /*填充将要发送的数据*/ for(i = 0;i < SENDBUFF_SIZE ; i++) { SendBuff[i] = (uint8_t)i; } }
Delay函数在两种情况下的反汇编代码
在完全保留代码的情况下
在注释掉部分代码的情况下
可以看到Delay函数的反汇编代码内容没有改变,改变的只是代码在Flash中的位置(当然也是程序执行时的位置)。
而后,我有试着在
DMA_USART1_Config代码的基础(3行语句)上不断增加代码,发现计时脉冲数周期性的在0xEA726、0x116638两个中变换。
由此可以确定,就是由于代码的位置改变了程序的运行时间,也即改变了程序的运行速度。
2、结论
即使是同样的代码,通过改变代码的位置可以改变代码的运行时间。而最根本的原因是有些指令的周期给指令所在的Flash中的位置有关。
3、还未完成的工作
但是,由于J-link硬件调试时,单步调试误差太大,取得的值很有出入,难以确定究竟是Delay函数中哪些指令引起时间偏差。