单片机课件单片机的C语言编程.ppt

上传人:sh****n 文档编号:6753274 上传时间:2020-03-03 格式:PPT 页数:92 大小:1.20MB
返回 下载 相关 举报
单片机课件单片机的C语言编程.ppt_第1页
第1页 / 共92页
单片机课件单片机的C语言编程.ppt_第2页
第2页 / 共92页
单片机课件单片机的C语言编程.ppt_第3页
第3页 / 共92页
点击查看更多>>
资源描述
第10章单片机的C语言编程 单片机原理 接口及应用 内容提要 C51程序结构 C51的数据类型 数据的存贮类型和存贮模式 C51对SFR 可寻址位 存储器和I O口的定义 C51的运算符 函数 C语言编程实例 单片机资源的C语言编程实例 汇编语言和C语言的混合编程 C语言函数库的管理与使用 小结 51系列单片机支持三种高级语言 即PL M C和BASIC 8052单片机内固化有解释BASIC语言 BASIC语言适用于简单编程而对编程效率运行速度要求不高的场合 PL M是一种结构化的语言 很象PASCAL PL M编译器好象汇编器一样产生紧凑的机器代码 可以说是高级汇编语言 但它不支持复杂的算术运算 无丰富库函数支持 学习PL M无异于学习一种新的语言 C语言是一种通用的程序设计语言 其代码率高 数据类型及运算符丰富 并具有良好的程序结构 适用于各种应用的程序设计 是目前使用较广的单片机编程语言 单片机的C语言采用C51编译器 简称C51 由C51产生的目标代码短 运行速度高 所需存储空间小 符合C语言的ANSI标准 生成的代码遵循Intel目标文件格式 而且可与A51汇编语言或PL M51语言目标代码混和使用 应用C51编程具有以下优点 1 C51管理内部寄存器和存贮器的分配 编程时 无需考虑不同存储器的寻址和数据类型等细节问题 程序由若干函数组成 具有良好的模块化结构 有丰富的子程序库可直接引用 从而大大减少用户编程的工作量 C语言和汇编语言可以交叉使用 汇编语言程序代码短 运行速度快 但复杂运算编程耗时 如果用汇编语言编写与硬件有关的部分程序 用C语言编写与硬件无关的运算部分程序 充分发挥两种语言的长处 可以提高开发效率 10 1C51程序结构 同标准C一样 C51的程序由一个个函数组成 这里的函数和其他语言的 子程序 或 过程 具有相同的意义 其中必须有一个主函数main 程序的执行从main 函数开始 调用其他函数后返回主函数main 最后在主函数中结束整个程序而不管函数的排列顺序如何 C语言程序的组成结构如下所示 全局变量说明 可被各函数引用 main 主函数 局部变量说明 只在本函数引用 执行语句 包括函数调用语句 fun1 形式参数表 函数1 形式参数说明 局部变量说明执行语句 包括调用其他函数语句 funn 形式参数表 函数n 形式参数说明 局部变量说明执行语句 C语言的语句规则 1 每个变量必须先说明后引用 变量名英文大小写是有差别的 2 C语言程序一行可以书写多条语句 但每个语句必须以 结尾 一个语句也可以多行书写为好 3 C语言的注释用 表示 4 花括号必须成对 位置随意 可在紧挨函数名后 也可另起一行 多个花括号可以同行书写 也可逐行书写 为层次分明 增加可读性 同一层的 花括号对齐 采用逐层缩进方式书写 10 2C51的数据类型 C51的数据有常量和变量之分 常量 在程序运行中其值不变的量 可以为字符 十进制数或十六进制数 用0 x表示 常量分为数值型常量和符号型常量 如果是符号型常量 需用宏定义指令 define 对其进行定义 相当于汇编的 EQU 伪指令 如 definePI3 1415那么程序中只要出现PI的地方 编译程序都译为3 1415 变量 在程序运行中其值可以改变的量 一个变量由变量名和变量值构成 变量名即是存贮单元地址的符号表示 而变量的值就是该单元存放的内容 定义一个变量 编译系统就会自动为它安排一个存贮单元 具体的地址值用户不必在意 10 2 1C51变量的数据类型 无论哪种数据都是存放在存贮单元中的 每一个数据究竟要占用几个单元 即数据的长度 都要提供给编译系统 正如汇编语言中存放数据的单元要用DB或DW伪指令进行定义一样 编译系统以此为根据预留存贮单元 这就是定义数据类型的意义 C51编译器支持数据类型见表10 1 表10 1C51的数据类型 对表10 1作如下说明 1 字符型 char 整型 int 和长整型 long 均有符号型 signed 和无符号型 unsigned 两种 如果不是必须 尽可能选择unsigned型 这将会使编译器省却符号位的检测 使生成的程序代码比signed类型短得多 2 程序编译时 C51编译器会自动进行类型转换 例如将一个位变量赋值给一个整型变量时 位型值自动转换为整型值 当运算符两边为不同类型的数据时 编译器先将低级的数据类型转换为较高级的数据类型 运算后 运算结果为高级数据类型 3 51单片机内部数据存贮器的可寻址位 20H 2FH 定义为bit型 而特殊功能寄存器的可寻址位 即地址为X0H和X8H的SFR的各位 只能定义为sbit类型 10 2 2关于指针型数据 1 关于指针型变量在汇编语言程序中 要取存贮单元m的内容可用直接寻址方式 也可用寄存器间接寻址方式 如果用R1寄存器指示m的地址 用 R1取m单元的内容 相对应的在C语言中用变量名表示取变量的值 相当于直接寻址 也可用另一个变量 如P 存放m的地址 P就相当于R1寄存器 用 P取得m单元的内容 相当于汇编的间接寻址方式 这里P即为指针型变量 下面表格表示两种语言将m单元的内容送n单元的对照语句 注 上表省略了汇编语言程序中对符号地址n和m用EQU伪指令进行具体地址定义的语句以及C语言对变量n m和指针变量P进行类型定义的语句 实际程序设计中 此步是不可缺少的 表中 为取地址运算符 为取内容运算符 表1O 2汇编语言和C语言的对照 2 指针型数据的类型由于C51是结合51单片机硬件的 51单片机的不同存贮空间 有不同的地址范围 即使对于同一外部数据存贮器 又有用 Ri分页寻址 Ri为八位 和用 DPTR寻址 DPTR为十六位 两种寻址方式 而指针本身也是一个变量 有它存放的存贮区和数据长度 因此 在指针类型的定义中要说明 被指的变量的数据类型和存贮类型 指针变量本身的数据类型 占几个字节 和存贮类型 即指针本身存放在什么存贮区 例如类型定义为data或idata 表示指针指示内部数据存贮器 而pdata表示指针指向外部数据存贮器 用 Ri间址 以上均为八位地址 而类型code xdata表示指针指向外部程序存贮器或外部数据存贮器指针 本身 即被指示地址 应为十六位长度 如果想使指针能适用于指向任何存贮空间 则可以定义指针为通用型 此时指针长度为3字节 第一字节表示存贮器类型编码 第二 三字节分别表示所指地址的高位和低位 第一字节表示的存贮器类型编码见表10 3 表10 3通用型指针的存贮类型编码 10 3数据的存贮类型和存贮模式 10 3 1数据的存贮类型C51是面向8XX51系列单片机及硬件控制系统的开发语言 它定义的任何变量必须以一定的存贮类型的方式定位在8XX51的某一存贮区中 否则便没有意义 因此在定义变量类型时 还必须定义它的存贮类型 C51的变量的存贮类型如表10 4所示 表10 4C51的变量的存贮类型 访问内部数据存贮器 idata 比访问外部数据存贮器 xdata 相对要快一些 因此 可将经常使用的变量置于内部数据存贮器中 而将较大及很少使用的数据变量置于外部数据存贮器中 例如定义变量x语句 datacharx 等价于chardatax 如果用户不对变量的存贮类型定义 则编译器承认默认存贮类型 默认的存贮类型由编译控制命令的存贮的模式部分决定 10 3 2存贮器模式 存贮器模式决定了变量的默认存贮器类型 参数传递区和无明确存贮区类型的说明 C51的存贮器模式有SMALL LARGE和COMPACT 见表10 5 在固定的存贮器地址进行变量参数传递是C51的一个标准特征 在SMALL模式下参数传递是在内部数据存贮区中完成的 LARGE和COMPACT模式允许参数在外部存贮器中传递 C51同时也支持混合模式 例如在LARGE模式下生成的程序可将一些函数分页放入SMALL模式中从而加快执行速度 例如设C语言源程序为PROR C 若使程序中的变量类型和参数传递区限定在外部数据存贮区 有两种方法 方法1 用C51对PROR C进行编译时 使用命令C51PROR CCOMPACT 方法2 在程序的第一句加预处理命令 pragmacompact 表10 5存贮器模式 10 3 3变量说明举例 datacharvar 字符变量var定位在片内数据存贮区 charcodeMSG PARAMETER 字符数组MSG 定位在程序存贮区 unsignedlongxdataarray 100 无符号长型数组定位在片外RAM区 每元素占4bytes floatidatax y z 实型变量x y z 定位在片内用间址访问的内部RAM区 bitlock 位变量Lock定位在片内RAM可位寻址区 unsignedintpdatasion 无符号整型变量sion定位在分页的外部RAM unsignedcharxdatavector 10 4 4 无符号字符型三维数组 定位在片外RAM区 sfrP0 0 x80 定义P0口 地址为80H charbdataflags 字符变量flags定位在可位寻址内部RAM区 sbitflag0 flags 0 定义flag0为flags 0 如果在变量说明时略去存贮器类型标志符 编译器会自动选择默认的存贮器类型 默认的存贮器类型由控制指令SMALL COMPACT和LARGE限制 例如如果声明charvar 则默认的存贮器模式为SMALL var放在data存贮区 如果使用COMPACT模式 var放入idata存贮区 在使用LARGE模式的情况下 var被放入外部数据存贮区 xdata存贮区 10 3 4指针变量说明举例longxdata px 指针px指向long型xdata区 每个数据占四个单元 指针自身在默认存贮器 如不指定编译模式在data区 指针长度为2个字节 charxdata datapd 指针pd指向字符型xdata区 自身在data区 长度2字节 datacharxdata pd 与上例等效 dataint pn 和int datapn及intr pn等效 定义一个类型为int型的通用型指针 指针自身在data区长度为3字节 在上例的指针声明中包含如下几个内容 1 1 指针变量名 如px 前面冠以 表示px为指针型变量 此处 不带取内容之意 2 指针指向的存贮类型 即指向哪个存贮区 它决定了指针本身的长度 见表10 1 存贮类型声明的位置在数据类型和指针名 如 px 之间 如无次项声明 则此指针型变量为通用型 3 3 指针指向的存贮区的数据类型 即被指向的存贮区以多少个单元作一个数据单位 当程序通过指针对该区操作时 将按此规定的单元个数的内容作为一个数据操作 4 指针变量自身的存贮类型 即指针处于什么区与自身的长度无关 该声明可位于声明语句的开头 也可在 和变量名之间 此项由编译模式放在默认区 如无规定编译模式 通常在data区 10 4C51对SFR 可寻址位 存储器和I O口的定义 10 4 1特殊功能寄存器SFR定义C51提供了一种自主形式的定义方式 使用特定关键字sfr如sfrSCON 0 x98 串行通信控制寄存器地址98H sfrTMOD 0 x89 定时器模式控制寄存器地址89H sfrACC 0 xe0 A累加器地址E0H sfrP1 0 x90 P1端口地址90H 定义了以后 程序中就可以直接引用寄存器名 C51也建立了一个头文件reg51 h 增强型为reg52 h 在该文件中对所有的特殊功能寄存器的进行了sfr定义 对特殊功能寄存器的有位名称的可寻址位进行了sbit定义 因此 只要用包含语句 include 就可以直接引用特殊功能寄存器名 或直接引用位名称 要特别注意 在引用时特殊功能寄存器或者位名称必须大写 10 4 2对位变量的定义 C51对位变量的定义有三种方法 1 将变量用bit类型的定义符定义为bit类型 如bitmn mn为位变量 其值只能是 0 或 1 其位地址C51自行安排在可位寻址区的bdata区 2 采用字节寻址变量 位的方法 如bdataintibase ibase定义为整型变量 sbitmybit ibase 15 mybit定义为ibase的D15位 这里位是运算符 相当于汇编中的 其后的最大取值依赖于该位所在的字节寻址变量的定义类型 如定义为char最大值只能为7 3 对特殊功能寄存器的位的定义方法1 使用头文件及sbit定义符 多用于无位名的可寻址位 例如 includesbitP1 1 P1 1 P1 1为P1口的第1位 sbitac ACC 7 ac定义为累加器A的第7位 方法2 使用头文件reg51 h 再直接用位名称 例如 includeRS1 1 RS0 0 方法3 用字节地址位表示例如sbitOV 0 xD0 2 方法4 用寄存器名 位定义例如sfrPSW 0 xd0 定义PSW地址为d0H sbitCY PSW 7 CY为PSW 7 10 4 3C51对存贮器和外接I O口的绝对地址访问 1 对存贮器的绝对地址访问利用绝对地址访问的头文件absacc h可对不同的存贮区进行访问 该头文件的函数有 CBYTE 访问code区字符型 DBYTE 访问data区字符型 PBYTE 访问pdata或I O区字符型 XBYTE 访问xdata或I O区字符型 还有CWORD DWORD PWORD和XWORD四个函数 它们的访问区域同上 只是访问的类型为int型 例10 1 include definecomXBYTE 0 x07ff 那么后面程序com变量出现的地方 就是对地址为07ffH的外部RAM或I O口进行访问 例10 2XWORD 0 0 x9988 即将9988H int类型 送入外部RAM的0号和1号单元 使用中要注意 absacc h一定要包含进程序 XBYTE必须大写 2 对外部I O口的访问由于单片机的I O口和外部RAM统一编址 因此对I O口地址的访问可用XBYTE MOVX DPTR 或PBYTE MOVX Ri 进行 例10 3XBYTE 0Xefff 0 x10 将10H输出到地址为EFFFH端口 10 5C51的运算符 1 赋值运算符 将 的右边的值赋值给左边的变量 2 C51的算术运算符 加或正号 减或负号 乘号 除号 求余 优先级为 先乘除 后加减 先括号内 再括号外3 C51的关系运算符有六种 小于 大于 小于等于 大于等于 相等 不相等 优先级 前四个高 后二个 和 级别低 4 C51的逻辑运算符有三种 逻辑表达式和关系表达式的值相同 以0代表假 以1代表真 以上三种运算的优先级见图10 1 5 C51的按位操作的运算符有六种 按位与 按位或 按位异或 位取反 位右移 注 补零移位 例1 a 0 xf0H 表达式a a值为0FH例2 a 0 xea 表达式a 2值为A8H 即a值左移两位 移位后空白位补0 6 自增 自减运算符 i i 在使用i之前 先使i值加1 减1 i i 在使用i之后 再使i值加1 减1 例设i原值为5j i则j值为6 i值也为6j i 则j值为5 i值为6 非 算术运算 关系运算 和 赋值运算 图10 1运算符的优先级 7 复合赋值运算符 例 a b相当于a a b a 7 相当于a a 7 8 对指针操作的运算符 取地址运算 间址运算符例a b 取b变量的地址送变量ac b 将以b的内容为地址的单元的内容送c这里要注意 与按位与运算符的差别 如果 为 与 的两边必须为变量或常量 与指针定义时指针前的 的差别 如char pt 这里的 只表示pt为指针变量 不代表间址取内容的运算 10 6函数 C语言程序由函数组成 下面介绍函数的要点 10 6 1函数的分类及定义从用户使用角度划分 函数分为库函数和用户自定义函数 库函数是编译系统为用户设计的一系列标准函数 见本书附录二 用户只需调用 而无需自己去编写这些复杂的函数 如前面所用到的头文件reg51 h absacc h等 有的头文件中包括一系列函数 要使用其中的函数必须先使用 include包含语句 然后才能调用 用户自定义函数是用户根据任务编写的函数从参数形式上函数分为无参函数和有参函数 有参函数即是在在调用时 调用函数用实际参数代替形式参数 调用完返回结果给调用函数 10 6 2函数的定义 函数以 开始 以 结束 无参函数的定义 返回值类型函数名 函数体语句 如果函数没有返回值 可以将返回值类型设为void 有参函数的定义 返回值类型函数名 形式参数表列 形式参数类型说明 函数体语句return 返回形参名 也可以这样定义返回值类型函数名 类型说明形式参数表列 函数体语句return 返回参形名 其中形式参数表列的各项要用 隔开 通过return语句将需返回的值返回给调用函数 10 6 3函数的调用 函数调用的形式为 函数名 实际参数表列 实参和形参的数目相等类型一致 对于无参函数当然不存在实际参数表列 函数的调用方式有三种 函数调用语句 即把被调函数名作为调用函数的一个语句 如fun1 被调函数作为表达式的运算对象 如result 2 get a b 此时get函数中的a b应为实参 其以返回值参予式中的运算 被调函数作为另一个数的实际参数如m max a get a b 函数get a b 作为函数max 的一个实际参数 10 6 4对被调函数的说明 如果被调函数出现在主调函数之后 在主调函数前应对被调函数作以说明 形式为 返回值类型被调函数名 形参表列 如果被调函数出现在主调函数之前 可以不对被调函数说明 下面以一个简单例子来说明intfun1 a b inta b intc c a b return c main intd 0u 3 v 2 d 2 fun u v 上例被调函数在主调函数前 不用说明 intfun1 a b main intd u 3 v 2 d 2 fun1 u v intfun1 a b inta b intc c a b return c 上例中被调函数在主调函数后 在前面对被调函数进行说明 10 7C语言编程实例 为了使C语言的编程方法和汇编语言的编程方法有一个对比 本节采用3 1节的例题 由于C51编译器是针对单片机的 因此ANSIC中的scanf和printf等对PC电脑的键盘和监视器的输入 输出语句无效 运算的数据可以通过变量置入或取出 这时C51会自动安排使用的存贮单元 当然也可以用户自行通过具体的内存地址置入数据或从特定地址取出数据 这就少不了要会观察具体地址的内容或改变该地址的内容 C语言的编程上机调试见本教材的实验部分 下面通过一个例子说明C语言程序编译后生成的机器代码及对应的反汇编程序 从中引出一些道理 10 7 1顺序程序的设计 例10 5完成19805 24503的编程分析 两个乘数比较大 其积更大 采用unsignedlong类型 设乘积存放在外部数据存贮器0号开始的单元 程序如下 main unsignedlongxdata p 设定指针p指向类型为unsignedlong的外部RAM区 unsignedlonga 19805 设置a为unsignedlong类型 并赋初值 unsignedlongb 24503 c 设置b和积为unsignedlong类型 并赋初值 p 0 设地址指向0号单元 c a b p c 积存入外部RAM0号单元 上机通过WAVE软件仿真调试 在变量观察窗口看到运算结果c 48528195 即为乘积的十进制数 观察XDATA区 外部RAM 的0000H 0003H单元分别为1CECD07B 即存放的为乘积的十六进制数 观察DATA区 内部RAM区 地址0405060708090A0B0C0D0E0F1CECD07B00004D5D00005FB7C变量 积 a变量b变量可见定义为unsignedlong类型 给每个变量分配四个单元 如果定义类型不对 将得不到正确的结果 对于复杂的运算通常采用查表的方法 如同汇编程序设计一样 在程序存贮器建立一张表 在C语言中表格定义为数组 表内数据 元素 的偏移量表现为下标 数组的使用如同变量一样 要先进行定义 说明数组名 维数 数据类型和存贮类型 在定义数组的同时 还可以给数组各元素赋初值 通过下例说明C51数组的定义方法和用C语言编查表程序的方法 例10 6片内RAM20H单元存放着一个0 05H的数 用查表法 求出该数的平方值放入内部RAM21H单元 main charx pcharcodetab 6 0 1 4 9 16 25 p 0 x20 x tab p p p x 10 7 2循环程序的设计 C语言的循环语句有以下几种形式1 while 表达式 语句 其中表达式为循环条件 语句为循环体 当表达式值为真 值为非0 重复执行 语句 语句可只一条以 结尾 可以多条组成复合语句 复合语句必须用 括起 也可以没有语句 通常用于等待中断 或查询 2 do 语句 while 表达式 表达式为真执行循环体 语句 直至表达式为假 退出循环执行下一个语句 3 for 表达式1 表达式2 表达式3 语句 其中语句为循环体 执行过程是 执行表达式1后进入循环体 如表达式2为假 按表达式3修改变量 再执行循环体 直到表达式2为真 语句中的表达式可以省其中任一项甚至全部 但二个分号不可省 如for 语句 为无限循环 for i 4 i 语句 i从4开始无限循环 for i 100 相当于while i 100 例10 7while P1 本程序完成0 1 2 10的累加 执行后sum 55 例10 9将例10 8改用for语句编程main intsum 0 i for i 0 i 10 i sun i 10 7 3分支程序的设计 C语言的分支选择语句有以下几种形式 1 if 表达式 语句 句中表达式为真执行语句 否则执行下一条语句 当花括号中的语句不只一条 花括号不能省 2 if 表达式 语句1 else 语句2 句中表达式为真执行语句1 否则执行语句2为了能无论哪种情况 执行完后都执行下一条语句 if语句可以嵌套 3 switch 表达式 case常量表达式1 语句1 break case常量表达式2 语句2 break case常量表达式n 语句n break default 语句n 1 说明 语句先进行表达式的运算 当表达式的值与某一case后面的常量表达式相等 就执行它后面的语句 当case语句后有break语句时 执行完这一case语句后 跳出switch语句 当case后面无break语句 程序将执行下一条case语句 如果case中常量表达式值和表达式的值都不匹配 就执行default后面的语句 如果无default语句就退出switch语句 default的次序不影响执行的结果 也可无此语句 case语句适于多分支转移的情况下使用 例10 10片内RAM的20H单元存放一个有符号数x 函数y与x有如下关系式 xx 0y 20Hx 0 x 5x 0设y存放于21H单元 程序如下main charx p y p 0 x20 y 0 x21 for x p if x 0 y x if x 0 y x 5 if x 0 y 0 x20 程序中为观察不同数的执行结果 采用了死循环语句for 上机调试时退出死循环可用Ctrl C 例10 11有两个数a和b 根据R3的内容转向不同的处理子程序 r3 0 执行子程序pr0 完成两数相加 r3 1 执行子程序pr1 完成两数相减 r3 2 执行子程序pr2 完成两数相乘 r3 3 执行子程序pr3 完成两数相除 分析 C语言中的子程序即为函数 因此需编四个处理的函数 如果主函数在前 主函数要对子函数进行说明 如果子函数在前 主函数无须对子函数说明 但是无论子 主函数的顺序如何 程序总是从主函数开始执行 执行到调用子函数就会转到子函数执行 在C51编译器中通过头文件reg51 h可以识别特殊功能寄存器 但不能识别R0 R7通用寄存器 因此R0 R7只有通过绝对地址访问识别 程序如下 include definer3DBYTE 0 x03 intc c1 a b pr0 c a b pr1 c a b pr2 c a b pr3 c a b main a 90 b 30 for switch r3 case0 pr0 break case1 pr1 break case2 pr2 break case3 pr3 break c1 56 在上述程序中 为便于调试观察 加了C1 56语句 并使用了死循环语句for 用Ctrl C可退出死循环 10 8单片机资源的C语言编程实例 例10 12在3 1节曾用汇编语言完成了外部RAM的000EH单元和000FH单元的内容交换 现改用C语言编程 C语言对地址的指示方法可以采用指针变量 也可以引用absacc h头文件作绝对地址访问 下面采用绝对地址访问方法 includemain charc for c XBYTE 14 XBYTE 14 XBYTE 15 XBYTE 15 c 程序中为方便反复观察 使用了死循环语句for 只要用Ctrl C即可退出死循环 上面程序通过编译 生成的机器代码和反汇编程序如下 0000020014LJMP0014H000390000EMOVDPTR 000EH0006E0MOVXA DPTR0007FFMOVR7 A0008A3INCDPTR0009E0MOVXA DPTR000A90000EMOVDPTR 000EH000DF0MOVX DPTR A000EA3INCDPTR000FEFMOVA R70010F0MOVX DPTR A 001180F0SJMP0003H001322RET0014787FMOVR0 7FH0016E4CLRA0017F6MOV R0 A0018D8FDDJNZR0 0017H001A758107MOVSP 07H001D020003LJMP0003H 例中可见 一进入C语言程序 首先执行初始化 将内部RAM的0 7FH128个单元清零 然后置SP为07H 视变量多少不同 SP置不同值 依程序而定 因此如果要对内部RAM置初值 一定要在执行了一条C语言语句后进行 C语言程序设定的变量 C51自行安排寄存器或存贮器作参数传递区 通常在R0 R7 一组或两组 视参数多少定 因此 如果对具体地址置数据 应避开这些R0 R7的地址 如果不特别指定变量的存贮类型 通常被安排在内部RAM中 10 8 2并行口及键盘的C语言编程 例10 13用P1 0输出1KHz和500Hz的音频信号驱动扬声器 作报警信号 要求1KHz信号响100ms 500Hz信号响200ms 交替进行 P1 7接一开关进行控制 当开关合上 响报警信号 当开关断Kk告警信号停止 编出程序 分析500Hz信号周期为2ms 信号电平为每1ms变反一次 1KHz信号周期为1ms 信号电平每500 s变反一次 用C语言编程如下 includesbitP10 P1 0 sbitP17 P1 7 main unsignedchari j while 1 while P17 0 for i 1 i 150 i 控制音响时间 P10 P10 for j 0 j 50 j 延时完成信号gou周期时间 for i 1 i 100 i 控制音响时间 P10 P10 for j 0 j 100 j 延时 完成信号周期时间 例10 14在下图中8XX51接有五个共阴极数码管的动态显示接口电路 开关打向位置 1 时 显示 12345 字样 当开关打向 2 时 显示 HELLO 字样 C语言编程程序清单如下 图10 7接五个共阴极数码管的动态显示接口 用C语言完成上述功能编程 include defineuintunsignedint deefineucharunsignedcharsbitP17 P1 7 main ucharcodetab1 5 0 x86 0 xdb 0 xcf 0 xe6 0 xed 1 5 的字形码 因P1 7接的开关 最高位送的 1 ucharcodetab2 5 0 xf8 0 xf9 0 xb8 0 xb8 0 x bf HELLO 的段码 1 uchari unitj while 1 p3 0 x011for i 0 i if p17 1 P1 tab1 i elseP1 tab2 i P3 1 for j 0 j 25000 j 例10 15以P1 0 P1 3作输出线 以P1 4 P1 7作输入线 如图4 6所示 C语言编程程序清单如下 include defineucharunsignedchar defineuintunsignedintvoiddlms void ucharkbscan void 函数说明 voidmain void0 ucharkey while 1 key kbscan 键盘扫描函数 返回键码送key保存 dlms voiddlms void 延时 uchari for i 200 i 0 i 图10 84 4矩阵键盘 ucharkbscan void 键盘扫描函数 ucharsccode recode P1 0 xf0 P1 0 P1 3发全0 P1 4 P1 7输入 if P1 无键按下 返回值为0 10 8 3C51中断程序的编制 C51使用户能编写高效的中断服务程序 编译器在规定的中断源的矢量地址中放入无条件转移指令 使CPU响应中断后自动地从矢量地址跳转到中断服务程序的实际地址 而无需用户去安排 中断服务程序定义为函数 函数的完整定义如下 返回值函数名 参数 模式 再入 interruptn usingm 其中必选项interruptn表示将函数声明为中断服务函数 n为中断源编号 可以是0 31间的整数 不允许是带运算符的表达式 n通常取以下值 0外部中断0 1定时器 计数器0溢出中断 2外部中断1 3定时器 计数器1溢出中断 4串行口发送与接收中断5定时器 计数器2中断 各可选项的意义如下 usingm定义函数使用的工作寄存器组 m的取值范围为0 3 可缺省 它对目标代码的影响是 函数入口处将当前寄存器保存 使用m指定的寄存器组 函数退出时原寄存器组恢复 选不同的工作寄存器组 可方便实现寄存器组的现场保护 再入 属性关键字reentrant将函数定义为再入的 在C51中 普通函数 非再入的 不能递归调用 只有再入函数才可被递归调用 中断服务函数不允许用于外部函数 它对目标代码影响如下 当调用函数时 SFR中的ACC B DPH DPL和PSW当需要时入栈 如果不使用寄存器组切换 中断函数所需的所有工作寄存器Rn都入栈 函数退出前 所有工作寄存器都出栈 函数由 RETI 指令终止 下面示例说明C语言的编程方法 例10 15对10 2 3的例10 4 见图 要求每中断一次 发光二极管显示开关状态用C语言编程 include int0 interrupt0 INT0中断函数 P1 0 x0f 输入端先置1 灯灭 P 4 读入开关状态 并左移四位 使开关反映在发光二极管上 main EA 1 开中断总开关 EX0 1 允许INT0中断 IT0 1 下降沿产生中断 while 1 等待中断 例10 16记录并显示中断次数用C语言编程 可有两种编程方法 法1 在主程序中判断中断次数 程序如下 include chari codechartab 16 0 x3f 0 x06 0 x5b 0 x4F 0 x66 0 x6d 0 x7d 0 x07 0 x7f 0 x6f 0 x77 0 x7c 0 x39 0 x5e 0 x79 0 x71 int interrupt2 i 计中断次数 P1 tab i 查表 次数送显示 main EA 1 EX1 1 IT1 1 ap5 P1 0 x3f 显示 0 for i 0 i 16 当i小于16等待中断 gotoap5 当i 16重复下一轮16次中断 法2 在中断程序中判断中断次数 include chari codechartab 16 0 x3f 0 x06 0 x5b 0 x4F 0 x66 0 x6d 0 x7d 0 x07 0 x7f 0 x6f 0 x77 0 x7c 0 x39 0 x5e 0 x79 0 x71 int interrupt1 i if i 16 P1 tab i else i 0 P1 0 x3f main EA 1 EX1 1 IT1 1 P1 0 x3f while 1 等待中断 10 8 4定时 计数器的C语言编程 例10 17在P1 7端接一个发光二极管LED 要求利用定时控制使LED亮一秒灭一秒周而复始 设fosc 6MHz 分析T0定时100ms初值 100 103 2 50000 即初值为 50000 T1计数5个脉冲工作于方式2 计数初值为 5 T0和T1均采用中断方式 程序如下 include reg51 h sbitP1 0 P1 0 sbitP1 7 P1 7 timer0 interrupt1using1 T0中断服务程序 P1 0 P1 0 100ms到P1 0反相 TH0 50000 256 重载计数初值 TL0 50000 256 timerl interrupt3using2 T1中断服务程序 P1 7 P1 7 1s到 灯改变状态 main P1 7 0 置灯初始灭 P1 0 1 保证第一次反相便开始计数 TMOD 0 x61 T0方式1定时 T1方式2计数 TH0 50000 256 预置计数初值 TL0 50000 256 TH1 5 TL1 5 IP 0 x08 置优先级寄存器 EA 1 ET0 1 ET1 1 开中断 TR0 1 TR1 1 启动定时 计数器 for 等待中断 例10 18在内部数据存贮器20H 3FH单元中共有32个数据 要求采用方式1串行发送出去 传送速率为1200波特 设fosc 12MHZ 方法 T1工作于方式2作波特率发生器 取SMOD 0 T1的时间常数计算如下 波特率 2SMOD 32 fosc 12 256 x 1200 1 32 12 106 12 256 x x 230 E6H 1 查询方式编程C语的编程 发送程序 include main unsingnedchari char p TMOD 0 x20 TH1 0 xe6 TL1 0 xe6 TR1 1 SCON 0 x40 p 0 x20 for i 0 i 32 i SBUF p p while TI TI 0 接收程序 include main unsingnedchari char p TMOD 0 x20 TH1 0 xe6 TL1 0 xe6 TR1 1 SCON 0 x50 p 0 x20 for i 0 i 32 i while RI RI 0 p SBUF p 10 8 6外扩并行I O口的C语言编程 例10 19用8155作6位共阴极LED显示器接口 PB口经驱动器7407接LED的段选 PA0 PA5位反相驱动器7406接位选 待显示字符依次存于dis buf数组 从右向左顺序显示 8155命令字03 table为段码表 动态显示6个字符 8155和8XX51的接口见图10 12 图10 128155和8XX51单片机的接口电路 各口的地址 A口 7FF1H B口 7FF2HC口 7FF3H命令 状态口 7FF0HC语言程序如下 include include defineucharunsignedchar defineCOM8155XBYTE 0 x7ff0 definePA8155XBYTE 0 x7ff1 definePB8155XBYTE 0 x7ff2 definePC8155XBYTE 0 x7ff3 ucharidatadis 6 2 4 6 8 10 12 存放显示字符2 4 6 8 A C ucharcodetable 18 0 x3f 0 x06 0 x5b 0 x4f 0 x66 0 x6d 0 x7d 0 x07 0 x7f 0 x6f 0 x77 0 x7c 0 x39 0 x5e 0 x79 0 x71 0 x40 0 x00 voiddisplay ucharidata p ucharsel i j COM8155 0 x03 scl 0 x01 送命令字 选最右边的LED for i 0 i 6 i PB8155 table p PA8155 sel 送段码和位码 for j 400 j 0 j 延时 p 地址指针下移位 sel sel 1 左移一位 main display dis 5 10 8 7D A的C语言编程 单级缓冲工作方式下的DAC0832与51系列单片机的接口地址为7FFFH 要求输出端得到锯齿波电压信号用C语言编程 程序如下 include include defineda0832XBYTE 0X7fff main unsignedchari j while 1 for i 0 i 255 i da0832 i 启动转换 for j 0 j 255 j 延时 图10 4锯齿波电压信号 10 9汇编语言和C语言的混合编程 本节介绍不同的模块 不同的语言相结合的编程方法 通常情况下以高级语言编写主程序 用汇编语言编与硬件有关的子程序 高级语言不同的编译程序对汇编的调用方法不同 在KEILC51中 是将不同的模块 包括不同语言的模块 分别汇编或编译 再通过连接生成一个可执行文件 C语言程序调用汇编语言程序要注意以下几点 1 被调函数要在主函数中说明 在汇编程序中 要使用伪指令使CODE选项有效并声明为可再定位段类型 并且根据不同情况对函数名作转换 见表10 6 表10 6函数名的转换 2 对为其他模块使用的符号进行PUBLIC声明 对外来符号进行EXTRN声明 3 要注意参数的正确传递 10 9 1C语言程序和汇编语言程序参数的传递 在混合语言编程中 关键是入口参数和出口参数的传递 KEILC51编译器可使用寄存器传递参数 也可以使用固定存贮器或使用堆栈 由于8XX51的堆栈深度有限 因此多用寄存器或存贮器传递 用寄存器传递最多只能传递三个参数 选择固定的寄存器 见表10 7 表10 7参数传递的寄存器选择 例如funcl inta a 是第一个参数 在R6 R7传递 func2 intb intc int d b 在R6 R7中传递 c 在R4 R5中传递 指针变量 d 在R1 R2 R3中传递 如果传递参数寄存器不够用 可以使用存贮器传送 通过指针取得参数 汇编语言通过寄存器或存贮器传递参数给C语言程序 汇编语言通过寄存器传递给C语言的返回值见表10 8 表10 8汇编语言通过寄存器传递给C语言的返回值 下面通过实例说明混合编程的方法及参数传递过程 10 9 2C语言程序调用汇编语言程序举例 例1 用P1 0产生周期为4ms的方波 同时用P1 1产生周期为8ms的方波 说明 用C语言编写主程序 使P1 1产生周期为8ms的方波为模块一 P1 0产生周期为4ms的方波为模块二 用汇编语言编写的延时1ms程序为模块三 模块一调用模块二获得8ms方波 模块二调模块三时向汇编程序传递了字符型参数 x 2 延时2ms 程序如下 C语言程序模块一 include defineucharunsignedchar sbitP1 1 P1 1 voiddelay4ms void 定义延时4ms函数 模块二 main uchari for P1 1 0 delay4ms 调模块二延时4ms P1 1 1 delay4ms 调模块二延时4ms 模块二 include defineucharunsignedchar sbitP1 0 P1 0 delaylms ucharx 定义延时1ms函数 模块三 voiddelay4ms void P1 0 0 delaylms 2 调汇编函数 模块三 P1 0 1 delaylms 2 调汇编函数 模块三 模块三 PUBLIC DELAY1MS DELAY1MS为其他模块调用 DESEGMENTCODE 定义DE段为再定位程序段 RSEGDE 选择DE为当前段 DELAY1MS NOP DELA MOVR1 0F8H 延时 LOP1 NOPNOPDJNZR1 LOP1 DJNZR7 DELA R7为C程序传递过来的参数 EXIT RET END 上例可见汇编语言程序从R7中获取参数 x 2 模块编译连接方法以上各模块可以先分别汇编或编译 选择DEBUG编译控制项 生成各自的 OBJ文件 然后运行L51将各OBJ文件连接 生成一个新的文件 在集成环境下的连接调试可以连续进行 比上面方法更为方便 现使用wave 伟福 的仿真软件的编译连接步骤如下 1 编辑好各个模块 保存 2 点击文件 新建项目 弹出项目窗口 3 点击项目菜单 选加入模块 此时弹出有文件目录的对话框 选中要加入刚才编辑好的文件 模块 并打开 此时在项目窗口中可以看到加入的模块文件 4 点击项目菜单中的全部编辑 并取名保存项目 于是系统对加入各模块进行编译 并进行连接 5 编译连接完成会弹出信息窗口 如编译连接有错 信息窗口将出现错误信息 6 模块连接成功 生成二进制文件 BIN 和十六进制文件 HEX 7 点击跟踪或单步按钮 就可对程序进行跟踪调试 程序运行到不同模块时 wave就会弹出相应的模块源程序窗口 显示程序运行情况 例2 在汇编程序中比较两数大小 将大数放到指定的存储区 由C程序的主调函数取出模块一 C语言程序 defineucharunsignedchar voidmax uchara ucharb 定义汇编函数 main uchara 5 b 35 c d c 0 x30 c指针变量指向内部RAM30H单元 max a b 调汇编函数 a b为传递的参数 d c d存放模块二传递过来的参数 模块二 汇编语言程序PUBLIC MAXMAX为其他模块调 DESEGMENTCODE 定义DE段为再定位程序段 RSEGDE 选择DE为当前段 MAX MOVA R7 取模块一的参数a MOV30H R5 取模块一的参数b CJNEA 30H TAG1 比较a b的大小 TAG1 JCEXIT MOV30H R7 大数存于30H单元 EXIT RET END 此例中 C语言程序通过R7和R5传递字符型参数a和b到汇编语言程序 汇编语言程序将返回值放在固定存贮单元 主调函数通过指针取出返回值 10 9 3C和汇编混合编程传递的参数多于三个的编程方法 C语言程序调用汇编程序最多只能传递三个参数 如果多于三个参数 就需要通过存贮区传递 可以通过数组 也可以在汇编程序中建立数据段 下面例中C语言程序向汇编传递的参数多于三个的编程方法例3A D采用查询方式采样50个数据 A D地址为7FF8H 将其求平均并送数码管显示 分析8位A D最大值255 用三个数码管显示 以P3 4为查询位 电路设计如图 以汇编编A D转换程序 采集50个数据 以C编求平均值 变十进制显示 程序如下 图例3电路 include defineuintunsignedint defineucharunsignedcharexternvoidcallasm uchar 定义外部汇编函数 externvoiddayl uint 定义外部汇编函数dayl voidmain void uinti j m total 0 ucharidatabuf 50 dis 3 ucharcodetab 16 0 x3f 0 x06 0 x5b 0 x4f 0 x66 0 x6d 0 x7d 0 x07 0 x7f 0 x6f 0 x77 0 x7c 0 x39 0 x5e 0 x79 0 x71 段码表 P1 0 xf8 while 1 total 1 callasm buf 调汇编函数 传递参数为数组首址 for i 50 i 0 i 汇编函数执行完后返回于此 total buf i 1 50个数累加 total total 50 求平均 dis 0 total 10 求个位 并存入显示缓冲区 total total 10 dis 1 total 10 求十位 并存入显示缓冲区 dis 2 total 10 求百位 并存入显示缓冲区 P3 0 x01 P3口位选 for m 0 m 50 m for i 0 i 3 i 显示 P1 tab dis i dayl 50 调汇编函数DAYL 延时 P3 1 汇编语言程序CALLASM ASM 完成50个数据采集并存于BUF为首址的单元 PUBLIC CALLASM 公共符号定义DFFESEGMENTCODE DFFE定为可再定位段RSEGDFFE DFFE为当前段 CALLASM PUSH07HPUSH00H 保护变量 因在下述程序中要用R7和R0MOVA R7 取BUF地址MOVR0 A R0指示存放地址MOVR7 50MOVDPTR 7FF8H DPTR指向A D地址AGA MOVA 0 MOVX DPTR A 启动转换JBP3 4 等待转换结束MOVXA DPTR 读转换数据MOV R0 A 存入BUF数组INCR0DJNZR7 AGAPOP00HPOP07H 恢复BUF地址RETEND 汇编语言程序DAYL ASM 延时PUBLIC DAYL 公共符号定义DTESEGMENTCODE 定义DTE段为再定位程序段 RSEGDTE 选择DTE为当前段 DAYL NOP DELA MOVR1 0F8H 延时 LOP1 NOPNOPDJNZR1 LOP1 DJNZR7 DELA R7为C程序传递过来的参数 EXIT RET END 10 10C语言函数库的管理与使用 C语言作为一种高级编程语言 其主要的优势之一就是有大量的丰富的库函数可直接使用 而库函数的使用是解决程序共享和提高编程效率的最有效的途径之一 函数库是具有目标代码形式的函数的集合 虽然在许多方面 库就像一个独立编译的模块 但它有一个不同于目标文件的特别之处 当某个独立编译的目标文件与其它文件连接时 所有该目标文件中的函数 无论它们是否真正被程序所用 都成为可执行的一部分 而当一个库文件与其它文件连接时 可执行程序中只包含那些真正由程序所用的库函数 例如 C51标准库中包含很多函数 而你的程序只包含真正由你的程序所调用的函数 10 10 1库函数的编写库函数的编写同普通的函数编写的方法一样 需要主要的几点是 库函数命名时 不能用主函数名或C51已有的库函数名 需要改写C51提供的函数库例外 在采用RTOS时 多要求系统调用的函数为可重入函数 因此若要编写可重入的函数 则需按照可重入函数的编写规则编写即可 C51的函数库是分存储模式的 因此 在编写库函数是一定要注意区分C51的存储模式 不同模式下编译出来的库函数一般是不能混用的 某些特殊的库函数程序可以采用汇编编写 而在C51中调用即可 编辑好库函数对应的头文件 以便用户引用 保存好库函数的源程序代码 以供维护升级时使用 一个简单的加法例子如下所示 includeintadd inta intb ints s a b return s 10 10 2函数库的管理 未经特殊说明 我们这里的主要讨论是针对小存储模式的情形 在谈函数库管理前 先要获得在小模式下编译通过的二进制代码 常用的函数库管理命令有创建库文件 在库文件中添加模块 删除模块和替换模块等 下面分别介绍之 C51库管理的执行程序为lib51 exe 位于其安装路径下bin子目录中 lib51的操作有两种 一是运行lib51程序 进入库管理控制台 键入help 便可显示所有的操作控制台命令 如下图所示 另一种就是直接以命令行方式一次实现 这种方式使用灵活 既可以做成批处理一次自动完成一批库管理的操作 又可以与功能强大的文
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!