第五章单片机的C语言程序设计及仿真调试_第1页
第五章单片机的C语言程序设计及仿真调试_第2页
第五章单片机的C语言程序设计及仿真调试_第3页
第五章单片机的C语言程序设计及仿真调试_第4页
第五章单片机的C语言程序设计及仿真调试_第5页
已阅读5页,还剩73页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第五章单片机的C语言程序设计及仿真调试任课教师:刘忠国山东大学课程中心网站:

http:///G2S/stcmcu.cc宏晶官方网站:http://STC单片机编译(汇编)/编程(烧录)/仿真工具说明书;stc15系列单片机器件手册等keilμvision软件下载及指导手册(Help→μvisionHelp)/KeilSoftware–Cx51编译器用户手册:Cx51编译器--对传统和扩展的8051微处理器的优化的C编译器和库参考2第五章单片机的C语言程序设计及仿真调试本章学习目标掌握单片机C语言程序中的常用功能掌握KeilC的程序设计掌握STC15F2K60S2单片机C语言程序调试过程3汇编语言和C语言的选择问题设计规模较小的嵌入式应用系统时,可以使用汇编语言。因为代码一般不长,且较简单。当程序比较复杂,且没有很好的注释时,使用汇编语言编写的程序,可读性和可维护性会很差,代码的可重用性也比较低。使用C语言编程,编写简单、直观易读、便于维护、通用性好。在控制任务比较复杂或者具有大量运算的系统中,C语言优势明显。由于模块化,用C语言编写的程序具有很好的可移植性。4§5.1单片机C语言程序中的常用功能5.1.1逻辑运算和位运算1、逻辑运算符逻辑运算符包括与(&&),或(||),非(!)3种,用于逻辑运算。逻辑运算符的表达式,返回0表示“假”,

返回1表示“真”。5与运算符(&&)功能:两个条件同时满足时(即两个条件都为真时),结果才为真。如一个程序在同时满足条件a<10和b==7时,则执行某些操作,应使用关系运算符和逻辑与运算符(&&)来写这个条件的代码:(a<10)&&(b==7)。1、逻辑运算符或运算符(||)功能:检查两个条件中是否有一个为真,只要有一个条件为真,运算结果就为真。对条件a<10和b==7,若任一条件为真,程序执行某操作,则条件代码为:

(a<10)||(b==7);6逻辑非运算符(!)功能:表示对表达式的真值取反。例如,如果变量s小于10,程序需执行某些操作,则条件代码如下:(s<10)

也可以写成:(!(s>=10))//s不大于等于102、位运算符很多系统程序常要求进行位(bit)运算或处理。C语言提供了六种位运算符:按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)和右移(>>)

。7(1)按位“与”运算:

按位与运算符“&”是双目运算符。功能:参与运算的两数各对应二进制位相与。只有对应的两位均为1时,结果位才为1,否则为0。例如,9&5的算式:00001001&00000101=00000001按位与运算通常用来对某些位清0或保留某些位。例把16位数a的高8位清0,保留低8位,可作运算:a&255(255的二进制数为0000000011111111)。(2)按位“或”运算按位或运算符“|”是双目运算符。功能是参与运算的两数各对应的二进制位相或。只要对应的两个位有一个为1时,结果位就为1。例如,9|5的算式:00001001(十进制为9)

|00000101(十进制为5)00001101(十进制13)或运算通常用来对某些位置1。8(2)按位“异或”运算按位异或运算符“^”是双目运算符。功能是参与运算的两数各对应的二进制位相异或。当两个对应的位相异时,结果为1。例如,9^5的算式:00001001(十进制为9)

^00000101(十进制为5)00001100(十进制12)异或运算通常用来对某些位取反。9(4)求反运算求反运算符“~”为单目运算符,具有右结合性。功能是对参与运算的数的各二进制位按位求反。例如,~9的运算为:~(0000000000001001)结果为:111111111111011010(5)左移运算左移运算符“<<”是双目运算符。功能:把“<<”左边运算数的各二进制位全部左移若干位,

由“<<”右边的数指定移动位数,高位丢弃,低位补0。例如:a<<4指把a的各二进位向左移动4位。如a=00000011(十进制3),

左移4位后为00110000(十进制48)。(6)右移运算右移运算符“>>”是双目运算符。功能是把“>>”左边的运算数的各二进制位全部右移若干位,“>>”右边的数指定移动的位数。例如,设a=15,a>>2000001111右移2位为00000011(十进制3)。对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0,而为负数时,符号位为1,最高位是补0或是补1取决于编译系统的规定。115.1.2预处理以“#”号开头的命令是预处理命令。C语言的预处理功能包括宏定义#define,文件包含#include,条件编译等。合理地使用预处理功能,可以使得编写的程序便于阅读、修改、移植和调试,也利于模块化程序设计。121、宏定义(#define)在C语言源程序中允许用一个标识符来表示一个字符串,称为宏。被定义为宏的标识符称为宏名。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为宏代换或宏展开。宏代换是由预处理程序自动完成的。在C语言中,宏分为有参数和无参数两种。(1)无参宏定义无参宏的宏名后不带参数。其定义的一般形式为:#define标识符字符串其中,标识符为所定义的宏名。字符串可以是常数,表达式,格式串等。若要终止宏定义,可用#undef命令:#undef标识符符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。13例如:#definePI3.14159#define

