本 章 重 点 单片机的C程序设计基础 单片机内部资源的C编程 课时安排:6个课时.

Slides:



Advertisements
Similar presentations
单片机 C 语言应用程序设计 主讲教师:廉哲 QQ: 电话: 办公室:综合楼 C318.
Advertisements

现代电子技术实验 ——综合实验之单片机部分
主讲人:刘利 交通大学网络控制课程系列 上海交通大学机电控制研究所 交通大学网络控制课程系列 考试课、专业基础课、必修课
第7章 AT89S51单片机的 串行口 1.
2017/3/22 如何用C 来完成SN8系列芯片的程序设计 2017/3/22.
本章内容: 中断的概念 MCS-51单片机中断系统 外部事件中断及应用
8051 指令.
單晶片MCS-51 C語言入門實習 第1章 微電腦與單晶片MCS-51架構 作者:董勝源.
第四章 指令系统及汇编语言程序设计.
本章分为四节,主要介绍: 4.1 程序编制的方法和技巧 4.2 源程序的编辑和汇编 4.3 基本程序结构 4.4 常用程序举例.
项目2 2个LED发光二极管控制 知识与能力目标 熟悉单片机的I/O口功能与特性。
得技通电子 问题 1 右何者非為假指令 (1) XRL (2) EQU (3) MACRO (4) ORG.
本章小结 C51单片机指令系统概述 C51单片机寻址方式 C51单片机指令系统
单片机原理与应用.
第2章 MCS-51单片机指令系统与汇编语言程序设计
報告者:朱耿育 紀翔舜 組員:詹以群 張永傑 指導老師:梁新潁
复 习 一. 计算机中的数和编码 1. 2,10,16进制数及其之间的转换(整数) 按权展开,除x取余 2
第二部分 微机原理 第4章 汇编语言 程序设计 主讲教师:喻红.
本章内容: 中断的概念 MCS-51单片机中断系统 外部事件中断及应用
第二部分 微机原理 第3章 MCS-51的 指令系统 主讲教师:喻红.
一、任务描述 二、任务分析 三、任务演示 四、相关知识 五、任务布置. 一、任务描述 二、任务分析 三、任务演示 四、相关知识 五、任务布置.
第6章 MCS - 51单片机内部定时器/ 计数器 及串行接口 6.1 定时器/计数器的结构及工作原理 6.2 方式和控制寄存器
單晶片微電腦控制實習 使用計時中斷作走馬燈 計時器的基礎實習 國立大甲高工 電機科 2018年11月21日
第3章 AT89C51指令系统 3.1基本概念内部结构和引脚功能 指令、指令系统、机器代码
单片机原理及应用 MCS-51系列单片机的基本硬件结构 MCS-51指令系统 MCS-51单片机的系统扩展与应用.
第五章 单片机的C语言程序设计及仿真调试.
初始化串列通訊埠 在啟始串列傳輸介面時有以下3個步驟:假設傳輸的通訊協定為9600bps,傳送8個位元資料,沒有同位位元,1個停止位元。
第五章 单片机的C语言程序设计及仿真调试.
单片机原理及应用 ——基于Proteus与Keil C 哈工大出版社
第2章 单片机的结构原理与 简单应用 (课时:10学时).
本 章 重 点 单片机的简单I/O扩展 8255A可编程并口芯片 8279可编程键盘/显示器接口芯片 单片机键盘接口技术
单片机原理 单 片 机 单片机接口技术 单片机应用技术.
第七章 定时/计数器.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
周国运 Keil C51应用 主 页:
第2章 单片机系统组成原理 2.1 MCS-51单片机组成原理 2.2 单片机复位电路设计 2.3 MCS-51存储器配置
6.1 输入/输出 6.2 CPU与外设数据传送方式 6. 3 MCS-51中断系统 6. 4 中断应用举例
本 章 重 点 单片机的结构特点 单片机的存储器特点 I/O端口的特点 CPU时序 课时安排:3个课时.
一、任务描述 二、任务分析 三、任务演示 四、相关知识 五、任务布置. 一、任务描述 二、任务分析 三、任务演示 四、相关知识 五、任务布置.
第四章 指令系统及汇编语言程序设计.
第4章 中断技术 一个完整的微机系统是由硬件和软件共同构成的。微机系统的硬件有CPU、存储器和I/O口,外设组成。CPU与存储器之间的信息交换比较简单,而CPU与外设之间进行信息交换之前必须确定外设是否准备好,即选择I/O传送方式。I/O传送方式有4种:无条件、查询、中断和DMA。本章学习中断传送方式的有关内容。
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C语言程序设计 主讲教师:陆幼利.
第四章 MCS-51定时器/计数器 一、定时器结构 1.定时器结构框图
第3章 MCS-51指令系统 介绍MCS—51系列单片机的寻址方式 介绍MCS—51系列单片机的指令系统
第4章 80C51系列指令系统 教学目的:熟悉80C51系列单片机的寻址方式及 每一种寻址方式对应的寻址空间;掌 握每一条指令功能。
本章内容 MCS-51单片机指令系统的格式 MCS-51单片机寻址方式 指令系统的分析
汽车单片机应用技术 学习情景1: 汽车空调系统的单片机控制 主讲:向楠.
单片机原理与应用 主讲人:张荣刚 福建师范大学福清分校.
KEIL C51的应用 C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并具备汇编语言的功能。目前,使用C语言进行程序设计已经成为软件开发的一个主流。用C语言开发系统可以大大缩短开发周期,明显增强程序的可读性,便于改进和扩充。而针对8051的C语言日趋成熟,成为了专业化的实用高级语言。
(Random Access Memory)
第三章 MCS 51的硬件结构.
实验三 16位算术逻辑运算实验 不带进位控制的算术运算 置AR=1: 设置开关CN 1 不带进位 0 带进位运算;
第三章 计算机系统的组成与工作原理.
汽车单片机应用技术 学习情景1: 汽车空调系统的单片机控制 主讲:向楠.
单片机原理及应用 实践部分 主讲人:刘 强 四川工商学院单片机教学团队 单片机原理及应用 实践部分 主讲人:刘 强
2. MCS-51单片机的组成及结构分析 2.1 MCS-51单片机的内部结构及结构特点
四、手工汇编 完成汇编的方法有两种:手工汇编和汇编程序汇编 1.手工汇编步骤 A
单片机原理与应用.
单片机原理及应用 (C语言版) 第8章 单片机系统扩展
单片机应用技术 (C语言版) 第4章 C51程序设计入门
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
循环程序设计 在程序中包含重复执行的程序段称为循环程序设计。循环程序可以使程序结构性强、可读性好,从而大大提高了程序质量。
上节复习(11.14) 1、方式2、方式0的特点? 2、定时/计数器的编程要点? 3、实验5方案优化问题.
单片机应用技术 (C语言版) 第3章 MCS-51指令系统及 汇编程序设计
上节复习(11.7) 1、定时/计数器的基本原理? 2、定时/计数器的结构组成? 3、定时/计数器的控制关系?
第4章 MCS-51汇编语言程序设计 教学基本要求: (1)、了解MCS-51汇编语言程序设计的特点;
第2章 80C51单片机的硬件结构 教学基本要求: (1)、熟悉单片机的定义、名称、分类方法;
第1章 微型计算机基础.
主讲教师:廉哲 QQ: 电话: 办公室:综合楼C318
Presentation transcript:

