(*(volatile unsigned int *)0X56000000)详解

本文详细解析了*(volatile unsigned int*)语法的意义及其在嵌入式系统中的应用。通过具体的宏定义实例,深入探讨了如何利用C语言访问特定内存地址,并解释了volatile关键字的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

*(volatile unsigned int *))详解

                                                

摘自:新浪博客>阿拉丁神丢 10/10/06

在看vivi代码时,Nand_read.c文件中有下面一段

#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))

#define NF_BASE  0x4e000000
#define NFCONF  __REGi(NF_BASE + 0x0)
#define NFCMD  __REGb(NF_BASE + 0x4)
#define NFADDR  __REGb(NF_BASE + 0x8)
#define NFDATA  __REGb(NF_BASE + 0xc)

#define NFSTAT  __REGb(NF_BASE + 0x10)

把宏定义放入句中就是(*(volatile unsigned int *))(0x4e000000),不知其何意??好像是定义一指针,该指针指向的内容就是0x4e000000该寄存器的内容。

 

网上查了资料,先看英文的,看看外国人怎么解释:

Using C, I was trying to assign a variable name to a register address so that my code would be readable. An example of how to do this is as follows:

#define DDRA (*(volatile unsigned char *)(0x22))

This means that if a register or memory location exists at address 0×22, I can use DDRA to read or write to it like so..

DDRA = 0x05

 

In my C code.The #define looks really cryptic at first. The way to understand this is by breaking it down into pieces.

First of all,

unsigned char

means we are using a byte-sized memory location. Byte being 8-bits wide.

unsigned char *

means we are declaring a pointer that points to a byte-sized location.

(unsigned char *) (0x22)

means the byte-sized pointer points to address 0×22. The C compiler will refer to address 0×22 when the variable DDRA is used. The assembly code will end up using 0×22 in Load(LD) and Store (STR) insturctions.

(*(unsigned char *)(0x22))

The first asterisk from the left signifies that we want to manipulate the value in address 0×22. * means “the value pointed to by the pointer.

volatile

volatile forces the compiler to issue a Load or Store anytime DDRA is accessed as the value may change without the compiler knowing it.

 

再看看中文的解释:
   
 使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义

    #define RAM_ADDR     (*(volatile unsigned long *)0x0000555F)
   
 然后就可以用C语言对这个内存地址进行读写操作了
   
 读:tmp = RAM_ADDR
   
 写:RAM_ADDR = 0x55
定义volatile是因为它的值可能会改变,大家都知道为什么改变了;
如果在一个循环操作中需要不停地判断一个内存数据,例如要等待RAM_ADDRI标志位置位,因为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,那么就是#define  A  *p,即AP指针指向位置的内容了。这里就是通过内存寻址访问到寄存器A,可以读/写操作。
GCC编译时。volatile所指示的寄存器不进行优化!!!


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

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

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

 

 

