Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字

一、问题描述

在学习野火霸天虎F407寄存器点亮LED时,出现实验现象:LED灯不亮,野火霸天虎F407资料。
Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字_第1张图片

main.c代码如下:

#include "stm32f4xx.h"

void Delay(unsigned int count);

int main(void)
{   
#if 0
    /* 第一步:开启GPIO端口的时钟 */
    /* 打开GPIOF端口的时钟 */
    *(unsigned int *)(0x40023800+0x3f0) |= (1<<5);
    
    /* 第二步:控制GPIO的方向 */
    /* GPIOF 配置为输出 */
    *(unsigned int *)(0x40021400+0x00) &= ~((0x03) << (2*6));
    *(unsigned int *)(0x40021400+0x00) |= (1 << (2*6));
    
    /* 第三步:控制GPIO的数据输出寄存器 */
    /* PF6 输出高电平 */
    *(unsigned int *)(0x40021400+0x14) |= (1 << 6);
    /* PF6 输出低电平 */
    *(unsigned int *)(0x40021400+0x14) &= ~(1 << 6);
    
#elif 0
    /* 第一步:开启GPIO端口的时钟 */
    /* 打开GPIOF端口的时钟 */
    RCC_AHB1ENR |= (1<<5);
    
    /* 第二步:控制GPIO的方向 */
    /* GPIOF 配置为输出 */
    GPIO_MODER &= ~((0x03) << (2*6));
    GPIO_MODER |= (1 << (2*6));
    
    /* 第三步:控制GPIO的数据输出寄存器 */
    /* PF6 输出高电平 */
    GPIO_ODR |= (1 << 6);
    /* PF6 输出低电平 */
    GPIO_ODR &= ~(1 << 6);
#elif 0
    //任务1-把其他两个灯也点亮
    RCC_AHB1ENR |= (1<<5); //开启GPIO端口时钟
    
    //设置GPIOF6为推挽输出
    GPIO_MODER &= ~((0x03) << (2*6));  
    GPIO_MODER |= (1 << (2*6));
    GPIO_ODR |= (1 << 6);
    GPIO_ODR &= ~(1 << 6);
    //设置GPIOF7为推挽输出
    GPIO_MODER &= ~((0x03) << (2*7));  
    GPIO_MODER |= (1 << (2*7));
    GPIO_ODR |= (1 << 7);
    GPIO_ODR &= ~(1 << 7);
    //设置GPIOF8为推挽输出
    GPIO_MODER &= ~((0x03) << (2*8));  
    GPIO_MODER |= (1 << (2*8));
    GPIO_ODR |= (1 << 8);
    GPIO_ODR &= ~(1 << 8);

#elif 1
    //任务1-把其他两个灯也点亮
    RCC_AHB1ENR |= (1<<5); //开启GPIO端口时钟
    
    //设置GPIOF6为推挽输出
    GPIO_MODER &= ~((0x03) << (2*6));  
    GPIO_MODER |= (1 << (2*6));
    //设置GPIOF7为推挽输出
    GPIO_MODER &= ~((0x03) << (2*7));  
    GPIO_MODER |= (1 << (2*7));
    //设置GPIOF8为推挽输出
    GPIO_MODER &= ~((0x03) << (2*8));  
    GPIO_MODER |= (1 << (2*8));
    
    while(1)
    {
        GPIO_ODR &= ~(1 << 6);
        Delay(0xfffff);
        GPIO_ODR |= (1 << 6);
        Delay(0xfffff);
        
        GPIO_ODR &= ~(1 << 7);
        Delay(0xfffff);
        GPIO_ODR |= (1 << 7);
        Delay(0xfffff);
        
        GPIO_ODR &= ~(1 << 8);
        Delay(0xfffff);
        GPIO_ODR |= (1 << 8);
        Delay(0xfffff);
    }  
 #endif 
}


//延时函数
void Delay(unsigned int count)
{
    for(;count!=0;count--);
}

void SystemInit(void)
{
    /* 函数体为空,目的是为了骗过编译器不报错 */
}

/*
1-把其他两个灯也点亮
2-实现三个灯闪烁(时间的控制使用软件延时)
*/

二、问题分析

通过分析main.c代码,导致出现上述现象的间接原因是延时函数没有起作用。检查延时函数的实现代码,并没有错误。这不禁使我想起《程序员的自我修养——链接、装载、库》一书所提到的程序源代码经过预编译-》编译-》汇编-》链接,所以极大可能是编译器在编译过程中优化掉了我的延时函数,使得整个程序不能按照预定功能实现。

打开keil5的调试功能,查看对应main.c的反汇编文件:
经过优化的delay函数:
Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字_第2张图片
未经过优化的delay函数:
Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字_第3张图片
优化之后的 delay 函数没有for循环延时操作,因此失去延时的效果。

三、问题解决

3.1 降低ARM Compiler version

在Target设置界面下,Code Generation默认的是ARM Compiler version 6。
Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字_第4张图片
将ARM Compiler version 6改为ARM Compiler version 5即可。Keil5中写的软件延时函数不起作用现象解析_ARM_Compiler_volatile关键字_第5张图片

3.2 volatile关键字修饰

volatile关键字影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错。volatile关键字最通俗的解释是,告诉编译器这个变量我有其他用,不要给我随便优化掉。

原延时函数

//延时函数
void Delay(unsigned int count)
{
    for(;count!=0;count--);
}

添加volate关键字修饰

//延时函数
void Delay(volatile unsigned int count)
{
    for(;count!=0;count--);
}

推荐使用方法2,原因如下:

1.自定义延时函数中使用 volatile 去声明 val 变量可以解决编译器优化带来的延时失效问题;
2.编译器优化可以使代码更加精炼,执行效率更高。

参考资料

1. Keil AC5 和 AC6的一些区别
2. 编译器优化对自定义延时程序的影响(volatile详解实验一)
3. C语言丨深入理解volatile关键字

你可能感兴趣的:(#,STM32,arm开发,嵌入式开发,stm32,单片机)