本 章 重 点 单片机的C程序设计基础 单片机内部资源的C编程 课时安排:6个课时

C51 程序设计基础 C是一种源于UNIX操作系统的语言,它是一种结构化语言,可以产生紧凑代码。C可以进行许多机器级函数控制而不用汇编语言。与汇编相比,有如下优点: (1)对单片机的指令系统不要求了解,仅要求对8051的存储器结构有初步了解; (2)寄存器分配、不同存储器的寻址以及数据类型等细节可由编译器管理; (3)程序有规范的结构,可分为不同的函数,这种方式可使程序满足结构化的要求;

C语言作为一种非常方便的语言而得到广泛的支持,C语言程序本身并不依赖于机器硬件系统,基本上不作修改就可根据单片机的不同较快地移植过来。 (4)关键字及运算函数可用近似人的思维过程方式使用; (5)编程及程序调试时间显著缩短,从而提高效率; (6)提供的库包含许多标准子程序,具有较强的数据处理能力; (7)已编号的程序可容易地植入新程序中,因为它具有方便的模块化编程技术。 C语言作为一种非常方便的语言而得到广泛的支持,C语言程序本身并不依赖于机器硬件系统,基本上不作修改就可根据单片机的不同较快地移植过来。

用C语言编写MCS–51单片机的应用程序,虽然不像用汇编语言那样具体地组织、分配存储器资源和处理端口数据,但在C语言编程中,对数据类型与变量的定义,必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位。 用C语言编写单片机应用程序与编写标准的C语言程序的不同之处就在于根据单片机存储结构及内部资源定义相应的C语言中的数据类型和变量,其它的语法规定、程序结构及程序设计方法都与标准的C语言程序设计相同。

