大家好,欢迎来到IT知识分享网。
目录
前言
通过使用C语言可以进行对MCS—51单片机的开发,实现各种外设功能,我们称用来开发51单片机的“C语言”为C51。C51是基于标准C、专门为开发51单片机而设计的,与标准C有一定区别,有很多内容是专门为51单片机设定的。
本文主要介绍C51的特别之处, 由于C51的很多特有内容与51单片机的内部硬件结构是息息相关的,所以在阅读之前有必要先熟悉一下51单片机的硬件结构,可以参考下面内容:
51单片机内核及其工作原理-CSDN博客
一、C51与标准C的区别
异:
①库函数不同。标准C的库函数是PC来定义的,而C51的库函数是按照51单片机相应情况来制定的。
②数据类型的区别。C51中增加了几种特有的数据类型。
③变量的存储模式区别。C51中变量的存储模式与51单片机的存储结构紧密相关。
④输入输出处理不同。C51中的输入输出往往是通过串口实现的。
⑤函数使用方面不同。C51有特定的中断函数,以及main()函数无限循环。
同:
其他语法、程序结构及程序设计等方法与标准C语言一致,所以,在第一次玩51单片机时,就会认为它和所学的标准C语言一样。
二、C51数据类型
C51与标准C中的数据类型基本相同,主要不同在于C51中有专门针对51单片机的特殊功能寄存器(SFR)型和位类型。
| 基本数据类型 | 长度 | 取值范围 |
| bit | 1位 | 0或1 |
| sbit | 1位 | 0或1 |
| sfr | 1字节 | 0~255 |
| sfr16 | 2字节 | 0~65535 |
| unsigned char | 1字节 | 0~255 |
| (signed)char | 1字节 | -128~+127 |
| unsigned int | 2字节 | 0~65535 |
| (signed)int | 2字节 | -32768~+32767 |
| unsigned long | 4字节 | 0~ |
| (signed)long | 4字节 | -~+ |
| float | 4字节 | 1.175494E-38~ 3.402823E+38 |
bit类型:位变量,要么是0,要么是1。
如:bit flag = 0;
sfr类型:将特殊功能寄存器的地址重定义,之后便可以把它当作变量直接操作(下面的数据类型也相同)。
如:sfr P1 = 0x90;
例:P1 = 0xFF; //将P1端口的8个引脚全部输出高电平(1)。
sfr16类型:给16位的特殊功能寄存器的地址重定义。
如:sfr16 DPTR = 0x82;
sfr或sfr16:51单片机内的SFR,用于控制单片机的内部定时器/计数器、串口、I/O及其他功能部件,每一个特殊功能寄存器在片内RAM中都对应一个字节单元或两个字节单元。C51提供了一种自主定义的方法,其与标准C不兼容,只适用与51系列单片机。
sbit类型:给特殊功能寄存器的位地址重定义。
如:sbit P1_1 = 0x91;
//另外一种定义方式。
sfr P1 = 0x90;
sbit P1_1 = P1^1;
C51中为了用户处理方便,C51编译器把MCS—51单片机的常用的特殊功能寄存器和特殊位进行了定义,放在一个“reg51.h”或“reg52.h”的头文件中,当用户使用时,只需要包含该头文件即可。如下:
/*-------------------------------------------------------------------------- REG52.H Header file for generic 80C52 and 80C32 microcontroller. Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. All rights reserved. --------------------------------------------------------------------------*/ #ifndef __REG52_H__ #define __REG52_H__ /* BYTE Registers */ sfr P0 = 0x80; sfr P1 = 0x90; sfr P2 = 0xA0; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; sfr B = 0xF0; sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; sfr PCON = 0x87; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TL1 = 0x8B; sfr TH0 = 0x8C; sfr TH1 = 0x8D; sfr IE = 0xA8; sfr IP = 0xB8; sfr SCON = 0x98; sfr SBUF = 0x99; /* 8052 Extensions */ sfr T2CON = 0xC8; sfr RCAP2L = 0xCA; sfr RCAP2H = 0xCB; sfr TL2 = 0xCC; sfr TH2 = 0xCD; /* BIT Registers */ /* PSW */ sbit CY = PSW^7; sbit AC = PSW^6; sbit F0 = PSW^5; sbit RS1 = PSW^4; sbit RS0 = PSW^3; sbit OV = PSW^2; sbit P = PSW^0; //8052 only /* TCON */ sbit TF1 = TCON^7; sbit TR1 = TCON^6; sbit TF0 = TCON^5; sbit TR0 = TCON^4; sbit IE1 = TCON^3; sbit IT1 = TCON^2; sbit IE0 = TCON^1; sbit IT0 = TCON^0; /* IE */ sbit EA = IE^7; sbit ET2 = IE^5; //8052 only sbit ES = IE^4; sbit ET1 = IE^3; sbit EX1 = IE^2; sbit ET0 = IE^1; sbit EX0 = IE^0; /* IP */ sbit PT2 = IP^5; sbit PS = IP^4; sbit PT1 = IP^3; sbit PX1 = IP^2; sbit PT0 = IP^1; sbit PX0 = IP^0; /* P3 */ sbit RD = P3^7; sbit WR = P3^6; sbit T1 = P3^5; sbit T0 = P3^4; sbit INT1 = P3^3; sbit INT0 = P3^2; sbit TXD = P3^1; sbit RXD = P3^0; /* SCON */ sbit SM0 = SCON^7; sbit SM1 = SCON^6; sbit SM2 = SCON^5; sbit REN = SCON^4; sbit TB8 = SCON^3; sbit RB8 = SCON^2; sbit TI = SCON^1; sbit RI = SCON^0; /* P1 */ sbit T2EX = P1^1; // 8052 only sbit T2 = P1^0; // 8052 only /* T2CON */ sbit TF2 = T2CON^7; sbit EXF2 = T2CON^6; sbit RCLK = T2CON^5; sbit TCLK = T2CON^4; sbit EXEN2 = T2CON^3; sbit TR2 = T2CON^2; sbit C_T2 = T2CON^1; sbit CP_RL2 = T2CON^0; #endif
三、C51变量的特殊属性
变量在使用前必须进行定义,其中,可以对变量设置C51特有的存储器模式,以便编译器为它分配相应的存储单元。一般格式为:
[存储种类] 数据类型 [存储器类型] 变量1[可以赋初值],变量2[可以赋初值]……;
其中存储种类和存储器类型可以省略,系统会有默认的存储种类和存储器类型,如下例子:
const char a=0x10; extern unsigned int b=0x1234; static float xdata c=123.456; float code d=123.456;
存储器类型
C51变量所处的单片机的存储器区域,由其变量定义中特殊的存储器类型决定。C51的存储器类型有一下几种:
| 存储器类型 | 描述 |
| data | 直接寻址的片内RAM低128B,访问速度快 |
| bdata | 片内RAM的可位寻址区(20H~2FH),允许字节和位混合访问 |
| idata | 间接寻址访问的片内RAM,允许访问全部片内RAM |
| pdata | 用Ri间接访问的片外RAM低256B |
| xdata | 用DPTR间接访问的片外RAM,允许访问全部64kB片外RAM |
| code | 程序存储器ROM64kB空间 |
如下,几个实例:
unsigned char data a;//在片内RAM低128B内定义字符型变量a。 unsigned int idata a;//在片内RAM256B内定义整型变量a。 float xdata a;//在片外RAM64kB空间定义浮点型变量a。 unsigned int code a;//在ROM空间内定义整型变量a。 unsigned char bdata a;//在片内RAM位寻址区定义可字节处理和位处理的无符号字符型变量a。
当存储器类型缺省时,C51编译器将按照存储模式默认的存储器类型。
存储模式
C51编译器支持三种存储模式:SMALL模式、COMPACT模式和LARGE模式。不同的存储模式对变量默认的存储器类型不一样。
| SMALL模式 | 函数参数和变量被默认在片内RAM中,存储器类型为data |
| COMPACT模式 | 函数参数和变量被默认在片外RAM的低256B空间,存储器类型为pdata |
| LARGE模式 | 函数参数和变量被默认在片外RAM的64kB空间,存储器类型为xdata |
在程序中变量的存储模式的指定通过#pragma预处理命令来实现。函数的存储模式可以通过在函数定义时后面带存储模式说明。如果没有指定,则系统都隐含为SMALL模式。如下:
/*变量*/ #pragma small //变量的存储模式为SMALL char a; //存储器类型为:data int xdata b; //存储器类型为:xdata #pragma compact //变量的存储模式为COMPACT char c; //存储器类型为:pdata int xdata d; //存储器类型为:xdata /*函数*/ int fun1(int x1,int x2) large //函数的存储器模式为LARGE,形参x1和x2的存储器类型为:xdata { return(x1+x2); } int fun2(int y1,int y2) //函数的存储器模式隐含为SMALL,形参y1和y2的存储器类型为:data { retrun(y1+y2); }
绝对地址访问
C51通过三种方式实现绝对地址访问,即通过地址来访问存储空间的内容。
1、使用C51运行库中预定义宏
C51编译器提供了一组宏定义对5系列单片机的code、data、pdata和xdata空间进行绝对寻址。规定只能以无符号数的方式访问,定义了8个宏定义,其函数原型如下:
#include <absacc.h> //宏定义所在的头文件,使用时需要引用该头文件。
#define CWORD((unsigned int volatile code *)0)
#define DWORD((unsigned int volatile data *)0)
#define PWORD((unsigned int volatile pdata *)0)
#define XWORD((unsigned int volatile xdata *)0)
如下例子:
#include <absacc.h> #include <reg52.h> #define uchar unsigned char #define uint unsigned int void main(void) { uchar a; uint b = 2024; a = XBYTE[0xF005];//读片外RAM的F005H字节单元。 XWORD[0xD002] = b;//写片外RAM的D002H字单元。 while(1){} }
2、通过指针访问
在C语言中,指针可以用来代表地址。采用指针的方法,可以实现对任意指定存储器单元进行访问。如下:
#define uchar unsigned char #define uint unsigned int void fun(void) { uchar data a; //定义一个data区的变量a。 uchar pdata *p1; //定义一个指向pdata区的指针p1。 uint xdata *p2; //定义一个指向xdata区的指针P2。 uchar data *p3; //定义一个指向data区的指针p3。 p1 = 0x30; //给指针p1赋值,指向pdata区的30H单元 p2 = 0x1000; //给指针p2赋值,指向xdata区的1000H单元 p3 = &a; //给指针p3赋值,指向data区的a变量的地址单元 *p1 = 0x12; //将数据0x12送到片外RAM30H单元 *p2 = 0x1234; //将数据0x234送到片外RAM1000H单元 *p3 = 0x12; //将变量a赋值为0x12 }
3、使用C51扩展关键字:_at_
使用_at_对指定存储器空间的绝对地址进行访问,一般格式如下:
[存储器类型] 数据类型 变量名 _at_ 绝对地址(地址常数)
使用_at_定义的变量必须为全局变量,如下实例:
#define uchar unsigned char #define uint unsigned int data uchar a _at_ 0x40; //在data区中定义字节变量a,地址为40H xdata uint b _at_ 0a2000; //在xdata区中定义字节变量b,地址为2000H void main(void) { a = 0x12; //对片内40H单元赋值 b = 0x1234; //对片外2000H单元赋值 while(1){} }
四、C51中断函数和输入输出
中断函数
中断函数的特点:
①中断函数不能使用参数传递。若进行参数的申请将产生编译错误。
②中断函数没有返回值。定义时应将该函数定义为void类型。
③中断函数只有相应的中断产生时才能由系统自动调用。其他函数不能调用,否则产生编译错误。
④中断函数最好不要调用别的函数,如需调用,要保证其能尽快执行完,同时被调用函数将使用相同的寄存器组。
⑤中断函数中禁止使用extern存储类型说明。
中断函数的定义格式为:
函数类型 函数名(形式参数表) [interrupt m] [using n]
interrupt m选项(重点):指明定义的函数为中断响应函数,其中m为中断号。
using n选项:指定本函数中使用的寄存器组号(0~3)。
void time0() interrupt 1 using 1 //定时器0中断函数。 { //中断处理内容。 }
但在平时使用中往往省略[using n]选项。
输入输出
单片机系统的输入、输出(如键盘、显示器、打印机等)往往是通过接口扩展实现的,访问时常采用“绝对地址访问”方式进行。51单片机还提供了一种“串口打印”输入输出方式:需引用头文件“stdio.h”,并配置串口(中断方式)。然后就可以用printf语句通过串口输入和输出数据了。
stdio.h:是标准C的输入输出库,但在C51中,将其制定为通过串口输入输出的相关函数。
完结
可以查阅相关内容:
51单片机内核及其工作原理-CSDN博客
有误之处望指正
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/121310.html
1.175494E-38~