搜索
bottom↓
回复: 22

求教:固件库中定义外设变量时该不该使用volatile!!?

[复制链接]

出0入25汤圆

发表于 2012-4-12 17:03:20 | 显示全部楼层 |阅读模式
STM32官方固件库中有以下定义:

#define GPIOA     ((GPIO_TypeDef *) GPIOA_BASE)

我想问的是,为什么不是下面的样子啊???:

#define GPIOA      ((volatile GPIO_TypeDef *) GPIOA_BASE)

难道不怕被优化吗???


我猜想:是不是因为上面的定义中定义的指针指向的内存空间是外设空间,而编译器自己就知道“处于该空间内的存储单元的访问都不应该被优化”,所以就不需要使用volatile关键字定义了。。是不是这个意思啊???

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2012-4-12 17:25:27 | 显示全部楼层
#ifdef __cplusplus
#define     __I     volatile                  /*!< defines 'read only' permissions      */
#else
#define     __I     volatile const            /*!< defines 'read only' permissions      */
#endif
#define     __O     volatile                  /*!< defines 'write only' permissions     */
#define     __IO    volatile                  /*!< defines 'read / write' permissions   */

出0入31汤圆

发表于 2012-4-12 17:28:44 | 显示全部楼层
会怎么优化呢?优化后会有什么影响呢?这只是一个地址啊

出0入25汤圆

 楼主| 发表于 2012-4-12 17:47:19 | 显示全部楼层
zchong 发表于 2012-4-12 17:28
会怎么优化呢?优化后会有什么影响呢?这只是一个地址啊

读缓存。。。

volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.

出0入31汤圆

发表于 2012-4-12 17:49:37 | 显示全部楼层
#define GPIOA     ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOA    ((GPIO_TypeDef *) 0x50001000)
哪跟哪儿啊

出0入25汤圆

 楼主| 发表于 2012-4-12 17:59:25 | 显示全部楼层
zchong 发表于 2012-4-12 17:49
#define GPIOA     ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOA    ((GPIO_TypeDef *) 0x50001000)
哪跟 ...

不太懂您的意思!!!

我说的被优化,是说在GPIOA变量被使用的时候被优化,凡是被volatile修饰的变量,使用的时候不会被优化;而不用volatile修饰的变量,在使用的时候有可能会被又优化掉,比如,对某个变量的读会被编译器聪明的优化掉:不进行真正的读操作,而是将之前读出来的存储在通用寄存器里面的值直接返回,这样就避免了一次对内存的读操作,可是这对于一些情况是不允许的,比如:中断或者另一个线程,或者是硬件设备会修改此变量的值的情况下,所以要确保这些变量的读写操作不会被编译器优化。。。

出0入0汤圆

发表于 2012-4-12 18:16:16 | 显示全部楼层
typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

自己去看__IO 是怎么定义的,grep一下很难么?

出0入25汤圆

 楼主| 发表于 2012-4-12 18:45:26 | 显示全部楼层
