volatile unsigned char详解
- 格式:doc
- 大小:33.50 KB
- 文档页数:2
volatile和static的使用(存贮数据到指定位置)普通变量的定义和访问同标准C语言,在HCS08 C语言中我们主要要解决映像寄存器变量和某些特殊变量的定位问题,即把这些变量存放在RAM中指定的位置。
1映像寄存器定位映像寄存器单片机中跟硬件有关的寄存器,它们都有各自固定RAM地址,其定位有3种方法1)宏定义例如:#define PortA ( * ( volatile unsigned char * ) 0x0000 ) 这样 PortA 成为一个地址在0x0000的unsigned char类型变量。
这个定义看起来很复杂,其实它也可以分解成几个很简单的部分来看。
( volatile unsigned char * )是C语言中的强制类型转换,它的作用是把0x0000这个纯粹的十六进制数转换成为一个(地址)指针,其中volatile并不是必要的,它只是告诉编译器,这个值与外界环境有关,对它优化接下来在外面又加了一个*号,就表示0x0000内存单元中的内容了。
经过这个宏定义之后,PortA就被可以做为一个普通的变量来操作,所有出现PortA的地方编译的时候都被替换成( * ( volatile unsigned char * ) 0x0000 ),外面一层括号是为了保证里面的操作不会因为运算符优先级或者其它不可预测的原因被改变而无法得到预期的结果。
这种定义方法适合所有的C编译器,可移植性好,但PortA并不是一个真正的变量,只是一个宏名,当你调试一个程序的时候,无法在调试窗口观察它的值。
另外连接器也失去了灵活性,它得防止其它变量跟此变量冲突。
2)使用@关键字例如: volatile unsigned char PortA @0x0000;@是编译器扩展的一个特殊修饰符,其它编译器很可能并不认识。
这种定义具有很好的可读性,失去了可移植性。
3)使用段定义这种方法分为2个步骤首先把变量定义在段中,其次在连接参数文件(*.prm)中把段定位在一个合适的位置例如:第1步:在源程序文件中#pragma DATA_SEG PORTB_SEGvolatile unsigned char PortA;#pragma DATA_SEG DEFAULT这样变量 PortA 定义在段 PORTB_SEG 中第2步:在 prm 文件中SECTIONSPORTB_SEG = READ_WRITE 0x0000 SIZE 1;这样段 PORTB_SEG 定位在地址0x0000上。
#ifndef _HTC_H_#warning Header file pic168xa.h included directly. Use #include <htc.h> instead. #endif/** Microchip单片机的头文件* PIC 16F873A chip* PIC 16F874A chip* PIC 16F876A chip* PIC 16F877A chip* 中档单片机*/#if defined(_16F874A) || defined(_16F877A)#define __PINS_40#endifvolatile unsigned char INDF @ 0x00;//间接寻址寄存器volatile unsigned char TMR0 @ 0x01;//定时器0volatile unsigned char PCL @ 0x02;//低8位程序计数器volatile unsigned char STATUS @ 0x03;//程序状态寄存器volatile unsigned char FSR @ 0x04;//特殊功能寄存器volatile unsigned char PORTA @ 0x05;//端口A寄存器volatile unsigned char PORTB @ 0x06;//端口B寄存器volatile unsigned char PORTC @ 0x07;//端口C寄存器#ifdef __PINS_40volatile unsigned char PORTD @ 0x08;//端口D寄存器volatile unsigned char PORTE @ 0x09;//端口E寄存器#endifvolatile unsigned char PCLATH @ 0x0A;//高5位程序计数器volatile unsigned char INTCON @ 0x0B;//中断控制寄存器volatile unsigned char PIR1 @ 0x0C;//中断标志寄存器PIR1volatile unsigned char PIR2 @ 0x0D;//中断标志寄存器PIR2volatile unsigned char TMR1L @ 0x0E;//T1低字节计数寄存器volatile unsigned char TMR1H @ 0x0F;//T1高字节计数寄存器volatile unsigned char T1CON @ 0x10;//TMR1控制寄存器volatile unsigned char TMR2 @ 0x11;//定时/计数器TMR2volatile unsigned char T2CON @ 0x12;//TMR2控制寄存器volatile unsigned char SSPBUF @ 0x13;//收/发数据缓冲器volatile unsigned char SSPCON @ 0x14;/*同步串口控制寄存器,对MSSP模块的功能和指标进行设置和定义*/volatile unsigned char CCPR1L @ 0x15;//捕获/比较/PWM寄存器低字节volatile unsigned char CCPR1H @ 0x16;//捕获/比较/PWM寄存器低字节volatile unsigned char CCP1CON @ 0x17;//CCP1CON寄存器volatile unsigned char RCSTA @ 0x18;//USART接收控制兼状态寄存器volatile unsigned char TXREG @ 0x19;//USART发生缓冲器volatile unsigned char RCREG @ 0x1A;//USART接收缓冲器volatile unsigned char CCPR2L @ 0x1B;//捕获/比较/PWM寄存器低字节volatile unsigned char CCPR2H @ 0x1C;//捕获/比较/PWM寄存器低字节volatile unsigned char CCP2CON @ 0x1D;//CCP2CON寄存器volatile unsigned char ADRESH @ 0x1E;//ADC转换结果寄存器高字节volatile unsigned char ADCON0 @ 0x1F;//A/D转换器开关位/* bank 1 registers */volatile unsigned char OPTION @ 0x81;/*/选择寄存器,用于配置TMR0/WDT预分频系数、外部INT中断、TMR0和端口B的弱上拉。
Volatile的陷阱对于volatile关键字,大部分的C语言教材都是一笔带过,并没有做太过深入的分析,所以这里简单整理了一些关于volatile的使用注意事项。
实际上从语法上来看volatile和const 是一样的,但是如果const用错,几乎不会有什么问题;而volatile用错,后果可能很严重。
所以在volatile的使用上,建议大家还是尽量求稳,少用一些没有切实把握的技巧。
注意volatile修饰的是谁首先来看下面两个定义的区别:uchar * volatile reg;这行代码里volatile修饰的是reg这个变量。
所以这里实际上是定义了一个uchar类型的指针,并且这个指针变量本身是volatile 的。
但是指针所指的内容并不是volatile的!在实际使用的时候,编译器对代码中指针变量reg本身的操作不会进行优化,但是对reg所指的内容*reg却会作为non-volatile内容处理,对*reg的操作还是会被优化。
通常这种写法一般用在对共享指针的声明上,即这个指针变量有可能会被中断等函数修改。
将其定义为volatile 以后,编译器每次取指针变量的值的时候都会从内存中载入,这样即使这个变量已经被别的程序修改了当前函数用的时候也能得到修改后的值(否则通常只在函数开始取一次放在寄存器里,以后就一直使用寄存器内的副本)。
volatile uchar *reg;这行代码里volatile修饰的是指针所指的内容。
所以这里定义了一个uchar类型的指针,并且这个指针指向的是一个volatile的对象。
但是指针变量本身并不是volatile的。
如果对指针变量reg本身进行计算或者赋值等操作,是可能会被编译器优化的。
但是对reg所指向的内容*reg的引用却禁止编译器优化。
因为这个指针所指的是一个volatile的对象,所以编译器必须保证对*reg的操作都不被优化。
通常在驱动程序的开发中,对硬件寄存器指针的定义,都应该采用这种形式。
宏定义一个固定地址的变量2010-04-08 17:31内核中有这样一个#define GPFCON (*(volatile unsigned *)0x56000050)#define(*(volatile unsigned *) ) 讲解对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成:1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。
2)volatile,关键字 volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。
例如用while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。
用了volatile 则要求每次都去读0x20的实际值。
那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。
而char *u则是个指针变量。
再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。
那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。
而0x20只是个常量,不是指针更不是变量。
关于volatile关键字这个多是嵌入式编程时可能会用到。
寄存器操作方法_对寄存器操作的通用方法总结寄存器,是集成电路中非常重要的一种存储单元,通常由触发器组成。
在集成电路设计中,寄存器可分为电路内部使用的寄存器和充当内外部接口的寄存器这两类。
内部寄存器不能被外部电路或软件访问,只是为内部电路的实现存储功能或满足电路的时序要求。
而接口寄存器可以同时被内部电路和外部电路或软件访问,CPU中的寄存器就是其中一种,作为软硬件的接口,为广泛的通用编程用户所熟知。
本文主要详解寄存器操作方法以及对寄存器操作的通用方法总结,具体的跟随小编来了解一下。
一、寄存器操作1、#define方法1)寄存器地址的定义:#define UART_BASE_ADRS (0x10000000)/* 串口的基地址*/#define UART_RHR *(volatile unsigned char *)(UART_BASE_ADRS + 0)/* 数据接受寄存器*/#define UART_THR *(volatile unsigned char *)(UART_BASE_ADRS + 0)/* 数据发送寄存器*/2)寄存器读写操作:UART_THR = ch; /* 发送数据*/ch = UART_RHR; /* 接收数据*/也可采用定义带参数宏实现#define WRITE_REG(addr,ch)*(volatile unsigned char *)(addr)= ch#define READ_REG(addr,ch)ch = *(volatile unsigned char *)(addr)3)对寄存器相应位的操作方法:定义寄存器#define UART_LCR *(volatile unsigned char *)(UART_BASE_ADRS + 3)/* 线控制寄存器*/。
c语言中volatile的用法引言在C语言中,volatile是一个非常重要的关键字,它告诉编译器某个变量可能会被意外的改变,从而防止编译器对这些变量进行优化。
本文将介绍volatile的定义、用法以及其在多线程、嵌入式开发中的应用。
一、定义与作用volatile关键字是C语言中用来声明变量的一种类型修饰符,它用于告知编译器该变量可能被在程序执行中意外地改变,编译器在编译过程中会尽量避免对volatile 变量的优化。
volatile常见的作用有以下几个方面: 1. 防止编译器优化:编译器在进行优化时,会根据程序的逻辑简化一些操作,如果一个变量的值不会被程序以外的因素改变,编译器可能会进行一些优化,将变量的读取操作删除或进行替换。
而使用volatile 修饰变量,可以告诉编译器不要对该变量进行优化,保证变量的读取和写入操作不被删除或替换。
2.处理硬件映射:在嵌入式开发中,通常会有一些变量与硬件设备进行映射,这些变量的值可能会被硬件设备修改。
如果不使用volatile修饰这些变量,在编译器优化的过程中可能会导致未预料的结果,使用volatile修饰这些变量可以确保程序正确地与硬件设备进行通信。
3.多线程同步:在多线程编程中,不同线程可能同时访问同一个变量,如果不使用volatile修饰该变量,编译器对该变量的优化可能导致线程读取到脏数据。
通过使用volatile修饰变量,可以确保在多线程环境下变量的可见性和一致性。
二、volatile与多线程并发编程中最常见的一个问题就是可见性问题,即一个线程对共享变量的修改对另一个线程是否可见。
而volatile关键字可以用来确保变量在多线程环境下的可见性。
以下是volatile与多线程相关的一些要点:1. 可见性使用volatile修饰的变量在多线程环境下保证了其可见性。
如果一个线程对一个volatile变量进行了修改,那么其他线程在读取该变量时可以立即看到最新的值,而不会使用缓存中的旧值。
部分库函数简介一、通用输入/输出(GPIO)--------------------------------------------------------------------------------------------3二、外部中断/事件控制器(EXTI)-----------------------------------------------------------------------------------7三、通用定时器(TIM)-------------------------------------------------------------------------------------------------9四:ADC寄存器------------------------------------------------------------------------25五:备份寄存器(BKP)-------------------------------------------------------------------------------------------------33六、DMA控制器(DMA)---------------------------------------------------------------3 7七、复位和时钟设置(RCC)------------------------------------------------------------------------------------------41 八、嵌套向量中断控制器(NVIC)-----------------------------------------------------------------------------------49命名规则在函数名中,只允许存在一个下划线,用以分隔外设缩写和函数名的其它部分。
标签:*(volatile unsigned char*)
(*(volatile unsigned char *)0x56000010)
以前看到#define SREG (*(volatile unsigned char *)0x5F)这样的定义,总是感觉很奇怪,不知道为什么,今天终于有了一点点心得,请大虾们多多批砖~~~ 嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。
既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。
所以,知道要访问的内存地址后,比如0x5F,
第一步是要把它强制转换为指针类型
(unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向unsi gned char类型。
volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。
这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
第二步,对指针变量解引用,就能操作指针所指向的地址的内容了
*(volatile unsigned char *)0x5F
第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所以#defi ne SREG (*(volatile unsigned char *)0x5F)
类似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义:
#define RAM_ADDR (*(volatile unsigned long *)0x0000555F)
然后就可以用C语言对这个内存地址进行读写操作了
读:tmp = RAM_ADDR;
写:RAM_ADDR = 0x55;
zhiwei 发表于 2005-4-30 18:59 AVR 单片机
定义未volatile是因为它的值可能会改变,大家都知道为什么改变了;
如果在一个循环操作中需要不停地判断一个内存数据,例如要等待SREG的I标志位置位,因为SREG也是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把SREG 读取到Register中,然后不停地判断Register相应位。
而不会再读取SREG,这样当然是不行了,因为程序或其它事件(中断等)会改变SREG,结果很可能是一个死循环出不来了。
如果定义成volatile型变量,编译的代码是这样的:每次要操作一个变量的时候都从内存中读取一次。
#define SREG (*(volatile unsigned char *)0x5F) 之后,可以进行如下基本操作,unsigned char temp,*ptr;
temp=SREG;把SREG值保存到temp中
SREG=temp;把temp的值赋给SREG
ptr = & SREG; 不知对否,大家试一下。
(volatile unsigned char *)0x20
默认分类2010-04-07 23:58:38 阅读49 评论0 字号:大中小
对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成:
1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char。
2)volatile,关键字volatile 确保本条指令不会因 C 编译器的优化而被省略,且要求每次直接读值。
例如用while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。
用了volatile 则要求每次都去读0x20的实际值。
那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。
而char *u则是个指针变量。
再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。
那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。
而0x20只是个常量,不是指针更不是变量。
PS:设计过程的最后阶段,曾出现接受的串口数据结构体变量值不停改变。
经讨论后,认为是编译器可能将这个变量优化到CPU寄存器中,从而中断时,寄存器值将保存到堆栈;中断恢复后,堆栈值覆盖回以后的改变值,使得两种值在不断的相互覆盖。
考虑到这种情况后,先将该结构体定义为一种类型,再将接受数据的此类型变量同时定义为volatile 类型。
此后,具有volatile类型的变量将告诉编译器其内容会被硬件修改,因此,编译器不会对其进行优化。
因此,假如一个变量除了可被程序改变以外还可被其他代理(如硬件)改变,为了保证程序在运行时能每次都读取该变量的最新值,而不是读取因为编译器的优化存放该变量的临时寄存器中的值(存放临时寄存器中的值是不变的),应该使用volatile。
区别:
1、volatile unsigned char *p
2、const unsigned char *p
3、unsigned char * const p
4、volatile const unsigned char *p
1、指明p所指的值(*p)是可被硬件修改的,提示编译器不要对其进行优化。
2、const指明p所指的值(*p)不可被程序修改
3、p不能被程序修改,但p所指的值(*p)可以被修改。
4、p所指的值(*p)不可被程序修改,但是可以被其他代理如硬件修改(如一个变量存放系统时钟),提示编译器不要对其优化。