[ZLG-ARM] 嵌入式C指针问题

[复制链接]
6019|11
 楼主| zwen01 发表于 2007-12-20 16:30 | 显示全部楼层 |阅读模式
各位高手:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上的指针表示<br />/*&nbsp;General&nbsp;Purpose&nbsp;Input/Output&nbsp;(GPIO)&nbsp;*/<br />/*&nbsp;通用并行IO口的特殊寄存器&nbsp;*/<br /><br />#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br />#define&nbsp;IO0SET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028004))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br />#define&nbsp;IO0DIR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028008))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br />#define&nbsp;IO0CLR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE002800C))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为什么(volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;这里要括号呢?<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面的意思应该是定义IO0PIN&nbsp;为&nbsp;0xE0028000(IO0PIN的寄存器为0xE0028000)&nbsp;&nbsp;&nbsp;&nbsp;,&nbsp;&nbsp;直接&nbsp;#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0xE0028000&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;不就得了吗。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;还有下面<br />*((volatile&nbsp;INT16U&nbsp;*)FLASH_START_ADDR)=FLASH_SID_EXIT;<br /><br />*((volatile&nbsp;INT16U&nbsp;*)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;<br /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;哪位高手帮忙解惑一下。<br />
XDream 发表于 2007-12-20 19:20 | 显示全部楼层

re

首先你要对这条代码进行理解<br />(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000))<br />这条代码的意思是,取0xE0028000地址单元的内容。<br /><br />而0xE0028000则是代表一个数值。<br /><br />编译器处理是不一样的。
 楼主| zwen01 发表于 2007-12-21 14:45 | 显示全部楼层

还是不清楚

直接&nbsp;#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0xE0028000&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;<br />应该是可以的,如下例:<br /><br /><br />#define&nbsp;&nbsp;&nbsp;BEEPCON&nbsp;&nbsp;0x00000080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;P0.7引脚控制B1,低电平蜂鸣<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />/****************************************************************************<br />*&nbsp;名&nbsp;&nbsp;&nbsp;&nbsp;称:main()<br />*&nbsp;功&nbsp;&nbsp;&nbsp;&nbsp;能:控制蜂鸣器蜂鸣。<br />****************************************************************************/<br />int&nbsp;&nbsp;main(void)<br />{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;PINSEL0&nbsp;=&nbsp;0x00000000;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;设置管脚连接GPIO&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;IO0DIR&nbsp;=&nbsp;BEEPCON;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;设置I/O为输出<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;while(1)&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IO0SET&nbsp;=&nbsp;BEEPCON;&nbsp;//&nbsp;&nbsp;把0x00000080值送入0xE0028000寄存器,即p0.7置高<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;return(0);<br />}<br /><br />意思我知道,但具体分析就不懂了。
ATmega16 发表于 2007-12-22 09:27 | 显示全部楼层

看C语言书去吧

