草庐IT

数值常量如何转化为内存地址?

技术让梦想更伟大 2023-03-28 原文

最近在使用Nordic的最新蓝牙芯片nRF52832开发过程中,因为做一些测试涉及到对内存地址的操作,有(*(volatile unsigned int *)0xE000EDFC)的用法然后进行宏定义,本文将解析一下这种用法。

代码解析

先来看下面一段代码:

#define ARM_CM_DEMCR (*(volatile unsigned int *)0xE000EDFC)
#define ARM_CM_DWT_CTRL (*(volatile unsigned int *)0xE0001000)
#define ARM_CM_DWT_CYCCNT (*(volatile unsigned int *)0xE0001004)

这段代码的结构分析一下,由于nRF52832是Cotex-M4内核的,在ARM处理器中,只能识别为一个十六进制数值,具体是数据还是地址,它并不能自动区分。

而使用(unsigned int *)0xE000EDFC,对此数据进行强制转换,表明此数值为一个无符号整型地址指针值,关键字volatile告诉编译器它指向的内容是易变的,可能会被硬件等意外地修改;

*(volatile unsigned int *)0xFFE00000,则是获取指针所指向地址处的内容;

把#define宏中的参数用括号括起来,在用户程序中对ARM_CM_DEMCR的操作,就等同于在0xE000EDFC地址上进行读写操作了。

应用

上面说到了,在32位处理器,要对一个32位的内存地址进行访问,然后进行读写操作

tmp = ARM_CM_DEMCR;//读
ARM_CM_DEMCR = 0x55;//写

使用volatile修饰是因为它的值可能会改变,我们假设在一个循环操作中需要不停地判断一个内存数据,例如要等待ARM_CM_DEMCR的flag标志位置位,因为ARM_CM_DEMCR是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把ARM_CM_DEMCR读取到寄存器中,然后不停地判断寄存器相应位,而不会再读取ARM_CM_DEMCR.

而实际工程中,程序例如中断事件会改变ARM_CM_DEMCR,而寄存器相应位没有更新,会造成死循环了。如果volatile来修饰,那每次要操作一个变量的时候会都从内存中读取一次。

嵌入式系统编程中要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。

对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。

volatile关键字的用途

volatile的意思是告诉编译器,在编程源代码时,对这个变量不要使用优化,C语言中可能会优化一些运算过程中的变量值导致最后的结果不对,这里就不多说了。

volatile还可以防止编译器优化去掉某些语句,在arm中假如需要写1清中断,举例子如下:

#define INTPAND *(volatile unsigned int *)0x560012300
INTPAND = INTPAND; // 清中断

INTPAND = INTPAND;这种操作,如果没有volatile修饰,编译器就很有可能会去掉INTPAND = INTPAND;,相当于没这句话了。

在嵌入式编程中,当地址是io端口的时候,读写这个地址是不能对它进行缓存的(有cache才),比如写这个io端口的时候,如果没有volatile修饰,编译器会先把值先写到一个缓冲区,到一定时候再写到io端口,这样就不能使数据及时的写到io端口,有了volatile修饰就会直接写到io端口,从而避免了读写io端口的延时。

编译器对代码的优化

再说说编译器的优化,CPU在执行的过程中,因为访问内存的速度远没有cpu的执行速度快,为了提高效率,引入了高速缓存cache。

C编译器在编译时如果不知道变量会被其它外部因素(操作系统、硬件或者其它线程)修改,那么就会对该变量进行优化(当然也有些IDE可以设置优化等级),这个变量在CPU的执行过程中会被放到高速缓存cache去,进而达到对变量的快速访问。

在一些寄存器变量或数据端口的使用中,因为寄存器变量本身也是靠cache来处理,为了避免引起错误,也可以使用volatile修饰符。

那为什么要让变量在执行的过程中不被放到cache中去呢?

如果变量是被外部因素改变,那么cpu就无法判断出这个变量已经被改变,那么程序在执行的过程中如果使用到该变量,还会继续使用cache中的变量(已经改变),需要到内存地址中更新,所以变量在执行的过程中不能被放到cache中。

总结

使用volatile的目的就是让对volatile变量的存取不能缓存到寄存器,每次使用时需要重新存取。在嵌入式开发中这种用法很常见也很关键,需要掌握。

参考:https://blog.csdn.net/u010404580/article/details/11638619

有关数值常量如何转化为内存地址?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

随机推荐