C51语言编程基础
C51语言介绍
-
C51语言能直接对8051单片机硬件进行操作
-
Keil C51,在标准C语言的基础上发展的。
使用C51语言软件开发优点
- 可读性好
- 模块化开发与资源共享,可以不需要修改直接拿去别的地方使用,先建立好库函数直接调用即可。
- 可移植性好,比如对51单片机开发的C语言,只需要把51单片机编写的程序改头文件就可以用到其他型号单片机上。
- 生成的代码效率高
C51与标准C语言的比较
C51,标准C语言有许多相同的地方,但也有自身特点。对于51单片机,目前广泛使用的是Keil C51语言,简称C51语言。下面是二者的主要区别。
- 库函数的不同
例如库函数printf与scanf,在标准C中,这两个函数通常用于屏幕打印和接受字符,而在c51中,主要用于串行口数据的收发。
- 数据类型有一定的区别
c51中增加了几种针对51单片机特有的数据类型,在标准c的基础上有扩展了4种位操作功能
- c语言中,数据类型有整型,字符型,浮点型等。
- 51单片机包含位操作空间,和丰富的位操作指令,因此,C51语言与标准C相比就要增加位类型。
- c51的变量存储模式与标准c中的变量存储模式数据不一样
- 对于c语言是针对通用计算机来设计的,计算机中只有一个程序和数据统一寻址的内存空间,只有ROM和RAM内存。
- c51语言针对单片机系统,单片机系统有很多存储器,根据数据处于不同的区来选择。
- 数据存储类型的不同
- 51单片机存储区可分为内部数据存储区,外部数据存储区,以及程序存储区。
- 内部数据存储区:分为3个不同的c51存储类型:data,idata,bdata
- 外部数据存储区:分为2个不同的c51存储类型:xdata,pdata
- 程序存储区:只能读不能写,位于51单片机内部或者外部。c51提供了code存储类型来访问程序存储区。
- 标准c语言中没有处理单片机中断的定义
c51中有专门的中断函数。有专门的引脚去接外部中断。
- c51语言与标准c语言的输入输出处理不一样
c51语言中的输入输出是通过51单片机的串行口来完成的,输入输出指令执行前必须对串行口进行初始化。
- 头文件的不同
51单片机内部的外设硬件资源如定时器、中断、I/O口等所相应的功能寄存器,需要写入头文件内。指令执行前必须对串行口进行初始化。
- 程序结构的差异
51单片机硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准c所具备的递归特性不被c51语言支持。
C51语言程序设计基础
C51的数据类型与存储类型
-
数据是计算机操作的对象,包含常量和变量。
-
数据类型:就是数据的格式,它决定数据的值域范围、占用存储单元的个数及能参与哪种运算。
- 编写C51程序,先要确定数据类型、储存分区
- 应尽可能采用无符号格式(unsigned)
- C51数据类型表
- 红色的是c51增加的4个数据类型。16位的SFR有PC和DPTR
C51的扩展数据类型
- 位变量bit
bit定义位变量名字,编译器在位寻址区自动分配该位变量,变量名称叫flag1。
bit flag1 =0;
- 特殊功能位sbit
片内特殊功能寄存器的可寻址位。
下面两串代码就是先定义PSW的值,用sfr
,再用sbit
定义程序状态字的第二位。sbit定义寄存器的第几位
sfr PSW=0×d0;/*定义PSW寄存器地址为0×d0*/
sbit PSW ^2=0×d2;/*定义OV位为PSW.2*/
- 特殊功能寄存器sfr
地址单元80H~FFH之间,“sfr”数据类型占用一个内存单元,利用它可以访问AT89S51单片机内部的所有特殊功能寄存器。sfr定义这个寄存器。
sfr P1 = 0×90;/*定义了P1端口在片内的寄存器*/
P1=0×ff;/*对P1进行赋值ff(必须ff,使P1处于高电平,正确读入外部信息)*/
- 特殊功能寄存器sfr16(PC和DPTR)
“sfr16”数据类型占两个内存单元,它用于操作占两个字节的特殊功能寄存器。
sfr16 DPTR =0×82;/*定义了片内16位数据指针寄存器DPTR,其低八位字节地址为82H,高8位字节地址为83H*/
C51数据的存储类型
存储区 | 存储类型 | 与存储空间的对应关系 |
---|---|---|
DATA | data | 片内RAM直接寻址区,位于片内RAM的低128位字节 |
BDATA | bdata | 片内RAM位寻址区,位于20H~2FH空间 |
IDATA | idata | 片内RAM的256字节,必须间接寻址的存储区 |
XDATA | xdata | 片外64KB的RAM空间,使用@DPTR间接寻址 |
PDATA | pdata | 片外RAM的256字节,使用@Ri间接寻址 |
CODE | code | 程序存储区,使用DPTR寻址 |
- 经常使用的变量应该置于片内RAM中,要用bdata、data、idata定义
- 不经常使用的变量或规模较大的变量应该置于片外RAM中,要用pdata、xdata来定义。
bit bdata flags;/*位变量flags定位在片内位寻址区*/
char data var;/*字符变量var定位在片内RAM区*/
floar idata x,y;/*实型变量x,y定位在片内间接寻址RAM区*/
unsigned char pdata z;/*无符号字符变量z定位在片外分页间址RAM区*/
C51的数据存储模式
默认的变量存储分区
编译模式 | 默认存储分区 | 特点 |
---|---|---|
SMALL | data | 小模式,变量默认在片内RAM,空间小,速度快。 |
COMPACT | pdata | 紧凑模式,变量默认在片外的RAM的页(256字节,页号由P2口决定) |
LARGE | xdata | 大模式,变量默认在片外RAM的64KB范围,空间大,速度慢。 |
- 如果未对变量存储分区定义时,C51编译器采用默认存储分区。
例如:char,var;//SMALL模式时,var定位于data存储区。
//COMPACT模式时,var定位于pdata存储区。
//LARGE模式时,var定位于xdata存储区。
C51语言的SFR及位变量定义
- 特殊功能寄存器的C51定义
-
使用关键字定义sfr
- 语法如下:
sfr 特殊功能寄存器名字=特殊功能寄存器地址;
- 例如:
sfr IE=0×48;/*中断允许寄存器地址A8H*/
- 例如:
sfr TCON=0×88;/*定时器/技术去控制寄存器地址88H*/
- 例如:
sfr SCON=0×98H;/*串行口控制寄存器地址98H*/
- 例如:
sfr16 DPTR=0×82;/*数据指针DPTR的低8位地址为82H,高8位地址为83H*/
(注意:16位SFR的低字节地址必须作为“SFR16”的定义地址)
- 语法如下:
-
通过头文件访问SFR
-
C51语言把51单片机(或52单片机)的常用的特殊功能寄存器和其中的可寻址位进行了定义,放在一个reg51.h(或reg52.h)的头文件中。在使用的时候需要调用头文件。
-
使用预处理命令
#include<reg51.h>
-
头文件引用举例如下:
#include<reg51.h> /*头文件为51型单片机的头文件*/ void main(void) { TLO=0×F0; /*给定时器T0低字节TL0设置时间常数,已在reg51.h中定义*/ TH0=0×3F; /*给T0高字节TH0设时间常数*/ TR0=1; /*启动定时器0*/ …… }
-
-
特殊功能寄存器中的位定义
-
sbit 位名=特殊功能寄存器^位置;
sfr PSW=0×D0; sbit CY=PSW^7; sbit OV=PSW^2;
-
sbit 位名=字节地址^位置
sbit CY=0×D0^7; sbit OV=0×D0^2;
-
sbit 位名=位地址
-
这种方法将位的绝对地址赋给变量。位地址必须在0×80~0×FF之间。
sbit CY=0×D7; sbit OV=0×D2;
-
例:片内P1口
sfr P1=0×90; sbit P1_7=P1^7; sbit P1_6=P1^6; sbit P1_5=P1^5; sbit P1_4=P1^4; sbit P1_3=P1^3; sbit P1_2=P1^2; sbit P1_1=P1^1; sbit P1_0=P1^0;
-
- 位变量的C51定义
-
位变量的C51定义
- C51采用关键字“bit”来定义位变量
bit bit_name
如:
bit ov_flag; /*将ov_flag定义为位变量*/
bit lock_pointer; /*将lock_pointer定义为位变量*/
- 函数可以包含类型为bit的参数,也可将其作为返回值。
例如:
bit func(bit b0,bit b1); /*位变量b0与b1作为函数func的参数*/
{
……
return(b1); /*位变量b1作为函数的返回值*/
}
-
位变量定义的限制
-
位变量不能用来定义指针和数组
bit *ptr; /*错误,不能用位变量来定义指针*/
bit array[] /*错误,不能用位变量来定义数组array[]*/
-
C51语言的绝对地址访问
-
绝对宏
C51编译器提供了一组宏定义来对code、data、pdata和xdata空间进行绝对寻址。
在程序中,用
#include<absacc.h>
来对absacc.h中声明的宏来访问绝对地址,包括“CBYTE,CWORDE,DBYTE,DWORD,XBYTE,XWORD,PBYTE,PWORD”,具体使用方法参考absacc.h头文件。- CBYTE以字节形式对code区寻址;
- CWORD以字形式对code区寻址;
- DBYTE以字节形式对data区寻址;
- DWORD以字形式对data区寻址;
- XBYTE以字节形式对xdata区寻址;
- XWORD以字形式对xdata区寻址;
- PBYTE以字节形式对pdata区寻址;
- PWORD以字形式对pdata区寻址;
#include<absacc.h> #define PORTA XBYTE[0×FFC0] /*将PORTA定义为外部I/O口,地址为0×FFC0,长度16位*/ #define NRAM DBYTE[0×50] /*将NRAM定义为片内RAM,地址为0×50,长度8位*/
#include<absacc.h> #define PORTA XBYTE[0×FFC0] #define NRAM DBYTE[0×40] main() { PORTA=0×3D;/*将数据3D写入地址FFC0的外部I/O端口*/ NRAM=0×01;/*将数据01H写入片内RAM的40H单元*/ }
-
_at_关键字
使用关键字_at_可对指定的存储器空间的绝对地址进行访问,格式如下:
[存储器类型] 数据类型说明符 变量名_at_地址常数
void main(void)
{
data unsigned char y1_at_0×50;/*在data区定义字节变量y1,它的地址为50H*/
xdata unsigned int y1_at_0×4000;/*在xdata区定义字节变量y2,它的地址为4000H*/
y1=0×ff;
y1=0×1234;
……
while(1);
}
例:将片外RAM 2000H开始的连续20个字节单元清0.
程序如下:
xdata unsigned char
buffer[20]_at_0×2000;/*buffer表示数组*/
void main(void)
{
unsigned char i;
for(i=0;i<20;i++){
buffer[i]=0
}
}
例:如果把片内RAM 40H单元开始的8个单元内容清0,则程序如下:
data unsigned char
buffer[8]_at_0×40;
void main(void)
{
unsigned char j;
for(j=0;j<8;j++){
buffer[j]=0
}
}