单片机:C51语言编程

C51语言编程基础

C51语言介绍

  • C51语言能直接对8051单片机硬件进行操作

  • Keil C51,在标准C语言的基础上发展的。

使用C51语言软件开发优点

  • 可读性好
  • 模块化开发与资源共享,可以不需要修改直接拿去别的地方使用,先建立好库函数直接调用即可。
  • 可移植性好,比如对51单片机开发的C语言,只需要把51单片机编写的程序改头文件就可以用到其他型号单片机上。
  • 生成的代码效率高

C51与标准C语言的比较

C51,标准C语言有许多相同的地方,但也有自身特点。对于51单片机,目前广泛使用的是Keil C51语言,简称C51语言。下面是二者的主要区别。

  1. 库函数的不同

例如库函数printf与scanf,在标准C中,这两个函数通常用于屏幕打印和接受字符,而在c51中,主要用于串行口数据的收发

  1. 数据类型有一定的区别

c51中增加了几种针对51单片机特有的数据类型,在标准c的基础上有扩展了4种位操作功能

  • c语言中,数据类型有整型,字符型,浮点型等。
  • 51单片机包含位操作空间,和丰富的位操作指令,因此,C51语言与标准C相比就要增加位类型
  1. c51的变量存储模式与标准c中的变量存储模式数据不一样
  • 对于c语言是针对通用计算机来设计的,计算机中只有一个程序和数据统一寻址的内存空间,只有ROM和RAM内存。
  • c51语言针对单片机系统,单片机系统有很多存储器,根据数据处于不同的区来选择。
  1. 数据存储类型的不同
  • 51单片机存储区可分为内部数据存储区,外部数据存储区,以及程序存储区。
    • 内部数据存储区:分为3个不同的c51存储类型:data,idata,bdata
    • 外部数据存储区:分为2个不同的c51存储类型:xdata,pdata
    • 程序存储区:只能读不能写,位于51单片机内部或者外部。c51提供了code存储类型来访问程序存储区。
  1. 标准c语言中没有处理单片机中断的定义

c51中有专门的中断函数。有专门的引脚去接外部中断。

  1. c51语言与标准c语言的输入输出处理不一样

c51语言中的输入输出是通过51单片机的串行口来完成的,输入输出指令执行前必须对串行口进行初始化。

  1. 头文件的不同

51单片机内部的外设硬件资源如定时器、中断、I/O口等所相应的功能寄存器,需要写入头文件内。指令执行前必须对串行口进行初始化。

  1. 程序结构的差异

51单片机硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准c所具备的递归特性不被c51语言支持。

C51语言程序设计基础

C51的数据类型与存储类型

  • 数据是计算机操作的对象,包含常量和变量

  • 数据类型:就是数据的格式,它决定数据的值域范围、占用存储单元的个数及能参与哪种运算

    • 编写C51程序,先要确定数据类型、储存分区
    • 应尽可能采用无符号格式(unsigned)
    • C51数据类型表
    • 红色的是c51增加的4个数据类型。16位的SFR有PC和DPTR

C51的扩展数据类型

  1. 位变量bit

bit定义位变量名字,编译器在位寻址区自动分配该位变量,变量名称叫flag1。

bit flag1 =0;

  1. 特殊功能位sbit

片内特殊功能寄存器的可寻址位。

下面两串代码就是先定义PSW的值,用sfr,再用sbit定义程序状态字的第二位。sbit定义寄存器的第几位

sfr PSW=0×d0;/*定义PSW寄存器地址为0×d0*/

sbit PSW ^2=0×d2;/*定义OV位为PSW.2*/

  1. 特殊功能寄存器sfr

地址单元80H~FFH之间,“sfr”数据类型占用一个内存单元,利用它可以访问AT89S51单片机内部的所有特殊功能寄存器。sfr定义这个寄存器。

sfr P1 = 0×90;/*定义了P1端口在片内的寄存器*/

P1=0×ff;/*对P1进行赋值ff(必须ff,使P1处于高电平,正确读入外部信息)*/

  1. 特殊功能寄存器sfr16(PC和DPTR)

“sfr16”数据类型占两个内存单元,它用于操作占两个字节的特殊功能寄存器。

sfr16 DPTR =0×82;/*定义了片内16位数据指针寄存器DPTR,其低八位字节地址为82H,高8位字节地址为83H*/

C51数据的存储类型

C51语言存储类型与8051存储空间的对应关系
存储区 存储类型 与存储空间的对应关系
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及位变量定义

  1. 特殊功能寄存器的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;
    
  1. 位变量的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语言的绝对地址访问

  1. 绝对宏

    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单元*/
    }
    
  2. _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
	}
}