打印
[ZLG-ARM]

嵌入式C指针问题

[复制链接]
4994|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zwen01|  楼主 | 2007-12-20 16:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
各位高手:
     以上的指针表示
/* General Purpose Input/Output (GPIO) */
/* 通用并行IO口的特殊寄存器 */

#define IO0PIN          (*((volatile unsigned long *) 0xE0028000))      /* no in lpc210x*/
#define IO0SET          (*((volatile unsigned long *) 0xE0028004))      /* no in lpc210x*/
#define IO0DIR          (*((volatile unsigned long *) 0xE0028008))      /* no in lpc210x*/
#define IO0CLR          (*((volatile unsigned long *) 0xE002800C))      /* no in lpc210x*/


                         为什么(volatile unsigned long *) 这里要括号呢?
       上面的意思应该是定义IO0PIN 为 0xE0028000(IO0PIN的寄存器为0xE0028000)    ,  直接 #define IO0PIN     0xE0028000    // 不就得了吗。

       还有下面
*((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;

*((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;


     哪位高手帮忙解惑一下。

相关帖子

沙发
XDream| | 2007-12-20 19:20 | 只看该作者

re

首先你要对这条代码进行理解
(*((volatile unsigned long *) 0xE0028000))
这条代码的意思是,取0xE0028000地址单元的内容。

而0xE0028000则是代表一个数值。

编译器处理是不一样的。

使用特权

评论回复
板凳
zwen01|  楼主 | 2007-12-21 14:45 | 只看该作者

还是不清楚

直接 #define IO0PIN     0xE0028000    // 
应该是可以的,如下例:


#define   BEEPCON  0x00000080        // P0.7引脚控制B1,低电平蜂鸣
            
/****************************************************************************
* 名    称:main()
* 功    能:控制蜂鸣器蜂鸣。
****************************************************************************/
int  main(void)
{  
    PINSEL0 = 0x00000000;               // 设置管脚连接GPIO   
    IO0DIR = BEEPCON;                     // 设置I/O为输出

    while(1)    
    {  
        IO0SET = BEEPCON; //  把0x00000080值送入0xE0028000寄存器,即p0.7置高
        
    }
   return(0);
}

意思我知道,但具体分析就不懂了。

使用特权

评论回复
地板
ATmega16| | 2007-12-22 09:27 | 只看该作者

看C语言书去吧

(*((volatile unsigned long *) 0xE0028000))

((volatile unsigned long *) 0xE0028000)
都分不清,好好地看C语言书去吧。

使用特权

评论回复
5
zlgarm| | 2007-12-22 11:17 | 只看该作者

关于<<嵌入式C指针问题>>

zwen01 您好!

#define IO0PIN          (*((volatile unsigned long *) 0xE0028000))      /* no in lpc210x*/

以此句为例,简述如下:

与51单片机的头文件<reg51.h>类似,在ARM处理器中,也必须有自己的寄存器头文件声明,不同的是,在ARM应用中,需要用户自己来编写.

0xE0028000:在ARM处理器中,只能识别为一个十六进制数值,具体是数据还是地址,它并不能自动区分.

而使用(volatile unsigned int *)0xE0028000,对此数据进行强制转换,表明此数值为一个无符号的长整型地址指针值,关键字volatile 告诉编译器它指向的内容是易变的,可能会被硬件等意外地修改.这一点很关键.
*((volatile unsigned int *) 0xFFE00000),则是获取指针所指向地址处的内容,从而,使用了#define 预处理语句对IO0PIN进行定义后,在用户程序中,对IO0PIN的操作,就等同于在0xE0028000地址上进行读写操作.
具体C语言的知识,请参考相关书籍.如果平时在学习中遇到ARM相关的问题,也可以登陆以下网页:

http://www.zlgmcu.com.cn/index.asp?boardid=29&page=1

我们会将一些解决方案汇总在这个网页中进行回复.敬请留意.

                                            zlgarm _wgg

使用特权

评论回复
6
chenmi0201| | 2007-12-23 20:01 | 只看该作者

zlgarm请继续关注 关于<<嵌入式C指针问题>>

请问楼上的 zlgarm
#define IO0PIN    (*((volatile unsigned long *)0xE0028000))  /* no in lpc210x*/
  就像C51的绝对地址访问

但是在EasyARM2200实验板的例程实验十三中(存储器重映射实验),定义了一个宏#define  VECTOR_RAM           ((unsigned char *) 0x40000000)
这也是绝对地址访问啊,但是为什么没有存储修饰符volatile。地址0x40000000指向的是LPC2210内部的静态RAM,数据也容易改变且也易被编译器优化掉。请大侠给予解惑!
 实验程序如下:
#include  "config.h"
#define  VECTOR_RAM         ((unsigned char *) 0x40000000)
/**********************************************************
* 名称:main()
* 功能:存储器重映射。
**********************************************/
int  main(void)
{  uint8  i;
   uint8  *addr;

   MEMMAP = 0x02;     // 中断向量从静态RAM重映射(用户RAM模式)

   addr = VECTOR_RAM;
   for(i=0; i<64; i++)    // 用0x55填充0x40000000~0x4000003F
   {  *addr = 0x55;
      addr++;
   }
   
   addr = VECTOR_RAM;
   for(i=0; i<64; i++)        // 用0xAA填充0x40000000~0x4000003F
   {  *addr = 0xAA;
      addr++;
   }

   MEMMAP = 0x00;               
   while(1);
   return(0);
}

使用特权

评论回复
7
王紫豪| | 2007-12-23 23:40 | 只看该作者

强制类型转换

使用特权

评论回复
8
XDream| | 2007-12-24 08:43 | 只看该作者

volatile

编译器并不是什么都优化的,它有一定规则。
建议先了解一下volatile。

使用特权

评论回复
9
chenmi0201| | 2007-12-24 11:43 | 只看该作者

volatile

个人认为
#define  VECTOR_RAM         ((unsigned char *) 0x40000000)
 加上volatile更可靠。因为在编写代码初期,我们没有去关注编译器如何优化代码的
 以上纯属我个人的意见,如果有错误,望大家见量,并且提出宝贵的意见,并多多的指点。

使用特权

评论回复
10
zwen01|  楼主 | 2007-12-25 12:18 | 只看该作者

有点明白

    首先,谢谢zlgarm!
  
  用我是会用,只是知其然不知其所以然,  可能如你所说“在ARM处理器中,只能识别为一个十六进制数值,具体是数据还是地址,它并不能自动区分”, 所能要这样定义。
    #define IO0PIN          (*((volatile unsigned long *) 0xE0028000))      /* no in lpc210x*/
    整句是不是可以这样理解:定义IO0PIN 为一个指针,这个指针为一个易变的无符号长整数型的地址为0xE0028000的指针。
    为什么*要跟前面类型结合,而不是跟0xE0028000先结合了。

使用特权

评论回复
11
machunshui| | 2007-12-27 14:40 | 只看该作者

这样理解

#define IO0PIN          (*((volatile unsigned long *) 0xE0028000)) 

(volatile unsigned long *) 0xE0028000 
把0xE0028000这个地址强制转换为long型指针.
即对指针的操作的范围是从0xE0028000开始的4个字节(long型).

volatile 限定编译器不对这个指针的指向的存储单元进行优化,
即不用通用寄存器暂时代替这个指针的指向的存储单元,
而是每次取值都直接到指针的指向的存储单元取值.

volatile 主要用于变量会异步改变的情况下,主要有三个方面:

1.cpu外设寄存器
2.中断和主循环都会用到的全局变量
3.操作系统中的线程间都会用到的公共变量.




使用特权

评论回复
12
hasayakipw| | 2007-12-28 21:49 | 只看该作者

转:volatile

volatile的本意是“易变的”,由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
static int i=0;
int main(void)
{     ...
     while (1)
     {
         if (i) dosomething();
     }
}

/* Interrupt service routine. */
void ISR_2(void)
{
      i=1;
}

程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过 i,因此可能只执行一次对从 i 到某寄存器的读操作,然后每次 if 判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的 读写操作都不会被优化(肯定执行)。此例中 i 也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

简单点:
就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化(所有的编译器的优化均假设编译器知道变量的变化规律)

2.关于volatile关键字的说明以及测试 
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
使用该关键字的例子如下:

int volatile nVint;

当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。例如:

volatile int i=10;
int a = i;
。。。//其他代码,并未明确告诉编译器,对i进行过操作
int b = i;

volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器 发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者 表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。
注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响:

首先用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:
#include <stdio.h>
void main()
{
int i=10;
int a = i;

printf("i= %d ",a);
        //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm {
   mov          dword ptr [ebp-4], 20h
}

int b = i;
printf("i= %d ",b);
}

然后,在调试版本模式运行程序,输出结果如下:
i = 10
i = 32

然后,在release版本模式运行程序,输出结果如下:
i = 10
i = 10

输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。

下面,我们把 i的声明加上volatile关键字,看看有什么变化:

#include <stdio.h>
void main()
{
volatile int i=10;
int a = i;

printf("i= %d ",a);
__asm {
   mov          dword ptr [ebp-4], 20h
}

int b = i;
printf("i= %d ",b);
}

分别在调试版本和release版本运行程序,输出都是:
i = 10
i = 32

这说明这个关键字发挥了它的作用!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

14

帖子

0

粉丝