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

news2024/9/21 19:06:39

一、问题描述

在学习野火霸天虎F407寄存器点亮LED时,出现实验现象:LED灯不亮,野火霸天虎F407资料。
在这里插入图片描述

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函数:
在这里插入图片描述
未经过优化的delay函数:
在这里插入图片描述
优化之后的 delay 函数没有for循环延时操作,因此失去延时的效果。

三、问题解决

3.1 降低ARM Compiler version

在Target设置界面下,Code Generation默认的是ARM Compiler version 6。
在这里插入图片描述
将ARM Compiler version 6改为ARM Compiler version 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关键字

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Axure教程——循环倒计时

本文介绍的是用Axure制作的循环倒计时 效果 预览&#xff1a;https://zhgcck.axshare.com 功能 1、点击“开始”按钮&#xff0c;倒计时开始 2、数值到1时&#xff0c;从10重新倒计时 制作 一、需要的元件 矩形、动态面板 二、制作过程 拖入一个动态面板&#xff0c;命名为…

gnuplot 命令行绘图工具命令

gnuplot命令行绘图工具命令 绘图示例预览 gnuplot工具非常强大&#xff0c;可以在命令行进行曲线绘图&#xff0c;当然也可以在UI界面绘图。 绘图命令&#xff1a; gnuplot> plot test.csv u ($0):1 w lp t c1, test.csv u ($0):2 w lp t c2绘图效果&#xff1a; 数据文…

CSDN 成长记

博客之星入围排行榜 - 2023.5.7 博文 PaddleVideo 简介以及文件目录详解 - 入选内容榜咯 - 2023.5.9 付费专栏 微机系统与接口上机实验_TD PITE型 终于开张咯 - 2023.5.15 博文 ResNet 论文理解含视频 - 入选内容榜第13名 - 2023.5.16 博文 ResNet 论文理解含视频 - 入选全站综…

4.设计模式之后七种模式后11种模式命令访问者迭代器发布订阅中介者忘备录解释器状态策略职责链和空模式

1.命令(command)模式 不知道命令接收者(对象)是谁,支持撤销 (接受者 间接调用执行 的具体行为) 命令调用者和接收者解耦 //只要实现命令接口即可 (就是客户端给个命令,然后命令类传给接收类执行) 优点和缺点 容易撤销操作 命令队列可以多线程操作 增加过多的命令类 空命令也是一…

关于credal set和credal decision tree的一点思考(其实就是论文笔记)

阅读Abelln老师的Credal-C4.5时&#xff0c;发现好难。。。然后又额外补充了一些论文&#xff0c;终于稍微懂一点点了&#xff0c;所以记录如下。 credal set在DS theory的定义如下 [1]&#xff1a; 这句话的意思是&#xff08;证据理论中的&#xff09;credal set是一个概率…

Web安全-Behinder(冰蝎)Webshell管理工具使用

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 工具下载地址02 运行环境03 工具介绍04 使用案例 01 工具下载地址 https://github.com/rebeyond/Behinder点击页面右侧"releases"&#xff0c;进入工具的版本下载页面。 在个人终端安…

经典软件工程复兴?大模型驱动的软件工程实践标准化

TL&#xff1b;DR。 简单来说&#xff0c;本文探讨了大模型驱动的软件工程实践标准化&#xff0c;以及如何将需求和设计规范化为 DSL 格式。通过这种方式&#xff0c;可以让 AI 更自动化、高效地编写代码。 随着大语言模型在软件开发中的应用越来越广泛&#xff0c;传统的软件工…

sourcetree中添加秘钥 - 工具篇

文章目录 1. 首先配置秘钥2. 打开sourcetree 1. 首先配置秘钥 参考文章&#xff1a;https://blog.csdn.net/qq_40968685/article/details/131328007 2. 打开sourcetree 打开“选项” 然后&#xff0c;从gitlab中复制ssh地址到sourcetree中&#xff0c;会看到如图提示&…

【python】ndarray的广播功能

目录 0.环境 1.背景简介 2.ndarray的广播功能 2.1概念 2.2意义 2.3使用前提 2.4举例 2.5完整代码 0.环境 windows jupyter notebook验证代码 1.背景简介 我是在查询【ndarray和array的区别】时&#xff0c;看到了“广播功能”这个词&#xff0c;之前只在网络的相关定义…

分布式版本控制系统Git介绍

