ARTS 挑战打卡的第8天 ---volatile 关键字在MCU中的作用,四个实例讲解(Tips)

前言

(1)volatile 关键字作为嵌入式面试的常考点,很多人都不是很了解,或者说一知半解。
(2)可能有些人会说了,volatile 关键字不就是防止编译器优化的吗?有啥好详细讲解的?那么,我就反问一句,为什么要防止编译器优化,编译器优化什么?编译器优化之后会产生什么问题?
(3)今天我就来详细解答一下这些疑惑。

软件延时所造成的bug

(1)在初学51单片机的时候,我们都是使用软件延时,例如下面是STC89,12MHZ晶振的1ms的软件延时。
(2)有些人说,这样写延时可以啊,没有问题。但是,假如你在MSP430中这样写,一定会产生bug。你会发现,软件延时没效果。
(3)这个时候,有些人会告诉你,要让CCS的编辑优化等级降低然后就可以了。
(4)这是为什么呢?如下代码,编译会发现,这就是让两个变量进行自减,于是编译器自作主张,认为这是没有意义的代码,并且将其删除。于是,你看汇编代码会发现,这里没有进行自减操作。
(5)但是,如果你加上volatile 关键字,就会发现,软件延时能够正常运行。这个是为什么呢?volatile 关键字会告诉编译器,这个变量你没有权限动,你不要擅自主张的进行优化。
(6)因此,我们可以知道,volatile 关键字其实就是告诉编译器,不要对变量进行优化。