M(y*y+3*y)程序中的语句:L=2*PI;s=3*M+M/2;经宏代换后为:L=2*3.14159;s=3*(y*y+3*y)+(y*y+3*y)/2(2)带参宏定义在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。带参宏定义的一般形式为:#define

宏名(形参表)

字符串14在字符串中含有各个形参。例如:#defineMAX(a,b)(a>b)?a:b

//取a和b的最大数例如,程序中的语句:max=MAX(x,y);经宏代换后为:max=(x>y)?x:y;带参宏调用的一般形式为:宏名(实参表);2、文件包含(#include)文件包含的一般形式为:

#include"文件名"功能是把指定文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。15说明:(头)文件名可用双引号括起来(用户头文件),也可用尖括号括起来(系统头文件)。如:#include“stdio.h”,#include<math.h>。二者区别:用尖括号表示在包含文件目录中去查找(包含目录可由用户在开发环境中设置,一般在\INC\),而不在源文件目录去查找;用双引号则表示首先在当前源文件目录中查找,若未找到才到包含目录中去查找。3、条件编译条件编译就是按不同的条件去编译不同的程序部分,从而产生不同的目标代码文件。条件编译对于程序的移植和调试(可以分段调试)非常有用。特别在操作系统的裁减中,经常使用条件编译。16(1)第一种形式:#ifdef标识符程序段1#else

程序段2#endif功能是:如果标识符已被#define命令定义过,则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else可以没有。3、条件编译(2)第二种形式:

#ifndef标识符程序段1#else

程序段2#endif功能是:如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。173、条件编译(3)第三种形式:

#if常量表达式程序段1#else

程序段2#endif功能是,如果常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。18§5.2KeilC和ANSICKeilC51基本语法与ANSIC相同,针对8051内核单片机硬件等进行了扩展。5.2.1KeilC51扩展关键字(共19个)_at_,sbit,sfr,bit,sfr16,idata,bdata,xdata,pdata,data,code,alien,small,compact,large,using,reentrant,interrupt,_task_下面分类介绍常用关键字:19(或参考keil官网http:///support/man/docs/c51/)(参考keil软件帮助文件bookswindow

→Cx51CompilerUser'sGuide)(或参考“keilsoftwareCx51编译器用户手册09.2001-对传统和扩展的8051微处理器的优化的C编译器和库参考”)1、存储区域(MemoryAreas)8051结构支持几个物理分开的程序和数据存储空间,这些存储空间可能:可读不可写;可读写;读写比别的存储空间快。(1)程序存储器:程序(CODE)存储区只读不能写,可在8051CPU片内或片外或都有,根据8051派生硬件决定,STC15F2K60S只在片内。最多可有64K字节的程序存储区。存储程序代码,包括所有函数和库,常数变量。8051可执行程序只保存在程序存储区。C51编译器中,通过存储类型标识符code可访问程序存储器。20关键字1、存储区域(MemoryAreas)(1)程序存储器:通过存储类型标识符code可访问程序存储器。code用来限定常量或函数的存储类型,而程序的函数默认为code存储类型,并不需code限定符(也可写上)。code一般只用来定义常量,例:staticcodeunsignedcharDPY_TAB[16]={0X3f,0X06,0X5b,0X4f,0X66,0X6d,0X7d,0X07,0X7f,0X6f,0X77,0X7c,0X39,0X5e,0X79,0X71};

//

用code定义共阴极数码管显示0~9,A~F时对应的字模

21关键字1、存储区域(MemoryAreas)data:直接寻址区,内部RAM的低128字节,地址范围是:00H~7FH。idata:间接寻址区,包括整个内部RAM区,256字节,地址范围为00H~0FFH。bdata:可位寻址区,地址范围为20H~2FH。22(2)内部RAM数据存储器。位于51片内,可读写。根据51种类不同,最多256字节。低128字节可直接寻址,而高128字节只能间接寻址。可用以下关键字分为3种存储类型:特殊功能寄存器(0x80~0xFF)单独一类,见后。1、存储区域(MemoryAreas)(3)外部数据存储器:可读写。采用数据指针访问,比访问内部数存要慢,最多可达64K字节。外部RAM可由以下关键字标识分为2种存储类型::xdata:可指定多达64KB的外部直间接寻址区,地址范围0000H~0FFFFH。23xdata可访问外部数存64K字节的任意位置,只能通过数据指针(MOVX@DPTR)来访问。pdata:可访问外部数存的一页即256字节,通过R0和R1(MOVX@Ri)来访问(有时可能需P2指出高8位地址)。51对外设采取统一编址,外设也占用此空间,可通过访问外部数存来控制外设----存储器映射I/O,所以可用空间可能不足64K字节。在Xtal(MHz)右侧框输入6,其余按默认设置。4-10-2KeilμVision集成开发环境调试汇编语言程序方法Project窗口,选中Target1,并单击右键,出现浮动菜单。浮动菜单中选中OptionsforTarget‘Target1’...选项。24*出现OptionsforTarget‘Target1’对话框界面。在该界面中,点击Target标签。在该标签界面中,按下面设置参数:在此设置使用晶振的频率3.针对目标硬件设置工具选项MemoryModel右侧下拉选择:Small,Compact,large。1、存储区域(MemoryAreas)数据存储类型的指定:变量或函数参数存储类型可由存储模式(Small,large,Compact)(OptionsforTarget‘Target1’...选项)指定缺省存储类型;在small模式下,函数参数和局部变量位于由data定义的单片机片内数据RAM(00~7FH)中;在compact模式下,函数参数和局部变量位于pdata定义的扩展数据RAM中(访问用MOVX@Ri)。在large模式下,函数参数和局部变量位于xdata定义的扩展数据RAM中(访问用MOVX@DPTR)25数据1、存储区域(MemoryAreas)数据存储类型的指定:变量或函数参数存储类型可由存储模式(Small,large,Compact)(OptionsforTarget‘Target1’...选项)指定缺省存储类型;也可由关键字code、data、idata、xdata、pdata直接声明指定。例如:unsignedchardatabuffer;

