野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 8435|回复: 4

看原子的开发板的程序也是volatile的问题

[复制链接]
发表于 2017-11-26 02:12:27 | 显示全部楼层 |阅读模式
本帖最后由 smallcsduck 于 2017-11-26 02:52 编辑

这个很重要,有时候你程序莫名其妙的问题都是这个导致的。
我在做外置sram实验的时候没有注意,导致从sram里面出来的数据和写进去的不一样,像sram芯片有坏块一样。

//读LCD数据

//返回值:读到的值

u16 LCD_RD_DATA(void)

{

        vu16 ram;                        //防止被优化

        ram=LCD->LCD_RAM;        

        return ram;         

}                                       

像这种的代码,明显的问题,自己都写了开了高等级优化选项会被优化掉。

正确的写法是

u16 LCD_RD_DATA(void)

{

        volatile u16 ram=LCD->LCD_RAM;//或者__IO u16 ram=LCD->LCD_RAM;

        return ram;         

}               

地址操作的时候也是这样,像下面这个正确应该这么写

typedef struct

{

volatile u16 LCD_REG;

volatile u16 LCD_RAM;

} LCD_TypeDef;

//使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A18作为数据命令区分线

//注意设置时STM32内部会右移一位对其!                             

#define LCD_BASE        ((u32)(0x60000000 | 0x0007FFFE))

#define LCD             ((volatile LCD_TypeDef *) LCD_BASE)



vu16 其实就是typedef __IO uint16_t vu16;  已经给你加了volatile。#define     __IO    volatile

还有就是在sdram里面的变量,外设寄存器的变量,中断中改变的变量都要加。

如果你-o0的情况下正常的程序,一开-o3就不正常了,那一般都是volatile这个问题。



原因:

编译器会优化代码的

比如你往一个地址写个数值,后面的程序没有去读他。或者你从一个地址读,但是前面没有往这个地址写东西。编译器都会认为你这个读写操作是无用的操作,直接给你优化掉。

加了volatile就是告诉编译器,每次都必须去读写这个变量。

其他的道理都一样,你程序流程里没有明确改变的变量,编译器都可能不生成掉读他或者写他的代码。所以在你程序流程外有可能改变的变量都要加volatile。比如中断里面改变的变量。

修改寄存器也是一样的,你写了个数值到外设寄存器里面,后面又没有读他。编译器认为你这个写操作没有意义,无效代码,直接给你省略掉了。所以你看人家库里面的寄存器地址都是带volatile。
编译器这个逻辑其实很正常。比如你建立了一个全局变量,但是你的程序根本没有用他,那为了节省内存当然是不建立这个变量。你往一个地址写数据后面又从来没有用过他,编译器当然认为这个写操作是可以省略掉的。


回复

使用道具 举报

发表于 2017-11-27 08:49:52 | 显示全部楼层
是的,使用这个可以防止编译器的意外优化
回复 支持 反对

使用道具 举报

发表于 2017-11-28 08:58:41 | 显示全部楼层
LCD_RD_DATA函数的两种写法不是应该一样的吗?我调试的时候看到汇编代码也是一样的。
  1.    100: u16 LCD_RD_DATA(void)
  2. 0x080005CC 4770      BX       lr
  3.    101: {
  4.    102:         vu16 ram;                     //防止被优化
  5. 0x080005CE B508      PUSH     {r3,lr}
  6.    103:         ram=LCD->LCD_RAM;        
  7. 0x080005D0 48F3      LDR      r0,[pc,#972]  ; @0x080009A0
  8. 0x080005D2 1C80      ADDS     r0,r0,#2
  9. 0x080005D4 8800      LDRH     r0,[r0,#0x00]
  10. 0x080005D6 9000      STR      r0,[sp,#0x00]
  11.    104:         return ram;      
  12. 0x080005D8 F8BD0000  LDRH     r0,[sp,#0x00]
  13.    105: }         



  14.    100: u16 LCD_RD_DATA(void)
  15. 0x080005CC 4770      BX       lr
  16.    101: {
  17. 0x080005CE B508      PUSH     {r3,lr}
  18.    102:         vu16 ram=LCD->LCD_RAM;   
  19. 0x080005D0 48F3      LDR      r0,[pc,#972]  ; @0x080009A0
  20. 0x080005D2 1C80      ADDS     r0,r0,#2
  21. 0x080005D4 8800      LDRH     r0,[r0,#0x00]
  22. 0x080005D6 9000      STR      r0,[sp,#0x00]
  23.    103:         return ram;      
  24. 0x080005D8 F8BD0000  LDRH     r0,[sp,#0x00]
  25.    104: }  
复制代码

大佬可以解析一下吗
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-11-28 17:58:19 | 显示全部楼层
本帖最后由 smallcsduck 于 2017-11-28 18:40 编辑
mXing 发表于 2017-11-28 08:58
LCD_RD_DATA函数的两种写法不是应该一样的吗?我调试的时候看到汇编代码也是一样的。
大佬可以解析一下吗

vu16 其实就是typedef __IO uint16_t vu16;  已经给你加了volatile。
#define     __IO    volatile
另外 不同的编译器 优化策略是不一样的 你用的编译器可能能识别这个情况 一个简单的例子可能体现不出来
反正现在最新的armcc编译器在读写外置sram内的大数组不加volatile  用-o3编译  
写入和读取是不一样的  你每次读取的数据都不一样
这个可以很容验证
这个问题困扰了我好长一段时间我一直以为是我时序设置有问题 造成读数据不稳定  调了好久  


回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-11-29 04:19:57 | 显示全部楼层
mXing 发表于 2017-11-28 08:58
LCD_RD_DATA函数的两种写法不是应该一样的吗?我调试的时候看到汇编代码也是一样的。
大佬可以解析一下吗

#define LCD_FRAME_BUF_ADDR                        0XC0000000  
        u32 ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));
*(u32*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x))=color;

这种代码armcc -o3下是不能正常工作的  我都不用去试 一看就知道了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-9-21 00:43 , Processed in 0.030138 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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