一、C51的程序结构 二、C51的数据类型 与一般C语言的结构相同,以main()函数为程序人口,程序体中包含若干语句还可以包含若干函数。 常用的数据类型有: 位型 字符型 整型 长整型 浮点型 bit char int long int float 1位 1字节 2字节 4字节 4字节 数组型 指针型

三、C51数据的存储类型 data bdata idata pdata xdata code 存储类型 与存储空间的对应关系 直接寻址片内RAM区,访问速度快(128byte) 可位寻址片内RAM区,运行位和字节混合访问(16byte) 间接寻址片内RAM区,可访问片内全部RAM(256byte) 分页寻址片外RAM区(256byte),由MOVX @R0访问 片外RAM区(64Kbyte),由MOVX @DPTR访问 程序存储器ROM区,由MOVC @A+DPTR访问

unsigned int pdata dim; unsigned char xdata vector[10][10]; 变量的存储类型定义举例: char var; bit bdata flag; float idata x, y; unsigned int pdata dim; unsigned char xdata vector[10][10]; #define uint unsigned int #define uchar unsigned char (1)var被定义在片内RAM区,地址00H~0FFH。 (2)flag被定义在片内RAM的可位寻址区,地址20H~2FH。 (3)x,y被定义在片内RAM区,只能用间接寻址方式。 (4)dim被定义的片外RAM区,并用 MOVX @Ri访问。 (5)vector被定义在片外RAM区, 并用 MOVX @DPTR访问。

四、C51数据的存储模式 如果在定义变量时略去存储类型标志符,编译器会自动选择默认的存储类型。默认的存储类型进一步由SMALL、COMPACT、LARGE存储模式指令限制。 例如:若声明char var;在SMALL存储模式下,var变量被定位在DATA存储区;在COMPACT存储模式下, var变量被定位在PDATA存储区;在LARGE存储模式下,var变量被定位在XDATA存储区。 存储模式:存储模式决定了变量的默认存储类型,参数传递区和无明确存储类型的说明。

参数及局部变量放入可直接寻址的片内RAM区 (最大128字节,默认存储类型是DATA),因此访 问十分方便。 存储模式 说明 SMALL 参数及局部变量放入可直接寻址的片内RAM区 (最大128字节,默认存储类型是DATA),因此访 问十分方便。 参数及局部变量放入分页片外RAM区(最大256 字节,默认存储类型是PDATA),通过寄存器R0和 R1间接寻址。 LARGE COMPACT 参数及局部变量放入片外RAM区(最大64K字 节,默认存储类型是XDATA), 使用数据指针DPTR 来间接寻址。 用DPTR进行访问效率较低。

五、C51包含的头文件 通常有:reg51.h math.h absacc.h stdio.h stdlib.h 注:这些头文件的位置是“C:\C51\INC”。

六、C51的运算符 与C语言基本相同: + - * / (加 减 乘 除) + - * / (加 减 乘 除) > >= < <= (大于 大于等于 小于 小于等于) == != (测试等于 测试不等于) && || ! (逻辑与 逻辑或 逻辑非 ) >> << (位右移 位左移) & | (按位与 按位或) ^ ~ (按位异或 按位取反)

七、C51的基本语句 与标准C语言基本相同: if 选择语言 switch/case 多分支选择语言 while 循环语言 do-while 循环语言 for 循环语言