/按键实验***********/ // 公司名称 :保定飞凌嵌入式技术有限公司 // 描 述 :按键控制蜂鸣器 // 版 权 :保定飞凌嵌入式技术有限公司 // 网 址 :www.witech.com.cn /***************************************************************/ /* 本实验接口说明 GPB5 ------ LED0 GPB6 ------ LED1 GPB8 ------ LED2 GPB10 ------ LED3 */ /------------------------地址声明---------------------------/ #define GPBCON (*(volatile unsigned )0x56000010) #define GPBDAT ((volatile unsigned )0x56000014) #define GPBUP ((volatile unsigned *)0x56000018) #define uchar unsigned char #define uint unsigned int /-----------------------定义全局变量------------------------/ /-----------------------函数声明----------------------------/ void Delay(int x); /------------------------------------------------------------/ 函数名称: 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--; } } /------------------------------------------------------------- 函数名称: ledMain 功能描述: 入口程序 初始化后,进入跑马灯死循环 传 参: 无 返 回 值: int 0 -------------------------------------------------------------/ int ledMain(void) { GPBCON = 0x1dd7fc; // GPB5,GPB6,GPB8,GPB10设置为输出 GPBDAT = ((1<<5)|(1<<6)|(1<<8)|(1<<10)); //使LED全灭 GPBDAT&=0xffe; //关闭蜂鸣器 GPBUP = 0x00; while (1) // 死循环 { GPBDAT = ~(1<<5); //LED0亮 Delay(500); GPBDAT = ~(1<<6); //LED1亮 Delay(500); GPBDAT = ~(1<<8); //LED2亮 Delay(500); GPBDAT = ~(1<<10); //LED3亮 Delay(500); } return 0; } ; init.s AREA |DATA|,CODE,READONLY ENTRY ldr r13, =0x1000 ;设置堆栈栈顶指针 IMPORT ledMain b ledMain END 预定义语句#define rGPBCON (*(volatile unsigned )0x56000010)中,关键字volatile的作用是什么?unsigned * 的作用的是什么? 第一个的作用是什么?rGPBCON代表的是什么特殊功能寄存器?其功能是什么? volatile 不稳定的,易变的,告诉编译器不要对volatile 修饰的变量或存储单元进行优化,其值随时可变,每次都直接访问存储单元。 unsigned * 的作用是强制类型转换,把0x56000010的本来的整数类型强制转换成无符号指针类型 第一个*的作用在地址0x56000010上,是指针运算符,访问地址为0x56000010的特殊功能寄存器 GPBCON是 GPIO的B端口的配置寄存器,为每一个GPIO B组的引脚定义功能(用2位定义一个引脚的功能) 00 - 输入 01-输出 10-定义具体硬件模块引脚功能 11 - 保留 把十六进制数0x1dd7fc转换成二进制,配置管脚GPB5的功能是什么?是GPBCON的哪些位段以及什么数值决定的? 语句rGPBDAT = ((1<<5)|(1<<6)|(1<<8)|(1<<10)); 执行后,GPB5、GPB6、GPB8、GPB10分别输出高电平还是低电平? 高电平, 对应的LED灯亮还是灭? 都处于熄灭状态(初始状态) 语句rGPBDAT = ~(1<<8); 执行后,GPB5、GPB6、GPB8、GPB10分别输出高电平还是低电平?对应的LED灯亮还是灭? GPB5、GPB6、GPB10输出高电平,对应的LED灯熄灭;GPB8输出低电平,对应的LED灯点亮 /按键实验***********/ // 公司名称 :保定飞凌嵌入式技术有限公司 // 描 述 :蜂鸣器 // 版 权 :保定飞凌嵌入式技术有限公司 // 网 址 :www.witech.com.cn /***************************************************************/ /* 本实验接口说明 GPB0 ------ 蜂鸣器控制口 */ void Delay(unsigned int); /------------------------地址声明-----------------------------/ #define GPBCON (*(volatile unsigned )0x56000010) #define GPBDAT ((volatile unsigned )0x56000014) #define GPBUP ((volatile unsigned *)0x56000018) /------------------------------------------------------------- 函数名称: BellMain 功能描述: 入口程序 初始化后,进入按键扫描死循环 传 参: 无 返 回 值: int 0 -------------------------------------------------------------/ int BellMain() { GPBUP &= 0XFFFFFFFE; //上拉使能GPB0 GPBCON &= 0XFFFFFFC; //GPB0设为输出 GPBCON |= 0X0000001; while(1) { GPBDAT &= 0xfe; //不能用!(非) Delay(40); GPBDAT |=0x1; //用与或方式,不影响其他位 Delay(40); } return(0); } /------------------------------------------------------------/ 函数名称: Delay 功能描述: 延时函数 延时count毫秒 传 参: int count 返 回 值: 无 -------------------------------------------------------------/ void Delay(unsigned int x ) { unsigned int i,j,k; for(i=0;i<=x;i++) for(j=0;j<=0xff;j++) for(k=0;k<=0xff;k++); } AREA |DATA|,CODE,READONLY ENTRY ldr r13,=0x1000 IMPORT BellMain b BellMain END1. BellMain ()函数中的两条语句rGPBCON &= 0xFFFFFFC; rGPBCON |= 0x0000001;的具体作用是什么? //上拉电阻使能GPB0 2. GPB0端口的上拉电阻有没有使能?判断依据是什么? 3. 语句rGPBDAT &= 0xFE; rGPBDAT |= 0x1; 的作用是什么? 写出实验小结
06-09
/******************************* * 核心设置 * *******************************/ // 引脚定义 #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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值