charcodearray[]=“hello!”;

unsignedcharxdataarr[10][4][4];26数据1、存储区域(MemoryAreas)(4)特殊功能寄存器(SFR)

(128字节:0x80~0xFF)单片机特殊功能寄存器(SFR)可位寻址,字节寻址或字寻址,用来控制定时器,计数器,串口,I/O及其他部件。通过关键字sfr,sfr16和sbit声明数据类型,访问SFR。27sfr:字节寻址。语法如下:

sfr

sfr_name=int_constant;如sfrP0=0x80;

0x80为P0口寄存器的地址,

“=”

后为常数用来指定地址,且其范围必须位于80H到~FFH。sfr16:字寻址。

如sfr16DPTR=0x82;指定DPTR地址DPL=0x82,DPH=0x83。(4)特殊功能寄存器(SFR)(128字节:0x80~0xFF)sbit:位寻址用于声明可位寻址的特殊功能寄存器位变量,三种方法:28方法1:sbitbitname=sfr_name^bit_number;

sfr_name必须是已定义SFR的名字,bit_number是位号(0~7)。如:sbitCY=PSW^7;//定义CY为PSW的第7位。方法2:sbitbitname=sfr_address^bit_number;

sfr_address是SFR所在地址(0x80~0xff),bit_number是位号(0~7)。如:sbitOV=0xD0^2;//定义PSW中的OV位方法3:sbitbitname=bit_address;其中,bit_address是位地址。如:sbitEA=0xAF;//第0xAF位为IE寄存器的EA位

(见46页特殊功能寄存器SFR中位地址)IE位地址AFHAEHADHACHABHAAHA9HA8H寄存器IE位名称EAELVDEADCESET1EX1ET0EX0IE字节地址A8H*位寻址区--图3-11特殊功能寄存器SFR中位地址特殊功能寄存器中位地址范围:80H~0FFH。图3-11特殊功能寄存器中的位地址291、存储区域(MemoryAreas)KeilCx51没有预先定义SFR的名字,而是提供了一个包含所有特殊功能寄存器和它们的位定义的头文件reg51.h。通过包含头文件可很容易的进行新的扩展。附录C提供了STC15F2K60S2单片机的头文件stc15.h的内容,其中包含了标准8051单片机寄存器的定义,编程时只需包含这一个文件即可。该文件可从中下载。在STC新ISP软件V6.85I中选择菜单选项“头文件”即可找到STC各系列的头文件(包括stc15.h)。301、存储区域(MemoryAreas)STC15F2K60S2单片机的头文件stc15.h的内容,31/*---------8051内核特殊功能寄存器-------------*/sfr

ACC=0xE0;//累加器sfr

B=0xF0; //B寄存器sfr

PSW=0xD0;//程序状态字寄存器sbit

CY=PSW^7; //进位标志位sbit

AC=PSW^6;//辅助进位标志位sbit

F0=PSW^5; //用户标志位0sbit

RS1=PSW^4;//工作寄存器组选择控制位sbit

RS0=PSW^3;//工作寄存器组选择控制位sbit

OV=PSW^2;//溢出标志位sbitF1=PSW^1; //用户标志位1sbit

P=PSW^0; //奇偶标志位sfr

SP=0x81; //堆栈指针寄存器sfr

DPL=0x82; //数据指针0低字节sfr

