51单片机学习笔记Word文档格式.docx

上传人:b****6 文档编号:16634638 上传时间:2022-11-25 格式:DOCX 页数:35 大小:39.22KB
下载 相关 举报
51单片机学习笔记Word文档格式.docx_第1页
第1页 / 共35页
51单片机学习笔记Word文档格式.docx_第2页
第2页 / 共35页
51单片机学习笔记Word文档格式.docx_第3页
第3页 / 共35页
51单片机学习笔记Word文档格式.docx_第4页
第4页 / 共35页
51单片机学习笔记Word文档格式.docx_第5页
第5页 / 共35页
点击查看更多>>
下载资源
资源描述

51单片机学习笔记Word文档格式.docx

《51单片机学习笔记Word文档格式.docx》由会员分享,可在线阅读,更多相关《51单片机学习笔记Word文档格式.docx(35页珍藏版)》请在冰豆网上搜索。

51单片机学习笔记Word文档格式.docx

idataidatamemory(数据存储区)只能用于声明变量,不能用来声明函数.该区域位于片内,采用8位地址线编码,内存大小被限制在256byte或更少。

该区域的低地址区与datamemory地址一致;

高地址区域是52系列在51系列基础上扩展的并与特殊功能寄存器具有相同地址编码的区域。

即:

datamemory是idatamemory的一个子集。

xdataxdatamemory只能用于声明变量,不能用来声明函数,该区域位于MCU

外部,采用16位地址线进行编码,存储大小被限制在64KB以内。

unsignedcharxdatacount=0;

pdatapdatamemory只能用于声明变量,不能用来声明函数,该区域位于MCU外部,采用8位地址线进行编码。

存储大小限制在256byte.是xdatamemory的低256byte。

为其子集。

使用方法

unsignedcharpdatacount=0;

bdatabdatamemory只能用于声明变量,不能用来声明函数。

该区域位于8051内部位数据地址。

定义的量保存在内部位地址空间,可用位指令直接读写。

unsignedcharbdatavarab=0

注:

有些资料讲,定义字符型变量时,在缺省unsigned时,字符型变量,默认为无符号,与标准C不同,但我在KeiluVision3中测试的时候发现并非如此。

在缺省的情况下默认为有符号。

或许在以前的编译器是默认为无符号。

所以看到有的资料上面这样讲的时候,要注意一下,不同的编译器或许不同。

所以我们在写程序的时候,还是乖乖的把unsignedsigned加上,咱也别偷这个懒。

2函数的参数和局部变量的存储模式

C51编译器允许采用三种存储器模式:

SMALL,COMPACT和LARGE。

一个函数的存储器模式确定了函数的参数的局部变量在内存中的地址空间。

处于SMALL模式下的函数参数和局部变量位于8051单片机内部RAM中,处于COMPACT和LARGE模式下的函数参数和局部变量则使用单片机外部RAM。

在定义一个函数时可以明确指定该函数的存储器模式。

方法是在形参表列的后面加上一存储模式。

示例如下:

#pragmalarge//此预编译必须放在所有头文前面

intfunc0(charx,y)small;

charfunc1(intx)large;

intfunc2(charx);

上面例子在第一行用了一个预编译命令#pragma它的意思是告诉c51编译器在对程序进行编译时,按该预编译命令后面给出的编译控制指令LARGE进行编译,即本例程序编译时的默认存储模式为LARGE.随后定义了三个函数,第一个定义为SMALL存储模式,第二个函数定义为LARGE第三个函数未指定,在用C51进行编译时,只有最后一个函数按LARGE存储器模式处理,其它则分别按它们各自指定的存储器模式处理。

本例说明,C51编译器允许采用所谓的存储器混合模式,即允许在一个程序中将一些函数使用一种存储模式,而其它一些则按另一种存储器模式,采用存储器混合模式编程,可以充分利用8051系列单片机中有限的存储器空间,同时还可以加快程序的执行速度。

3绝对地址访问absacc.h(相当重要)

#defineCBYTE((unsignedcharvolatilecode*)0)