(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000))<br />与<br />((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000)<br />都分不清,好好地看C语言书去吧。
zlgarm 发表于 2007-12-22 11:17 | 显示全部楼层

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

zwen01&nbsp;您好!<br /><br />#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br /><br />以此句为例,简述如下:<br /><br />与51单片机的头文件&ltreg51.h&gt类似,在ARM处理器中,也必须有自己的寄存器头文件声明,不同的是,在ARM应用中,需要用户自己来编写.<br /><br />0xE0028000:在ARM处理器中,只能识别为一个十六进制数值,具体是数据还是地址,它并不能自动区分.<br /><br />而使用(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0xE0028000,对此数据进行强制转换,表明此数值为一个无符号的长整型地址指针值,关键字volatile&nbsp;告诉编译器它指向的内容是易变的,可能会被硬件等意外地修改.这一点很关键.<br />*((volatile&nbsp;unsigned&nbsp;int&nbsp;*)&nbsp;0xFFE00000),则是获取指针所指向地址处的内容,从而,使用了#define&nbsp;预处理语句对IO0PIN进行定义后,在用户程序中,对IO0PIN的操作,就等同于在0xE0028000地址上进行读写操作.<br />具体C语言的知识,请参考相关书籍.如果平时在学习中遇到ARM相关的问题,也可以登陆以下网页:<br /><br />http://www.zlgmcu.com.cn/index.asp?boardid=29&page=1<br /><br />我们会将一些解决方案汇总在这个网页中进行回复.敬请留意.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;zlgarm&nbsp;_wgg
chenmi0201 发表于 2007-12-23 20:01 | 显示全部楼层

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

请问楼上的&nbsp;zlgarm<br />#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)0xE0028000))&nbsp;&nbsp;/*&nbsp;no&nbsp;in&nbsp;lpc210x*/<br />&nbsp;&nbsp;就像C51的绝对地址访问<br /><br />但是在EasyARM2200实验板的例程实验十三中(存储器重映射实验),定义了一个宏#define&nbsp;&nbsp;VECTOR_RAM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((unsigned&nbsp;char&nbsp;*)&nbsp;0x40000000)<br />这也是绝对地址访问啊,但是为什么没有存储修饰符volatile。地址0x40000000指向的是LPC2210内部的静态RAM,数据也容易改变且也易被编译器优化掉。请大侠给予解惑!<br />&nbsp;实验程序如下:<br />#include&nbsp;&nbsp;&quot;config.h&quot;<br />#define&nbsp;&nbsp;VECTOR_RAM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((unsigned&nbsp;char&nbsp;*)&nbsp;0x40000000)<br />/**********************************************************<br />*&nbsp;名称:main()<br />*&nbsp;功能:存储器重映射。<br />**********************************************/<br />int&nbsp;&nbsp;main(void)<br />{&nbsp;&nbsp;uint8&nbsp;&nbsp;i;<br />&nbsp;&nbsp;&nbsp;uint8&nbsp;&nbsp;*addr;<br /><br />&nbsp;&nbsp;&nbsp;MEMMAP&nbsp;=&nbsp;0x02;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;中断向量从静态RAM重映射(用户RAM模式)<br /><br />&nbsp;&nbsp;&nbsp;addr&nbsp;=&nbsp;VECTOR_RAM;<br />&nbsp;&nbsp;&nbsp;for(i=0;&nbsp;i&lt64;&nbsp;i++)&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;用0x55填充0x40000000~0x4000003F<br />&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;*addr&nbsp;=&nbsp;0x55;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr++;<br />&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;addr&nbsp;=&nbsp;VECTOR_RAM;<br />&nbsp;&nbsp;&nbsp;for(i=0;&nbsp;i&lt64;&nbsp;i++)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;用0xAA填充0x40000000~0x4000003F<br />&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;*addr&nbsp;=&nbsp;0xAA;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr++;<br />&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;MEMMAP&nbsp;=&nbsp;0x00;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;while(1);<br />&nbsp;&nbsp;&nbsp;return(0);<br />}<br />
王紫豪 发表于 2007-12-23 23:40 | 显示全部楼层

强制类型转换

  
XDream 发表于 2007-12-24 08:43 | 显示全部楼层

volatile

编译器并不是什么都优化的,它有一定规则。<br />建议先了解一下volatile。
chenmi0201 发表于 2007-12-24 11:43 | 显示全部楼层

volatile

个人认为<br />#define&nbsp;&nbsp;VECTOR_RAM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((unsigned&nbsp;char&nbsp;*)&nbsp;0x40000000)<br />&nbsp;加上volatile更可靠。因为在编写代码初期,我们没有去关注编译器如何优化代码的<br />&nbsp;以上纯属我个人的意见,如果有错误,望大家见量,并且提出宝贵的意见,并多多的指点。<br />
 楼主| zwen01 发表于 2007-12-25 12:18 | 显示全部楼层

有点明白

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

这样理解

#define&nbsp;IO0PIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*((volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000))&nbsp;<br /><br />(volatile&nbsp;unsigned&nbsp;long&nbsp;*)&nbsp;0xE0028000&nbsp;<br />把0xE0028000这个地址强制转换为long型指针.<br />即对指针的操作的范围是从0xE0028000开始的4个字节(long型).<br /><br />volatile&nbsp;限定编译器不对这个指针的指向的存储单元进行优化,<br />即不用通用寄存器暂时代替这个指针的指向的存储单元,<br />而是每次取值都直接到指针的指向的存储单元取值.<br /><br />volatile&nbsp;主要用于变量会异步改变的情况下,主要有三个方面:<br /><br />1.cpu外设寄存器<br />2.中断和主循环都会用到的全局变量<br />3.操作系统中的线程间都会用到的公共变量.<br /><br /><br /><br /><br />
hasayakipw 发表于 2007-12-28 21:49 | 显示全部楼层

转:volatile

volatile的本意是“易变的”,由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:<br />static&nbsp;int&nbsp;i=0;<br />int&nbsp;main(void)<br />{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(i)&nbsp;dosomething();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br />/*&nbsp;Interrupt&nbsp;service&nbsp;routine.&nbsp;*/<br />void&nbsp;ISR_2(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i=1;<br />}<br /><br />程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过&nbsp;i,因此可能只执行一次对从&nbsp;i&nbsp;到某寄存器的读操作,然后每次&nbsp;if&nbsp;判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的&nbsp;读写操作都不会被优化(肯定执行)。此例中&nbsp;i&nbsp;也应该如此说明。<br />一般说来,volatile用在如下的几个地方:<br />1、中断服务程序中修改的供其它程序检测的变量需要加volatile;<br />2、多任务环境下各任务间共享的标志应该加volatile;<br />3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;<br />另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。<br /><br />简单点:<br />就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化(所有的编译器的优化均假设编译器知道变量的变化规律)<br /><br />2.关于volatile关键字的说明以及测试&nbsp;<br />volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。<br />使用该关键字的例子如下:<br /><br />int&nbsp;volatile&nbsp;nVint;<br /><br />当要求使用volatile&nbsp;声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。例如:<br /><br />volatile&nbsp;int&nbsp;i=10;<br />int&nbsp;a&nbsp;=&nbsp;i;<br />。。。//其他代码,并未明确告诉编译器,对i进行过操作<br />int&nbsp;b&nbsp;=&nbsp;i;<br /><br />volatile&nbsp;指出&nbsp;i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器&nbsp;发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者&nbsp;表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。<br />注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响:<br /><br />首先用classwizard建一个win32&nbsp;console工程,插入一个voltest.cpp文件,输入下面的代码:<br />#include&nbsp;&ltstdio.h&gt<br />void&nbsp;main()<br />{<br />int&nbsp;i=10;<br />int&nbsp;a&nbsp;=&nbsp;i;<br /><br />printf(&quot;i=&nbsp;%d
&quot;,a);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道<br />__asm&nbsp;{<br />&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dword&nbsp;ptr&nbsp;[ebp-4],&nbsp;20h<br />}<br /><br />int&nbsp;b&nbsp;=&nbsp;i;<br />printf(&quot;i=&nbsp;%d
&quot;,b);<br />}<br /><br />然后,在调试版本模式运行程序,输出结果如下:<br />i&nbsp;=&nbsp;10<br />i&nbsp;=&nbsp;32<br /><br />然后,在release版本模式运行程序,输出结果如下:<br />i&nbsp;=&nbsp;10<br />i&nbsp;=&nbsp;10<br /><br />输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。<br /><br />下面,我们把&nbsp;i的声明加上volatile关键字,看看有什么变化:<br /><br />#include&nbsp;&ltstdio.h&gt<br />void&nbsp;main()<br />{<br />volatile&nbsp;int&nbsp;i=10;<br />int&nbsp;a&nbsp;=&nbsp;i;<br /><br />printf(&quot;i=&nbsp;%d
&quot;,a);<br />__asm&nbsp;{<br />&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dword&nbsp;ptr&nbsp;[ebp-4],&nbsp;20h<br />}<br /><br />int&nbsp;b&nbsp;=&nbsp;i;<br />printf(&quot;i=&nbsp;%d
&quot;,b);<br />}<br /><br />分别在调试版本和release版本运行程序,输出都是:<br />i&nbsp;=&nbsp;10<br />i&nbsp;=&nbsp;32<br /><br />这说明这个关键字发挥了它的作用!<br />
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

14

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部