DPH=0x83; //数据指针0高字节/*------------系统管理特殊功能寄存器-------------*/sfrPCON=0x87; //电源控制寄存器sfrAUXR=0x8E;//辅助寄存器sfrINT_CLKO=0x8F;//外部中断和时钟输出控制寄存器sfrAUXR1=0xA2;//辅助寄存器1sfrCLK_DIV=0x97;//时钟分频控制寄存器sfrBUS_SPEED=0xA1;//总线速度控制寄存器sfrWKTCL=0xAA;//掉电唤醒专用定时器低字节sfrWKTCH=0xAB;//掉电唤醒专用定时器高字节/*-----------中断控制特殊功能寄存器--------------*/sfrIE=0xA8; //中断允许寄存器sbitEA=IE^7; //总中断允许位sbitELVD=IE^6; //低电压检测中断控制位sbitEADC=IE^5; //ADC中断允许控制位sbitES=IE^4; //串口1中断允许位sbitET1=IE^3; //定时器1溢出中断允许位sbitEX1=IE^2; //外部中断1允许位sbitET0=IE^1; //定时器0溢出中断允许位sbitEX0=IE^0; //外部中断0允许位sfrIE2=0xAF;//中断允许寄存器2sfrIP=0xB8; //中断优先级寄存器sbitPPCA=IP^7; //PCA中断优先级控制位sbitPLVD=IP^6;//低电压检测中断优先级控制位sbitPADC=IP^5;//ADC中断优先级控制位sbitPS=IP^4;//串口1中断优先级控制位sbitPT1=IP^3;//定时器1中断优先级控制位sbitPX1=IP^2;//外部中断1优先级控制位sbitPT0=IP^1;//定时器0中断优先级控制位sbitPX0=IP^0;//外部中断0优先级控制位sfrIP2=0xB5;//第二中断优先级寄存器低字节/*---------------I/O口特殊功能寄存器-------------------*/sfrP0=0x80; //P0口寄存器sfrP0M1=0x93;//P0口工作模式寄存器1sfrP0M0=0x94;//P0口工作模式寄存器0sfrP1=0x90;//P1口寄存器sfrP1M1=0x91;//P1口工作模式寄存器1sfrP1M0=0x92;//P1口工作模式寄存器0sfrP1ASF=0x9D;//P1口模拟量功能设置寄存器sfrP2=0xA0;//P2口寄存器sfrP2M1=0x95;//P2口工作模式寄存器1sfrP2M0=0x96;//P2口工作模式寄存器0sfrP3=0xB0; //P3口寄存器sbitT1=P3^5;//定时器1外部输入sbitT0=P3^4;//定时器0外部输入sbitINT1=P3^3;//外部中断1sbitINT0=P3^2;//外部中断0sbitTXD=P3^1; //串行输入通道sbitRXD=P3^0; //串行输出通道sfrP3M1=0xB1;//P3口工作模式寄存器1sfrP3M0=0xB2;//P3口工作模式寄存器0sfrP4=0xC0;//P4口寄存器sfrP4M1=0xB3;//P4口工作模式寄存器1sfrP4M0=0xB4;//P4口工作模式寄存器0sfrP5=0xC8;//P5口(只有P5.3P5.2P5.1P5.0)sfrP5M1=0xC9;//P5口工作模式寄存器1sfrP5M0=0xCA;//P5口工作模式寄存器0sfrP_SW2=0xBA; //外设功能切换控制寄存器/*-----------------定时器特殊功能寄存器-----------------*/sfrTCON=0x88; //定时/计数控制寄存器sbitTF1=TCON^7; //定时器1溢出中断标志sbitTR1=TCON^6; //定时器1运行控制位sbitTF0=TCON^5; //定时器0溢出中断标志sbitTR0=TCON^4; //定时器0运行控制位sbitIE1=TCON^3; //外部中断1请求标志sbitIT1=TCON^2;//选择外部中断请求1为边沿触发方式的控制位sbitIE0=TCON^1; //外部中断0请求标志sbitIT0=TCON^0;

//选择外部中断请求0为边沿触发方式的控制位sfrTMOD=0x89;//定时/计数模式控制寄存器sfrTL0=0x8A; //定时/计数器0低字节sfrTH0=0x8C; //定时/计数器0高字节sfrTL1=0x8B; //定时/计数器1低字节sfrTH1=0x8D; //定时/计数器1高字节sfrT2H=0xD6;//定时器2重新装载时间常数高字节sfrT2L=0xD7;//定时器2重新装载时间常数低字节/*--------------串行口特殊功能寄存器------------------*/sfrSCON=0x98; //串行口控制寄存器sbitSM0=SCON^7;

//串行口工作方式设定控制位0(与FE功能复用)sbitFE=SCON^7;sbitSM1=SCON^6; //串行口工作方式设定控制位1sbitSM2=SCON^5; //UART的SM2设定sbitREN=SCON^4; //接收允许位sbitTB8=SCON^3; //发送数据的第九位sbitRB8=SCON^2; //接收数据的第九位sbitTI=SCON^1; //发送中断标志sbitRI=SCON^0; //接收中断标志sfrSBUF=0x99; //串口数据缓冲器sfrSADEN=0xB9;//从机地址掩码寄存器sfrSADDR=0xA9;//从机地址寄存器sfrS2CON=0x9A;//串行口2控制寄存器sfrS2BUF=0x9B;//串行口2数据缓冲器/*----------------看门狗定时器寄存器------------------*/sfrWDT_CONTR=0xC1;//看门狗定时器控制寄存器/*----------------PCA寄存器-----------------*/sfrCCON=0xD8;//PCA控制寄存器sbitCF=CCON^7;