#defineDBYTE((unsignedcharvolatiledata*)0)

#definePBYTE((unsignedcharvolatilepdata*)0)

#defineXBYTE((unsignedcharvolatilexdata*)0)

功能:

CBYTE寻址CODE区

DBYTE寻址DATA区

PBYTE寻址XDATA(低256)区

XBYTE寻址XDATA区

例:

如下指令在对外部存储器区域访问地址0x1000

xvar=XBYTE[0x1000];

XBYTE[0x1000]=20;

#defineCWORD((unsignedintvolatilecode*)0)

#defineDWORD((unsignedintvolatiledata*)0)

#definePWORD((unsignedintvolatilepdata*)0)

#defineXWORD((unsignedintvolatilexdata*)0)

与前面的一个宏相似,只是它们指定的数据类型为unsignedint.。

通过灵活运用不同的数据类型,所有的8051地址空间都是可以进行访问。

DWORD[0x0004]=0x12F8;

即内部数据存储器中(0x08)=0x12;

(0x09)=0xF8

注:

用以上八个函数,可以完成对单片机内部任意ROM和RAM进行访问,非常方便。

还有一种方法,那就是用指钟,后面会对C51的指针有详细的介绍。

4寄存器变量(register)

为了提高程序的执行效率,C语言允许将一些频率最高的那些变量,定义为能够直接使用硬件寄存器的所谓的寄存器变量。

定义一个变量时,在变量类型名前冠以“register”即将该变量定义成为了寄存器变量。

寄存器变量可以认为是一自动变量的一种。

有效作用范围也自动变量相同。

由于计算机寄存器中寄存器是有限的。

不能将所有变量都定义成为寄存器变量,通常在程序中定义寄存器变量时,只是给编译器一个建议,该变量是否真正成为寄存器变量,要由编译器根据实际情况来确定。

另一方面,C51编译器能够识别程序中使用频率最高的变量,在可能的情况下,即使程序中并未将该变量定义为寄存器变量,编译器也会自动将其作为寄存器变量处理。

被定义的变量是否真正能成为寄存器变量,最终是由编译器决定的。

5内存访问杂谈

1指钟

指钟本身是一个变量,其中存放的内容是变量的地址,也即特定的数据。

8051的地址是16位的,所以指针变量本身占用两个存储单元。

指针的说明与变量的说明类似,仅在指针名前加上“*”即可。

如int*int_point;

声明一个整型指针

char*char_point;

声明一个字符型指针

利用指针可以间接存取变量。

实现这一点要用到两个特殊运算符

&

取变量地址

*取指针指向单元的数据

示例一:

inta,b;

int*int_point;

//定义一个指向整型变量的指针

a=15;

int_point=&

a;

//int_point指向a

*int_point=5;

//给int_point指向的变量a赋值5等同于a=5;

示例二:

chari,table[6],*char_point;

char_point=table;

for(i=0;

i<

6;

i++)

{

*char_point=i;

char_point++;

}

指针可以进行运算,它可以与整数进行加减运算(移动指针)。

但要注意,移动指针后,其地址的增减量是随指针类型而异的,如,浮点指针进行自增后,其内部将在原有的基础上加4,而字符指针当进生自增的时候,其内容将加1。

原因是浮点数,占4个内存单元,而字符占一个字节。

宏晶科技最新一代STC12C5A360S2系列,每一个单片机出厂时都有全球唯一身份证号码(ID号),用户可以在单片机上电后读取内部RAM单元F1H~F7H的数值,来获取此单片机的唯一身份证号码。

使用MOV@Ri指令来读取。

下面介绍C51获取方法:

charid[7]={0};

chari;

charidata*point;

7;

id[i]=*point;

point++;

(此处只是对指针做一个小的介绍,达到访问内部任何空间的方式,后述有对指针使用的详细介绍)

2对SFR,RAM,ROM的直接存取

C51提供了一组可以直接对其操作的扩展函数

若源程序中,用#include包含头文件,io51.h后,就可以在扩展函数中使用特殊功能寄存器的地址名,以增强程序的可读性:

注此方法对SFR,RAM,ROM的直接存取不建议使用.因为,淡io51.h这个头文件在KEIL中无法打开,可用指针,或是采用absacc.h头文件,

3PWM与PCA

STC12系列有两路PWM/PCA

PWM:

(PulseWidthModulation)脉宽调制,是一种使用程序来控制波形占空比,周期,相位波形的技术。

PCA:

(ProgrammableCounterArray)可编程计数阵列,它比通常的定时/计数器的定时能力强,需要CPU的干预少。

其优势一是软件简单,二是精度大有提高。

我们平时写单片机应用程序的时候,所使用的头文件大多都是用的的reg51.h或是用reg52.h。

会写C51的人都会用,但对其头文件内部的定义有所了解的人确并不多。

下面对其内部做详细解释,方便读者作进一步的了解,并能运用各类型号的单片机。

因为增强型号的单片机的增强功能都是通过特殊功能寄存器控制。

打开reg52.h头文件,会发现是由大量的sfr,sbit的声明组成,甚至于还有sfr16.其实这样的声明都是与单片机内部功能寄存器(特殊功能寄存器)联系起来的,下面对其做出详细解释

sfr:

声明变量

SFR声明一个变量,它的声明与其它的C变量声明基本相同,唯一的区别,SFR在声明的同时为其指定特殊功能寄存器作为存储地址,而不同于C变量声明的整型,字符型等等由编译器自动分配存储空间。

如reg52.h头文件,第一条声明就是sfrP0=0x80;

此处声明一个变量P0,并指定其存储地址为特殊功能寄存器0x80;

在加入reg52.h头文件后。

编写应用程序时P0就可以直接使用而无需定义,对P0的操作就是,对内部特殊功能寄存器(0x80对应用MCU的P0口)的操作,可进行读写操作。

如果将第一条声明改为sfrK0=0x80;

那么,如果要把单片机的P0口全部拉低,则不能写P0=0x00;

而应保存后再在应用程序中写成K0=0x00;

否则编译器会提示“P0为未定义标识符”

sfr[variable]=[address]//为变量分配一个特殊功能寄存器。

1等号右边,只能是十进制,十六进制整型的数据常量,,不允许带操作符的表达式

经典的8051内核支持的SFR地址从0x80H~0xFF飞利浦80C51MX系列0x180H~0x1FF

2SFR不能声明于任何函数内部,包括main函数。

只能声明于函数外。

3用SFR声明一个变量后,不能用取地址运算符&

获取其地址,编译无法通过,编译器会提示非法操作。

4有一点须特别注意,51内核0x80~0xff,为特殊功能寄存器地址区间,但并不是所有的地址都有定义,如果说你所用的MCU芯片上对于某个地址没有定义,那么用sfr在定义变量的时候,不要把变量的地址分配到未定义的特殊功能寄存器上,虽然编译时能通过,用KEIL仿真时貌似是没有问题,但下载到芯片里运行时,是会出问题的。

比如说,向一个未定义的特殊功能寄存器执行读操作,读出来的就是一个未知的数。

(读者可自行测试,先把串口通信调通,然后做一个简单的人机交互。

读出一个数后,再发给计算机,用串口调试助手或是串口监控查看。

这用方法在仿真的时候很有用。

)所以具体那些特殊功能寄存器能够用,就要查看你使用的芯片手册。

5若遇到增强性的单片机,只要知道其扩展的特殊功能寄存器的地址,用SFR定

就可以很方便进行编程。

sbit:

sbit同样是声明一个变量,和SFR使用方法类似,但是SBIT是用来声明一个位变量,因为,在51系列的应用中,非常有必要对SFR的单个位进行存取,而通过bit数据类型,使其具备位寻址功能。

如,在reg52.h中有如下声明

sfrIE=0xA8;

sbitEA=IE^7;

sbitET2=IE^5;

//8052only

sbitES=IE^4;

sbitET1=IE^3;

sbitEX1=IE^2;

sbitET0=IE^1;

sbitEX0=IE^0;

所以,对EA的操作即是对IE最高位的操作。