Git 是一款开源的分布式版本控制系统&#xff0c;具备分布式、轻量级分支、强大的协作能力以及适用于大小项目的版本管理。本文简要介绍Git工具的特性、Git中的对象以及分支管理&#xff0c;以加深了解。 1、版本控制系统介绍 版本控制是指对软件开发过程中各种程序代码、配置…

Docker是什么以及docker的常用命令

Docker简介 Docker是一种开源的容器化平台&#xff0c;用于构建、部署和运行应用程序。它允许开发人员将应用程序及其所有依赖项打包到一个称为"容器"的独立单元中。这个容器可以在任何支持Docker的环境中运行&#xff0c;无论是开发人员的本地机器、虚拟机还是云服务…

Bert

参考资料&#xff1a; 《机器学习》李宏毅 1 Bert 是怎么运作的&#xff1f; Bert 是一种自监督学习&#xff08;Self-supervised Learning&#xff09;模型。Bert 的目标是 pre-train 出一个能够理解语义的多功能语言模型&#xff0c;使之能够在特定任务上只学习较少的带标…

快速上手 vercel,手把手教你白嫖部署上线你的个人项目

壹 ❀ 关于 vercel Vercel 是一个云服务平台&#xff0c;支持静态网站&#xff08;纯静态页面&#xff0c;比如现在base utils 文档也是基于vercel&#xff09;和动态网站的应用部署、预览和上线。如果你用过 GitHub Pages &#xff0c;那么心里可能不会太陌生&#xff0c;但你…

Web3 在Truffle项目中编写出自己的第一个solidity智能合约

好 上文Web3本地搭建truffle智能合约开发环境我们在本地搭起了一个 Truffle 项目 然后 我们说明项目中文件的用途 然后 我们还是通过ganache模拟一个本地的区块链环境 然后打开我们搭建的Truffle项目 首先 我们第一个要看的肯定是truffle-config.js 肯定要先配好连接的环境 我…

UDP 协议【传输层协议】

文章目录 1. 传输层1.1 TCP 与 UDP 2. 端口号2.1 端口号标识进程2.2 通过IP地址、端口号、协议号进行通信识别2.3 协议号2.4 端口号的范围2.5 常用命令netstatiostatpidof 2.6 存疑 3. UDP 协议3.1 地位3.2 报头的分离和交付3.3 UDP 报文的格式3.4 UDP 数据封装和分用数据封装数…

rk3288中apk串口打开失败,selinux权限问题

apk打开串口失败 报错信息&#xff1a; 07-03 17:05:27.030 4582 4582 W jw.ComAssistant: type1400 audit(0.0:56): avc: denied { read } for name"ttyS2" dev"tmpfs" ino7704 scontextu:r:untrusted_app:s0:c512,c768 tcontextu:object_r:ttyS2_dev…

《YOLOv8魔术师》专栏介绍 CSDN独家改进创新实战专栏目录

&#x1f4a1;&#x1f4a1;&#x1f4a1;Yolov8魔术师&#xff0c;独家首发创新&#xff08;原创&#xff09;&#xff0c;持续更新&#xff0c;适用于Yolov5、Yolov7、Yolov8等各个Yolo系列&#xff0c;专栏文章提供每一步步骤和源码&#xff0c;轻松带你上手魔改网络 &…

xml合并

from xml.etree.ElementTree import ElementTree, Element, parseimport xml.etree.ElementTree as ETimport osimport shutilhole_path ./Annotationsarm_path ./Annotations1out_path ./out# 格式化def __indent(elem, level0):i "\n" level*"\t"if…

UE5.2 LyraDemo源码阅读笔记(三)

进入淘汰模式游戏流程 启动游戏&#xff0c;进入流程&#xff1a; 1、进入Lyra游戏第三个体验入口FrontEnd。 2、来到大厅主界面后&#xff0c;点击&#xff1a; Play Lyra > Start A Game >ELIMINATION就会进入到淘汰模式游戏。 看看蓝图和代码&#xff0c;其中的UI流程…

dubbo-admin下载安装教程

目录 1.下载 2.项目结构 3.后端打包 4.前端打包 4.1.node js环境准备 4.2.依赖安装 4.3.启动、打包 1.下载 下载地址&#xff1a; GitHub - apache/dubbo-admin at develop dubbo-admin项目有多个分支&#xff0c;最新版本的dubbo-admin在使用go语言进行重构&#xf…