单片机c51编程_单片机c51编程和c语言区别 - CSDN
  • 单片机C51编程规范

    2006-03-10 08:17:00
    1单片机C51编程规范- 前言 为了提高源程序的质量和可维护性,从而最终提高软件产品生产力,特编写此规范。 2 单片机C51编程规范-范围 本标准规定了程序设计人员进行程序设计时必须遵循的规范。本规范主要针对C51...
    1单片机C51编程规范- 前言 
      为了提高源程序的质量和可维护性,从而最终提高软件产品生产力,特编写此规范。

    2 单片机C51编程规范-范围
      本标准规定了程序设计人员进行程序设计时必须遵循的规范。本规范主要针对C51编程语言和keil编译器而言,包括排版、注释、命名、变量使用、代码可测性、程序效率、质量保证等内容。

    3 单片机C51编程规范-总则
    l 格式清晰
    l 注释简明扼要
    l 命名规范易懂
    l 函数模块化
    l 程序易读易维护
    l 功能准确实现
    l 代码空间效率和时间效率高
    l 适度的可扩展性

    4 单片机C51编程规范-数据类型定义
    编程时统一采用下述新类型名的方式定义数据类型。
    建立一个datatype.h文件,在该文件中进行如下定义:
    typedef bit BOOL; // 位变量 //
    typedef unsigned char INT8U; // 无符号8位整型变量 //
    typedef signed char INT8S; // 有符号8位整型变量 //
    typedef unsigned int INT16U; // 无符号16位整型变量 //
    typedef signed int INT16S; // 有符号16位整型变量 //
    typedef unsigned long INT32U; // 无符号32位整型变量 //
    typedef signed long INT32S; // 有符号32位整型变量 //
    typedef float FP32; // 单精度浮点数(32位长度) //
    typedef double FP64; // 双精度浮点数(64位长度) //

    5 单片机C51编程规范-标识符命名

    5.1 命名基本原则
    l 命名要清晰明了,有明确含义,使用完整单词或约定俗成的缩写。通常,较短的单词可通过去掉元音字母形成缩写;较长的单词可取单词的头几个字母形成缩写。即"见名知意"。
    l 命名风格要自始至终保持一致。
    l 命名中若使用特殊约定或缩写,要有注释说明。
    l 除了编译开关/头文件等特殊应用,应避免使用以下划线开始和/或结尾的定义。
    l 同一软件产品内模块之间接口部分的标识符名称之前加上模块标识。

    5.2 宏和常量命名
    宏和常量用全部大写字母来命名,词与词之间用下划线分隔。对程序中用到的数字均应用有意义的枚举或宏来代替。

    5.3 变量命名
    变量名用小写字母命名,每个词的第一个字母大写。类型前缀(u8\s8 etc.)全局变量另加前缀g_。
    局部变量应简明扼要。局部循环体控制变量优先使用i、j、k等;局部长度变量优先使用len、num等;临时中间变量优先使用temp、tmp等。

    5.4 函数命名
    函数名用小写字母命名,每个词的第一个字母大写,并将模块标识加在最前面。

    5.5 文件命名
    一个文件包含一类功能或一个模块的所有函数,文件名称应清楚表明其功能或性质。
    每个.c文件应该有一个同名的.h文件作为头文件。

    6 单片机C51编程规范-注释

    6.1 注释基本原则
    l 有助于对程序的阅读理解,说明程序在"做什么",解释代码的目的、功能和采用的方法。
    l 一般情况源程序有效注释量在30%左右。
    l 注释语言必须准确、易懂、简洁。
    l 边写代码边注释,修改代码同时修改相应的注释,不再有用的注释要删除。

    6.2 文件注释
    文件注释必须说明文件名、函数功能、创建人、创建日期、版本信息等相关信息。
    修改文件代码时,应在文件注释中记录修改日期、修改人员,并简要说明此次修改的目的。所有修改记录必须保持完整。
    文件注释放在文件顶端,用"/*……*/"格式包含。
    注释文本每行缩进4个空格;每个注释文本分项名称应对齐。
    /***********************************************************
    文件名称:
    作 者:
    版 本:
    说 明:
    修改记录:
    ***********************************************************/

    6.3 函数注释
    6.3.1 函数头部注释
    函数头部注释应包括函数名称、函数功能、入口参数、出口参数等内容。如有必要还可增加作者、创建日期、修改记录(备注)等相关项目。
    函数头部注释放在每个函数的顶端,用"/*……*/"的格式包含。其中函数名称应简写为FunctionName(),不加入、出口参数等信息。
    /***********************************************************
    函数名称:
    函数功能:
    入口参数:
    出口参数:
    备 注:
    ***********************************************************/

    6.3.2 代码注释
    代码注释应与被注释的代码紧邻,放在其上方或右方,不可放在下面。如放于上方则需与其上面的代码用空行隔开。一般少量注释应该添加在被注释语句的行尾,一个函数内的多个注释左对齐;较多注释则应加在上方且注释行与被注释的语句左对齐。
    函数代码注释用"//…//"的格式。
    通常,分支语句(条件分支、循环语句等)必须编写注释。其程序块结束行"}"的右方应加表明该程序块结束的标记"end of ……", 尤其在多重嵌套时。

    6.4 变量、常量、宏的注释
    同一类型的标识符应集中定义,并在定义之前一行对其共性加以统一注释。对单个标识符的注释加在定义语句的行尾。
    全局变量一定要有详细的注释,包括其功能、取值范围、哪些函数或过程存取它以及存取时的注意事项等。
    注释用"//…//"的格式。

    7 单片机C51编程规范-函数

    7.1 设计原则
    函数的基本要求:
    l 正确性:程序要实现设计要求的功能。
    l 稳定性和安全性:程序运行稳定、可靠、安全。
    l 可测试性:程序便于测试和评价。
    l 规范/可读性:程序书写风格、命名规则等符合规范。
    l 扩展性:代码为下一次升级扩展留有空间和接口。
    l 全局效率:软件系统的整体效率高。
    l 局部效率:某个模块/子模块/函数的本身效率高。

    编制函数的基本原则:
    l 单个函数的规模尽量限制在200行以内(不包括注释和空行)。一个函数只完成一个功能。
    l 函数局部变量的数目一般不超过5~10个。
    l 函数内部局部变量定义区和功能实现区(包含变量初始化)之间空一行。
    l 函数名应准确描述函数的功能。通常使用动宾词组为执行某操作的函数命名。
    l 函数的返回值要清楚明了,尤其是出错返回值的意义要准确无误。
    l 不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。
    l 减少函数本身或函数间的递归调用。
    l 尽量不要将函数的参数作为工作变量。

    7.2 函数定义
    l 函数若没有入口参数或者出口参数,应用void明确申明。
    l 函数名称与出口参数类型定义间应该空一格且只空一格。
    l 函数名称与括号()之间无空格。
    l 函数形参必须给出明确的类型定义。
    l 多个形参的函数,后一个形参与前一个形参的逗号分割符之间添加一个空格。
    l 函数体的前后花括号"{}" 各独占一行。

    7.3 局部变量定义
    l 同一行内不要定义过多变量。
    l 同一类的变量在同一行内定义,或者在相邻行定义。
    l 先定义data型变量,再定义idtata型变量,再定义xdata型变量.
    l 数组、指针等复杂类型的定义放在定义区的最后。
    l 变量定义区不做较复杂的变量赋值。

    7.4 功能实现区规范
    l 一行只写一条语句。
    l 注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。
    l 各程序段之间使用一个空行分隔,加以必要的注释。程序段指能完一个较具体的功能的一行或多行代码。程序段内的各行代码之间相互依赖性较强。
    l 不要使用难懂的技巧性很高的语句。
    l 源程序中关系较为紧密的代码应尽可能相邻。
    l 完成简单功能、关系非常密切的一条或几条语句可编写为函数或定义为宏。

    8 单片机C51编程规范-排版

    8.1 缩进
    代码的每一级均往右缩进4个空格的位置。

    8.2 分行
    过长的语句(超过80个字符)要分成多行书写;长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进适当的缩进,使排版整齐,语句可读。避免把注释插入分行中。

    8.3 空行
    l 文件注释区、头文件引用区、函数间应该有且只有一行空行。
    l 相邻函数之间应该有且只有一行空行。
    l 函数体内相对独立的程序块之间可以用一行空行或注释来分隔。
    l 函数注释和对应的函数体之间不应该有空行。
    l 文件末尾有且只有一行空行。

    8.4 空格
    l 函数语句尾部或者注释之后不能有空格。
    l 括号内侧(即左括号后面和右括号前面)不加空格,多重括号间不加空格。
    l 函数形参之间应该有且只有一个空格(形参逗号后面加空格)。
    l 同一行中定义的多个变量间应该有且只有一个空格(变量逗号后面加空格)。
    l 表达式中,若有多个操作符连写的情况,应使用空格对它们分隔:
    在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符前后均加一个空格;在两个以上的关键字、变量、常量进行非对等操作时,其前后均不应加空格;
    逗号只在后面加空格;
    双目操作符,如比较操作符, 赋值操作符"="、"+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位操作符"<<"、"^"等,前后均加一个空格;
    单目操作符,如"!"、"~"、"++"、"-"、"&"(地址运算符)等,前后不加空格;
    "->"、"."前后不加空格;
    if、for、while、switch等关键字与后面的括号间加一个空格;

    8.5 花括号
    l if、else if、else、for、while语句无论其执行体是一条语句还是多条语句都必须加花括号,且左右花括号各独占一行。
    l do{}while()结构中,"do"和"{"均各占一行,"}"和"while();"共同占用一行。
    if ( ) do
    { {

    } }while( );
    else
    {

    }

    8.6 switch语句
    l 每个case和其判据条件独占一行。
    l 每个case程序块需用break结束。特殊情况下需要从一个case块顺序执行到下一个case块的时候除外,但需要在交界处明确注释如此操作的原因,以防止出错。
    l case程序块之间空一行,且只空一行。
    l 每个case程序块的执行语句保持4个空格的缩进。
    l 一般情况下都应该包含default分支。
    Switch ( )
    {
    case x:

    break;

    case x:

    break;

    default:

    break;
    }

    9 单片机C51编程规范-程序结构

    9.1 基本要求
    l 有main()函数的.c文件应将main()放在最前面,并明确用void声明参数和返回值。
    l 对由多个.c文件组成的模块程序或完整监控程序,建立公共引用头文件,将需要引用的库头文件、标准寄存器定义头文件、自定义的头文件、全局变量等均包含在内,供每个文件引用。通常,标准函数库头文件采用尖角号< >标志文件名,自定义头文件采用双撇号″″标志文件名。
    l 每个.c文件有一个对应的.h文件,.c文件的注释之后首先定义一个唯一的文件标志宏,并在对应的.h文件中解析该标志。
    在.c文件中:
    #define FILE_FLAG
    在.h文件中:
    #ifdef FILE_FLAG
    #define XXX
    #else
    #define XXX extern
    #endif
    l 对于确定只被某个.c文件调用的定义可以单独列在一个头文件中、单独调用。

    9.2 可重入函数
    可重入函数中若使用了全局变量,应通过关中断、信号量等操作手段对其加以保护。

    9.3 函数的形参
    l 由函数调用者负责检查形参的合法性。
    l 尽量避免将形参作为工作变量使用。

    9.4 循环
    l 尽量减少循环嵌套层数
    l 在多重循环中,应将最忙的循环放在最内层
    l 循环体内工作量最小
    l 尽量避免循环体内含有判断语句
    展开全文
  • 51单片机组成 CPU:由运算和逻辑控制组成,同时还包括中断系统和部分外部特殊功能寄存器; RAM:用以存放可以读写的数据。如运算的中间结果、最终结果以及欲显示的结果; ROM:用以存放程序,一些原始数据及表格; I/O...

    C51单片机之keil编程入门(一)

    编程需结合原理图来理解
    附上单片机原理图http://file.elecfans.com/web1/M00/56/5B/pIYBAFs9rlmAcK6tABHJTk7qMTw228.pdf

    内容提要:

    • 单片机组成
    • 点亮一个发光管
    • 流水灯设计
    • 蜂鸣器发声
    • 数码管静态显示

    51单片机组成

    • CPU:由运算和逻辑控制组成,同时还包括中断系统和部分外部特殊功能寄存器;
    • RAM:用以存放可以读写的数据。如运算的中间结果、最终结果以及欲显示的结果;
    • ROM:用以存放程序,一些原始数据及表格;
    • I/O口:四个8位并行I/O口,即可用作输入,也可用作输出;
    • T/C:两个定时/记数器,既可以工作在定时模式,也可工作在记数模式;

    1.点亮一个发光管

    //通过位操作点亮发光管
    include<reg52.h>
    sbit D1=P1^0;//sbit位定义,P1^0表示P1口的第一位(从0开始计数)
    int main()
    {
    	D1=0;//单片机的P1.0端口输出低电平时发光管亮,51单片机默认上电后所有I/O口为高电平
    }
    
    //直接对P1口进行总线操作
    include<reg52.h>
    int main()
    {
    	P1=0xfd;//0x在C/C++中表示十六进制,fd即1111 1101
    	        //1111 1101为点亮P1口上的第二位(P1.1)上的发光管,P1.0为最低位,P1.7为最高位
    }
    

    先下载程序再打开单片机电源

    2.流水灯设计

    include<reg52.h>
    include<intrins.h>
    #define uint unsigned int
    #define uchar unsigned char
    
    uchar temp;
    void delay(uint n);
    
    int main()
    {
    	temp=0xfe;
    	P1=temp;
    	while(1)
    	{
    		temp=_crol_(temp,1);//C51库函数,功能:将temp循环左移1位
    		delay(600);
    		P1=temp;
    	}
    }
    
    //延时函数
    void delay(uint n)
    {
    	uint x,y;
    	for(x=100;x>0;x--)
    		for(y=n;y>0;y--);
    }
    
    

    3.蜂鸣器发声

    include<reg52.h>
    sbit beep=P2^3;
    int main()
    {
    	beep=0;
    }
    

    4.数码管静态显示

    • 结合原理图理解
    • 用锁存器来实现段选和位选(先控制位选,再控制段选)
    • 锁存器的第11管脚为锁存端
    //显示1
    #include<reg52.h>
    sbit Dula=P2^6;//段选--控制亮出什么数字
    sbit Wela=P2^7;//位选--控制哪一个数码管亮
    int main()
    {
    	Wela=1;//锁存端为高电平时,其输入端和输出端置通
    	P0=0xfe;
    	Wela=0;//锁存端为低电平时,输入端和输出端断开,输出端保持原来的值不变
    	
    	Dula=1;
    	P0=0x06;
    	Dula=0;
    	
    	while(1);
    }
    
    展开全文
  • 1、头文件:#include   2、预定义:sbit LED = P1^0// 定义 P1 口的 0 位为 LED   注:“P1^0”这个写法,与 A51 不同(A51 是 P1.0),P1 是一组端口,端口号范围 0~7 ...注3:以下写法是错误的:

    1、头文件:#include

     

    2、预定义:sbit LED = P1^0// 定义 P1 口的 0 位为 LED

     

    注:“P1^0”这个写法,与 A51 不同(A51 P1.0)P1 是一组端口,端口号范围 0~7

     

    2sbit 用于定义 SFR(特殊功能寄存器)的位变量,上例中 LED 作为全局变量进行定义

     

    3:以下写法是错误的:

     

    sbit code table[ ] = {P1^0, P1^1, P1^2, P1^3};// 想用 table[i] 指定不同的引脚,但这么做会报错

     

    sbit table[ ] = {P1^0, P1^1, P1^2, P1^3};            // 考虑到上面可能是 code 关键字使用错误,使用标准 C 数组写法,但这同样是错的

     

    3、主函数写法:void main (void)

     

    4、数值的表示:

     

    P1 = 1111 1111// 二进制

    P1 = 0xff 或者 P1 = 0xFF// 十六进制,0x 开头,且数值不分大小写

    P1 = 255// 十进制

     

    5、定义小数值时,可以使用 unsigned char i,这样 i 的范围为 0~255,作为循环变量比较好用

     

    6、左右移位:

     

    P1 <<= 2 等价于 P1 = P1 << 2// P1 左移 2 位,左移一位相当于乘以 2

    P1 >>= 3 等价于 P1 = P1 >> 3// P1 右移 3 位,右移一位相当于除以 2

     

    注:左右移位默认为逻辑移位,即无论左移还是右移,空位都补 0

     

    7、按位与或:

    P1 = P1 & 0x01

    P1 = P1 | 0x01

     

    8、定义 ROM 表格(就是数据为常量的数组)

     

    unsigned char code table[ ] = {0xff, 0xff, 0xff, 0xff};

     

    使用:P1 = table[i]

     

    注:table[ ] 定义为全局变量,上例中 i 的范围为 0~3

     

    2code 定义的常量存于代码区,即 ROM 区,可以节省 RAM 空间

     

    9、在使用数码管编程时,假如你正在使用 temp[i] 代表某一个显示字符,突然想显示小数点,可以使用 temp[i] | 0x80,通过运算实现加上小数点……

     

    10、如果你用 Keil C51 进行编译,记住一点:它不区分大小写!!!卧槽,今天编程序那个调错啊,就因为一个数组名和一个变量名完全一样,只是大小写不一样罢了,标准 C 我怎么记得这样可以啊……上网一查,卧槽,Keil C51 不区分大小写,准确的说是连接的时候不区分大小写,更准确一点就是具有外部连接的变量区分大小写,内部连接 static 区分大小写”……至少 Keil uVision2 是这样,不知道别的版本是不是,待验证……

     

    11、没有 unsigned float x !float 型变量从来没有前边加 unsigned 的语法!

     

    12Keil 编译的程序,main 函数执行完不会停止,会循环执行 main 函数,何解?

     

    结论 1:如果主程序中没有 while(1) 这个无限循环,程序走到最后会再次从头开始执行。

     

    结论 2:如果主程序有 while(1) 这个无限循环,程序走到最后会一直在这个死循环中运行,不会出现再从头执行的情况。

     

    这应该属于 Keil 编译器的 bug,有网友做过实验,表示 Keil 编译后期产生的汇编代码中,结尾有一条 LJMP main,意思就是跳到 main 函数重复执行……还有一种说法是 PC 指针溢出,溢出后的地址指向开头,造成继续执行的效果……(博主觉得还是 Keil 的问题,要是 Keil 编译器不产生 LJMP main 这种语句,也不会产生循环效应……)

     

    不管怎么说,在程序结尾加上 while(1) 能够解决循环执行 main 函数的 bug……

     

    展开全文
  • 深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一。因为大多数扩展功能都是直接针对8051系列CPU硬件的。大致有以下8类: l 8051存储类型及存储区域 l 存储模式 l 存储器类型声明 l 变量类型声明 l 位变量与位...
    深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一。因为大多数扩展功能都是直接针对8051系列CPU硬件的。大致有以下8类:
    l 8051存储类型及存储区域
    l 存储模式
    l 存储器类型声明
    l 变量类型声明
    l 位变量与位寻址
    l 特殊功能寄存器(SFR)
    l C51指针
    l 函数属性
    具体说明如下(8031为缺省CPU)。
    1. 第一节 Keil C51扩展关键字
    C51 V4.0版本有以下扩展关键字(共19个):
    _at_ idata 
    sfr16 alien 
    interrupt small
    bdata large 
    _task_ Code bit 
    pdata using 
    reentrant xdata 
    compact sbit 
    data sfr
    2. 第二节 内存区域(Memory Areas):
    1. 1. Pragram Area:
    由Code说明可有多达64kBytes的程序存储器
    2. 2. Internal Data Memory:
    内部数据存储器可用以下关键字说明:
    data:直接寻址区,为内部RAM的低128字节 00H~7FH
    idata:间接寻址区,包括整个内部RAM区 00H~FFH
    bdata:可位寻址区,  20H~2FH
    3. 3. External Data Memory
    外部RAM视使用情况可由以下关键字标识:
    xdata:可指定多达64KB的外部直接寻址区,地址范围0000H~0FFFFH
    pdata:能访问1页(25bBytes)的外部RAM,主要用于紧凑模式(Compact Model)。
    4. 4. Speciac Function Register Memory
    8051提供128Bytes的SFR寻址区,这区域可位寻址、字节寻址或字寻址,用以控制定时器、计数器、串口、I/O及其它部件,可由以下几种关键字说明:
    sfr:字节寻址 比如 sfr P0=0x80;为PO口地址为80H,“=”后H~FFH之间的常数。
    sfr16:字寻址,如sfr16 T2=0xcc;指定Timer2口地址T2L=0xcc T2H=0xCD
    sbit:位寻址,如sbit EA="0xAF";指定第0xAF位为EA,即中断允许
    还可以有如下定义方法:
    sbit 0V=PSW^2;(定义0V为PSW的第2位)
    sbit 0V=0XDO^2;(同上)
    或bit 0V-=0xD2(同上)。
    . 第三节 存储模式
    存储模式决定了没有明确指定存储类型的变量,函数参数等的缺省存储区域,共三种:
    1. 1. Small模式
    所有缺省变量参数均装入内部RAM,优点是访问速度快,缺点是空间有限,只适用于小程序。
    2. 2. Compact模式
    所有缺省变量均位于外部RAM区的一页(256Bytes),具体哪一页可由P2口指定,在STARTUP.A51文件中说明,也可用pdata指定,优点是空间较Small为宽裕速度较Small慢,较large要快,是一种中间状态。
    3. 3. large模式
    所有缺省变量可放在多达64KB的外部RAM区,优点是空间大,可存变量多,缺点是速度较慢。
    提示:存储模式在C51编译器选项中选择。
    4. 第四节 存储类型声明
    变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字直接声明指定。各类型分别用:code,data,idata,xdata,pdata说明,例:
    data uar1
    char code array[ ]=“hello!”;
    unsigned char xdata arr[10][4][4];
    5. 第五节 变量或数据类型
    C51提供以下几种扩展数据类型:
    bit 位变量值为0或1
    sbit 从字节中定义的位变量 0或1
    sfr sfr字节地址 0~255
    sfr16 sfr字地址 0~65535
    其余数据类型如:char,enum,short,int,long,float等与ANSI C相同。
    6. 第六节 位变量与声明
    1. 1. bit型变量
    bit型变量可用变量类型,函数声明、函数返回值等,存贮于内部RAM20H~2FH。
    注意:
    (1) 用#pragma disable说明函数和用“usign”指定的函数,不能返回bit值。
    (2) 一个bit变量不能声明为指针,如bit *ptr;是错误的
    (3) 不能有bit数组如:bit arr[5];错误。
    2. 2. 可位寻址区说明20H-2FH
    可作如下定义:
    int bdata i;
    char bdata arr[3],
    然后:
    sbit bito=in0;sbit bit15=I^15;
    sbit arr07=arr[0]^7;sbit arr15=arr[i]^7;
    7. 第七节 Keil C51指针
    C51支持一般指针(Generic Pointer)和存储器指针(Memory_Specific Pointer).
    1. 1. 一般指针
    一般指针的声明和使用均与标准C相同,不过同时还可以说明指针的存储类型,例如:
    long * state;为一个指向long型整数的指针,而state本身则依存储模式存放。
    char * xdata ptr;ptr为一个指向char数据的指针,而ptr本身放于外部RAM区,以上的long,char等指针指向的数据可存放于任何存储器中。
    一般指针本身用3个字节存放,分别为存储器类型,高位偏移,低位偏移量。
    2. 2. 存储器指针
    基于存储器的指针说明时即指定了存贮类型,例如:
    char data * str;str指向data区中char型数据
    int xdata * pow; pow指向外部RAM的int型整数。
    这种指针存放时,只需一个字节或2个字节就够了,因为只需存放偏移量。
    3. 3. 指针转换
    即指针在上两种类型之间转化:
    l 当基于存储器的指针作为一个实参传递给需要一般指针的函数时,指针自动转化。
    l 如果不说明外部函数原形,基于存储器的指针自动转化为一般指针,导致错误,因而请用“#include”说明所有函数原形。
    l 可以强行改变指针类型。
    8. 第八节 Keil C51函数
    C51函数声明对ANSI C作了扩展,具体包括:
    1. 1. 中断函数声明:
    中断声明方法如下:
    void serial_ISR () interrupt 4 [using 1]
    {
    /* ISR */
    }
    为提高代码的容错能力,在没用到的中断入口处生成iret语句,定义没用到的中断。
    /* define not used interrupt, so generate "IRET" in their entrance */
    void extern0_ISR() interrupt 0{} /* not used */
    void timer0_ISR () interrupt 1{} /* not used */
    void extern1_ISR() interrupt 2{} /* not used */
    void timer1_ISR () interrupt 3{} /* not used */
    void serial_ISR () interrupt 4{} /* not used */
    2. 2. 通用存储工作区
    3. 3. 选通用存储工作区由using x声明,见上例。
    4. 4. 指定存储模式
    由small compact 及large说明,例如:
    void fun1(void) small { }
    提示:small说明的函数内部变量全部使用内部RAM。关键的经常性的耗时的地方可以这样声明,以提高运行速度。
    5. 5. #pragma disable
    在函数前声明,只对一个函数有效。该函数调用过程中将不可被中断。
    6. 6. 递归或可重入函数指定
    在主程序和中断中都可调用的函数,容易产生问题。因为51和PC不同,PC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而51一般使用寄存器传递参数,内部变量一般在RAM中,函数重入时会破坏上次调用的数据。可以用以下两种方法解决函数重入:
    a、在相应的函数前使用前述“#pragma disable”声明,即只允许主程序或中断之一调用该函数;
    b、将该函数说明为可重入的。如下:
    void func(param...) reentrant;
    KeilC51编译后将生成一个可重入变量堆栈,然后就可以模拟通过堆栈传递变量的方法。
    由于一般可重入函数由主程序和中断调用,所以通常中断使用与主程序不同的R寄存器组。
    另外,对可重入函数,在相应的函数前面加上开关“#pragma noaregs”,以禁止编译器使用绝对寄存器寻址,可生成不依赖于寄存器组的代码。
    7. 7. 指定PL/M-51函数
    由alien指定。
    4. 第四章 Keil C51高级编程
    本章讨论以下内容:
    l 绝对地址访问
    l C与汇编的接口
    l C51软件包中的通用文件
    l 段名转换与程序优化
    1. 第一节 绝对地址访问
    C51提供了三种访问绝对地址的方法:
    1. 1. 绝对宏:
    在程序中,用“#include<absacc.h>”即可使用其中定义的宏来访问绝对地址,包括:
    CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD
    具体使用可看一看absacc.h便知
    例如:
    rval=CBYTE[0x0002];指向程序存贮器的0002h地址
    rval=XWORD [0x0002];指向外RAM的0004h地址
    2. 2. _at_关键字
    直接在数据定义后加上_at_ const即可,但是注意:
    (1)绝对变量不能被初使化;
    (2)bit型函数及变量不能用_at_指定。
    例如:
    idata struct link list _at_ 0x40;指定list结构从40h开始。
    xdata char text[25b] _at_0xE000;指定text数组从0E000H开始
    提示:如果外部绝对变量是I/O端口等可自行变化数据,需要使用volatile关键字进行描述,请参考absacc.h。
    3. 3. 连接定位控制
    此法是利用连接控制指令code xdata pdata \data bdata对“段”地址进行,如要指定某具体变量地址,则很有局限性,不作详细讨论。
    2. 第二节 Keil C51与汇编的接口
    1. 1. 模块内接口
    方法是用#pragma语句具体结构是:
    #pragma asm
    汇编行
    #pragma endasm
    这种方法实质是通过asm与ndasm告诉C51编译器中间行不用编译为汇编行,因而在编译控制指令中有SRC以控制将这些不用编译的行存入其中。
    2. 2. 模块间接口
    C模块与汇编模块的接口较简单,分别用C51与A51对源文件进行编译,然后用L51将obj文件连接即可,关键问题在于C函数与汇编函数之间的参数传递问题,C51中有两种参数传递方法。
    (1) 通过寄存器传递函数参数
    最多只能有3个参数通过寄存器传递,规律如下表:
    参数数目 char int long,float 一般指针 123 R7R5R3 R6 & R7R4 & R5R2 & R3 R4~R7R4~R7 R1~R3R1~R3R1~R3
    (2) 通过固定存储区传递(fixed memory) 这种方法将bit型参数传给一个存储段中:  ?function_name?BIT 将其它类型参数均传给下面的段:?function_name?BYTE,且按照预选顺序存放。 至于这个固定存储区本身在何处,则由存储模式默认。 (3) 函数的返回值 函数返回值一律放于寄存器中,有如下规律:
    return type Registev 说明 bit 标志位 由具体标志位返回 char/unsigned char 1_byte指针 R7 单字节由R7返回 int/unsigned int 2_byte指针 R6 & R7 双字节由R6和R7返回,MSB在R6 long&unsigned long R4~R7 MSB在R4, LSB在R7 float R4~R7 32Bit IEEE格式 一般指针 R1~R3 存储类型在R3 高位R2 低R1 (4) SRC控制 该控制指令将C文件编译生成汇编文件(.SRC),该汇编文件可改名后,生成汇编.ASM文件,再用A51进行编译。 3. 第三节 Keil C51软件包中的通用文件 在C51\LiB目录下有几个C源文件,这几个C源文件有非常重要的作用,对它们稍事修改,就可以用在自己的专用系统中。 1. 1. 动态内存分配 init_mem.C:此文件是初始化动态内存区的程序源代码。它可以指定动态内存的位置及大小,只有使用了init_mem( )才可以调回其它函数,诸如malloc calloc,realloc等。 calloc.c:此文件是给数组分配内存的源代码,它可以指定单位数据类型及该单元数目。 malloc.c:此文件是malloc的源代码,分配一段固定大小的内存。 realloc.c:此文件是realloc.c源代码,其功能是调整当前分配动态内存的大小。 2. 2. C51启动文件STARTUP.A51 启动文件STARTUP.A51中包含目标板启动代码,可在每个project中加入这个文件,只要复位,则该文件立即执行,其功能包括: l 定义内部RAM大小、外部RAM大小、可重入堆栈位置 l 清除内部、外部或者以此页为单元的外部存储器 l 按存储模式初使化重入堆栈及堆栈指针 l 初始化8051硬件堆栈指针 l 向main( )函数交权 开发人员可修改以下数据从而对系统初始化 常数名 意义 IDATALEN 待清内部RAM长度 XDATA START 指定待清外部RAM起始地址 XDATALEN 待清外部RAM长度 IBPSTACK 是否小模式重入堆栈指针需初始化标志,1为需要。缺省为0 IBPSTACKTOP 指定小模式重入堆栈顶部地址 XBPSTACK 是否大模式重入堆栈指针需初始化标志,缺省为0 XBPSTACKTOP 指定大模式重入堆栈顶部地址 PBPSTACK 是否Compact重入堆栈指针,需初始化标志,缺省为0 PBPSTACKTOP 指定Compact模式重入堆栈顶部地址 PPAGEENABLE P2初始化允许开关 PPAGE 指定P2值 PDATASTART 待清外部RAM页首址 PDATALEN 待清外部RAM页长度 提示:如果要初始化P2作为紧凑模式高端地址,必须:PPAGEENAGLE=1,PPAGE为P2值,例如指定某页1000H-10FFH,则PPAGE=10H,而且连接时必须如下: L51<input modules> PDATA(1080H),其中1080H是1000H-10FFH中的任一个值。 以下是STARTUP.A51代码片断,红色是经常可能需要修改的地方: ;------------------------------------------------------------------------------ ; This file is part of the C51 Compiler package ; Copyright KEIL ELEKTRONIK GmbH 1990 ;------------------------------------------------------------------------------ ; STARTUP.A51: This code is executed after processor reset. ; ; To translate this file use A51 with the following invocation: ; ; A51 STARTUP.A51 ; ; To link the modified STARTUP.OBJ file to your application use the following ; L51 invocation: ; ; L51 <your object file list>, STARTUP.OBJ <controls> ; ;------------------------------------------------------------------------------ ; ; User-defined Power-On Initialization of Memory ; ; With the following EQU statements the initialization of memory ; at processor reset can be defined: ; ; ; the absolute start-address of IDATA memory is always 0 IDATALEN EQU 80H ; the length of IDATA memory in bytes. ; XDATASTART EQU 0H ; the absolute start-address of XDATA memory XDATALEN EQU 0H ; the length of XDATA memory in bytes. ; PDATASTART EQU 0H ; the absolute start-address of PDATA memory PDATALEN EQU 0H ; the length of PDATA memory in bytes. ; ; Notes: The IDATA space overlaps physically the DATA and BIT areas of the ; 8051 CPU. At minimum the memory space occupied from the C51 ; run-time routines must be set to zero. ;------------------------------------------------------------------------------ ; ; Reentrant Stack Initilization ; ; The following EQU statements define the stack pointer for reentrant ; functions and initialized it: ; ; Stack Space for reentrant functions in the SMALL model. IBPSTACK EQU 0 ; set to 1 if small reentrant is used. IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1. ; ; Stack Space for reentrant functions in the LARGE model. XBPSTACK EQU 0 ; set to 1 if large reentrant is used. XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ; ; Stack Space for reentrant functions in the COMPACT model. PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ; ;------------------------------------------------------------------------------ ; ; Page Definition for Using the Compact Model with 64 KByte xdata RAM ; ; The following EQU statements define the xdata page used for pdata ; variables. The EQU PPAGE must conform with the PPAGE control used ; in the linker invocation. ; PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. PPAGE EQU 0 ; define PPAGE number. ; ;------------------------------------------------------------------------------ 3. 3. 标准输入输出文件 putchar.c putchar.c是一个低级字符输出子程,开发人员可修改后应用到自己的硬件系统上,例如向CLD或LEN输出字符。 缺省:putchar.c是向串口输出一个字符XON|XOFF是流控标志,换行符“\*n”自动转化为回车/换行“\r\n”。 getkey.c getkey函数是一个低级字符输入子程,该程序可用到自己硬件系统,如矩阵键盘输入中,缺省时通过串口输入字符。 4. 4. 其它文件 还包括对Watch-Dog有独特功能的INIT.A51函数以及对8×C751适用的函数,可参考源代码。 4. 第四节 段名协定与程序优化 1. 1. 段名协定(Segment Naming Conventions) C51编译器生成的目标文件存放于许多段中,这些段是代码空间或数据空间的一些单元,一个段可以是可重定位的,也可以是绝对段,每一个可重定位的段都有一个类型和名字,C51段名有以下规定: 每个段名包括前缀与模块名两部分,前缀表示存储类型,模块名则是被编译的模块的名字,例如: ?CO?main1 :表示main1模块中的代码段中的常数部分 ?PR?function1?module 表module模块中函数function1的可执行段,具体规定参阅手册。 2. 2. 程序优化 C51编译器是一个具有优化功能的编译器,它共提供六级优化功能。确保生成目标代码的最高效率(代码最少,运行速度最快)。具体六级优化的内容可参考帮助。 在C51中提供以下编译控制指令控制代码优化: OPTIMIZE(SJXE):尽量采用子程序,使程序代码减少。 NOAREGS:不使用绝对寄存器访问,程序代码与寄存器段独立。 NOREGPARMS:参数传递总是在局部数据段实现,程序代码与低版本C51兼容。 OPTIMIZE(SIZE)AK OPTIMIZE(speed)提供6级优化功能,缺省为: OPTIMIZE(6,SPEED)。 5. 第五章 Keil C51库函数参考 C51强大功能及其高效率的重要体现之一在于其丰富的可直接调用的库函数,多使用库函数使程序代码简单,结构清晰,易于调试和维护,下面介绍C51的库函数系统。 1. 第一节 本征库函数(intrinsic routines)和非本征证库函数 C51提供的本征函数是指编译时直接将固定的代码插入当前行,而不是用ACALL和LCALL语句来实现,这样就大大提供了函数访问的效率,而非本征函数则必须由ACALL及LCALL调用。 C51的本征库函数只有9个,数目虽少,但都非常有用,列如下: _crol_,_cror_:将char型变量循环向左(右)移动指定位数后返回 _iror_,_irol_:将int型变量循环向左(右)移动指定位数后返回 _lrol_,_lror_:将long型变量循环向左(右)移动指定位数后返回 _nop_: 相当于插入NOP _testbit_: 相当于JBC bitvar测试该位变量并跳转同时清除。 _chkfloat_: 测试并返回源点数状态。 使用时,必须包含#inclucle <intrins.h>一行。 如不说明,下面谈到的库函数均指非本征库函数。
    展开全文
  • 单片机原理及接口技术(C51编程)课件,直接从人民邮电出版社教学服务与资源网下载下来的。文件虽小,却比较难下载的哟~
  • 此压缩包内包含完整C51单片机联系实例,包含各大模块,是学习单片机不可或缺的练习实例,好不好,谁用谁知道~
  • 好久没碰单片机了,有些生疏了。到现在还依稀记得自己独自坐在电脑屏幕前自学单片机,硬生生的去看了单片机的基础,现在想想还是挺有趣的。附上当初一点初学的单片机小程序,请大家勿怪! 用P1口作为输出口,接8...
  • C51程序设计

    2020-07-23 23:32:34
    C51应用程序设计规范,详细介绍C51编程中用到的语言设计及程序设计思路。
  • 单片机C51编程几个有用的模块(1)   Keil C51常用功能模块使用说明 说明 本文档包括单片机系统中常用到的时钟中断、通讯及键盘扫描等模块(见所附源程序)的说明。这些模块使用前后台系统模型。...
  • 本文主要对单片机 C51 编程时需要注意的问题进行了总结,希望对你的学习有所帮助。
  • 单片机C51编程几个有用的模块 作者:佚名 更新时间:2005-04-12 Keil C51常用功能模块使用说明 2001/11/19 说明 本文档包括单片机系统中常用到的时钟中断、通讯及键盘扫描等模块(见所附源程序)的说明。这些模块使用...
  • 本规范主要针对C51编程语言和keil编译器而言,包括排版、注释、命名、变量使用、代码可测性、程序效率、质量保证等内容。 3.总则  a.格式清晰  b.注释简明扼要  c.命名规范易懂  d.
  • 嵌入式C51编程规范

    2014-09-08 10:01:30
    单片机编写C51代码,程序的可行性当然是必须保证的。但是包括笔者在内的很多新手,都忽略了程序的另一面——可读性、可维护性以及可扩展性。只要稍微有些嵌入式开发经验的读者,若看到笔者在“Zigbee之旅”系列...
  • 单片机手机版程序

    2020-07-28 23:31:16
    在手机就可以学习单片机的程序开发,各种单片机学习基础程序,让你更加灵活的学习单片机
  • 单片机原理及接口技术 C51编程
  • 单片机原理及接口技术 C51编程 第2版__张毅刚_北京:人民邮电出版社_P385_2016.06_14103695
  • 51系统单片机基本结构 8051系列单片机的内部结构是各种逻辑单元及其之间的互连构成的。其主要由中央处理器cpu,程序存储器ROM、数据存储器Ram、串行接口、并行I/O接口,定时/计数器、中断系统等几大单元,以及数据...
  • 单片机内部资源的C51编程ppt,本文主要讲了单片机内部资源的C51编程
  • 一.常量与变量1.常量又称为标量,在程序执行过程中不能改变,常量数据类型...C51对变量的定义格式:【存储种类】数据类型【存储器类型】变量名C51中的四中存储种类:auto(自动)、extern(外部)、static(静态)...
  • 单片机原理及接口技术(C51编程)张毅刚,全版教案。
1 2 3 4 5 ... 20
收藏数 3,767
精华内容 1,506
关键字:

单片机c51编程