theophilus 发表于 2012-4-12 18:16
typedef struct
{
  __IO uint32_t CRL;

啊!!!谢谢提醒,竟然忘了是在那里有,,,真是太粗心了。。。


谢谢指点!!!

出0入0汤圆

发表于 2012-4-12 18:55:11 | 显示全部楼层
去看一看 GPIO_TypeDef 究竟是个什么东西不就明白了么。。。。。

出0入0汤圆

发表于 2012-7-16 10:24:16 | 显示全部楼层
这正是我在寻找的问题,楼主问的问题很好,但是大家的回答依旧没有没有把问题说清楚

不是volatile类型的结构体,内部有volatile的成员,当你访问这些成员时,是否是volatile类型  ,这个在c标准中是 undefined

请看这里,
*************************************************************************************
例:定义为volatile的结构体成员

考察下面对一个设备硬件寄存器结构类型的定义:

struct devregs{
    unsigned short volatile csr;
    unsigned short const volatile data;
};
我们的原意是希望声明一个设备的硬件寄存器组。其中有一个16bit的CSR控制/状态寄存器,这个寄存器可以由程序向设备写入控制字,也可以由硬件设备设置反映其工作状态。另外还有一个16bit的DATA数据寄存器,这个寄存器只会由硬件来设置,由程序进行读入。

看起来,这个结构的定义没有什么问题,也相当符合实际情况。但是如果执行下面这样的代码时,会发生什么情况呢?

struct devregs * const dvp = DEVADDR;

while ((dvp->csr & (READY | ERROR)) == 0)
    ; /* NULL - wait till done */
通过一个non-volatile的结构体指针,去访问被定义为volatile的结构体成员,编译器将如何处理?答案是:Undefined!C99 标准没有对编译器在这种情况下的行为做规定。所以编译器有可能正确地将dvp->csr作为volatile的变量来处理,使程序运行正常;也有可能就将dvp->csr作为普通的non-volatile变量来处理,在while当中优化为只有开始的时候取值一次,以后每次循环始终使用第一次取来的值而不再从硬件寄存器里读取,这样上面的代码就有可能陷入死循环!!
****************************************************************************************************

这个是引文地址
http://blog.163.com/zhaojie_ding ... 952007925115019663/

出0入0汤圆

发表于 2012-7-16 10:25:41 | 显示全部楼层
我也在想这个问题,为什么stm32固件库在定义这些寄存器结构体类型时候,怎么没有定义成volatile类型啊,
白思不得其解释

出0入0汤圆

发表于 2012-7-16 10:34:11 | 显示全部楼层
bluelucky兄,看到过你在XIVN1987的帖子” 问个只要用Cortex-M单片机都会遇到的问题,关于堆栈的 “中的精彩回复,期待你再回复下他的另外一个问题,关于固件库中 外设结构体的volatile类型  
点此链接,不胜感激
http://www.amobbs.com/thread-5466313-1-1.html


呼唤 bluelucky兄 ,我不能发送短消息 ,请哪位兄弟看到此,帮我代发一下

出0入0汤圆

发表于 2013-2-27 15:25:57 | 显示全部楼层
兄弟,我用mdk470真的发现会把volatile优化掉

出0入0汤圆

发表于 2019-2-7 21:23:12 | 显示全部楼层
myxiaonia 发表于 2012-7-16 10:25
我也在想这个问题,为什么stm32固件库在定义这些寄存器结构体类型时候,怎么没有定义成volatile类型啊,
白 ...


对结构体、共用体而言,如果成员操作符“.”“->”之前的表达式具有类型限定符,或者它的成员具有类型限定符,则最终结果将具有这些限定符的并集。此外,编译器将忽略同一声明中重复的类型限定符。
所以,你在10楼引用的文章内容并不正确。

如下类型声明和变量定义,RVMKD编译器将为他们生成完全一样的目标代码:
  1. typedef struct {
  2.   volatile uint32_t  Variable;
  3. } Test_TypeDef2;

  4. typedef volatile struct {
  5.   uint32_t  Variable;
  6. } Test_TypeDef3;

  7. typedef volatile struct {
  8.   volatile uint32_t  Variable;
  9. } Test_TypeDef4;
  10. //-----------------------------------------------------------------------------
  11. Test_TypeDef2  *Test_Type22;
  12. Test_TypeDef3  *Test_Type32;
  13. Test_TypeDef4  *Test_Type42;
复制代码




下图中变量定义后面的注释数字表示汇编代码中“LDR + STR + LDR”指令条数,三个数字一样的变量定义它们的目标代码是完全一样的。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入34汤圆

发表于 2019-2-8 03:34:58 | 显示全部楼层
楼主,我首先关心您于一楼设置字体的大小。
再说,能用 Volatile 就尽量用,免得找 Bug 会找上大半天滴。

出0入0汤圆

发表于 2019-2-8 16:47:47 | 显示全部楼层
laoshuhunya 发表于 2019-2-7 21:23
对结构体、共用体而言,如果成员操作符“.”“->”之前的表达式具有类型限定符,或者它的成员具有类型限 ...

老树昏鸦兄,谢谢你的回复,这个疑问过去好几年了,当时我也是特别研究了好久,我倾向于认为这是个vendor implement的特性,c99没有规定,所以用的时候就尽量和官方库一样的做法

出0入0汤圆

发表于 2019-2-9 01:43:41 来自手机 | 显示全部楼层
myxiaonia 发表于 2019-2-8 16:47
老树昏鸦兄,谢谢你的回复,这个疑问过去好几年了,当时我也是特别研究了好久,我倾向于认为这是个vendor ...

这个是C99的标准哦,不是编译器的自定义,我记的在ISO/IEC9899-1999 TC2的Structure and union members章节有说明。

出0入0汤圆

发表于 2019-2-9 14:03:30 | 显示全部楼层
laoshuhunya 发表于 2019-2-9 01:43
这个是C99的标准哦,不是编译器的自定义,我记的在ISO/IEC9899-1999 TC2的Structure and union members章 ...

我按照你的指引,找到了这个章节,里面通过一个例子说明了这个问题,外部限定符会作用到内部所有类型上。 我把它贴在这里
  1. 7 EXAMPLE 2 In:
  2. struct s { int i; const int ci; };
  3. struct s s;
  4. const struct s cs;
  5. volatile struct s vs;
  6. the various members have the types:
  7. s.i int
  8. s.ci const int
  9. cs.i const int
  10. cs.ci const int
  11. vs.i volatile int
  12. vs.ci volatile const int
复制代码

出0入0汤圆

发表于 2019-2-9 14:44:43 | 显示全部楼层
本帖最后由 laoshuhunya 于 2019-2-9 14:49 编辑
myxiaonia 发表于 2019-2-9 14:03
我按照你的指引,找到了这个章节,里面通过一个例子说明了这个问题,外部限定符会作用到内部所有类型上。 ...


是的,我一直认为RVMDK在遵守C标准方面做得很好。
我刚刚也翻看了标准文档,实际上是下图中高亮的那句话在起作用(编辑补充,还有第3点最后一句类似的话):


因为结构体中可以包含结构体以及指向结构体的指针,所以按照上面那句话的规定,最终结果将会包含所有的类型限定符。
标准一般会使用最精简的表述, 实际应用时需要按照它的逻辑来展开,如果有疑义则会给出一些例子。

关于volatile用于结构体、共用体的各种组合我在RVMDK上都验证过,它们都符合C99标准。以下是其中的一段:
  1. typedef struct {
  2.   uint32_t  *pVariable;
  3. } Test_TypeDef1A;

  4. typedef struct {
  5.   volatile uint32_t  *pVariable;
  6. } Test_TypeDef2A;

  7. typedef volatile struct {
  8.   uint32_t  *pVariable;
  9. } Test_TypeDef3A;

  10. typedef volatile struct {
  11.   volatile uint32_t  *pVariable;
  12. } Test_TypeDef4A;
  13. //-----------------------------------------------------------------------------
  14. Test_TypeDef1A  Test_Type10A;    // 2+1+0
  15. Test_TypeDef2A  Test_Type20A;    // 2+1+2
  16. Test_TypeDef3A  Test_Type30A;    // 2+1+2
  17. Test_TypeDef4A  Test_Type40A;    // 2+1+2

  18. volatile Test_TypeDef1A  Test_Type11A;    // 2+1+2
  19. volatile Test_TypeDef2A  Test_Type21A;    // 2+1+2
  20. volatile Test_TypeDef3A  Test_Type31A;    // 2+1+2
  21. volatile Test_TypeDef4A  Test_Type41A;    // 2+1+2

  22. Test_TypeDef1A  *Test_Type12A;    // 3+1+0
  23. Test_TypeDef2A  *Test_Type22A;    // 3+1+3
  24. Test_TypeDef3A  *Test_Type32A;    // 3+1+3
  25. Test_TypeDef4A  *Test_Type42A;    // 3+1+3

  26. volatile Test_TypeDef1A  *Test_Type13A;    // 3+1+3
  27. volatile Test_TypeDef2A  *Test_Type23A;    // 3+1+3
  28. volatile Test_TypeDef3A  *Test_Type33A;    // 3+1+3
  29. volatile Test_TypeDef4A  *Test_Type43A;    // 3+1+3

  30. Test_TypeDef1A  * volatile Test_Type14A;    // 3+1+3
  31. Test_TypeDef2A  * volatile Test_Type24A;    // 3+1+3
  32. Test_TypeDef3A  * volatile Test_Type34A;    // 3+1+3
  33. Test_TypeDef4A  * volatile Test_Type44A;    // 3+1+3

  34. volatile Test_TypeDef1A  * volatile Test_Type15A;    // 3+1+3
  35. volatile Test_TypeDef2A  * volatile Test_Type25A;    // 3+1+3
  36. volatile Test_TypeDef3A  * volatile Test_Type35A;    // 3+1+3
  37. volatile Test_TypeDef4A  * volatile Test_Type45A;    // 3+1+3
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2019-2-9 14:55:40 | 显示全部楼层
laoshuhunya 发表于 2019-2-9 14:44
是的,我一直认为RVMDK在遵守C标准方面做得很好。
我刚刚也翻看了标准文档,实际上是下图中高亮的那句话 ...

恩   我首先看到了你的例子  做的是很仔细覆盖情形的也很全面  不过我之前选择相信成见也是不好的决定 偷懒和相信经验 这是变老的表现啊

不过你都把引用的地方都指出了  我实在不能再搪塞过去了   那太不尊重人了   老实说我刚才看你高亮的那段话还半天没看明白  你现在一说我也是豁然开朗 这句话就是要表明你做的这个例子的意思

有时间我也要把这个标准好好看明白   这些基本知识是一切推断的基础

出0入0汤圆

发表于 2019-2-9 15:20:21 | 显示全部楼层
myxiaonia 发表于 2019-2-9 14:55
恩   我首先看到了你的例子  做的是很仔细覆盖情形的也很全面  不过我之前选择相信成见也是不好的决定 偷 ...

嗯嗯,对于标准中已定义的要求,RVMDK所做的真没有什么好挑剔的
例如,按照C99标准,下面两个类型声明在RVMDK中也是完全一样的:
  1. typedef volatile struct {
  2.   volatile uint32_t  *pVariable;
  3. } *Test_TypeDef8A;

  4. volatile struct {
  5.   volatile uint32_t  *pVariable;
  6. } typedef *Test_TypeDef12A;
复制代码

出0入0汤圆

发表于 2019-2-9 16:16:08 | 显示全部楼层
laoshuhunya 发表于 2019-2-9 15:20
嗯嗯,对于标准中已定义的要求,RVMDK所做的真没有什么好挑剔的
例如,按照C99标准,下面两个 ...

哈哈  你是看到了什么  在研究这些不引人注意的知识  老实说我还是第一次看到后一种用法  我记得早期c中函数定义以现在的观点看也比较奇怪

出0入0汤圆

发表于 2019-2-9 17:00:13 | 显示全部楼层
myxiaonia 发表于 2019-2-9 16:16
哈哈  你是看到了什么  在研究这些不引人注意的知识  老实说我还是第一次看到后一种用法  我记得早期c中 ...

也是迫不得已啊 最终目的就为了确保我们RTSS系统稳定可靠。
另外,keil帮助文档对volatile和优化也没有比较深入的介绍,编译器优化问题一度引起广大攻城狮的公愤,C标准只好引入序列点来约束优化行为,但制定C标准的参与者很多就是编译器的开发者,他们既是执法者又是立法者,所以指望那些“一根筋、自以为是、固执任性”的老家伙做出太多让步是不可能的,即使在文件里关闭优化再加上volatile,他们还是要任性的优化。没办法,要不自己写个编译器,要不弄清楚volatile
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-19 00:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表