(*(volatileunsigned ……

本文介绍了如何使用C语言通过内存映射的方式访问特定的内存地址,特别关注于嵌入式系统中的寄存器和I/O端口。通过具体实例展示了如何定义内存映射的宏,并解释了使用volatile关键字的原因及其重要性。
#define  GPIO_CTL_BASE0x56000000                                                

#define rTCFG0  (*(volatileunsigned *)0x51000000)

#define rTCFG1  (*(volatileunsigned *)0x51000004)

#define rTCNTB0  (*(volatileunsigned *)0x5100000C)

#define rTCMPB0  (*(volatileunsigned *)0x51000010)

#define rTCON  (*(volatileunsigned *)0x51000008)

#define rGPBCON  (*(volatileunsigned *)0x56000010)

#define rGPBUP  (*(volatileunsigned *)0x56000018)

#define rGPBDAT  (*(volatileunsigned *)0x56000014) 
 
下面是网上前辈看到的英文资料: 
Using C, I was trying to assign a variable nameto a register address so thatmy 
code would be readable. An example of how to dothis is as follows: 
#define DDRA (*(volatile unsigned char*)(0x22)) 
This means that if a register or memorylocation exists at address 0×22, I canuse 
DDRA to read or write to it likeso.. 
DDRA =0x05 

In my C code.The #define looks really crypticat first. The way to understandthis 
is by breaking it down intopieces. 
First ofall, 
unsignedchar 
means we are using a byte-sized memorylocation. Byte being 8-bitswide. 
unsigned char * means we are declaring apointer that points to a byte-sizedlocation. 
(unsigned char *)(0x22) 
means the byte-sized pointer points to address0×22. The C compiler will referto 
address 0×22 when the variable DDRA is used.The assembly code will end upusing 
0×22 in Load(LD) and Store (STR)insturctions. 
(*(unsigned char*)(0x22)) 
The first asterisk from the left signifies thatwe want to manipulate the valuein 
address 0×22. * means “the value pointed to bythe pointer”. 
volatile 
volatile forces the compiler to issue a Load orStore anytime DDRA is accessedas 
the value may change without the compilerknowing it. 

中文的解释: 

#define DDRA (*(volatile unsigned char*)(0x22)) 
   然后就可以用 C 语言对这个内存地址进行读写操作了 
   读:tmp = DDRA; 
   写:DDRA = 0x55; 
定义 volatile是因为它的值可能会改变,大家都知道为什么改变了; 
如果在一个循环操作中需要不停地判断一个内存数据,例如要等待 RAM_ADDR 的I标志位置
位, 因为 RAM_ADDR 也是映射在 SRAM 空间,为了加快速度,编译器可能会编译出这样的代码:
把 RAM_ADDR 读取到 Register 中,然后不停地判断 Register相应位。而不会再读取
RAM_ADDR,这样当然是不行了,因为程序或其它事件(中断等)会改变RAM_ADDR,结果很
可能是一个死循环出不来了。如果定义成 volatile型变量,编译的代码是这样的:每次要
操作一个变量的时候都从内存中读取一次。 
#define rGPACON(*(volatile unsigned long*)0x56000000) 
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支
持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因
为 C语言并没有提供真正的“端口”的概念。如果是内存映射,那就方便的多了。 
举个例子,比如像寄存器 A(地址假定为 0x48000000)写入数据0x01,那么就可以这样设
置了。 
#define A (*(volatile unsigned long*)0x48000000) 
... 
    A = 0x01; 
... 
   这实际上就是内存映射机制的方便性了。其中 volatile关键字是嵌入式系统开发的一个重要特点。上述表达式拆开来分析,首先(volatile unsigned long*)0x48000000的意思是把0x48000000 强制转换成 volatile unsigned long 类型的指针,暂记为p,那么就是#defineA *p,即A 为P 指针指向位置的内容了。 这里就是通过内存寻址访问到寄存器 A,
可以读/写操作。 
用 GCC 编译时。volatile所指示的寄存器不进行优化!!! 

理解#definerRTCCON   (*(volatile unsigned char *)0x57000043) //RTCcontrol 
嵌入式系统编程,要求程序员能够利用 C语言访问固定的内存地址。既然是个地址,那么按
照 C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,
比如0x57000043, 
  第一步是要把它强制转换为指针类型 
(unsigned char *)0x57000043, s3c2410的rRTCCON是单字节访问的(怎么看出来的???
我无法理解),所以 0x57000043 强制转换为指向 unsigned char类型。 
  volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不
会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改
变——意想不到。 
  第二步,对指针变量解引用,就能操作指针所指向的地址的内容了 
   *(volatileunsigned char *)0x57000043 
  第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯。 
在嵌入式系统中经常使用到 Volatile,对于volatile的用法 

编译器对代码的优化是指: 
CPU 在执行的过程中,因为访问内存的速度远没有 cpu的执行速度快,为了提高效率,引入了
高速缓存 cache. C编译器在编译时如果不知道变量会被其它外部因素(操作系统、硬件或者
其它线程)修改,那么就会对该变量进行标识,即优化.那么这个变量在CPU的执行过程中,就
会被放到高速缓存 cache 去,进而达到对变量的快速访问.在了解了优化的概念后,试想如
果我们事先就知道该变量会被外部因素改变,那么我们就在这个变量定义前加上Volatile,
这样编译器就不会对该变量进行优化.这样该变量在 cpu处理的过程当中,就不会被放到高
速缓存 cache中。 
为什么要让变量在执行的过程中不被放到 cache中去呢? 
如果变量是被外部因素改变,那么 cpu就无法判断出这个变量已经被改变,那么程序在执行
的过程中如果使用到该变量,还会继续使用 cache中的变量,但是这个变量其实已经被改变
了.需要到内存地址中更新其内容了. 
还有一个原因,在一些寄存器变量或数据端口的使用中,因为寄存器变量本身也是靠cache
来处理,为了避免引起错误,也可以使用volatile修饰符. 

简单的说使用volatile的目的就是: 
让对 volatile变量的存取不能缓存到寄存器,每次使用时需要重新存取。



参考阅读:

http://hi.baidu.com/hustor/item/e4e90d17a68b883ab93180d9

http://blog.sina.com.cn/s/blog_65861d3f0100xsz0.html

http://www.openedv.com/posts/list/918.htm

/******************************* * 核心设置 * *******************************/ // 引脚定义 #define BUTTON_PIN = P1_7; // 控制按键 #define LIMIT_UP_PIN = P3_7; // 上限位开关 #define LIMIT_DOWN_PIN = P3_6; // 下限位开关 // 电机控制引脚 #define MOTOR_PIN_A = P3_5; #define MOTOR_PIN_B = P3_4; #define MOTOR_PIN_C = P3_3; #define MOTOR_PIN_D = P3_2; // 可配置参数 #define TOTAL_STEPS = 2000; // 总步数(可修改) #define STEP_DELAY = 10; // 步进延迟ms(控制速度,可修改) /******************************* * 全局状态变量 * *******************************/ volatile unsigned char gStepIndex = 0; volatile unsigned int gCurrentPosition = 0; volatile unsigned char gMotorState = 0; // 0=停止, 1=正转, 2=反转 volatile unsigned int gSystemTimer = 0; /* 步进电机4相8拍控制表 */ code unsigned char stepTable[8] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09 }; /******************************* * 定时器初始化 * *******************************/ void Timer0Init(void) { TMOD = 0x01; // 定时器0模式1 TH0 = 0xFC; // 1ms中断 TL0 = 0x66; ET0 = 1; // 允许定时器0中断 TR0 = 1; // 启动定时器0 EA = 1; // 开启总中断 } /******************************* * 步进电机控制 * *******************************/ void MotorControl(void) { static unsigned int stepTimer = 0; if (gSystemTimer - stepTimer >= STEP_DELAY) { stepTimer = gSystemTimer; if (gMotorState != 0) // 电机运行中 { // 正转处理 if (gMotorState == 1) { gStepIndex++; if (gStepIndex >= 8) gStepIndex = 0; if (gCurrentPosition < TOTAL_STEPS) gCurrentPosition++; } // 反转处理 else { if (gStepIndex == 0) gStepIndex = 8; gStepIndex--; if (gCurrentPosition > 0) gCurrentPosition--; } // 输出步进信号 MOTOR_PIN_A = (stepTable[gStepIndex] & 0x01) ? 1 : 0; MOTOR_PIN_B = (stepTable[gStepIndex] & 0x02) ? 1 : 0; MOTOR_PIN_C = (stepTable[gStepIndex] & 0x04) ? 1 : 0; MOTOR_PIN_D = (stepTable[gStepIndex] & 0x08) ? 1 : 0; } } } /******************************* * 按键扫描处理 * *******************************/ void ButtonHandler(void) { static unsigned char lastState = 1; static unsigned int debounceTimer = 0; if (gSystemTimer - debounceTimer > 20) // 20ms消抖 { debounceTimer = gSystemTimer; unsigned char currentState = BUTTON_PIN; // 按键按下 if (currentState == 0 && lastState == 1) { // 状态切换: 停止->正转->反转->停止 if (gMotorState == 0) { gMotorState = 1; // 开始正转 } else if (gMotorState == 1) { gMotorState = 2; // 切换反转 } else { gMotorState = 0; // 停止 PowerOffMotor(); } } lastState = currentState; } } /******************************* * 限位检查处理 * *******************************/ void CheckLimits(void) { // 正转时碰到上限位 if (LIMIT_UP_PIN == 0 && gMotorState == 1) { gMotorState = 0; // 停止电机 PowerOffMotor(); gCurrentPosition = TOTAL_STEPS; // 设置位置为最大值 } // 反转时碰到下限位 if (LIMIT_DOWN_PIN == 0 && gMotorState == 2) { gMotorState = 0; // 停止电机 PowerOffMotor(); gCurrentPosition = 0; // 设置位置为0 } } /******************************* * 关闭电机输出 * *******************************/ void PowerOffMotor(void) { MOTOR_PIN_A = 0; MOTOR_PIN_B = 0; MOTOR_PIN_C = 0; MOTOR_PIN_D = 0; } /******************************* * 定时器中断 * *******************************/ void Timer0_ISR(void) interrupt 1 { TH0 = 0xFC; // 重装初值 TL0 = 0x66; gSystemTimer++; } /******************************* * 初始化函数 * *******************************/ void setup(void) { // GPIO初始化 P3M1 = 0x00; P3M0 = 0x3C; // P3.2-P3.5推挽输出 (电机控制) P1M1 = 0x00; P1M0 = 0x00; // P1.7准双向 (按键) P3M1 = 0x00; P3M0 = 0x00; // P3.6-P3.7准双向 (限位开关) // 外设初始化 Timer0Init(); // 初始状态 PowerOffMotor(); } /******************************* * 主循环函数 * *******************************/ void loop(void) { ButtonHandler(); CheckLimits(); MotorControl(); } /******************************* * 主函数 * *******************************/ void main(void) { setup(); while (1) { loop(); } } 以上代码编译出现以下问题.\twen\main.c:29: syntax error: token -> 'unsigned' , column 13,帮我解决一下
最新发布
07-02
/**********************按键实验*********************************/ // 公司名称 :保定飞凌嵌入式技术有限公司 // 描 述 :按键控制蜂鸣器 // 版 权 :保定飞凌嵌入式技术有限公司 // 网 址 :www.witech.com.cn /***************************************************************/ /* 本实验接口说明 */ #include "2440addr.h" /*-----------------------函数声明----------------------------*/ extern void Uart_Printf(char *fmt,...); void Wr24C02(U32 slvAddr,U32 addr,U8 data); void Rd24C02(U32 slvAddr,U32 addr,U8 *data); void Run_IicPoll(void); void IicPoll(void) ; void Delay(int x) ; static U8 iicData[IICBUFSIZE]; static volatile int iicDataCount; static volatile int iicStatus; static volatile int iicMode; static int iicPt; /*------------------------------------------------------------/ 函数名称: iicMain 功能描述: 入口函数 传 参: int count 返 回 值: 无 -------------------------------------------------------------*/ void iicMain(void) { unsigned int i,j; static U8 data[256]; Uart_Printf("IIC Test(Polling) using AT24C02\n"); //设置GPE15->IICSDA 和 GPE14->IICSCL rGPEUP |= 0xc000; //关断上拉 rGPECON &= ~0xf0000000; rGPECON |= 0xa0000000; //GPE15:IICSDA , GPE14:IICSCL //使能ACK, 预分频 IICCLK=PCLK/16, 使能中断, 发送时钟 Tx clock=IICCLK/16 rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); rIICADD = 0x10; //2440 从地址 = [7:1] rIICSTAT = 0x10; //IIC总线数据输出使能(Rx/Tx) Uart_Printf("Write test data into AT24C02\n"); //写入一个page的数据,page的大小是256byte, //page 的起始地址是0xa0,写入的数据是:01、2、...255。0xa0是AT24C02的页地址。 //AT24C02的页地址是0x00/0x20/0x40/0x60/0x80/0xa0/0xc0/0xe0。 for(i=0;i<256;i++) Wr24C02(0xa0,(U8)i,i); //U32 slvAddr,U32 addr,U8 data for(i=0;i<256;i++) //初始化data数组的值为0。 data[i] = 0; Uart_Printf("Read test data from AT24C02\n"); for(i=0;i<256;i++) //读24C02的0xa0地址中数据到data数组中。 Rd24C02(0xa1,(U8)i,&(data[i])); for(i=0;i<16;i++) //输出data数组接收数据的值 { for (j=0;j<16;j++) Uart_Printf("%2x ",data[i*16+j]); Uart_Printf("\n"); } Uart_Printf("OK! Write data is same to Read data!\n"); while(1); } /*------------------------------------------------------------/ 函数名称: Wr24C02 功能描述: 写24c02 传 参: U32 slvAddr,U32 addr,U8 data 返 回 值: 无 -------------------------------------------------------------*/ void Wr24C02(U32 slvAddr,U32 addr,U8 data) { iicMode = WRDATA; iicPt = 0; iicData[0] = (U8)addr; iicData[1] = data; iicDataCount = 2; //8-bit data shift register for IIC-bus Tx/Rx operation. rIICDS = slvAddr; //0xa0 //Master Tx mode, Start(Write), IIC-bus data output enable //Bus arbitration sucessful, Address as slave status flag Cleared, //Address zero status flag cleared, Last received bit is 0 rIICSTAT = 0xf0; //Clearing the pending bit isn't needed because the pending bit has been cleared. while(iicDataCount!=-1) Run_IicPoll(); iicMode = POLLACK; while(1) { rIICDS = slvAddr; iicStatus = 0x100; //To check if _iicStatus is changed rIICSTAT = 0xf0; //Master Tx, Start, Output Enable, Sucessful, Cleared, Cleared, 0 rIICCON = 0xe0;//0xaf; //Resumes IIC operation. //hzh while(iicStatus==0x100) Run_IicPoll(); if(!(iicStatus & 0x1)) break; //When ACK is received } rIICSTAT = 0xd0; //Master Tx condition, Stop(Write), Output Enable rIICCON = 0xe0;//0xaf; //Resumes IIC operation. //hzh Delay(1); //Wait until stop condtion is in effect. } /*------------------------------------------------------------/ 函数名称: Rd24C02 功能描述: 读24c02 传 参: U32 slvAddr,U32 addr,U8 *data 返 回 值: 无 -------------------------------------------------------------*/ void Rd24C02(U32 slvAddr,U32 addr,U8 *data) { iicMode = SETRDADDR; iicPt = 0; iicData[0] = (U8)addr; iicDataCount = 1; rIICDS = slvAddr; rIICSTAT = 0xf0; //MasTx,Start //Clearing the pending bit isn't needed because the pending bit has been cleared. while(iicDataCount!=-1) Run_IicPoll(); iicMode = RDDATA; iicPt = 0; iicDataCount = 1; rIICDS = slvAddr; rIICSTAT = 0xb0; //主接收开始 rIICCON = 0xe0; //Resumes IIC operation. while(iicDataCount!=-1) Run_IicPoll(); *data = iicData[1]; } /*------------------------------------------------------------/ 函数名称: Run_IicPoll 功能描述: 传 参: 无 返 回 值: 无 -------------------------------------------------------------*/ void Run_IicPoll(void) { if(rIICCON & 0x10) //Tx/Rx Interrupt Enable IicPoll(); } /*------------------------------------------------------------/ 函数名称: IicPoll 功能描述: 传 参: 无 返 回 值: 无 -------------------------------------------------------------*/ void IicPoll(void) { U32 iicSt,i; iicSt = rIICSTAT; if(iicSt & 0x8){}; //When bus arbitration is failed. 总线仲裁失败 if(iicSt & 0x4){}; //When a slave address is matched with IICADD if(iicSt & 0x2){}; //When a slave address is 0000000b if(iicSt & 0x1){}; //When ACK isn't received switch(iicMode) { case POLLACK: iicStatus = iicSt; break; case RDDATA: if((iicDataCount--)==0) { iicData[iicPt++] = rIICDS; rIICSTAT = 0x90; //Stop MasRx condition rIICCON = 0xe0; //Resumes IIC operation. Delay(1); //Wait until stop condtion is in effect. //Too long time... //The pending bit will not be set after issuing stop condition. break; } iicData[iicPt++] = rIICDS; //The last data has to be read with no ack. if((iicDataCount)==0) rIICCON = 0x60;//0x2f; //Resumes IIC operation with NOACK. else rIICCON = 0xe0;//0xaf; //Resumes IIC operation with ACK break; case WRDATA: if((iicDataCount--)==0) { rIICSTAT = 0xd0; //stop MasTx condition rIICCON = 0xe0;//0xaf; //resumes IIC operation. Delay(1); //wait until stop condtion is in effect. //The pending bit will not be set after issuing stop condition. break; } rIICDS = iicData[iicPt++]; //_iicData[0] has dummy. for(i=0;i<10;i++); //for setup time until rising edge of IICSCL rIICCON = 0xe0;//0xaf; //resumes IIC operation. break; case SETRDADDR: if((iicDataCount--)==0) break; //IIC operation is stopped because of IICCON[4] rIICDS = iicData[iicPt++]; for(i=0;i<10;i++); //for setup time until rising edge of IICSCL rIICCON = 0xe0;//0xaf; //resumes IIC operation. break; default: break; } } /*------------------------------------------------------------/ 函数名称: Delay 功能描述: 延时函数 传 参: int x 返 回 值: 无 -------------------------------------------------------------*/ void Delay(int x) { int k, j; while(x) { for (k=0;k<=0xff;k++) for(j=0;j<=0xff;j++); x--; } } #include <stdarg.h> #include "2440addr.h" void Uart_Init(int baud) { int i; rUFCON0 = 0x0; //UART channel 0 FIFO control register, FIFO disable //UART0 rULCON0 = 0x3; //Line control register : Normal,No parity,1 stop,8 bits // [10] [9] [8] [7] [6] [5] [4] [3:2] [1:0] // Clock Sel, Tx Int, Rx Int, Rx Time Out, Rx err, Loop-back, Send break, Transmit Mode, Receive Mode // 0 1 0 , 0 1 0 0 , 01 01 // PCLK Level Pulse Disable Generate Normal Normal Interrupt or Polling rUCON0 = 0x805; // Control register rUBRDIV0=( (int)(50000000/16./baud+0.5) -1 ); //Baud rate divisior register 0 //UART1 for(i=0;i<100;i++); } //===================================================================== void Uart_SendByte(int data) { if(data=='\n') { while(!(rUTRSTAT0 & 0x2)); // Delay(1); //because the slow response of hyper_terminal WrUTXH0('\r'); } while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty. // Delay(1); WrUTXH0(data); } //==================================================================== void Uart_SendString(char *pt) { while(*pt) Uart_SendByte(*pt++); } //===================================================================== //If you don't use vsprintf(), the code size is reduced very much. void Uart_Printf(char *fmt,...) { va_list ap; char str[255]; va_start(ap,fmt); vsprintf(str,fmt,ap); Uart_SendString(str); va_end(ap); } AREA |DATA|,CODE,READONLY ENTRY ldr r13,=0x1000 IMPORT iicMain b iicMain END 1. 通过UART0发送一个字符的数据(INT8U data)的发送语句是什么?该语句能得到执行的条件是什么?把该条件写成C语言语句。 2. 准备把要接收的字符放在INT8U类型的变量reData中,则接收一个字符的语句是什么?接收到字符的条件是什么?把该条件写成C语言语句。 3. 预定义语句 #define rUTXH0 (*(volatile unsigned *) 0x50000020)中,关键字volatile的作用是什么?unsigned * 的作用的是什么?第一个*的作用是什么? rUTXH0代表的是什么特殊功能寄存器?其功能是什么? 4. 以下两条语句的具体作用是什么? ULCON0 &=0xFFFFFF00; ULCON0 |=0x03; 5. 若PCLK为50MHz,UART0使用的波特率是115200pbs,则给rUBRDIV0 赋的值是多少? 6. S3C2410A的IIC总线有哪两条信号线?这两条信号线的作用分别是什么?为使用这两条信号线,对IIC接口的管脚该做如何设置?IIC是如何发出开始信号和停止信号的?
06-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值