void Delay1ms()		//@12.000MHz
{
	unsigned char i, j;
	//加上volatile 关键字
	//volatile unsigned char i, j;
	
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

外设寄存器被异步修改所产生的bug

(1)假设我们现在有一个外设寄存器叫做ExternalDevice ,这个寄存器会自动减少,地址为0x1000。(例如定时器的计数器就会自己增加或减少)
(2)现在我们要等ExternalDevice 寄存器值变成0的时候,再进行一些操作。
(3)但是你实际跑的时候会发现,这个地方要么无法阻塞,要么永远阻塞。这是为什么呢?编译器是不知道ExternalDevice这个变量是一个寄存器的,也不知道他最终是怎么变化的。所以他就会认为,这个地方是一个不变的变量进行反复判断。他就会把while()这一行代码删除,认为是没有意义的。
(4)于是我们需要加上volatile 告诉编译器,这个东西你别动。

int main()
{
	//下面这三种写法是等价的
	//int volatile *ExternalDevice = (uint8_t volatile *)0x1000;  
	//volatile int *ExternalDevice = (uint8_t volatile *)0x1000; 
	//volatile int *ExternalDevice = (volatile uint8_t *)0x1000; 
    int *ExternalDevice = (uint8_t *)0x1000;  // 假设外设寄存器的地址是 0x1000
	while(*ExternalDevice == 0); //等待这个外设寄存器的值变成0再进行操作
}


全局变量在中断和正常运行的程序存在竞争问题

(1)比如群友给出一个这样的代码,发现一直无法实现点灯。感到非常的疑惑。
(2)这个地方就涉及到全局变量在中断和正常运行的程序存在竞争问题。我们会发现,中断程序和主函数里面都调用了全局变量a。但是,我们要知道,编译器是无法知道运行态的情况的,他只能够进行静态优化
(3)比如这里,编译器他会认为,变量a的赋值是0。然后主函数里面的while()判断是判断他是否为0。这个时候,他无法查看到串口中断的情况,就会认为,你就是要进行一个死循环。所以,最终这个程序最终会卡死在while(a == 0);这里。
(4)因此我们要将a加上volatile 关键字。

在这里插入图片描述

多线程共享变量

(1)当我们上了操作系统之后,都是会跑多线程的。
(2)但是跑多线程,就会存在一个问题,我们很可能会让一个变量让多个线程之间共享。例如,下面我们需要创建两个线程,一个是GUI图像显示,一个是按键扫描。他们都需要共享要给变量key_num。这个时候,编译器无法知道key_num什么时候会进行改变,所以他可能就会想,既然我不知道,我就不要他。所以,我们需要加上volatile 关键字,告诉编译器,这里不要搞骚操作

uint8_t key_num;
//线程1
void GUI()
{
	while(1)
	{
		switch(key_num)
		{
			case key_short_down:
				//...
				break;
			case key_long_down:
				//...
				break;
			case key_up:
				//...
				break;
		}
	}
}
//线程2
void key_scanf()
{
	while(1)
	{
		if(key_Pin == HIGH) key_num = key_up;
		else if(key_time < 2000) key_num = key_short_down;
		else  key_num = key_short_down;
	}
}
void main()
{
	register_task(GUI);
	register_task(key_scanf);
	while(1);
}

总结

(1)volatile 关键字本质就是编译器防止优化。但是我们也要明白,为什么编译器会进行优化。知道这个以后,我们才能够更好的使用volatile 关键字。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Blazor前后端框架Known-V1.2.12

V1.2.12 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazo…

语聚AI如何通过对话方式让AI助手执行应用软件

1 什么是应用助手&#xff1f; 应用助手可以通过对话的方式让AI助手执行应用软件的操作。 例如&#xff0c;您可以调用“Bing搜索引擎搜索”热门信息&#xff0c;然后根据这些信息总结内容。 也可以使用“抖音”应用&#xff0c;搜索热门抖音视频&#xff0c;并根据热门视频生…

JavaEE初阶:多线程 - 编程

1.认识线程 我们在之前认识了什么是多进程&#xff0c;今天我们来了解线程。 一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行 着多份代码. 引入进程这个概念&#xff0c;主要是为了解决并发编程这样的…

Visual Studio 2022 中解决使用scanf报错的方法(一劳永逸)

目录 【前言】 一、scanf报错示例 二、解决使用scanf报错的方法 解决方法1&#xff08;不推荐&#xff09; 解决方法2&#xff08;不推荐&#xff09; 解决方法3&#xff08;强烈推荐&#xff09; 第一步 第二步 第三步 三、效果演示&#xff08;方法三&#xff09; …

Vue组件库 (一):Element常用组件

Element基于Vue2.0的桌面端组件库 组件&#xff1a;组成网页的部件。例如超链接、图片、按钮等。 一、环境配置 1、下载element 在vscode工程终端下下载。一定要注意&#xff1a;是在工程下安装&#xff01; npm install element -ui2.15.3 出现以下问题&#xff1a; 经判…

linux系统部署jenkins详细教程

一、Linux环境 1、下载war包 官网下载地址&#xff1a; https://get.jenkins.io/war-stable/2.332.4/jenkins.war 2、将war包上传至服务器 创建目录/home/ubuntu/jenkins 上传war包至该目录 3、将jenkins添加到环境变量 进入环境变量文件 vim /etc/profile # 文件下方追加…

Linux交叉编译opencv并移植ARM端

Linux交叉编译opencv并移植ARM端 - 知乎 一、安装交叉编译器 目标平台为arm7l&#xff0c;此为32位ARM架构&#xff0c;要安装合适的编译器 sudo apt install arm-linux-gnueabihf-gcc sudo apt install arm-linux-gnueabihf-g注意&#xff1a;64位ARM架构的编译器与32位ARM架…

opencv图像轮廓检测

效果展示&#xff1a; 代码部分&#xff1a; import cv2 import numpy as np img cv2.imread(C:/Users/ibe/Desktop/picture.PNG,cv2.IMREAD_UNCHANGED) # 类型转换 img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 结构元 kernel cv2.getStructuringElement(cv2.MORPH_REC…

运维监控学习笔记8

在服务器端&#xff0c;我们添加了nginx-server的主机&#xff1a; 在解决Error问题的过程中&#xff0c;我还通过zabbix_get这个命令进行了测试&#xff0c;发现是没有的&#xff0c;后来确认是在web页面配置的过程中&#xff0c;我输错了密码。 yum install zabbix-getzabbi…

快速注册微信小程序的账号

注册网站&#xff1a; 微信公众平台 1.点击该网站进行注册 2.选择小程序 3.前往注册小程序账号 4.填写相应的信息后&#xff0c;同意协议后就需要我们登录刚刚注册的邮箱 5.登录邮箱查看信息并进行激活 6.激活登录后就可以看到该页面&#xff0c;然后注册的类型为个人&#xff…

由浅入深学习Tapable

文章目录 由浅入深学习TapableTapable是什么Tapable的Hook分类同步和异步的 使用Sync*同步类型钩子基本使用bailLoopWaterfall Async*异步类型钩子ParallelSeries 由浅入深学习Tapable webpack有两个非常重要的类&#xff1a;Compiler和Compilation。他们通过注入插件的方式&a…

NPM与外部服务的集成(下)

目录 1、撤消访问令牌 2、在CI/CD工作流中使用私有包 2.1 创建新的访问令牌 持续整合 持续部署 交互式工作流 CIDR白名单 2.2 将令牌设置为CI/CD服务器上的环境变量 2.3 创建并签入特定于项目的.npmrc文件 2.4 令牌安全 3、Docker和私有模块 3.1 背景&#xff1a;运…

CSS变形与动画(二):perspctive透视效果 与 preserve-3d 3d效果(奥运五环例子)

文章目录 perspective 3d透视效果preserve-3d 3d嵌套效果例子 奥运五环 backface-visibility 背面效果 perspective 3d透视效果 perspective 指定了观察者与 z0 平面的距离&#xff0c;使具有三维位置变换的元素产生透视效果。z>0 的三维元素比正常大&#xff0c;而 z<0 …

零拷贝详解

1、在没有DMA技术之前的I/O过程是这样的&#xff1a; CPU发出对应的指令给磁盘控制器&#xff0c;然后返回磁盘控制器收到指令后&#xff0c;于是就开始准备数据&#xff0c;会把数据放入到磁盘控制器的内部缓冲区&#xff0c;然后产生中断CPU收到中断信号后&#xff0c;停下手…

Python opennsfw/opennsfw2 图片/视频 鉴黄 笔记

nsfw&#xff08; Not Suitable for Work&#xff09;直接翻译就是 工作的时候不适合看&#xff0c;真文雅 nsfw效果&#xff0c;注意底部的分数 大体流程&#xff0c;输入图片/视频&#xff0c;输出0-1之间的数字&#xff0c;一般情况下&#xff0c;Scores < 0.2 认为是非…

OpenCV-Python中的图像处理-霍夫变换

OpenCV-Python中的图像处理-霍夫变换 霍夫变换霍夫直线变换霍夫圆环变换 霍夫变换 霍夫(Hough)变换在检测各种形状的技术中非常流行&#xff0c;如果要检测的形状可以用数学表达式描述&#xff0c;就可以是使用霍夫变换检测它。即使要检测的形状存在一点破坏或者扭曲也是可以使…

第三章 图论 No.13拓扑排序

文章目录 裸题&#xff1a;1191. 家谱树差分约束拓扑排序&#xff1a;1192. 奖金集合拓扑序&#xff1a;164. 可达性统计差分约束拓扑序&#xff1a;456. 车站分级 拓扑序和DAG有向无环图联系在一起&#xff0c;通常用于最短/长路的线性求解 裸题&#xff1a;1191. 家谱树 119…

浅析3D打印技术

目录 1.3D打印的概念 2.3D打印的发展过程 3.3D打印的应用领域 4.3D打印带来的技术变革 1.3D打印的概念 3D打印是一种制造技术&#xff0c;它使用逐层堆叠材料的方式来创建物体。与传统的加工方法相比&#xff0c;3D打印具有很多优势。 在3D打印中&#xff0c;一种叫做CAD&am…

vue项目报错:node:internal/modules/cjs/loader:1080

运行项目报错&#xff1a; 原因&#xff1a; 看划线的地方&#xff0c;中文乱码导致找不见模块了 解决方案 将路径上的中文改为英文即可&#xff0c;项目命名最好只有英文、下划线&#xff08;_&#xff09;、数字、横杠&#xff08;-&#xff09;等英文符号组成