但如果想让SPDPLDPHPCONTMOCTL0TL1TH0TH1SBUF这些特殊功能寄存器具备位寻址,采用上述如IE类似的定义,是不行的,虽然修改后,在编译的时候不会出现错误,但只要用到你定义的位变量名时就会出错。

原因是,只有特殊功能寄存器的地址是8的倍数(十六进制以0或8结尾)才能进行位寻址。

打开reg52.h头文件可以看到,所有用sbit声明了的特殊功能寄存器的地址均是以0或8结尾

如硬要达到上述要求,可用带参的宏定义来完成。

此处不做详细说明(意义并不大)。

下面对sbit的使用做详细介绍:

随着8051的应用,非常有必要对特殊功能寄存器的单个bit位进行存取,C51编译器通过sbit数据类型,提供了对特殊功能寄存器的位操作。

以下是sbit的三种应用形式:

一,sbitname=sfr-name^bit-position;

sfrPSW=0xD0;

sfrIE=0xA8;

sbitOV=PSW^2;

sbitCY=PSW^7;

sbitEA=IE^7;

二,sbitname=sft-address^bit-position;

sbitOV=0xD0^2;

sbitCY=0xD0^7;

sbitEA=0xA8^7;

三,sbitname=sbit-address;

sbitOV=0xD2;

sbitCY=0xD7;

sbitEA=0xAF;

现对上述三种形式的声明做必要的说明

第一种形式sbitname=sfr-name^bit-position;

如sbitOV=PSW^2;

当中的这个特殊功能寄存器必须在此之前已经用sfr定义,否则编译会出错。

bit-position范围从0~7;

第二种形式sbitname=sft-address^bit-position如sbitOV=0xD0^2;

与第一种形式不同之外在于,此处直接使用PSW的地址.第一种形式须先定义PSW

第三种形式.sbitname=sbit-address如sbitOV=0xD2是直接用的OV的地址

OV的地址计算方式,是OV所在的寄存器地址加上OV的bit-position

注意:

不是所有的SFR都可位寻址。

只有特殊功能寄存器的地址是8的倍数(十六进制以0或8结尾)才能进行位寻址,并且sbit声明的变量名,虽可以是任意取,但是最好不要以下划线开头,因为以下划线开头的都保留给了C51的头文件做保留字。

sfr16:

许多8051的派生型单片机,用两个连续地址的特殊功能寄存器,来存储一个16bit的值。

例如,8052就用了0xCC和0xCD来保存定时/计数寄存器2的高字节和低字节。

编译器提供sfr16这种数据类型,来保存两个字节的数据。

虚拟出一个16bit的寄存器。

如下:

sfr16T2=0xCC

存储方面为小端存储方式,低字节在前,高字节在后。

定义时,只写低字节地址,如上,则定义T2为一个16位的特殊功能寄存器。

T2L=0CCh,T2H=0CDh

sfr[variable]=[low_address]

1等号右边,只写两个特殊功能寄存器的低地址,且只能是十进制,十六进制的整型数据常量,不允许带操作符的表达式

4当你向一个sfr16写入数据的时候,KEILCX51编译器生成的代码,是先写高字节,后写低字节,(可通过返汇编窗口查看)在有些情况下,这并非我们所想要的操作顺序。

使用时,须注意。

5当你所要写入sfr16的数据,当是高字节先写还是低字节先写非常重要的时候,就只能用sfr这个关键字来定义,并且任意时刻只保存一个字节,这样操作才能保证写入正确。

三,浅淡变量类型及其作用域

变量可分为1.局部变量

(按变量的有效作用范围划分)

2.全局变量

1.局部变量

是指函数内部(包括main函数)定义的变量,仅在定义它的那个函数范围内有效,不同函数可使用相同的局部变量名,函数的形式参数也属于局部变量,在一个函数的内部复合语句中也可以定义局部变量,该局部变量只在该复合语合中有效。

是指函数外部定义的变量,以称外部变量。

可为多个函数共同使用,其有效作用范围是从它定义开始到整个程序文件结束。