//PCA计数器溢出(CH,CL由FFFFH变为0000H)标志sbitCR=CCON^6;//PCA计数器计数允许控制位sbitCCF2=CCON^2;//PCA模块2中断标志sbitCCF1=CCON^1;//PCA模块1中断标志sbitCCF0=CCON^0;//PCA模块0中断标志sfrCMOD=0xD9;//PCA工作模式寄存器sfrCL=0xE9;//PCA计数器低8位sfrCH=0xF9;//PCA计数器高8位sfrCCAPM0=0xDA;//PAC模块0的工作模式寄存器sfrCCAPM1=0xDB;//PAC模块1的工作模式寄存器sfrCCAPM2=0xDC;//PAC模块2的工作模式寄存器sfrCCAP0L=0xEA;//PAC模块0捕捉/比较寄存器低8位sfrCCAP0H=0xFA;//PAC模块0捕捉/比较寄存器高8位sfrCCAP1L=0xEB;//PAC模块1捕捉/比较寄存器低8位sfrCCAP1H=0xFB;//PAC模块1捕捉/比较寄存器高8位sfrCCAP2L=0xEC;//PAC模块2捕捉/比较寄存器低8位sfrCCAP2H=0xFC;//PAC模块2捕捉/比较寄存器高8位sfrPCA_PWM0=0xF2;//PCA模块0PWM寄存器sfrPCA_PWM1=0xF3;//PCA模块1PWM寄存器sfrPCA_PWM2=0xF4;//PCA模块2PWM寄存器/*-----------------ADC寄存器-----------------*/sfrADC_CONTR=0xBC;//ADC控制寄存器,本寄存器不支持位操作sfrADC_RES=0xBD;//ADC转换结果高8位寄存器sfrADC_RESL=0xBE;//ADC转换结果低2位寄存器/*----------------SPI寄存器--------------------*/sfrSPSTAT=0xCD;

//SPI状态寄存器,本寄存器不支持位操作sfrSPCTL=0xCE;//SPI控制寄存器sfrSPDAT=0xCF;//SPI数据寄存器/*-----------------ISP_IAP_EEPROM寄存器------------------*/sfrIAP_DATA=0xC2;//ISP/IAPFlash数据寄存器sfrIAP_ADDRH=0xC3;//ISP/IAPFlash地址高字节sfrIAP_ADDRL=0xC4;//ISP/IAPFlash地址低字节sfrIAP_CMD=0xC5;//ISP/IAPFlash命令寄存器sfrIAP_TRIG=0xC6;//ISP/IAPFlash命令触发器sfrIAP_CONTR=0xC7;//ISP/IAP控制寄存器2、_at_关键字若要实现变量的绝对定位(称为绝对变量),可以直接在数据定义后加上“_at_常数地址”即可。

注意:(1)绝对变量不能被初始化;(2)bit型函数及变量不能用_at_指定。32例如:unsignedcharidataADCdata_at_0x40;//指定ADCdata变量在40H处unsignedcharxdatabuffer[20]_at_0x0010;//指定buffer数组从XRAM的0010H单元开始3、存储模式存储模式决定了没有明确指定存储类型的变量时、函数参数等的缺省存储区域,有Small、Compact和Large三种模式。33指定存储模式图5-1指定存储模式

3、存储模式(1)Small模式在该模式中所有变量都默认位于单片机内部数据存储器(00~7FH),这和使用data指定存储器类型的方式一样。此模式访问变量的效率很高,但所有的数据对象和堆栈必须适合内部RAM堆栈的大小。如果将变量都能配置在内部数据存储器内,Small模式是最佳选择。该模式的优点是访问速度快,缺点是空间有限,只适用于小程序。34(2)Compact模式所有缺省变量均位于外部RAM区的一页内(256字节),这和使用pdata指定存储器类型一样。该模式通过寄存器R0和R1(@R0,@R1)间接寻址。R0和R1只提供地址低字节,如果COMPACT模式使用多于256字节的外部存储区,高字节地址或页由8051的P2(口地址0A0H)提供;初始化PORT2及指定PDATA起始地址的工作可在起始代码中STARTUP.A51文件中说明。该模式空间比Small宽裕,速度比Small慢,比Large快,是一种中间状态。353、存储模式(3)Large模式所有缺省变量可放在多达64KB的外部RAM区,这和使用xdata指定存储器类型一样,使用数据指针DPTR进行寻址。通过数据指针访问外部数据存储器的效率较低,特别是当变量为2个字节或更多字节(用一般指针)时。Large模式的数据访问比Small和Compact产生更多的代码。优点是空间大,可存变量多,缺点是速度较慢。364、变量或数据类型表5-1C51数据类型数据类型含义位数(bit)字节数(byte)取值范围bit*位型11/80或1signedchar带符号字符型81-128~+127unsignedchar无符号字符型810~255enum8/161or2-128~+127or-32768~+32767signedshort带符号短型162-32768~+32767unsignedshort无符号短型1620~65535signedint带符号整型162-32768~+32767unsignedint无符号整型1620~65535signedlong带符号长整型324-2147483648~+2147483647unsignedlong无符号长整型3240~4294967295float浮点型324+1.175494E38~+3.402823E+38sbit*11/80-1sfr*810x80-0xffsfr16*1620x0000~0xffff374、变量或数据类型C51提供以下几种扩展数据类型:

bit:位变量值为0或1。格式:bit位名[=value];

sbit:从可位寻址的字节中定义的位变量(0或1)。

sfr:sfr字节地址(0x80~0xff)。

sfr16:sfr字地址(0x80~0xff,其实占用两个连续地址)。

例:sfr16DPTR=0x82h;其余数据类型如:char、enum、short、int、long、float等与ANSIC相同。下面着重介绍位变量及其声明。380或1*图3-10内部RAM中的位地址20H~2FH之间单元既可按字节存取,也可按位存取,共128位,位地址范围:00H~7FH。位寻址区394、变量或数据类型(1)bit型变量bit型变量可用于变量类型声明、函数参数列表和函数返回值等,存储于内部RAM的20H~2FH单元中。注意:

1)使用禁止中断(#pragmadisable)或用一个明确的寄存器组声明(usingn)的函数不能返回位值,否则,编译器会识别出来并产生一个错误信息。

2)位不能声明为一个指针。如bit*bit_poiter;是错误的。

3)不能有bit数组如:bitarr[5];是错误的。4)bit型函数及变量不能用不能定位到一个绝对地址。40(#pragma的作用是设定编译器的状态或者指示编译器完成一些特定的动作)4、变量或数据类型(2)可位寻址区的说明使用sbit声明可独立访问可位寻址对象的位。sbit声明要求基址对象的存贮器类型为“bdata”(特殊功能寄存器除外),否则只有绝对的位声明方法是合法的。位的位置(

‘^’操作符号后的数字)的最大值依赖于指定的基址变量类型对于char/uchar而言是0~7,对于int/uint/short/ushort而言是0~15,对于long/ulong而言是0~31。414、变量或数据类型下面举例说明位寻址的声明方法。例,intbdata

bittest_at_

0x20;//也可省略“_at_0x20”

sbitbit0=bittest^0;//0x20单元的第0位(bittest的第8位)sbit

bit15=bittest^15;//0x21单元的第7位(bittest的第7位)参见“Cx51CompilerUser‘sGuide”--“Bit-AddressableObjects”

最后一段“Physicalbitposition0referstobitposition0ofthefirstbyte.Physicalbitposition8referstobitposition0ofthesecondbyte.Becauseintvariablesarestoredhigh-bytefirst,bit0oftheintegerislocatedinbitposition0ofthesecondbyte.Thisisphysicalbitposition8whenaccessedusingansbitdatatype.”注意:可位寻址对象的位的声明只能放到main函数的外部,作为全局变量使用,否则,编译会出错。bit无限制。42先存高字节,再存低字节5.2.2扩展I/O口的使用STC15F2K60S2单片机除了芯片上的I/O口外,还可在片外扩展I/O端口。由于使用C语言访问外部I/O时用到指针的功能,因此,首先介绍KeilC51的指针。

431、KeilC51指针KeilC51支持一般指针(GenericPointer)和存储器指针(MemorySpecificPointer)。一般指针的声明和使用均与标准C相同,同时还可以说明指针的存储类型。1、KeilC51指针例如,下面的语句都声明pt为指向保存在外部RAM中unsignedchar数据的指针,但pt本身的保存位置却不同:unsignedcharxdata

*pt;

//pt本身依存储模式存放unsignedcharxdata*data

pt;//pt被保存在内部RAM中unsignedcharxdata

*xdatapt;

//pt被保存在外部RAM中44存储器指针1、KeilC51指针一般指针本身用3个字节存放,分别为存储器类型,高位偏移量和低位偏移量。基于存储器的指针,说明时即指定了存储类型,例如:

chardata*str;

//str指向data区中char型数据

intxdata*pow; //pow指向外部RAM的int型整数这种指针存放时,只需一个字节或2个字节就够了,因为只需存放偏移量。451、KeilC51指针Comparison:MemorySpecific&GenericPointersYoucansignificantlyacceleratean8051Cprogrambyusingmemoryspecificpointers.Thefollowingsampleprogramshowsthedifferencesincode&datasizeandexecutiontimeforvariouspointerdeclarations.46DescriptionidataPointerxdataPointerGenericPointerSampleProgramcharidata*ip;

charval;

val=*ip;charxdata*xp;

charval;

val=*xp;char*p;

charval;

val=*p;8051ProgramCode

GeneratedMOVR0,ip

MOVval,@R0MOVDPL,xp+1

MOVDPH,xp

MOVA,@DPTR

MOVval,AMOVR1,p+2

MOVR2,p+1

MOVR3,p

CALLCLDPTRPointerSize

CodeSize

ExecutionTime1byte

4bytes

4cycles2bytes

9bytes

7cycles3bytes

11bytes+librarycall