八、C51对特殊功能寄存器的定义 为了能直接访问特殊功能寄存器SFR,C51提供了一种自主形式的定义方法,这种定义方法与标准C语言不兼容,只适用于对8051系列单片机进行C编程。 这种定义的方法是引入关键字“sfr”,语法如下: sfr sfr_name ‘=’ int constant; 例如: sfr SCON = 0x98; sfr TMOD = 0x89;

对于要经常单独访问SFR中的位,C51使用关键字“sbit”可以访问位寻址对象。有以下几种方法: 方法一: 例如:sfr PSW = 0xD0; sbit OV = PSW^2; 方法二: 例如:sbit OV = 0xD0^2; 方法三: 例如:sbit OV = 0xD2;

九、C51对并行接口的定义 8051单片机没有专用I/O指令,其口地址与数据存储器地址是统一编址的,即把一个口当作数据存储器中的一个单元来看待。 对于8051片内I/O口用关键字“sfr”来定义。 例如: sfr P0 = 0x80; sfr P1 = 0x90;

#include <absacc.h> #define P_DATA XBYTE[0x5eff] 对于片外扩展I/O口,则根据其硬件译码地址,将其视为片外数据存储器的一个单元,使用#define语句进行定义。一旦在头文件或程序中对这些片内外I/O口进行定义后,在程序中就可以自由使用这些口了。 例如: #include <absacc.h> #define P_DATA XBYTE[0x5eff] #define P_CON XBYTE[0x5fff] XBYTE被定义为(char *)0x20000L,0x20000L为一般指针,其存储类型为2,基地址为0000H,这样XBYTE成为指向xdata零地址的指针。而XBYTE[0x5eff]则是外部数据存储器的0x5eff绝对地址。

十、C51程序设计举例 【例1】清零程序(将2000H—20FFH的内容清零) ★ 汇编语言程序 SE01: MOV R0, #00H ORG 0000H SE01: MOV R0, #00H MOV DPTR, #2000H ;(0000H)送DPTR LOO1: CLR A MOVX @DPTR, A ;0送(DPTR) INC DPTR ;DPTR+1 INC R0 ;字节数加1 CJNE R0, #00H, LOO1 ;不到FF个字节再清 LOOP: SJMP LOOP

uchar xdata *p=0x2000; /* 指针指向2000H单元 */ #include <reg51.h> #define uchar unsigned char void main( ) { int i; uchar xdata *p=0x2000; /* 指针指向2000H单元 */ for(i=0; i<256; i++) /*清零2000H-20FFH单元*/ *p = 0; p++; }

【例2】查找零的个数(在2000H--200FH中查出有几个字 节是零,把个数放在2100H单元中) ★ 汇编语言程序 ORG 0000H L00: MOV R0, #10H ;查找16个字节 MOV R1, #00H MOV DPTR, #2000H L11: MOVX A, @DPTR CJNE A, #00H, L16 ;取出内容与00H相等吗? INC R1 ;取出个数加1 L16: INC DPTR DJNZ R0, L11 ;未完继续 MOV DPTR, #2100H MOV A, R1 MOVX @DPTR, A ;相同数个数送2100H L1E: SJMP L1E

★查找零的个数C-51程序 #include <reg51.h> #define uchar unsigned char void main ( ) { uchar xdata *p=0x2000; /*指针p指向2000H单元*/ int num=0, i; for(i=0; i<16; i++) if(*p==0) num++; /* 若该单元内容为零,则n+1 */ p++; /* 指针指向下一单元 */ } p=0x2100; /* 指针p指向2100H单元 */ *p=n; /* 把个数放在2100H单元中 */

【例3】双字节无符号整数快速乘法 ★ 汇编程序 ORG 0000H ADD A, R4 MOV A, R3 XCH A, B MOV B, R7 MUL AB ;R3*R7 XCH A, R7 ;R7=(R3*R7)低字节 MOV R5, B ;R5=(R3*R7)高字节 MOV B, R2 MUL AB ;R2*R7 ADD A, R5 MOV R4, A CLR A ADDC A, B MOV R5, A ;R5=(R2*R7)高字节 MOV A, R6 MOV B, R3 MUL AB ;R3*R6 ADD A, R4 XCH A, B ADDC A, R5 MOV R5, A MOV PSW.5, C ;存CY MOV A, R2 MOV B, R6 MUL AB ;R2*R6 ADD A, R5 CLR A MOV ACC.0, C MOV C, PSW.5 ;加上次加法的位 ADDC A, B MOV R6, A JMP $

