第11章 C51串行接口程序设计
- 格式:ppt
- 大小:539.00 KB
- 文档页数:21
51单片机的串口通信程序(C语言) 51单片机的串口通信程序(C语言)在嵌入式系统中,串口通信是一种常见的数据传输方式,也是单片机与外部设备进行通信的重要手段之一。
本文将介绍使用C语言编写51单片机的串口通信程序。
1. 硬件准备在开始编写串口通信程序之前,需要准备好相应的硬件设备。
首先,我们需要一块51单片机开发板,内置了串口通信功能。
另外,我们还需要连接一个与单片机通信的外部设备,例如计算机或其他单片机。
2. 引入头文件在C语言中,我们需要引入相应的头文件来使用串口通信相关的函数。
在51单片机中,我们需要引入reg51.h头文件,以便使用单片机的寄存器操作相关函数。
同时,我们还需要引入头文件来定义串口通信的相关寄存器。
3. 配置串口参数在使用串口通信之前,我们需要配置串口的参数,例如波特率、数据位、停止位等。
这些参数的配置需要根据实际需要进行调整。
在51单片机中,我们可以通过写入相应的寄存器来配置串口参数。
4. 初始化串口在配置完串口参数之后,我们需要初始化串口,以便开始进行数据的发送和接收。
初始化串口的过程包括打开串口、设置中断等。
5. 数据发送在串口通信中,数据的发送通常分为两种方式:阻塞发送和非阻塞发送。
阻塞发送是指程序在发送完数据之后才会继续执行下面的代码,而非阻塞发送是指程序在发送数据的同时可以继续执行其他代码。
6. 数据接收数据的接收与数据的发送类似,同样有阻塞接收和非阻塞接收两种方式。
在接收数据时,需要不断地检测是否有数据到达,并及时进行处理。
7. 中断处理在串口通信中,中断是一种常见的处理方式。
通过使用中断,可以及时地响应串口数据的到达或者发送完成等事件,提高程序的处理效率。
8. 串口通信实例下面是一个简单的串口通信实例,用于在51单片机与计算机之间进行数据的传输。
```c#include <reg51.h>#include <stdio.h>#define BAUDRATE 9600#define FOSC 11059200void UART_init(){TMOD = 0x20; // 设置定时器1为模式2SCON = 0x50; // 设置串口为模式1,允许接收TH1 = 256 - FOSC / 12 / 32 / BAUDRATE; // 计算波特率定时器重载值TR1 = 1; // 启动定时器1EA = 1; // 允许中断ES = 1; // 允许串口中断}void UART_send_byte(unsigned char byte){SBUF = byte;while (!TI); // 等待发送完成TI = 0; // 清除发送完成标志位}unsigned char UART_receive_byte(){while (!RI); // 等待接收完成RI = 0; // 清除接收完成标志位return SBUF;}void UART_send_string(char *s){while (*s){UART_send_byte(*s);s++;}}void main(){UART_init();UART_send_string("Hello, World!"); while (1){unsigned char data = UART_receive_byte();// 对接收到的数据进行处理}}```总结:通过以上步骤,我们可以编写出简单的51单片机串口通信程序。
51单片机教程:单片机串行口通信程序设计1.串行口方式0应用编程 8051单片机串行口方式0为移位寄存器方式,外接一个串入并出的移位寄存器,就能扩展一个并行口。
单片机串行口通信程序设计硬件连接图例:用8051单片机串行口外接CD4094扩展8位并行输出口,如图所示,8位并行口的各位都接一个发光二极管,要求发光管呈流水灯状态。
串行口方式0的数据传送可采用中断方式,也可采用查询方式,无论哪种方式,都要借助于TI或RI标志。
串行发送时,能靠TI置位(发完一帧数据后)引起中断申请,在中断服务程序中发送下一帧数据,或者通过查询TI的状态,只要TI为0就继续查询,TI为1就结束查询,发送下一帧数据。
在串行接收时,则由RI引起中断或对RI查询来确定何时接收下一帧数据。
无论采用什么方式,在开始通信之前,都要先对控制寄存器SCON进行初始化。
在方式0中将,将00H送SCON就能了。
单片机串行口通信程序设计列子ORG 2000HSTART: MOV SCON,#00H ;置串行口工作方式0MOV A,#80H ;最高位灯先亮CLR P1.0 ;关闭并行输出(避象传输过程中,各LED的暗红现象) OUT0: MOV SBUF,A ;开始串行输出OUT1: JNB TI,OUT1 ;输出完否CLR TI ;完了,清TI标志,以备下次发送SETB P1.0 ;打开并行口输出ACALL DELAY ;延时一段时间RR A ;循环右移CLR P1.0 ;关闭并行输出JMP OUT0 ;循环说明:DELAY延时子程序能用前面我们讲P1口流水灯时用的延时子程序,这里就不给出了。
二、串行口异步通信org 0000HAJMP STARTORG 30HSTART:mov SP,#5fh ;mov TMOD,#20h ;T1: 工作模式2mov PCON,#80h ;SMOD=1mov TH1,#0FDH ;初始化波特率(参见表)mov SCON,#50h ;Standard UART settingsMOV R0,#0AAH ;准备送出的数SETB REN ;允许接收SETB TR1 ;T1开始工作WAIT:MOV A,R0CPL AMOV R0,AMOV SBUF,ALCALL DELAYJBC TI,WAIT1 ;如果TI等于1,则清TI并转WAIT1AJMP WAITWAIT1: JBC RI,READ ;如果RI等于1,则清RI并转READAJMP WAIT1READ:MOV A,SBUF ;将取得的数送P1口MOV P1,ALJMP WAITDELAY: ;延时子程序MOV R7,#0ffHDJNZ R7,$RETEND将程序编译通过,写入芯片,插入实验板,用通读电缆将实验板与主机的串行口相连就能实验了。
1Franklin C-51之南宫帮珍创作1.11.2Franklin C-51数据类型8051系列是8位机, 因而不存在字节校准问题.这意味着数据结构成员是顺序放置的.数据类型的转换:当计算结果隐含着另外一种数据类型时, 数据类型可以自动进行转换, 例如, 将一个位变量赋给一个整型变量时, 位型值自动转换为整型值, 有符号变量的符号也能自动进行处置.这些转换也可以用C语言的标准指令进行人工转换.1.2数据类型的物理结构1.2.1bit“bit”类型只有1位, 不允许有位指针和位数组.位对象始终位于8051 CPU的可寻址RAM空间.如果法式控制流允许, L51将位对象交迭.1.2.2signed/unsigned char;data/idata/pdata 指针“char”类型标量和基于存贮器的“data/idata/pdata”指针具有1个字节长度(8 bits).1.2.3signed/unsigned int/short;xdata/code 指针“int”和“short”类型标量及指向xdata/code区域的指针具有2字节长度(16bits).整型值(或偏移)0x1234以下面方式保管在内存中:地址:+0 +1内容: 0x120x341.2.4signed/unsigned long“long”类型标量长为4个字节(32 bits), 值0x12345678以下面方式放置:地址:+0+1+2+3内容: 0x12 0x34 0x56 0x781.2.5“一般”指针“一般”指针包括3个字节:2字节偏移和1字节存贮器类型:地址:+0 +1 +2内容:存贮器类型偏移高位偏移低位第一个字节代表了指针的存贮器类型, 存贮器类型编码如下:存贮器类型 IDATA XDATA PDATADATACODE值 12 345使用其它类型值可能招致不成预测的法式举措.XDATA类型的0x1234地址作为指针暗示如下:地址:+0+1 +2内容: 0x02 0x12 0x34当用常数作指针时, 必需注意正确界说存贮器类型和偏移.下例将值0x41写入绝对地址为0x8000的外部数据存贮器:#defineXBYTE ((char *)0x20000L)XBYTE[0x8000]=0x41;上例中用其它常数索引或索引变量也起作用.这样, 各种存贮器类型的绝对地址可以一种非常有效的方式访问.但有一个例外, 即SFR.注意:绝对地址界说为“long”型常量, 低16位包括偏移, 高8位标明了xdata类型.为了暗示这种指针, 必需用长整数来界说存贮器类型.C51编译器不检查指针常数, 用户必需选择有实际意义的值. 1.2.6float“float”类型为4个字节(32位), 使用的格式与IEEE-754标准(32位)具有24位精度, 尾数的高位始终为“1”, 因而不保管, 位的分布如下:● 1位符号● 8位指数位23位尾数符号位是最高位, 尾数为最低的位, 内存中按字节存贮如下:地址:+0 +1 +2+3内容:MMMM MMMM MMMM MMMME MMM MMMMS EEE EEEE其中:S:符号位, 1=负, 0=正E:指数(在两个字节中), 偏移为127M:23位尾数, 最高位“1”浮点值——12.5的十六进制为0xC1480000, 它按下面方式存贮:地址:+0+1+2+3内容: 0x00 0x00 0x48 0xc18051不包括捕捉浮点毛病(例外)的中断向量.用户软件因此必需对毛病条件作出适当反应.下面推荐一种方法(也可以用其它可靠法子):“union”用来保管浮点值, 这个“union”必需包括一个“float”和一个“unsigned long”, 以根据IEEE对毛病作出响应.除通常浮点值外, IEEE标准可能犯错的条件以下面二进制值暗示, 为检查可能呈现的计算毛病, 可在计算后进行检查.因为当执行一个运算时考虑了每个运算符的毛病状态而且该状态被送到结果中.NaN0xFFFFFFF不是一个数+INF0x7F80000正无穷(正溢出)-INF0XFF80000负无穷(负溢出)1.3C-51 的扩充界说1.3.1特殊功能寄存器的声明MSC-51 系列包括多种寄存器, 其中一些具有特殊功能, 如按时器, 端口的控制寄存器等, 为了能够直接访问这些寄存器, C51编译器提供了一种界说的自主形式, 这是需要的, 因为这些界说与标准C语言是不兼容的.为了支持这些特殊功能寄存器(SFR)的声明, 引入了关键词“sfr”, 语法如下:sfr-dcl:sfr sfr_name=int_constant例:sfr p0=0x80;sfr p1=0x90;必需注意的是“sfr”后不是一个地址而是一个名字.因此上例中名字P0和P1(port0和port1)界说为特殊功能寄存器并被赋予相应的绝对地址, 名字可按意愿自由选取, 源文件中不应有先界说的sfr名字.“=”号后的地址必需是常数, 不允许带有运算符的表达式, 这个常数表达式必需在特殊功能寄存器的地址范围内, 位于0X80到0XFF之间.8051系列寄存器数量和类型是极其分歧的, 因此建议将所有特另外“sfr”声明放入一个头文件, 头文件包括8051一些系列成员中的SFR界说.进一步的界说可由用户用一文件编纂器发生. 1.3.2对SFR的16位数据访问在新的8051系列产物中, SFR在功能上经常组合为16位的, 为了有效的访问这类SFR, 使用界说“sfr16”, 当“SFR”的高端直接位于低端后时, 对SFR16位的访问是可能的.例如8052的按时器2就是这种情况, 16位声明的语法与“sfr”相同, SFR低地址部份必需作为sfr16的地址:例:sfr16 T2=0xCC/*Timer2:T2L=0CCH, T2H=0CDH */sfr16 RCAP2=0xCA/*RCAP2L=0CAH, PCAP2H=0CBH */本例中, T2(由T2L和T2H组成)和RCAP2(由RCAP2L和RCAP2H组成)被界说为16位SFR, 即使在这种情况下, 声明中的名字后仍不是赋值语句, 而是一个SFR地址, 高字节必需直接位于低字节之后, 这种声明适用于所有新的SFR, 但不能用于Timer0和Timer1.1.3.3SBIT:特殊功能位声明在典范的8051应用问题中, 经常需要独自访问SFR中的位, C51扩充功能使之成为可能, 特殊位, 象SFR一样, 不与标准C 语言兼容, 使用保管字“sbit”可访问位寻址对象.与SFR声明一样, 用保管字“sbit”声明某些特殊位接受符号名, “=”后语句将绝对值地址赋给变量名, 这种地址分配有三种方法:方法1:sfr_name^int_constant当字节是特殊功能寄存器的地址可用这个方法.sfr_name必需是已界说的SFR的名字, “^”后的语句界说了基地址上的特殊位的位置, 该位置必需是一个0~7的数.例: sfr PSW=0xD0;sfr LE=0xA8;sbit OV=PSW^2;sbit CY=PSW^7;方法2:int_constant^int_constant这种方法以一整常数作基地址, 该值必需在0x80~0xFF之间, 并能被8整除, 确定位的位置方法同上.例: sbit OV=0xD0^2;sbit CV=0xD0^7;sbit EA=0xA8^7;方法3: int_constant这种方法是将位的绝对地址赋给变量, 地址必需位于0x80~0xFF之间.例: sbit OV=0xD2;sbit CY=0xD7;sbit EA=0xAF;特殊功能位代表了一个自力的声明类, 它不能与其它声明和位域互换.1.3.4BIT:位标量声明除通常的C数据类型外, C51编译器支持“bit”数据类型, 对此有下列扩充与限制:(1)函数可包括类型为“bit”的参数, 也可将其作为返回值.bit bfunc(bit b0, bit b1){/*……*/return(b1);}注:使用禁止中断(#pragma disable)或包括明确的寄存器组切换(using n)的函数不能返回位值, 在这种情况下, 编译器会识别出来并发生一个毛病信息.(2)位标量声明的语法及C声明的语义static bit dirction_bit;extern bit lock_printer_port;bit display_invers;(3)对位声明的限制●位不能声明为一个指针(bit *bit_poiter)●不存在位数组(bit b_array[5])位声明中允许界说存贮器类型, 位都被放入一个位段, 它总是在8051内部RAM中, 因此存贮器类型限制为DATA或IDATA, 声明为其它存贮器类型都将招致编译犯错.1.3.5可位寻址对象可位寻址对象指可以字节或位寻址的对象, 当对象位于MSC-51可寻址RAM中时会有这种情况, C51允许带“bdata”类型的对象放入可位寻址存贮器中.bdata int ibase; /*位寻址指针 int*/bdata char bary[4]; /*位寻址数组 arrray*/使用“sbit”声明可自力访问可位寻址对象的位:sbit mybit0=ibase^0;sbit mybit15=ibase^15;sbit ary07=bary[0]^7;sbit ary37=bary[3]^7;对象“ibase”和“bary”也可位寻址:ary37=0;/*寻址“bary[3]”中的位7*/ibase=-1;/*寻址字节地址*/mybit15=0;/*寻址“ibase”的位15*/sbit声明要求基址对象的存贮器类型为“bdata”, 否则只有绝对的位声明方法是合法的.位位置(‘^’把持符号后)的最年夜值依赖于指定的基类型, 这个值于char/uchar而言是0~7, 对int/uint/short/ushort而言是0~15, 对long/ulong而言是0~31.在编译器内存贮器类型bdata与data一样把持, 而且只作与可再定位的sbit的运算.注:可位寻址的的段长最年夜不能超越16字节, 可再定位的sbit声明自动转为公共的(PBULIC)以使它们能被其它C模块使用.模块1:sbitary37=bary[3]^7;模块2:externbit ary37;sbit声明也可为结构和函数所用:unionlft{float mf;long ml;} ;bdatastructbad { char ml; union lft u; }tcp;sbit tcpf31=tcp.u.ml^31; /*浮点限制*/sbit tcpml0=tcp.ml^0;sbit tcmpl7=tcp.ml.^7;注:位位置的指定不能直接被float类型所用, 如果需要这样做, 浮点标量必需与一个长整型标量一起放入一个联合中而且位位置必需由长整型题目指定(见上例).1.4存贮器类型C51编译器完全支持8051微处置器及其系列的结构, 可完全访问MCS-51硬件系统所有部份.每个变量可准确地赋予分歧的存贮器类型(data, idata, pdata, xdata, code).访问内部数据存贮器(idata)要比访问外部数据存贮器(xdata)相对要快一些, 因此, 可将经常使用的变量置于内部数据存贮器中, 而将较data char charvar;char code msg[]=”ENTER PARAMETER:”;unsigned long xdata array[100];float idata x,y,z;unsigned char xdata vector[10][4][4];sfr p0=0x80;sbit RI=0x98;char bdata flags;sbit flago=flags^0;如果在变量说明时略去存贮器类型标识表记标帜符, 编译器会自动选择默认的存贮器类型.默认的存贮器类型进一步由控制指令SMALL、COMPACT和LARGE限制.例如:如果声明char charvar, 则默认的存贮器模式为SMALL, charvar放在data存贮器;如果使用COMPACT模式, 则charvar放入idata存贮区;在使用LARGE模式的情况下, charvar被放入外部存贮区或xdata存贮区. 1.5存贮器模式存贮器模式决定了自动变量和默认存贮器类型, 参数传递区和无明确存贮区类型的说明.在固定的存贮器地址变量参数传递是C51的一个标准特征, 在SMALL模式下参数传递是在内部数据存贮区中完成的.LARGRE和COMPACT模式允许参数在外部存贮器中传递.C51同时也支持混合模式, 例如在LARGE模式下生成的法式指针Franklin C-51支持“基于存贮器的”和“一般指针”.1.6.1基于存贮器的指针基于存贮器的指针由C源代码中存贮器类型决定并在编译时确定, 用这种指针可高效访问对象且只需一个字节(idata*, data*, pdata*)或2个字节(code*, xdata*).把持较短指针的代码被缩短, 一般被“内行”编码;库调用不再需要.上面例子说明了指针的一般声明及使用.它们与所有的数据类型和存贮器类型相关.所有用于一般指针的把持同样可用于基于存贮器的指针.1.6.2一般指针“一般”指针需3个字节:1个字节为存贮类型, 2个字节为偏移量.存贮器类型决定了对象所用的8051存贮器空间, 偏移量指向实际地址.一个“一般”指针可访问任何变量而不论它在8051存贮器空间中的位置.这样就允许一般性函数, 如memcpy将数据从任意一个地址拷贝到另一个地址空间.1.6.3基于存贮器的指针与一般指针的转换一个已定位指针可转换为一个一般指针(3字节)及副本.这在某些时候是很有用的.例如库函数包括一个一般指针变量, 象printf(), sprintf(), gets()等包括一个一般指针变量, 如同以前的编译器和库版本一样.这些函数因而广泛适用.例:extern int printf(void *format, …);在printf调用中, 2字节指针自动转换为一个3字节指针, 而printf的原型正需要一个一般指针(3字节)作为其第一参量.注:如果没有函数原型, 函数调用的参量中指针总是转换为一般指针.如果函数确实需要一个短指针作参量, 这会发生毛病, 为了防止在法式中发生这类毛病, 需要使用头文件, 或某些函数声明函数原型.这将保证让编译器转换为所需类型, 否则会发生类型不匹配毛病.1.6.4笼统指针类型用来在每个存贮区访问任意绝对地址, 或来发生绝对CALLs.在这个过程中, 常数类型或字符型、整型都用笼统类型作了原则性修改(类型整理)以允许进行绝对访问或调用.例:char xdata *px;char idata *pi;char code *pc;char c;int i;pc = (void*)main;i = ((int(code*)(void))0xFF00(); /*LCALL 0FF00H*/c = *((char code*)0x8000); /*char [code[0x8000]]*/i = *((int code*)0x1200); /*int from code[0x1200]*/px = *((char xdata *xdata*)0x4000); /*x ptr from xdata[0x4000]*/px = ((char xdata *xdata*)0x4000)[0]; /*同上*/px = ((char xdata *xdata *)0x4000)[1] /*x ptr from xdata[0x4002]*/1.7寄存器组界说8051系列的器件包括4个相同的寄存器组, 每个寄存器组包括8个寄存器(R0~R7), C51编译器可使在一函数中决定用哪一寄存器组成为可能.绝对寄存器的访问可用AREGS/NOAREGS和REGISTERBANK来控制.界说一个带扩展性的函数语法如下:返回类型函数名([参数])[模式][再入][中断 n]using n 再入和中断将在后两节讨论.例:void rb_function(void) using 3;“using”不允许用于外部函数, 它对函数的目标代码影响如下:●函数入口处将以后寄存器保管入栈;●指它的寄存器还会改变;●函数退出前寄存器组被恢复.“using”界说对返回一个寄存器内的值的函数是无用的.编程者必需十分小心以保证任何寄存器切换都只在仔细控制的区域发生.如果不做到这一点将会发生不正确的函数结果.即使当编程者使用同一寄存器组时, 带“using”属性的函数原则上也不能返回一个位值.实际发生的代码决定于编译器及分歧开关条件, 有时用命令发生绝对的寄存器地址, 当需要进行这样的地址计算时, 使用REGISTERBANK指令的影响只是计算Arn寄存器使用的地址, 而必进行实际切换.1.8中断服务法式C51编译器及其对C语言的扩充允许编程者对中断的所有方面进行控制.这种支持能使系统编程者创立高效的中断服务法式, 用户只需在普通和高级方式下关心中断及需要的寄存器组切换把持, C51编译器将发生最合适的代码.1.8.1中断服务法式的界说使用中断服务函数的完整语法如下:返回值函数名([参数])[模式][再入] interrupt n[using n]“interrupt”后接一个0~31的常数, 不允许使用表达式.中断不允许用于外部函数, 它对函数目标代码的影响如下:●当使用函数时, SFR中的ACC、B、DPH、DPL和PSW(当需要时)入栈;●如不使用寄存器组切换, 甚至中断函数所需的所有工作寄存器(Rn)都入栈;●函数退出前, 所有的寄存器内容出栈;●函数由8051控制命令“RETI”终止.1.8.2开发中断过程时的规则●不能进行参数传递, 如果中断过程包括任何参数声明, 编译器将发生一个毛病信息;●无返回值, 如果想界说一个返回值将发生毛病, 然而, 如果返回整型值编译器将不发生毛病信息, 因为整型值是默认值, 因而编译器不能清楚识别.●编译器会识别对中断过程的直接调用并拒绝它们, 在任何情况下不能直接调用中断过程, 因为退出该过程是由把持码RETI完成的.RETI影响8051芯片的硬件中断系统, 由于硬件上没有中断请求存在, 因而这个把持码的结果是不定的而且通常是致命的.由于疏忽, 可能用指针来间接调用它, 这是值得注意的.●编译器从绝对地址8n+3处发生一个中断向量, 其中n为中断号, 该向量包括一个到中断过程的跳转, 向量的发生可由指令NOINTVECTOR压缩.因而用户有能力从自力的汇编模块中提供中断向量.●C51编译器允许0~31个中断, 究竟允许哪些中断依赖于使用的8051系列芯片, 编译器不能检查.●如果中断法式中有浮点运算, 必需坚持浮点寄存器状态,当没有其它法式执行浮点运算时, 可能不保管, 函数“fsave”和“fprestore”用来保管浮点状态.●中断过程调用的函数所使用的寄存器必需与中断过程相同,当没有使用“using”指令时, 编译器会选择一个寄存器组作绝对寄存器访问, 当子法式使用另一个寄存器组时会发生毛病, 用户必需保证按要求使用相应寄存器组, C编译器不会对此检查.例:unsigned int interruptent;unsigned char second;time() interrupt 1 using 2/*按时器0中断服务法式,工作寄存器使用2区*/{if(++interruptcnt==4000) {second++;/*秒计数加一*/interruptcnt=0;/*清中断计数*/}}1.9再入函数再入函数可被递归调用, 调用可发生在任何时候, 即使是在中断过程中.在实时处置的应用问题中经常需要再入函数.使用关键字“reentrant”可有选择地界说函数有再入能力.在存贮器模式的基础上为再入函数在内部或外部存贮器中模拟了一个栈区域.由于MCS-51缺乏合适的寻址方法, 使用栈结构是相当需要的.因而应尽量少用再入函数.界说一再入函数的语法如下:返回值函数名([参数])[模式]reetrant[interrupt n][using n]例:int calc(char i,int b) reentrant {int x;x=table[i];return(x*b);}使用再入函数有如下规定:●不能传递类型为“bit”的参数.也不能声明一个局部标量,再入功能不能包括位把持及MCS-51可位寻址区域.●不能在“alien”函数调用再入函数.●再入函数可同时有其它属性, 如“using”函数模式和“interrupt”.●再入函数不能同时有“alien”属性, 从而遵守PL/M规则.●返回地址及可能的PUSH/POP把持存入MCS-51的栈中或被执行(不在再入栈中).●在同一模块中, 任意模块的再入函数(small reentrant,lage reentrant, compact reentrant)不能与具有分歧模式的再入函数混合.再入函数举例:/*这个再入函数可以从“main”及中断法式中调用*/int calc(char i,int b)reentrant {int x;x=table[i];return(x*b);}1.10参数传递通过CPU的寄存器可传递至多三个参数.这样发生与汇编子法式相当的有效参数机制.如果寄存器被占用, 或说明了“#pragma NOREGPARMS”, 参数变量将使用固定的存贮器位置, 存贮器模式决定了8051存贮器为参数提供的位置.函数的返回值放在CPU固定的寄存器中, 列表如下.这样, 与汇编子法式的接口变得非常容易.1PL/M51接口Franklin C51利用关键字“alien”提供了一个与Intel PL/M-51直接和简单和接口, 关键字“alien”在所有存贮器模式下可用于“extern”和“public”函数.现有的PL/M-51法式利用C语言的强年夜功能可与Franklin C-51连接起来.使用关键字“alien”, C51可用PL/M-51规定的参数传递方式工作.“alien”可用于外部或公共函数, 并可用于任一模式, 这样, 已有的PL/M-51法式可加入到C-51中.Alien函数始终包括一个标准的参数数量, 因此, C中界说的三点(…)记号不被接受, 且会发生一个毛病信息.例:extern alien char plm_function(unsigned char,unsigned int);extern char c_function(unsigned char x,unsigned char y) {return(x*y);}PL/M-51兼容函数必需界说以关键字“alien”.这样, PL/M函数的参数传递及参数返回规定在C编译器中才被考虑.1.12汇编接口参数是通过固定的CPU寄存器传给汇编法式的, 当使用“#pragma NOREGPARMS”时, 则通过固定的存贮器位置传递参数.这样就给汇编与Franklin C-51之间提供了一个非常简洁的接口.返回值在CPU寄存器中.下例为在汇编中用来编码的“toupper”函数, 参数传递发生在寄存器中.UPPERSEGMENTCODE;法式代码段PUBLIC_toupper;入口地址RESGUPPER;选择法式代码段toupper:MOVA,R7;char 参数在寄存器R7中CJNEA,#’a’,UPP1UPP1:JCUPPERTCJNEA,#’z’+1,UPP2UPP2:JNEUPPRETUPPRET:MOVR7,A;char 返回值在寄存器R7中RET;返回C1.13内部函数Franklin C-51支持下列内部函数.内部函数既是再入的又是有效的.表:C51的内部函数1.代码优化Franklin C51可将即使有经验的法式员编制的代码进行优化.用户可选6个优化级, 另外, 用OPTIMIZE(SIZE), NOREGPARMS 和NOAREGS时会影响生成代码的类型.C51的所有优化如下:(1)一般优化:●常数折迭:发生在一个表达式或地址计算中的几个常数值组合为一个常数.●跳转优化:跳转转到最终的目标地址, 以提高法式效率.●死码消除:不成执行代码(死码)可从法式中去失落.●寄存器变量:只要有可能, 自动变量和参量放入寄存器中,为这些变量保管的数据存贮器将去除.●通过寄存器传递参数:寄存器中可传递最多三个参数.●全局公共子式消除:相同的子表达式或地址计算(屡次发生在同一函数中)将被识别出来, 而且只要有可能, 将只计算一次.(2)基于8051的优化:●窥孔(PEEPHOLE)优化:只要能节省存贮空间或执行时间,复杂的运算都将化简.●访问优化:常数和变量直接包括在把持中.●数据覆盖:函数的数据和位移被标识表记标帜为OVERLAYABLE, 被L51用其它数据和位覆盖.●CASE/SWITCH 优化:SWITCH/CASE 语句优化为一个跳转或一串跳转. (3) 代码生成选项:● OPTMIZE (SIZE ):共同的“C ”把持被子法式取代:法式码长被压缩.● NOAREGS :不使用绝对寄存器访问, 法式代码在这种方式下自力于寄存器组.● NOREGPARMS :参数传递总是在本数据段完成, 法式代码与早期C-51版本兼容.1.15 C 库C-51编译器包括6个分歧的编译库, 可根据分歧函数的需要进行优化, 这些库几乎支持所有的ANSI 函数调用.因此, 用此标准的CC51关的修改.用户改变对现有硬件输入和输出结构的两个模块, 就可修改所有库函数, 同样也可以重新很快地构造如“printf ”和“puts ”函数用LCD 显示.L51连接器的检查从而保证所有模块都用一种模式编译并自动选择编译库, 从而使用户完全可以不用分歧库的细节. 1.16 配置文件C51编译器可根据分歧的硬件环境由4个文件作出修改.下列用.如果用户改变一个文件, 可将其编译后与其它目标文件一起连接, 因而不用改动运行库.库中原文件自动忽略.例:文件STARTUP.51开头包括一些C编译结构使用的EQU语句.每个EQU语句的功能描述如下:IDATALEN 声明系统开始时有几多内存需要用0初始化.默认值为80H, 因为几乎每个8051指令至少包括128字节内部RAM.对256字节内部RAM的8052可使用100H.当用户法式在开始时需要使用0初始化的内存时才有需要作改动.如果内存初始化必需坚持对失落电模式系统的完全抑制, IDATALEN应设为0.这种情况下至少得坚持所有位于段?C_LIB_DATA和?C_LIB_DBIT中的变量都置为0.否则有些库函数不能完全发挥作用, ?C_LIB_DATA段的长度因分歧应用问题而分歧,其以后长度可在MAP文件中找到.XDATASTARTXDATALEN标明了需要以0初始化的PDATA区首址和长度, XDATASTART指明了XDATA区首址, XDATALEN标明了需初始化的字节数.PDATASTARTPDATALEN标明了需以0初始化的PDATA区首址及长度, PDATASTART指明了首址, XDATALEN指定了长度. LBPSTACKLBPSTACKTOP 界说了SMALL模式下创立的再入函数使用的栈区.LBPSTACK标明是否对栈指针(变量?C_LBP)初始化,LBPSTACKTOP指明了栈顶首址.对具有256字节内部RAM的8051系统, 当存贮区作首址为0XFF的栈时, 可不初始化.C51不作栈区是否满足特定应用的检查, 用户必需自己进行测试.XBPSTACKXBPSTACKTOP 为在LARGE模式下创立的再入函数界说了栈区, XBPSTACK标明指针(变量?C_XBP)是否初始化,XBPSTACKTOP指定了栈顶地址.当存贮区作为首址为0Xffff(在XDATA区)的栈时, 可不作初始化.同上一样, C51不作栈检查, 需要用户自己测试.PBPSTACKPBPSTACKTOP 为在COMPACT模式下创立的再入函数界说了栈区, PBPSTACK标明栈指针(变量?C_PBP)是否初始化.PBPSTACKTOP指定了栈顶地址.当存贮区作为首址为0Xff(在PDATA区)的栈时, 可不作初始化.同上一样,C51不作栈检查, 需要用户自己测试.PPAGEENABLEPPAGE当在COMPACT模式中用16位寻址XDATA存贮区时需要这些指令.对使用LARGE模式的法式, 可用它提高运行速度或减小代码长度.PPAGEENABLE允许8051端口2的初始化, 对端口2的寻址允许在任意XDATA页256字节变量空间的映射.这两个指令必需和L51的控制指令PDATA一起使用.PDATA指定了XDATA存贮器中PDATA区的首址.例:在STARTUP.A51中, PPAGEENABLE置为1, PPAGE置为10H.这种情况下PDATA区首址为1000H(10H页),而L51必需包括一个值在1000和10FFH之间的控制语句:L51〈输入模块〉PDATA(1050H).注:L51和C51都分歧毛病PPAGE/PDATA指令正确性进行检查, 用户必需保证PPAGE和PDATA包括一个合适的值.INIT.A51:“看门狗”刷新的宏.当系统包括“看门狗”以及用户变量初始化时间比“看门狗”刷新时间要长时, 必需改变这个宏.这种情况下, 宏WATCHCOG必需包括“看门狗”刷新的代码.例:;Watchdog refresh for 80515 systemWATCHDOG MACROSETBWDTSETBSWDTENDM文件PUTCHAR.C包括字符输出的核心法式, 该文件通过串行口输出.这种情况下考虑了XON/XOFF协议, 字符LF()被转为字符串CR, LF, 这在很多终端中是需要的.用户可按自己的要求改变putchar()函数.文件GETKEY.C包括字符输入的核心法式,该文件从串行接口读入一个字符, 不妥准据转换, 用户可根据需要修改getkey()函数. 1.17优化法式本节包括几个怎样提高8051法式效率的注解.定位变量经常访问的数据对象应放入在片内数据RAM中, 这可在任一模式(COMPACT/LARGE)下用输入存贮器类型的方法实现.访问片内数据RAM要比访问外部数据存贮器快很多.片内RAM由寄存器组, 位数据区栈和其它由用户用“data”类型界说的变量共享.由于片内RAM容量的限制(128~256字节, 由使用的处置器决定), 必需权衡利弊以解决访问效率和这些对象的数量之间的矛盾.总是使用可能的最小数据类型8051系列CPU都是8位机, 因此, 显然对具有“char”类型的对象的把持比“int”或“long”类型的对象方便很多.建议编程者只要满足要求, 应尽量使用最小数据类型.C51编译器直接支持所有的字节把持, 因而如果不是运算符要求, 就不作“int”类型的转换, 这可用一个乘积运算来清楚说明, 两个“char类型”对象的乘积与8051把持码“MUL AB”刚好相符.如果用整型量完成同样的运算, 则需要调用库函数.只要有可能, 使用“unsigned”数据类型8051系列CPU其实不直接支持有符号数的运算.因而C51编译器必需发生与之相关的更多的代码以解决这个问题.如果使用无符号类型, 发生的代码要少很多.只要有可能, 使用局部函数变量编译器总是检验考试在寄存器里坚持局中变量.这样, 将索引变量(如FOR和WHILE循环中计数变量)声明为局部变量是最好的, 这个优化步伐只为局部变量执行.使用“unsigned char/int”的对象通常能获得最好的结果.2C51编译器控制指令编译选项可能被控制指令激活、禁止或改变.这些指令可在命令行输入或在源文件上加入#pragma给预处置器.控制指令分为两组, 即首要的和一般的, 并可分为三类:源控制、目标控制、列表控制.见表2.1编译控制指令.“P/G”列标明指令是首要的或是一般的, 首要指令仅呈现一次, 一般指令按需要可呈现屡次.表2.1 编译控制指令。
第1章C51数据类型与运算1.1C51数据类型C51的数据类型如下所示:C51编译器支持的数据类型、长度和值域如下表1-1所示。
与面向数学运算的计算机相比,51单片机对变量类型或数据类型的选择更具有关键性意义。
如果在程序设计中使用大量而不必要的变量类型,这会导致C编译器调用库函数的数量,以处理大量的变量类型和数据类型。
所以必须特别慎重地进行变量和数据类型的选择。
1.2C51数据存储类型C51编译器还可以通过将变量、常量定义成不同的存储类型(data,bdata,idata,pdata,xdata,code)的方法,将它们定义在不同的存储区中。
存储类型与51单片机实际存储空间的对应关系如表1-2所示。
储区中。
片内RAM是存放临时性传递变量或使用频率较高变量的理想场所。
访问片内数据存储器(data、bdata、idata)比访问片外数据存储器(xdata、pdata)相对快一些,因此可将经常使用的变量置于片内数据存储器,而将规模较大的或不常使用的数据置于片外数据存储器中。
C51存储类型及其大小和值域如表1-3所示。
例如:表1-3 C51存储类型及其大小和值域char data var1; /*字符变量var1被定义为data存储类型,定位在片内RAM中*/bit bdata flags; /*位变量flags被定义为data存储类型,定位在片内RAM中的位寻址区*//*(20H~2FH)*/float idata x,y,z; /*浮点变量x,y,z被定义为idata存储类型,定位在片内RAM中,并只能用间接寻址的方法进行访问*/unsigned int pdata dimension; /*无符号整型变量dimension被定义为pdata存储类型,定位在片外RAM中,并用MOVX @Ri访问*/ unsigned char xdata vector[10][4][4]; /*无符号字符三维数组变量vector[10][4][4]被定义为xdata存储类型,定位在片外RAM中,占据10×4×4=160个字节空间*/如果在变量定义时省略存储类型标志符,编译器会自动默认存储类型。