第4-5章 C51程序设计.ppt_第1页
第4-5章 C51程序设计.ppt_第2页
第4-5章 C51程序设计.ppt_第3页
第4-5章 C51程序设计.ppt_第4页
第4-5章 C51程序设计.ppt_第5页
已阅读5页,还剩161页未读 继续免费阅读

下载本文档

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

文档简介

实验5 1 5 2 6 1 6 2 7 1 7 2 7 3 8 1 1 2 第4章KeilC51简介及软件设计 4 1Keil开发工具简介4 1 2启动程序安装好了Keil软件以后 我们打开它 打开以后的界面如图所示 3 运行Keil软件后的界面 4 4 1 3工作环境介绍 演示并介绍主要的界面及菜单 5 4 2单片机软件开发流程创建工程项目 设置项目文件 编译与连接软件 调试软件 6 4 2 1建立工程 建立工程界面 7 选择工程文件要存放的路径 输入工程文件名hello 然后单击保存 选择确定路径和工程名 8 在弹出的对话框中选择CPU厂商及型号 选择好Atmel公司的AT89C51后 单击确定 选择CPU厂商及型号 9 4 2 2建立源代码文件 新建一个C51文件 单击左上角的NewFile 在编辑框内输入程序 10 保存新建的文件 单击SAVE 保存文件 注意后缀名必须为 c 11 保存好后把此文件加入到工程中 方法如下 用鼠标在SourceGroup1上单击右键 然后再单击AddFilestoGroup SourceGroup1 如图所示 选择要加入的文件 找到hello c后 单击Add 然后单击Close 12 将文件添加到工程 4 2 3工程设置 13 14 4 2 3工程设置 15 16 17 命令窗口 18 反汇编窗口 19 符号窗口 20 寄存器窗口 21 调用堆栈窗口 22 观察窗口 23 存储器窗口 24 串行输出窗口 25 分析窗口 26 跟踪窗口 27 外围器件窗口 28 第五章C51程序设计基础 5 1C程序设计的基本概念C语言是一种通用的计算机程序设计语言 在国际上十分流行 它既可用来编写计算机的系统程序 也可用来编写一般的应用程序 C5l是在5l系列单片机上使用的C语言 C51程序结构与一般C语言没有什么差别 一个C5l程序大体上是一个函数定义的集合 在这个集合中仅有一个名为main的函数 主函数 主函数是程序的入口 主函数中的所有语句执行完毕 则程序执行结束 C51中使用的编译器主要是KeilC5l KeilC51完全支持C的标准指令 并有很多用来优化805l指令结构的C的扩展指令 29 5 1 1主函数 C51的程序结构 预处理命令 include函数说明longfunl floatfun2 功能函数1fun1 函数体 主函数main 函数体 功能函数2fun2 函数体 30 5 1 2标识符和关键字 包括c语言ANSIC标准规定的32个关键字 Auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile whileC51编译器除了支持ANSIC标准的关键字以外 还扩展了13个关键字 31 C51扩展的关键字 33 5 1 3数据的基本类型 Keil编译器除了少数一些关键地方外 基本类似于ANSIC 差异主要是Keil可以让用户针对8051的结构进行程序设计 其它差异主要是8051的一些局限引起的 KeilC有ANSIC的所有标准数据类型 除此之外 为了更加有利地利用805l的结构还加入了一些特殊的数据类型 除了标准数据类型外 C5l编译器还支持一种位数据类型 一个位变量存在于内部RAM的可位寻址区中 可像操作其它变量那样对位变量进行操作 位数组和位指针是违法的 34 表5 3C51支持的数据类型 35 C5l语言的扩展 C5l是一个兼容ANSI的编译器 为了支持8O5l系列MCU 加入了一些扩展的内容 C5l编译器的扩展内容包括 数据类型存储器类型指针重入函数中断服务程序实时操作系统 36 特殊功能寄存器的数据类型 bit 定义一个位变量或函数 地址自动分配 sbit 定义可位寻址RAM和特殊功能寄存器的位变量 类似给地址一个别名 例如可 bitksw但不能sbitksw而只能是sbitksw 0 x90 地址 bitksw 0 或1 等号后是数值sbitksw 0 x90等号后是地址sfr 定义8位特殊功能寄存器如 sfrP1 0 x90 sfrl6 定义16位特殊功能寄存器 如DPTR 通过名字或地址来引用特殊功能寄存器 sbit sfr和sfrl6为805l硬件和C5l编译器特有 它们不是ANSIC的一部分 也不能用指针对它们进行存取 37 例 sfrP0 0 x80 声明一个变量P0 并把它和位于0 x80 805l的端口0 处的特殊功能寄存器联系在一起 sfrSCON 0 x98 定义SCON sbitSM0 0 x9F 定义SCON的一位 38 5 1 4常量和变量 1 常量常量不变的量 如固定的数据表 字库等 常量区分为不同的数据类型 说明如下 1 整型常量能表示为十进制如123 0 89等 十六进制则以0 x开头如0 x34 0 x3B等 长整型就在数字后面加字母L 如104L 034L等 2 浮点型常量可分为十进制和指数表示形式 十进制由数字和小数点组成 如 0 0 25 5 789 0 13 5 0 300 267 8230等均为合法的实数 整数或小数部分为0 能省略但必须有小数点 指数形式由十进制数加阶码标志 e 或 E 以及阶码 只能为整数 可以带符号 组成 其一般形式为aEn a为十进制数 n为十进制整数 其值为a 10n 如 2 1E5 等于2 1 105 39 5 1 1常量和变量 3 字符型常量是单引号内的字符 如 a d 等 不能显示的控制字符 能在该字符前面加一个反斜杠 组成专用转义字符 常用转义字符的含义见表5 4 4 字符串型常量由双引号内的字符组成 如 test OK 等 当引号内没有字符时 为空字符串 在使用特殊字符时同样要使用转义字符如双引号 在C语言中字符串常量是做为字符类型数组来处理的 在存储字符串时系统会在字符串尾部加上 o 转义字符以作为该字符串的结束符 字符串常量 A 和字符常量 A 是不一样的 前者在存储时多占用一个字节的空间 5 位标量 它的值是一个二进制 5 1 1常量和变量 2 变量变量名a变量值存储单元在程序中使用变量必须先用标识符作为变量名 并指出所用的数据类型和存储器类型 这样编译系统才能为变量分配相应的存储空间 40 25 5 1 1常量和变量 2 变量变量名a变量值存储单元在程序中使用变量必须先用标识符作为变量名 并指出所用的数据类型和存储器类型 这样编译系统才能为变量分配相应的存储空间 41 25 42 5 1 1常量和变量 定义一个变量的格式如下 存储种类 数据类型 存储器类型 变量名表在定义格式中除了数据类型和变量名表是必要的 其它都是可选项 存储种类有四种 自动 auto 外部 extern 静态 static 和寄存器 register 缺省类型为自动 auto 存储器类型的说明就是指定该变量在单片机c语言硬件系统中所使用的存储区域 并在编译时准确的定位 43 1 变量的存储器类型 C51编译器完全支持51系列单片机的硬件结构 可以访问其硬件系统的所有部分 对于每个变量可以准确地赋予其存储器类型 从而可使之能够在单片机系统内准确地定位 表5 5中是C51所能识别的存储器类型 44 45 DATA存储类型 存储区 直接访问的内部数据存储器 RAM的低128个字节 可在一个周期内直接寻址 00H 7FH 访问速度最快 所以应该把使用频率高的变量放在DATA区 由于空间有限 必须注意使用 DATA区除了包含程序变量外 还包含了堆栈和寄存器组 例 unsignedchardatasystem status 0 unsignedintdataunit id 2 chardatainp string 16 floatdataoutp Value 46 BDATA存储类型 存储区 可位寻址的内部数据存储器 DATA区的16个字节的可位寻址区 20H 2FH 该区间可以用字节方式也可以用位方式进行访问 在位寻址区定义变量 这个变量就可进行位寻址 并且声明位变量 这对状态寄存器来说十分有用 编译器不允许在BDATA段中定义float和double类型的变量 例 unsignedcharbdatastatus byte unsignedintbdatastatus word unsignedlongbdatastatus dword 47 IDATA存储类型 存储区 间接访问的内部数据存储器 可以访问所有的内部存储器空间 256个字节 必须采用间接寻址 IDATA段也可存放使用比较频繁的变量 使用寄存器作为指针进行寻址 在寄存器中设置8位地址 进行间接寻址 和外部存储器寻址比较 它的指令执行周期和代码长度都比较短 例 unsignedcharidatasystem status 0 unsignedintidataunit id 2 charidatainp string 16 floatidataoutp value 48 PDATA和XDATA存储类型 PDATA存储区 分页的外部数据存储器 256个字节 00H FFH 使用指令MOVXA Ri访问 XDATA存储区 外部数据存储器 64K个字节 地址0000H FFFFH 使用指令MOVXA DPTR访问 对PDATA和XDATA的操作相似 对PDATA段寻址比对XDATA段寻址要快 因为对PDATA段寻址 只需要装入8位地址 而对XDATA段寻址 需装入16位地址 所以尽量把外部数据存储在PDATA段中 unsignedcharpdatainp regl unsignedcharxdatainp reg2 voidmain Void inp regl Pl inp reg2 P3 49 CODE存储类型 存储区 程序空间 程序存储区的64K个字节 0000H FFFFH 通过MOVCA A DPTR访问 代码段的数据是不可改变的 8051的代码段不可重写 一般代码段中可存放数据表 跳转向量和状态表 对CODE段的访问和对XDATA段的访问的时间是一样的 代码段中的对象在编译的时候初始化 否则得不到想要的值 例 unsignedcharcode str Howareyou 50 存储模式 存储模式决定了默认的存储器类型 此存储器类型将应用于函数参数 局部变量和定义时未包含存储器类型的变量 定义变量时 使用存储器类型显式定义将屏蔽默认存储器类型 存储模式有三种 小模式 SMAL 变量默认在8051的内部数据存储器中 data 此模式下变量访问快速 然而 所有数据对象 包括堆栈都必须放在内部RAM中 堆栈空间面临溢出 紧凑模式 COMPACT 变量默认在8051的外部RAM的一页中 pdata 此模式最多只能提供256字节的变量 大模式 LARGE 变量默认在外部存储器中 xdata 51 2 sfr sfr16变量的定义 51系列单片机具有多种内部寄存器 其中有一些是特殊功能寄存器 为了能够直接访问这些特殊功能寄存器 C51编译器扩充了关键字sfr和sfr16 利用这种扩充关键字可以在C语言源程序中直接对51单片机的特殊寄存器进行定义 定义方法如下 sfr特殊功能寄存器名 特殊功能寄存器地址常数 sfr16特殊功能寄存器名 特殊功能寄存器地址常数 例如 P1口的地址是90H 我们能这样定义AT89S52的P1口 sfrP1 0 x90 定义P1口地址为90H 52 2 sfr sfr16变量的定义 sfr关键字后面是一个要定义的名字 可任意选取 但要符合标识符的命名规则 名字最好有一定的含义如P1口能用P1为名 这样程序会变的好读好多 等号后面必须是常数 不允许有带运算符的表达式 而且该常数必须在特殊功能寄存器的地址范围之内 80H FFH sfr是定义8位的特殊功能寄存器 而sfr16则是用来定义16位特殊功能寄存器 如AT89S52的T2定时器 能定义为 sfr16T2 0 xCC 指定Timer2口地址T2L 0 xCC T2H 0 xCD 用sfr16定义16位特殊功能寄存器时 等号后面是它的低位地址 高位地址一定要位于物理低位地址之上 注意的是不能用于定时器0和1 53 3 sbit变量的定义 我们能照以下的方法去定义 1 sbit位变量名 位地址 这种方法将位的绝对地址赋给位变量 位地址必须位于80H 0FFH之间 例如 sbitEA 0 x91 指定0 x91位是EA 即中断允许 54 2 Sbit位变量名 特殊功能寄存器名 位位置 当可寻址位位于特殊功能寄存器中时可采用这种方法 位位置 是一个0 7之间的常数 先定义一个特殊功能寄存器名 再指定位变量名所在的位置 例如 sfrP1 0 x90 指定P1口地址为0 x90sbitP1 1 P1 1 指定P1 1为P1口的第2个引脚 3 sbit位变量名 字节地址 位位置 55 5 1 5运算符与表达式 1 赋值运算符对于 这个符号我们不会陌生 在C中它的功能是给变量赋值 称之为赋值运算符 它的作用就是但数据赋给变量 例如 a 6 是把6赋值给a 由此可见利用赋值运算符将一个变量与一个表达式连接起来的式子为赋值表达式 在表达式后面加 便构成了赋值语句 使用 的赋值语句格式如下 变量 表达式 56 5 1 5运算符与表达式 例如 a 0 xFF 将常数十六进制数FF赋于变量a b c 33 同时赋值给变量b c d e 将变量e的值赋于变量d f a b 将变量a b的值赋于变量f 57 5 1 5运算符与表达式 2 算术运算符单片机c语言中的算术运算符有如下几个 其中只有取正值和取负值运算符是单目运算符 其它则都是双目运算符 加或取正值运算符 减或取负值运算符 乘运算符 除运算符 求余运算符 58 5 1 5运算符与表达式 算术表达式的形式 表达式1算术运算符表达式2例如 a b 10 a x 9 y a 除法运算符和一般的算术运算规则有所不一样 如是两浮点数相除 其结果为浮点数 如10 0 20 0所得值为0 5 而两个整数相除时 所得值就是整数 例如7 3 值为2 C的运算符有优先级和结合性 同样可用用括号 来改变优先级 59 5 1 5运算符与表达式 3 增减量运算符这两个运算符是C语言中特有的一种运算符 在VB PASCAL等都是没有的 作用就是对运算对象作加1和减1运算 增量运算符 减量运算符要注意的是运算对象在符号前或后 其含义都是不一样的 虽然同是加1或减1 如a 或a 是先使用a的值 再执行a 1 或a 1 a 或 a 是先执行a 1 或a 1 再使用a的值 增减量运算符只允许用于变量的运算中 不能用于常数或表达式 60 5 1 5运算符与表达式 4 关系运算符单片机C语言中有六种关系运算符 大于 小于 大于等于 小于等于 等于 不等于前四个有相同的优先级 后两个有相同的优先级 前四个的优先级高于后2个的 当两个表达式用关系运算符连接起来时 就是关系表达式 关系表达式通常是用来判别某个条件是否满足 形式如下 表达式1关系运算符表达式2例如 I J I J I 4 J 3 J I J 61 5 1 5运算符与表达式 5 逻辑运算符关系运算符所能反映的是两个表达式之间的大小等于关系 那逻辑运算符则是用于求条件式的逻辑值 用逻辑运算符将关系表达式或逻辑量连接起来就是逻辑表达式了 逻辑表达式的一般形式为 逻辑与 条件式1 条件式2逻辑或 条件式1 条件式2逻辑非 条件式2 62 5 1 5运算符与表达式 逻辑与 是指当条件式1和条件式2都为真时结果为真 非0值 不然为假 0值 也就是说运算会先对条件式1进行判断 如果为真 非0值 则继续对条件式2进行判断 当结果为真时 逻辑运算的结果为真 值为1 如果结果不为真时 逻辑运算的结果为假 0值 如果在判断条件式1时就不为真 就不用再判断条件式2了 而直接给出运算结果为假 逻辑或 是指只要二个运算条件中有一个为真时 运算结果就为真 只有当条件式都不为真时 逻辑运算结果才为假 逻辑非则是把逻辑运算结果值取反 也就是说如果两个条件式的运算值为真 进行逻辑非运算后则结果变为假 条件式运算值为假时最后逻辑结果为真 63 5 1 5运算符与表达式 6 位运算符位运算符的作用是按位对变量进行运算 但是并不改变参与运算的变量的值 如果要求按位改变变量的值 则要利用相应的赋值运算 还有就是位运算符是不能用来对浮点型数据进行操作的 单片机c语言中共有6种位运算符 表5 6是它们的逻辑真值表 位运算一般的表达形式如下 变量1位运算符变量2位运算符也有优先级 从高到低依次是 按位取反 右移 按位与 按位异或 按位或 表5 6是位逻辑运算符的真值表 X表示变量1 Y表示变量2 64 5 1 5运算符与表达式 7 复合赋值运算符复合赋值运算符就是在赋值运算符 的前面加上其他运算符 表5 7是C语言中的复合赋值运算符 复合运算的一般形式为 变量复合赋值运算符表达式其含义就是变量与表达式先进行运算符所要求的运算 再把运算结果赋值给参与运算的变量 其实这是C语言中一种简化程序的一种方法 凡是二目运算都能用复合赋值运算符去简化表达 c 56 a等价c c 56 a 65 5 运算符与表达式 8 逗号运算符在C语言中逗号还是一种特殊的运算符 也就是逗号运算符 能用它将两个或多个表达式连接起来 形成逗号表达式 逗号表达式的一般形式为 表达式1 表达式2 表达式3 表达式n 66 5 1 5运算符与表达式 这样用逗号运算符组成的表达式在程序运行时 是从左到右依次计算出各个表达式的值 而整个用逗号运算符组成的表达式的值等于最右边表达式的值 就是 表达式n 的值 大部分情况下 使用逗号表达式的目的只是为了分别得到名个表达式的值 而并不一定要得到和使用整个逗号表达式的值 还需要注意的是 并不是在程序的任何位置出现的逗号 都能认为是逗号运算符 如函数中的参数 同类型变量的定义中的逗号只是用来间隔之用而不是逗号运算符 67 5 1 5运算符与表达式 9 条件运算符C语言中有一个三目运算符 它就是 条件运算符 它要求有三个运算对象 它能把三个表达式连接构成一个条件表达式 条件表达式的一般形式如下 逻辑表达式 表达式1 表达式2条件运算符的作用简单来说就是根据逻辑表达式的值选择使用表达式的值 当逻辑表达式的值为真时 非0值 时 整个表达式的值为表达式1的值 当逻辑表达式的值为假 值为0 时 整个表达式的值为表达式2的值 68 5 1 5运算符与表达式 10 指针和地址运算符指针是单片机C语言中一个十分重要的概念 也是学习单片机C语言中的一个难点 对于指针将会在后面的内容作详细的讲解 在这里我们先来了解一下单片机C语言中供给的两个专门用于指针和地址的运算符 取内容 取地址 69 5 1 5运算符与表达式 取内容和地址的一般形式分别为 取内容 变量 指针变量取地址 指针变量 目标变量取内容运算是将指针变量所指向的目标变量的值赋给左边的变量 取地址运算是将目标变量的地址赋给左边的变量 要注意的是 指针变量中只能存放地址 也就是指针型数据 一般情况下不要将非指针类型的数据赋值给一个指针变量 70 5 1 6函数 1 函数的调用函数的调用指的是在一个函数体中引用另外一个已经定义了的函数 前者称为主调用函数 后者称为被调用函数 函数调用的一般形式为 函数名 实际参数表列 其中 函数名指出被调用的函数 实际参数表能为零或多个参数 多个参数时要用逗号隔开 每个参数的类型 位置应与函数定义时所的形式参数一一对应 它的作用就是把参数传到被调用函数中的形式参数 如果类型不对应就会产生一些错误 调用的函数是无参函数时不写参数 但不能省后面的括号 71 5 1 6函数 函数调用的方式可以有以下三种 1 函数语句 在主调用函数中直接将被调用函数作为一个语句 如 printf HelloWorld n 调用printf函数输出HelloWorld 这种调用方式不要求函数向主调用函数返回值 只要求完成一定的操作 72 5 1 6函数 2 函数表达式 这种调用方式是将被调用函数作为一个表达式 这种表达式称为函数表达式 如 c sin x cos y 计算表达式c sin x cos y 通常情况下这种函数调用方式要求带回一个值 即在函数中有return语句 73 5 1 6函数 3 函数参数 这种函数调用方式是指将被调用函数作为另一个函数的实际参数 如 m exp sin 3 计算esin 3 实质上 函数调用作为函数的参数 还是函数作为表达式形式调用的一种 因为函数的参数本来就是表达式 这种调用方式也属于嵌套函数调用方式 74 5 1 6函数 2 被调用函数的说明与使用变量一样 在调用一个函数之前 包括标准库函数 必须对该函数的类型进行说明 即 先说明 后调用 如果调用的是标准库函数 一般应在程序的开始处用预处理命令 include命令将有关函数说明的头文件包含进来 如 include 75 5 1 6函数 at89x51 h定义了单片机的片内资源 如果不使用这个包含命令 就不能正确地控制单片机的片内资源 如果调用的是用户自定义的函数 而且该函数与调用它的主调用函数在同一个文件中 一般应在主调用函数中对被调用函数的类型进行说明 函数说明的一般形式为 类型标识符函数的名称 形式参数表 其中 类型标识符 说明了函数返回值的类型 形式参数表 说明了各个形式参数的类型 例如intdelay inta 声明了一个名为delay的函数 其返回值类型为int 形式参数为整型变量a 如果被调用函数是在主调用函数前面定义的 或者在程序文件的开始处说明了所有被调用函数的类型 此时在主调用函数中可以不必再对被调用函数进行说明 76 5 1 6函数 3 函数的返回值在调用函数过程中 经常希望得到一个从被调用函数中带回来的值 这就是函数的返回值 函数返回值是通过return语句得到的 如果函数有返回值 则这个值必定属于一个确定的数据类型 这个类型是在函数定义的头部说明的 如果函数类型和return语句中表达式的值类型不一致 则以函数类型为准 对于数值型数据 可以自动进行类型转换 return语句每次只能带回一个值 尽管在函数中可能有多个return语句 但只有其中的一个return语句会执行 也即只能带回一个值 77 5 1 6函数 4 参数的传递在函数调用过程中 必须用主调用函数的实际参数来替换被调用函数中的形式参数 则就是参数传递 在C51程序中 一般来说 参数传递有以下两种方式 1 数值传递 就是指在函数调用时 直接将实际参数值拷贝给形式参数在内存中的临时存储单元 在这种方式下 被调用函数在执行过程中改变了形式参数的值 是不会改变实际参数的值 因为形式参数和实际参数的地址是互不相同的 所以 这种传递又称为 单向值传递 在这种方式中 实际参数可以是变量 常量 也可以是表达式 78 5 1 6函数 2 地址传递 就是指在一个函数调用另一个函数时 并不是将主调函数中的实际参数值直接传送给被调用函数中的形式参数 而只是将存放实际参数的地址传送给形式参数 在这种方式下 形式参数与实际参数指向同一块存储单元 因此 如果在被调用函数中改变了形式参数指向存储单元的值 实际上也就改变了主调用函数中实际参数所指向存储单元的值 当被调用函数执行完成 形式参数空间释放 丢失的是形式参数中存放的地址 但是形式参数所指向的存储单元并不释放 因此 对形式参数的操作就保留下来 到主调用函数中 又可以通过实际参数中的地址访问经被调用函数修改后的存储单元的值 所以这种传递方式称为 地址传递 在这种方式中 实参可以是变量的地址或指针变量 79 5 1 6函数 5 重入函数重入函数 又叫再入函数 是一种可以在函数体内间接调用其自身的函数 这是C51语言相比标准C语言的一种扩展 重入函数声明的关键字为reentrant 重入函数声明的格式为 函数类型函数名 形式参数表 reentrant 80 5 1 6函数 在标准C语言中 调用函数时会将函数的参数和函数中使用的局部变量入栈 为了提高效率 C51没有提供这种堆栈方式 而是提供一种压缩栈的方式 即为每个函数设定一个空间用于存放局部变量 因此 通常情况下 C51函数不能在调用一个函数的过程中又间接或直接地调用该函数本身 即C51函数不能递归调用 而重入函数则可以被递归调用 以满足某些应用场合的需求 这主要是因为重入函数与一般函数的参数传递和局部变量的存储分配方法有所不同 C51编译器为重入函数生成一个模拟栈 通过这个模拟栈来完成参数传递和存放局部变量 81 5 1 6函数 在使用重入函数的过程中应注意 1 重入函数不能传送bit类型的参数 也不能定义一个局部位变量 重入函数不能包括位操作以及单片机的位可寻址区 2 与PL M51兼容的函数不能具有reentrant属性 也不能调用重入函数 3 在编译时 SMALL模式下的模拟堆栈区位于IDATA区 COMPACT模式下模拟堆栈区位于PDATA区 LARGE模式下模拟堆栈区位于XDATA区 82 5 1 6函数 4 在同一个程序中可以定义和使用不同存储器模式的重入函数 任意模式的重入函数不能调用不同模式的重入函数 但可任意调用非重入函数 5 在参数传递上 实际参数可以传递给间接调用的重入函数 无重入属性的间接调用函数不能包含调用参数 但是可以使用定义的全局变量来进行参数传递 83 5 1 6函数 6 中断服务函数C51编译器允许使用C语言创建中断服务程序 从而减轻了采用汇编语言编写中断服务程序的烦琐程度 中断服务函数声明的关键字为interrupt 声明的格式为 函数类型函数名 形式参数 interruptn usingn 关键字interrupt后面的n是中断号 n的取值范围为0 31 编译器从8n 3处产生中断向量 具体的中断号n和中断向量取决于8051系列单片机芯片的型号 常用中断源和中断向量如表5 8所示 84 5 1 6函数 85 5 1 6函数 using选项用于指定选用单片机内部4组工作寄存器中的哪个组 using后面的n是一个0 3的常整数 分别选中4个不同的工作寄存器组 在定义一个函数时using是可选项 如果不用该选项 编译器会自动选择一个寄存器组作绝对寄存器组访问 一个函数定义为中断服务函数后 编译器自动生成中断向量和程序的入栈及出栈代码 从而提高了工作的效率 86 5 1 6函数 使用中断服务函数时应注意 1 中断函数不能直接调用中断函数 2 不能通过形式参数来进行参数传递 3 在中断函数中调用其它函数 两者所使用的寄存器组应相同 4 中断函数没有返回值 5 关键字interrupt和using的后面都不允许跟带运算符的表达式 87 5 2基本的程序设计结构 5 2 1顺序结构C语言中的语句 1 表达式语句在表达式之后加上分号就构成了表达式语句 这是C的一个特色 2 空语句如果程序某行只有一个分号 作为语句的结束 我们就称之为空语句 注意空语句会导致逻辑上的语句错误 需要谨慎使用3 复合语句用花括号括起来的多个语句便组成了一个复合语句 其形式如下 语句1 语句2 语句n 88 例5 1 已知C 5 0 9 0 f 32 程序实现将华氏温度转换为摄氏温度 include stdio h I O库函数原型说明 include reg52 h 特殊寄存器的头文件 专供8051扩展系列的单片机使用 ifdefMONITOR51 是否需要使用Monitor 51调试charcodereserve 3 at 0 x23 如果是 留下该空间供串口使用 endif 89 main floatC 0 F 78 SCON 0 x52 TMOD 0 x20 TH1 0 xf3 TR1 1 以上3行为printf函数所必须printf 华氏温度为F 2f n F 显示华氏温度C 5 0 9 0 F 32 转换printf 求得的摄氏温度C 2f n C 显示摄氏温度while 1 注意 由于没有操作系统来接收main函数的返回值 所以对一个嵌入式系统来说 main函数永远不会被退出 他必须有一个循环来保证程序不会被终止 90 5 2 2选择结构 C语言的一个基本选择语句是if语句 它的基本结构是 if 表达式 语句 在这种结构中 如果括号中的表达式成立 为 真 则程序执行花括号的语句 否则程序将跳过花括号中的语句部分 执行下面其它语句 C语言提供了三种形式的if语句 1 if形式if 条件表达式 语句 91 5 2 2选择结构 2 if else形式if 表达式 语句1 Else语句2 在这个语句里 将先判断表达式是否成立 若成立 则执行语句1 若不成立则执行语句2 其中else部分也可以省略 即第一种形式 流程图如图所示 92 5 2 2选择结构 3 if elseif形式if else语句也可利用elseif指令串接为多重条件判断 即写成如下格式 if 表达式1 语句1 elseif 表达式2 语句2 elseif 表达式3 语句3 elseif 表达式4 例if elseif语句举例 main intabc charxyz if abc 1 xyz 0 x00 elseif abc 2 xyz 0 x20 elseif abc 3 xyz 0 x60 else xyz 0 xC0 93 94 5 2 2选择结构 2 switch case语句switch 表达式 case 常数1 语句1 break case 常数2 语句2 break default 语句n break 95 值得注意的是 在语句块1 n后有一个break语句 可退出switch case结构 如果语句块1 n后没有break语句 那么语句块1执行完将进入语句块2执行 语句块2执行完将进入语句块3执行 直到执行完语句块n 1后才退出switch case结构 96 5 2 3循环结构 1 while语句while语句的意思可以理解为 当条件为真的时候就执行后面的语句 while语句构成循环的一般形式 while 表达式 循环体语句 例while P1 97 98 5 2 3循环结构 do while语句while是先判断条件是否成立再执行循环体 而do while则是先执行循环体 再执行条件表达式 如果条件为真则循环继续 否则终止 这样就决定了循环体无论在任何条件下都会至少被执行一次 do 循环体语句 while 表达式 99 5 2 3循环结构 4 for语句for语句是最为灵活也是最为复杂的一种 它不仅可以用于循环次数已经确定的情况 而且可以用于循环次数不确定但已经给出循环条件的情况 除了被重复的循环指令体外 表达式模块由三个部分组成 第一部分是初始化表达式 任何表达式在开始执行时都应该做一次初始化 第二部分对是否结束循环进行测试 一旦测试表达式为假 就会结束循环 第三部分是尺度增量 100 for语句一般形式是 for 表达式1 表达式2 表达式3 循环体语句 101 例5 6 编写一程序 试求出0 100的和 main inti sum sum 0 for i 0 i 100 i sum i 运算结果sum值为5050 102 5 break语句和continue语句break语句可以使流程跳出switch结构 继续执行switch语句下面的语句 实际上 break语句还可以用来从循环体内跳出循环体 即提前结束循环 接着执行循环下面的语句 它的一般形式为 break continue语句的作用是结束本次循环 即跳过循环体中下面尚未执行的语句 接着进行下一次循环条件的判定一般形式为 continue 103 continue语句和break语句的区别在于 continue语句只结束本次循环 而不是终止整个循环的执行 而break语句则是结束整个循环过程 不再判断执行循环的条件是否成立 104 5 3C51构造数据类型 5 3 1数组1 数组的定义数组和普通变量一样 要求先定义了才能使用数据类型数组名 常量表达式 数据类型数组名 常量表达式1 常量表达式N 数据类型 是指数组中的各数据单元的类型 数组名 是整个数组的标识 命名方法和变量命名方法是一样的 在编译时系统会根据数组大小和类型为变量分配空间 数组名就是所分配空间的首地址的标识 常量表达式 是表示数组的长度和维数 它必须用 括起 括号里的数不能是变量只能是常量 105 5 3 1数组 例如 unsignedintxcount 10 定义无符号整形数组 有10个数据单元 charinputstring 5 定义字符形数组 有5个数据单元 floatoutnum 10 10 定义浮点型二维数组 有100个数据单元 106 5 3 1数组 2 数组的初始化数组中的值 可以在程序运行期间进行赋值 可以用数组初始化的方法来赋值 即在定义数组的同时 给数组赋初值 这项工作是在程序编译中完成的 赋初值的方式如下 数据类型 存储器类型 数组名 常量表达式 常量表达式 数据类型 存储器类型 数组名 常量表达式1 常量表达式N 常量表达式 常量表达式N 初值个数必须小于或等于数组长度 不指定数组长度则会在编译时由实际的初值个数自动设置 107 5 3 1数组 对全部元素赋初值 例如 unsignedcharLedNum 2 12 35 一维数组赋初值 inta 2 3 1 2 4 2 2 1 二维数组赋初值unsignedcharc 1 5 6 7 3 没有指定数组长度 编译时由实际的初值个数自动设置 108 对部分元素赋初值 例如 unsignedcharLedNum 2 12 一维数组赋初值 intonKey 2 3 1 2 2 二维数组赋初值 如这种情况 只对数组的部分元素赋初值 则数组中的其余元素被缺省地赋值为0 即例1中的LedNum数组中的元素是 12 0 例2中onKey数组的元素为 1 2 0 2 0 0 109 3 字符数组基本类型为字符类型的数组称为字符数组 在字符数组中 一个元素存放一个字符 所以可以用字符数组来存储长度不同的字符串 字符数组的定义与其他数组定义的方法一样 如chara 10 定义a为一个10个字符的一维字符数组 110 字符数组赋初值的最直接的方法是将各字符逐个赋给数组中的各个元素 如 chara 10 B E I J I N G 0 定义了一个字符型数组a 有10个数组元素 并且将9个字符 包括字符串结束标志 0 分别赋给了a 0 a 8 剩余的a 9 被系统自动赋予空格字符 还可以用字符串直接给字符数组赋初值 方法有以下两种形式 chara 10 BEIJING chara 10 BEIJING 111 用双引号 括起来的一串字符 称为字符串常量 比如 Happy C编译器会自动地在字符末尾加上结束符 0 NULL 用单引号 括起来的字符为字符的ASCII码值 而不是字符串 比如 a 表示a的ASCII码值97 而 a 表示一个字符串 由两个字符a和 0组成 一个字符串可以用一维数组来装入 但数组的元素数目一定要比字符多一个 以便C编译器自动在其后面加入结束符 0 112 若干个字符串可以装入一个二维字符数组中 称为字符数组 数组的第一个下标定义字符串的个数 第二个下标定义每个字符串的长度 该长度应当比这批字符串中最长的串多一个字符 用于装入字符串的结束符 0 比如chara 10 61 定义了一个二维字符数组a 可容纳10个字符串 每串最长可达60个字符 113 例如 ucharCodeMsg 17 Thisisatest n message1 n message2 n 这是一个二维数组 第二个下标必须给定 因为它不能从数据表中得到 第一个下标可缺省 由数据常量表决定 115 5 3 2指针 1 指针的定义所有的变量在使用之前必须定义 以确定其类型 指针变量也不例外 由于它是用来专门存放地址的 因此必须将它定义为 指针类型 指针定义的一般形式为 类型说明符 指针变量名 例如 int p 定义一个整形的指针变量pfloat pointer 定义一个浮点型的指针变量注意 指针变量名前面的 号表示该变量为指针变量 但指针变量名应该是p pointer 而不是 p pointer 116 5 3 2指针 2指针变量的引用若使用直接访问方式 则用x a x a为整型变量 若使用指针变量ap进行间接访问 如图所示 则用x ap 间接寻址与直接寻址的区别 此时程序先从指针变量ap中取出a变量的指针 地址 然后从此地址中取出a变量的值6赋给指针变量x 应当注意的是 在指针变量定义时和在指针运算时所代表的含义是不同的 在进行指针变量定义时 ap中的 是指针变量类型说明符 在进行指针运算时 x ap中的 是指针运算符 表示取对应内存单元的值 117 4 指针变量的运算由于指针也是变量 它具有变量的特性 可以对指针进行某些运算 但需要牢记一点 指针变量的值始终与某类型变量的地址有关 对于指针的运算 归纳起来 有如下4种运算 指针赋地址 指针加 减 一个整数 两个指针比较和两个指针相减等 1 指针变量赋值赋值运算是指给使指针变量指向一特定的内存地址 在前面一讲指针的引用时已经接触过 指针变量的赋值运算只能在相同的数据类型之间进行 118 2 指针加 减 一个整数指针加 减 一个整数的意义是当指针指向某存储单元时 使指针相对该存储单元移动位置 从而指向另一个存储单元 对于不同类型的指针移动的字节数是不一样的 指针移动是以它指向的数据类型所占的字节数为移动单位 例如 字符型指针每次移动一个字节 长整型指针每次移动四个字节 经常利用指针的加减运算移动指针来取得相邻存取单元的值 特别在使用数组时 经常使用该运算来存取不同的数组元素 119 3 两个指针比较两个指针进行比较运算可以使用关系运算符 对两个指针所指的地址进行比较 pipj pi pj pi pj 指针比较运算 经常用于数组 判定两个指针所指的数组元素的位置的先后 而将指向两个简单变量的指针进行比较或在不同类型指针之间的比较是没有意义的 指针与整数常量或变量的比较也没有意义 只有常量0例外 一个指针变量为0 NULL 时表示该指针为空 没有指向任何存储单元 120 4 两个指针相减当两个指针指向同一数组时 两个指针相减的差值即为两个指针相隔的元素个数 121 4 C51的指针类型由于805l及其派生系列所具有的独特结构 c5l编译器支持两种不同类型的指针 通用指针存储器指针 122 通用指针 通用指针的声明与标准C语言中一样 如 char s stringptr int numptr intptr long state longptr 通用指针需要三个字节来存储 第一个字节表示存储器类型 第二个字节是指针的高字节 第三个字节是指针的低字节 通用指针可以用来访问所有类型的变量 不管变量存储在哪个存储空间中 许多库函数都使用通用指针 通用指针很方便 但是也很慢 在存储空间不明确的情况下 它们用的最多 123 存储器指针 存储器指针在定义时包括一个存储器类型说明 并且总是指向此说明的特定存储器空间 例如 chardata str ptrtostringindata intxdata numtab ptrtoint s inxdata longcode powtab ptrtolong s incode 由于存储器类型在编译时已经确定 通用指针中用来表示存储器类型的字节就不再需要了 124 5 3 3 idata data bdata和pdata的存储器指针 用一个字节保存 code和xdata的存储器指针 用两个字节保存 使用存储器指针比通用指针效率高 速度快 在存储空间明确并不会变化的情况下 它们用的最多 125 5 3 3结构 1 定义结构类型定义结构的一般形式为struct结构名 结构成员说明 结构成员说明的格式为类型标识符成员名 126 例如 用一系列变量来描述一天的时间 需要定义时 分 秒变量 还要定义一个 天 的变量 通过使用结构可以把这四个变量定义在一起 给它们一个共同的名字 声明如下 structtime str charhour charmin charsec intdays 127 2 定义结构变量上面定义的structtime str只是结构体的类型名 而不是结构体的变量名 为了在程序中正常地执行结构操作 除了定义结构的类型名之外 还需要进一步定义该结构类型的变量名 定义一个结构体变量的方法有如下几种 128 1 先定义结构的类型 再定义该结构的变量名 其一般形式为 struct结构名 结构成员说明 结构名变量名1 变量名2 变量名n 129 例如 structtime str charhour charmin charsec intdays time strtime of day1 time of day2 130 2 在定义结构类型的同时定义该结构的变量 其一般形式为 struct结构名 结构成员说明 变量名1 变量名2 变量名n 131 例如structtime str charhour charmin charsec intdays time of day1 time of day2 132 3 直接定义结构类型变量 其一般形式为 struct 结构成员说明 变量名1 变量名2 变量名n 133 例如struct charhour charmin charsec intdays time of day1 time of day2 134 3 结构变量的引用对结构进行引用时 只能对结构类型变量进行赋值 存取和运算 而不能对结构类型作这些操作 结构不能作为一个整体参加赋值 存取和运算 也不能整体地作为函数的参数 或函数的返回值 对结构所执行的操作 只能用 运算符取结构的地址 或对结构变量的成员分别加以引用 结构体成员引用的一般形式为 结构变量名 成员名 135 例time of day1 hour 12 是成员运算符 它在所有的运算符中优先级最高 如果结构类型变量的成员本身又属于一个结构类型变量 则要用若干个成员运算符 一级一级地找到最低一级的成员 只有最低一级的成员才能参加赋值 存取和运算 和 等同 结构类型变量的成员可以像普通变量一样进行各种运算 136 4 结构数组在讲到structtime str结构类型时 虽然只定义了两个具有该类型的结构变量time of day1 time of day2 但在使用时已经感到了引用它们的麻烦 因为尽管这两个变量结构相同 具有同样的成员项 但当使用printf 语句打印它们时 必须分别使用两个printf 语句 试想 假如有若干个这样的结构变量 要将它们的内容全部打印出来 将是多么麻烦 要解决这个问题 可以将具有同样结构类型的若干个结构变量定义成结构数组 这样就可以使用循环语句对它们进行引用 从而大大提高效率 137 结构数组的每个元素都是具有相同结构类型的结构变量 它们都含有相同的成员项 结构数组的定义与结构

温馨提示

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

评论

0/150

提交评论