【例3】双字节无符号整数快速乘法改进 MULA1 DATA 30H MULA2 DATA 31H MULB1 DATA 32H RESUL0 DATA 34H RESUL1 DATA 35H RESUL2 DATA 36H RESUL3 DATA 37H ORG 0000H MAIN: MOV MULA1, #77H MOV MULA2, #19H MOV MULB1, #07H MOV MULB2, #11H MOV A, MULA1 MOV B, MULB1 MUL AB MOV RESUL0, A MOV RESUL1, B;第一次乘 MOV A, MULA2 ADD A, RESUL1 MOV RESUL1, A MOV A, B ADDC A, #00H MOV RESUL2, A;第二次乘

MOV RESUL2, A MOV A, B ADDC A, RESUL3 MOV RESUL3, A;第四次乘 SJMP $ END MOV A, MULA1 MOV B, MULB2 MUL AB ADD A, RESUL1 MOV RESUL1, A MOV A, B ADDC A, RESUL2 MOV RESUL2, A CLR A ADDC A, #00H MOV RESUL3, A;第三次乘 MOV A, MULA2 ADD A, RESUL2

★ C-51程序 #include <reg51.h> #define uint unsigned int #define ulong unsigned long void main( ) { uint xdata *p1=0x2000; /*双字节被乘数在2000H单元*/ uint xdata *p2=0x2002; /*双字节乘数在2002H单元 */ ulong xdata *p3=0x2010; /*4字节乘积放在2010H单元*/ *p3 = (*p1) * (*p2); }

十一、8051内部资源的C51编程 1、中断 C51编译器支持在C源程序中直接开发中断程序,因此减轻了用汇编语言开发中断程序的繁琐过程。使用该扩展属性的函数定义语法如下: 返回值 函数名 interrupt n [using m] { …… } (其中,n对应中断源的编号,m对应工作寄存器组编号)

编 号 中 断 源 入 口 地 址 1 2 3 4 表1 MCS-51中断源编号 外部中断0 0003H 定时器/计数器0 000BH 编 号 中 断 源 入 口 地 址 外部中断0 0003H 1 定时器/计数器0 000BH 2 外部中断1 0013H 3 定时器/计数器1 001BH 4 串行口中断 0023H

using m 选项用于实现工作寄存器组的切换,m是中断服务子程序中选用的工作寄存器组号(0 ~ 3)。在许多情况下,响应中断时需保护有关现场信息,以便中断返回后,能使中断前的源程序从断点处继续正确地执行下去。这在MCS-51单片机中,能很方便地利用工作寄存器组的切换来实现。即在进入中断服务程序前的程序中使用一组工作寄存器,进入中断服务程序后,由"using m"切换到另一组寄存器,中断返回后又恢复到原寄存器组。这样互相切换的两组寄存器中的内容彼此都没有被破坏。

【例4】下图所示是利用优先权解码芯片,在单片机8031的一个外部中断INT1上扩展多个中断源的原理电路图。图中是以开关闭合来模拟中断请求信号。当有任一中断源产生中断请求,能给8031的INT1引脚送一个有效中断信号,由P1的低3位可得对应中断源的中断号。 图 1 扩展多个中断源

#include <reg51.h> #define uchar unsigned char uchar status; bit flag=0; void service_int1( ) interrupt 2 using 2 /* INT1中断服务程序, { 使用第2组工作寄存器*/ flag = 1; /* 设置标志 */ status = p1 & 0x07; /* 存输入口状态 */ } void main( ) { IP = 0x04; /*置INT1为高优先级中断*/ IE = 0x84; /*INT1开中断,CPU开中断 */