如果全局变量,定义在一个程序文件的开始处,则在整个程序文件范围都可以使用它,如果一个全局变量不是在程序文件的开始处定义,但又希望在它定义之前的函数中引用该变量,这时应在引用该变量的函数中用关键字extern将其声明为“外部变量”。

另个,如果在一个程序模块文件中引用另一个程序模块文件中定义的变量时,也必须用extern进行说明。

外部变量的说明与外部变量的定义是不同的,外部变量定义只能有一次,定义的位置在所有函数之外,而同一个程序文件中(不是指模块文件)的外部变量声明可以有多次,声明的置在需要引用该变量的函数之内,外部变量的声明的作用只是声明该变量是一个已经在外部定义过了的变量而已。

如在同一个程序文件中,全局变量与局部变量同名,则在局部变量的有效作用范围之内,全局变量不起作用,也就是说,局部变量的优先级比全局变量高。

在编写C语言程序时,不是特别必要的地方一般不要使用全局变量,而应当尽可能的使用局部变量。

因为局部变量只在使用它的时候,才为其分配内存单元,而全局变量在整个程序的执行过程中都要占用内存单元,且当全局变量使用过多时,会降低程序的可读性。

变量的存储种类

1自动变量(auto)

定义变量时,在变量类型名前加上“auto”,自动变量是C语言中使用最为广泛的一类变量,在函数体内部或是复合语句内部定义的变量,如果省略了存储种类说明,则该变量默认为自动变量。

例如:

{等价于{

charx;

autocharx;

inty;

autointy;

…………

}}

自动变量的作用范围在定义它的函数体或是复合语句内部,只有在定义它的函数内被调用,或是定义它的复合语句被执行时,编译器才会为其分配内存空间,开始其生存期。

当函数调用结束返回,或复合语句执行结束,自动变量所占用的内存空间就被释放,变量的值当然也就不复存在,其生存期结束。

当函数再次调用,或是复合语句被再次执行时,编译器又会为其内部的自动变量重新分配内存空间。

但不会保留上一次运行的值。

而必须被重新分配。

因此自动变量始终是相对于函数或复合语句的局部变量。

2外部变量(extern)

用说明符“extern”定义的变量称为外部变量。

按缺省规则,凡是在所有函数之前,在函数外部定义的变量都是外部变量,定义时可以不写extern说明符,但是一个函数体内说明一个已在该函数体外或别的程序模块文件中定义过的外部变量时,刚必须要使用extern说明符。

外部变量定义后,它就被分配了固定的内存空间。

外部变量的生存期为程序的整个执行时间。

外部变量的存储不会随函数或复合语句执行完毕而释放,因此外部变量属于全局变量。

C语言允许将大型程序分解为若干个独立的程序模块文件,各个模块可分别进行编译,然后再将它们连接在一起,如果某个变量需要在所有程序模块文件中使用,只要在一个程序模块文件中将该变量定义成全局变量,而在其它程序模块文件中用extern声明该变量是已被定义过的外部变量就可以了。

函数是可以相互调用的,定义函数时,如果冠以关键字extern即将其明确定义为一个外部函数。

例如externintfunc2(chara,b)。

如果在定义函数时省略关键字extern,则隐含为外部函数。

如果在调用一个在本程序模块文件以外的其它模块文件所定义的函数,则必须要用关键字extern说明被调用的函数是一个外部函数。

对于具有外部函数相互调用的多模块程序,可用C51编译器分别对各个模块文件进行编译,最后再用L51连接定位器将它们连接成为一个完整的程序。

如下为一个多模块程序

程序模块1,文件名为file1.c

#include<

stdio.h>

intx=5;

voidmain()

externvoidfun1();

externviodfun2(inty);

fun1();

printf(“\n%d%d\n”,x,fun2(x));

程序模块2,文件名为file2.c

externintx;

voidfun1()

staticinta=5;

//静态变量只在第一次调用函数时赋值,退出函数时

//会保留上次的值,下次调用不再重新赋值。

intb=5;

printf(“%d%d%d|”,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 教育学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1