13cycles关于堆栈指针SP的设定一般情况下,用户不需在C语言程序中修改堆栈指针SP,但要关心一下SP的位置。C51为变量分配好内部RAM后,将SP放在第一个空闲的内部RAM处;可在编译后生成的.m51文件中观察到栈顶的位置,一般程序编译连接成功后要习惯性地看一下.m51文件,看一下是否有足够的栈空间可用。另外,C51是在startup.A51中设置SP指针的,用CODE选项生成的汇编代码中是找不到这段代码的。startup.A51是C51的初始化代码,单片机复位后先执行这段代码,完成初始化后由它调用main()函数。特殊需要时,可修改这段代码,然后连接到用户的程序中去。472、外部扩展I/O口的访问在C51中有两种方法访问外部I/O端口。

方法1:使用自定义指针。

由于片外I/O端口与片外存储器统一编址,所以可以定义xdata类型的指针访问外部I/O端口。48例如,单片机应用系统中,用8255扩展I/O端口,采用线选法对8255进行地址译码,单片机P2.7(A15)接8255的片选引脚,8255的命令口字地址为7FF3H,

PA口地址为7FF0H,(需改为7CF0H)

PB口地址为7FF1H,(需改为7DF1H)

PC口地址为7FF2H,(需改为7EF2H)访问8255的C程序如下:若与单片机接口的电路图同图8-56,需修改地址值,详见312页的修改。若不修改地址值,也可用下页接口图8255A与STC15F2K60S2单片机的接口电路。设8255A的命令口地址为7FF3H,PA口地址为7FF0H,PB口地址为7FF1H,PC口地址为7FF2H。(非唯一)图8-568255A与STC15F2K60S2单片机的接口电路49P0作低8位(A7~A0)地址,除A3A2=00,没用到的位为1,低8位地址:P2作高8位地址总线A15~A8,A15=0,没用到的位为1,11110000:PA口11110001:PB口11110010:PC口11110011:命令口A1A0P0.0——P0.1——8255A与STC15F2K60S2单片机的接口电路。设8255A的命令口地址为7FF3H,PA口地址为7FCF0H,PB口地址为7FDF1H,PC口地址为7FEF2H。(非唯一)图8-568255A与STC15F2K60S2单片机的接口电路50低8位(A7~A0)地址任意P2口作地址总线高8位A15~A8,没用到的位为1,高8位地址:01111100:PA口01111101:PB口01111110:PC口01111111:命令口A1A0方法1:使用自定义指针访问外部扩展I/O口写端口程序:charxdata*com8255;

//定义指向外部存储区的指针

com8255=0x7ff3;

//使指针指向8255的控制口(命令口)地址7FF3H*com8255=0x81;//输出命令字81H到命令口寄存器//

81H:PA,PB口都是模式0,直接输出,PC口直接输入以上C程序相当于下面的汇编语言程序:

MOVDPTR,#7FF3HMOVA,#81HMOVX@DPTR,A51方法1:使用自定义指针访问外部扩展I/O口读端口程序:

charxdata*com8255;//定义指针

com8255=0x7fcf0;

//使指针指向8255的PA口地址7FF0H

(需改为7CF0H)

chari;

i=*com8255;//读PA端口到变量i52若与单片机接口的电路图同图8-56,需修改地址值,详见312页的修改。方法2:使用C51预定义指针访问外部扩展I/O口为了方便访问外部存储器及I/O端口,在C51的absacc.h头文件做了如下定义,利用这些定义可以方便地访问外部I/O端口。#defineCBYTE((unsignedcharvolatilecode*)0)#defineDBYTE((unsignedcharvolatiledata*)0)#definePBYTE((unsignedcharvolatilepdata*)0)#defineXBYTE((unsignedcharvolatilexdata*)0)53volatile修饰了的变量随程序的执行其值会被改变,“易变”方法2:使用C51预定义指针访问外部扩展I/O口例如:#include<absacc.h>#definePORTAXBYTE[0x7ff0]//其中,PORTA为程序定义的I/O端口名称,[]内的内容//7ff0H为PORTA的地址voidmain(void){chara;PORTA=0x81;//*输出81H到端口7ff0Ha=PORTA;//读端口7ff0H到变量a}54#defineXBYTE((unsignedcharvolatilexdata*)0)定义XBYTE数组的首地址是0XBYTE[0x7ff0]的中括号内的值0x7ff0,指出(定义)了数组XBYTE的偏移地址5.2.3KeilC51函数C51的函数声明对ANSIC作了扩展,具体包括:1、中断函数声明55中断号中断源入口地址0外部中断INT00003H1T0溢出中断000BH2外部中断INT10013H3T1溢出中断001BH4串行口UART1中断0023H5ADC中断002BH6LVD中断0033H7PCA中断003BH8串行口UART2中断0043H9SPI中断004BH10外部中断INT20053H11外部中断INT3005BH12T2溢出中断0063H16外部中断INT40083H中断函数通过使用interrupt关键字和中断号(0~31)来声明。中断号告诉编译器中断服务程序入口地址。STC15F2K60S2单片机的中断号及中断服务程序入口地址如表所示。

1、中断函数声明例如,串行口1的中断函数可以声明如下:

voidUART1_ISR(void)interrupt4[using1]{/*中断服务程序的代码*/}上述代码声明了串行口1中断服务函数。其中,interrupt4指明是串行口1的中断,using1指明采用工作寄存器区1区,using1在中括号中,说明该部分可省略。其他中断函数的定义与此类似。中断函数具体是哪个中断的函数,与中断号有关,而与函数名无关。565.2.3KeilC51函数当需要指定函数中使用的工作寄存器区时,使用关键字using后跟一个0到3的数,对应着工作寄存器0到3区。例如,在下面的函数中使用了工作寄存器1区(相当于PSW.4=0,PSW.3=1):

unsignedcharGetKey(void)using1{/*用户程序代码*/}572、指定工作寄存器区程序状态标志寄存器PSW(8位)RS1,RS0(PSW.4~PSW.3):工作寄存器组选择控制位,其详细介绍见后续内容。OV(PSW.2):溢出标志位。指示运算过程中是否发生了溢出,在执行指令过程中自动形成。*位号D7D6D5D4D3D2D1D0符号CYACF0RS1RS0OVF1PRS1RS0OVP(PSW.0):奇偶标志位累加器ACC中1的个数为偶数,P=0;否则P=1。每个指令周期都由硬件来置“1”或清“0”。在具有奇偶校验的串行数据通信中,可根据P设置奇偶校验位。P58工作寄存器组的选择:PSW寄存器中的RS1和RS0两位组合决定当前使用的工作寄存器组。可通过位操作指令修改RS1和RS0的内容,选择不同的工作寄存器组。*RS1(PSW.4)RS0(PSW.3)工作寄存器组工作寄存器地址000R7~R0对应的地址为07H~00H011R7~R0对应的地址为0FH~08H102R7~R0对应的地址为17H~10H113R7~R0对应的地址为1FH~18H表3-1工作寄存器组选择18H~1FH10H~17H08H~0FH00H~07H3区(R7~R0)2区(R7~R0)1区(R7~R0)0区(R7~R0)595.2.3KeilC51函数用户可以使用small,compact及large说明存储模式。例如:

voidfun1(void)small{}提示:small说明的函数内部变量全部使用内部RAM。关键的、经常性的、耗时的地方可以这样声明,以提高运行速度。603、指定存储模式5.2.3KeilC51函数参数数目charintlong,float一般指针1R7R6&R7R4~R7R1~R32R5R4&R5R4~R7R1~R33R3R2&R3R1~R3614、函数的参数传递规则最多只能有3个参数通过寄存器传递,规律如表5-3所示。表5-3函数的参数传递规则5.2.3KeilC51函数返回值类型寄存器说明bitCychar/unsignedchar,1_byte指针R7单字节由R7返回int/unsignedint2_byte指针R6,R7R6放高位,R7放低位long&unsignedlongR4~R7R4放最高位,R7放最低位floatR4~R7IEEE标准R7放符号位及阶码一般指针R1,R2,R3R3放存储空间码,R2放高位,R1放低位625、函数返回值的规定函数返回值一律放于寄存器中,规则如下表5-4所示。5.2.3KeilC51函数6、函数的重入可在函数前声明函数的可重入性。如果声明为不可重入的,说明该函数调用过程中将不可被中断。一个可重入(或递归)函数可在同一时间被几个进程共享。当一个可重入函数运行时,别的进程可中断执行,并开始执行相同的可重入函数。正常情况,

Cx51编译器中的函数不能重入,原因是单片机一般使用寄存器传递参数,内部变量一般在固定的RAM中,函数重入时会破坏上次调用的数据。而PC可重入是因为,PC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;635.2.3KeilC51函数6、函数的重入可以在函数前声明函数的可重入性。如果声明为不可重入的,说明该函数调用过程中将不可被中断。递归或可重入函数是指在主程序和中断中都可调用的函数,容易产生问题。因为单片机和PC不同,PC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而单片机一般使用寄存器传递参数,内部变量一般在RAM中,函数重入时会破坏上次调用的数据。646、函数的重入可以用以下两种方法解决函数的重入问题:第一种方法:在相应的函数前使用“#pragmadisable”声明,即只允许主程序或中断之一调用该函数。第二种方法:将该函数说明为可重入的。如下:

voidfunc(param...)reentrant;65KeilC51编译后将生成一个可重入变量堆栈,然后就可以模拟通过堆栈传递变量的方法。因为单片机内部堆栈空间的限制,C51没有像大系统那样使用调用堆栈:一般在C语言中调用过程时,会把过程的参数和过程中使用的局部变量入栈。6、函数的重入为了提高效率,C51没有提供这种调用堆栈,而是提供一种压缩栈。每个过程被给定同一个空间,用于存放局部变量。过程中的每个变量都存放在这个空间的固定位置。当递归调用这个过程时,会导致变量被覆盖。在某些实时应用中,非重入函数是不可取的。因为,函数调用时可能会被中断程序中断,而在中断程序中可能再次调用这个函数,所以C51允许将函数定义成重入函数。重入函数可被递归调用和多重调用,而不用担心变量被覆盖,因为C51可模拟生成一个可重入变量堆栈,每次函数调用时的局部变量都会被单独保存。因为这些堆栈是模拟的,重入函数一般都比较大,运行起来也比较慢。666、函数的重入由于一般可重入函数由主程序和中

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论