while(1){ if(flag){ /* 表示有中断发生 */ switch(status){ /*选择中断源分支 */ case 0: program0( ); break; /* 处理IN0 */ case 1: program1( ); break; /* 处理IN1 */ case 2: program2( ); break; /* 处理IN2 */ case 3: program3( ); break; /* 处理IN3 */ case 4: program4( ); break; /* 处理IN4 */ case 5: program5( ); break; /* 处理IN5 */ case 6: program6( ); break; /* 处理IN6 */ case 7: program7( ); break; /* 处理IN7 */ } flag = 0; /* 处理完成清标志 */

2、定时器编程实例 【例5】设单片机的fosc=12MHz,要求在P1.0引脚上输出周期为2ms的方波。 分析:周期为2ms的方波,要求定时间隔为1ms,每次时间到P1.0取反。于是可计算出定时1ms需要计数1000次。 由于计数器是加1计数,为得到1000次计数,必须给定时器置初值“-1000”。 方法一:用T0的方式1编程,采用查询方式,

#include <reg51.h> sbit P1_0 = P1^0; void main( ) { TMOD = 0x01; /*定时器T0方式1 */ TR0 = 1; /*启动T0*/ while(1){ TH0 = -(1000 / 256); /*装载定时初值 */ TL0 = -(1000 % 256); while(!TF0){ ; } /*查询TF0=1?*/ P1_0 = !P1_0; /*1ms时间到,P1.0反相*/ TF0 = 0; /*软件清TF0*/ }

方法二:用T0的方式1编程,采用中断方式。 #include <reg51.h> sbit P1_0 = P1^0; void timer0(void) interrupt 1 using 1 { P1_0 = !P1_0; TH0 = -(1000 / 256); /*重新置定时初值 */ TL0 = -(1000 % 256); }

void main(void) { TMOD = 0x00; P1_0 = 0; TH0 = -(1000 / 256); /*装载定时初值 */ TL0 = -(1000 % 256); EA = 1; ET0 = 1; TR0 = 1; do{ ; } while(1); }

【例6】采用12 MHz晶振,在P1.0脚上输出周期为2.5s ,占空比20%的脉冲信号。 分析:12 MHz晶振,定时0.5s,可以将0.5s分解为10次50ms,则50ms的计数值是50000,选择方式1。 高电平是10倍的定时时间,低电平是40倍的定时时间.

#include <reg51.h> #define uchar unsigned char #define PERIOD 50 #define HIGH 10 timer0( ) interrupt 1 using 1 /* T/C0中断服务程序 */ { static uchar time=0; TH0= - (50000 / 256 ); /* 重置计数值 */ TL0= - (50000 % 256 ); time++; if(time <= HIGH) P1=1; /* 10次定时以内输出高电平 */ else /* 剩余40次定时输出低电平 */ if(time == PERIOD) time=0 ; P1=0 ; }

main( ) { TMOD = 0x01 ; /* 定时器0方式1 */ TH0 = - 50000 / 256 ; /* 预置计数初值 */ TL0 = - 50000 % 256 ; EA = 1; /* 开CPU中断 */ ET0 = 1 ; /* 开T/C0中断 */ TR0 = 1 ; /* 启动T/C0 */ do { ; }while(1) ; }

十二、8279编程实例 下图是8279与键盘、LED显示器的接口电路。当有键按下时,8279可用中断方式通知8031。编程实现功能为:当有键00H~0FH按下时,完成键值获取,并用LED输出显示键值。 根据接口电路中8279的片选信号和A0的解法,8279的端口地址为: 数据口地址:DFFEH 命令/状态口地址:DFFFH 设fosc=6MHz,分频次数N=10

. 8279 位驱动 Y0 Y1 Y2 Y4 Y5 Y3 A B C SL0 SL1 SL2 段 驱 动 8031 D0 D7 ALE RL0 . RL7 Y0 Y1 Y2 Y4 Y5 Y3 A B C SL0 SL1 SL2 段 驱 动 8031 74LS373 CLK A0 D0 D7 ALE P0.0 P0.7 8279 CE RD WR IRQ P2.5 INT0 OUTA3 OUTB0 74LS138

#include <reg51.h> #include <absacc.h> #define DATA XBYTE[0xdffe] #define COM XBYTE[0xdfff] #define uchar unsigned char /*table[ ]对应00H~0FH 的段码表*/ uchar code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71}; uchar idata diss[8] = {0, 1, 2, 3, 4, 5, 6, 7}; /*显示缓冲区*/ sbit flag = ACC^7; uchar keyin(void); /*取键值函数*/ uchar deky(void); /*判断是否有键按下函数*/ void disp(uchar idata *d); /*显示函数*/

void main(void) { uchar i; COM = 0xd1; /*总清除命令*/ do{ ACC = COM; }while(flag == 1); /*等待清除结束*/ COM = 0x00; /*键盘、显示方式*/ COM = 0x2a; /*时钟分频*/ while(1){ for(i=0; i<8; i++){ disp(diss); /*显示缓冲区内容*/ diss[i] = keyin( ); /*键值输入到显示缓冲区*/ }

void disp(uchar idata *d) /*显示函数*/ { uchar i; COM = 0x90; /*写显示RAM命令*/ for(i=0; i<8; i++){ COM = i + 0x80; /*写显示RAM命令*/ DATA = table[*d]; d++; } uchar deky(void) /*判断FIFO是否有键按下*/ { uchar k; k = COM; /*读8279的状态*/ return(k & 0x0f); }

uchar keyin(void) /*取键值函数*/ { uchar i; while(deky( ) == 0); /*无键按下,继续等待*/ COM = 0x40; /*读FIFO命令*/ i = DATA; i = i & 0x3f; /*取键盘数据低6位,因为有48个按键*/ return(i); /*返回键值*/ }

Keil C与汇编混合使用 所谓混合编程,就是在一个项目中,同时使用C和汇编两种语言。 C 语言和汇编语言混合编程的方法形式多样,可以是以汇编语言为主 体,在其中内嵌部分C语言;也可以是以C语言为主体在其中加入部 分汇编语言(此方法实用价值较高,而被工程师们广泛的采用)。 在此方法中,用汇编语言编写对有关硬件的驱动和处理、复杂的算 法、实时性要求较高等底层的东西,来满足单片机对某些硬件高 效、快速、精确的处理等性能上的要求。用C语言来编写程序的主体 部分。这样就将C语言的可移植性强和可读性好与汇编语言的高效、 快速及可直接对硬件进行操作等优点相结合。两者优劣互补、相得 益彰,加快我们产品的开发周期,具有极高的现实意义和实用价 值!

Keil C与汇编混合使用 一. KEIL C51的命名规则 在KEIL C51中,编译器对C语言程序中的函数会 自动的进行转换,转换规则如下表: 无参数或无寄存器参数传递的函数 void func1(void)   -> func1 含通过寄存器传递的参数 void func2(int) ->_func2 可重入函数 void func3(char)reentrant  ->?_func3

Keil C与汇编混合使用 二. KEIL  C51函数的参数传递规则 通过寄存器传递的函数参数表

Keil C与汇编混合使用 二. KEIL  C51函数的参数传递规则 函数返回值使用的寄存器列表

Keil C与汇编混合使用 方法1是利用编译控制命令#pragma asm(用来标识所插入的汇编语句的起始位置) 和 #pragma endasm (用来标识所插入的汇编语句的结束位置) ,这两条命令必须成对出现,并可以多次出现。在Keil C51中不对插入的汇编代码做任何的处理。

Keil C与汇编混合使用

Keil C与汇编混合使用 方法二、keilC中调用汇编子函数的方法 第一步:用C语言编写函数的框架,文件名为.c。如: PIDWork.c文件 void PID(void) //无参函数void PID(void) { //user1} void PID_k(void) { //user2} void PID_out(void) { //user3} long int PID_MUL(int a,int b) //有参函数long int PID_MUL(int a,int b) { //user4}

Keil C与汇编混合使用 方法二、keilC中调用汇编子函数的方法 第二步:建工程,添加PIDWork.c文件,设置编译模式,并添加相应的模式库文件。 如:编译模式为Small:variables in DATA,添加库文件C51S.LIB 第三步:选中PIDWork.c右击鼠标选中option for ,,,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”出现黑色勾。然后build,生成了PIDWork.SRC文件 第四步:将PIDWork.SRC文件扩展名改为.ASM,这样就完成了函数的接口规则,在在代码段里添加相应的汇编程序完成相应的功能。

Keil C与汇编混合使用 方法二、keilC中调用汇编子函数的方法 NAME PIDWORK ?PR?PID?PIDWORK      SEGMENT CODE ?PR?PID_k?PIDWORK    SEGMENT CODE ?PR?PID_out?PIDWORK  SEGMENT CODE ?PR?_PID_MUL?PIDWORK SEGMENT CODE ?DT?_PID_MUL?PIDWORK SEGMENT DATA OVERLAYABLE  PUBLIC _PID_MUL  PUBLIC PID_out  PUBLIC PID_k  PUBLIC PID  RSEG  ?DT?_PID_MUL?PIDWORK ?_PID_MUL?BYTE:           a?340:   DS   2  ORG  2           b?341:   DS   2

Keil C与汇编混合使用 ; ; void PID(void) //无参函数void PID(void)  RSEG  ?PR?PID?PIDWORK PID: ;添加汇编程序   RET    ; END OF PID ; ; void PID_k(void)  RSEG  ?PR?PID_k?PIDWORK PID_k: ;添加汇编程序   RET    ; END OF PID_k ;

Keil C与汇编混合使用 ; void PID_out(void)  RSEG  ?PR?PID_out?PIDWORK PID_out: ;添加汇编程序   RET    ; END OF PID_out ; ; long int PID_MUL(int a,int b) //有参函数long int PID_MUL(int a,int b)  RSEG  ?PR?_PID_MUL?PIDWORK _PID_MUL:    ; SOURCE LINE # 17  MOV   a?340,R6  MOV   a?340+01H,R7  MOV   b?341,R4  MOV   b?341+01H,R5 ;添加汇编程序   RET    ; END OF _PID_MUL  END

Keil C与汇编混合使用举例 //main.c文件 #include < reg51.h > #define uchar unsigned char #define uint unsigned int extern uint AFUNC(uchar v_achr,bit v_bflag); void main() {      bit BFLAG;      uchar mav_chr;      uint     mvintrslt;      mav_chr=0xd4; BFLAG=1;      mvintrslt=AFUNC(mav_chr,BFLAG); }

Keil C与汇编混合使用举例 //CFUNC.c文件 #define uchar unsigned char #define uint unsigned int uint AFUNC(uchar v_achr,bit v_bflag) {      uchar tmp_vchr;      uint   tp_vint;      tmp_vchr=v_achr;      tp_vint=(uint)v_bflag;      return tmp_vchr+(tp_vint<<8); }

Keil C与汇编混合使用举例 //编译后CFUNC.SRC文件 NAME CFUNC ?PR?_AFUNC?CFUNC      SEGMENT CODE ?BI?_AFUNC?CFUNC      SEGMENT BIT OVERLAYABLE      PUBLIC     ?_AFUNC?BIT      PUBLIC     _AFUNC      RSEG   ?BI?_AFUNC?CFUNC ?_AFUNC?BIT:      v_bflag?041:    DBIT    1 ; #define uchar unsigned char ; #define uint unsigned int ; ; uint AFUNC(uchar v_achr,bit v_bflag)    

Keil C与汇编混合使用举例 //编译后CFUNC.SRC文件 RSEG   ?PR?_AFUNC?CFUNC _AFUNC:      USING     0              ; SOURCE LINE # 5 ;---- Variable 'v_achr?040' assigned to Register 'R7' ---- ; {              ; SOURCE LINE # 6 ;      uchar tmp_vchr; ;      uint     tp_vint; ; ;      tmp_vchr=v_achr;              ; SOURCE LINE # 10 ;---- Variable 'tmp_vchr?042' assigned to Register 'R5' ----      MOV       R5,R7 ;      tp_vint=(uint)v_bflag;              ; SOURCE LINE # 11      MOV       C,v_bflag?041      CLR       A      RLC       A

Keil C与汇编混合使用举例 思考:C代码与汇编的优劣! //编译后CFUNC.SRC文件 ;---- Variable 'tp_vint?043' assigned to Register 'R6/R7' ---- ;      return tmp_vchr+(tp_vint<<8);              ; SOURCE LINE # 12      MOV       R6,A      MOV       R4,#00H      CLR       A      ADD       A,R5      MOV       R7,A      MOV       A,R4      ADDC      A,R6      MOV       R6,A ; }              ; SOURCE LINE # 13 ?C0001:      RET ; END OF _AFUNC      END 思考:C代码与汇编的优劣! Jc next Mov r6, #0 ret Next: mov r6, #1