Keil公司是一家业界领先的微控制器(MCU)软件开发工具的独立供应商。Keil公司由两家私人公司联合运营,分别是德国慕尼黑的Keil Elektronik GmbH和美国德克萨斯的Keil Software Inc。Keil公司制造和销售种类广泛的开发工具,包括ANSI C编译器、宏汇编程序、调试器、连接器、库管理器、固件和实时操作系统核心(real-time kernel)。有超过10万名微控制器开发人员在使用这种得到业界认可的解决方案。其Keil C51编译器自1988年引入市场以来成为事实上的行业标准,并支持超过500种80C51变种。Keil公司在2007年被ARM公司收购。其两家公司分别更名为ARM Germany GmbH和ARM Inc和。
2009年2月,Keil公司发布了Keil μVision4,Keil μVision4引入灵活的窗口管理系统,使开发人员能够使用多台监视器,提供可在虚拟接口上随意放置窗口的完整控制能力。新的用户界面可以更好地利用屏幕空间和更有效地组织多个窗口,提供一个整洁,高效的环境来开发应用程序。新版本支持更多最新的80C51兼容芯片及ARM芯片,还添加了一些其他新功能,如系统查看器(System Viewer)窗口、多项目工作空间(Multi-Project Workspace)等。
附A.1 Keil μVision4 集成开发环境
附A.1.1简介
Keil μVision4集成开发环境(Integrated Development Environment,IDE)是一个基于Windows的开发平台,它包含高效的源代码编辑器、项目(Project)管理器和程序生成(MAKE)工具。Keil μVision4支持所有的80C51嵌入式应用工具,它包括C/C++编译器宏汇编器、连接/定位器和一个HEX文件生成器。Keil μVision4通过以下特性加速MCU嵌入式应用系统的开发过程:
★ 全功能的源代码编辑器;
★ 器件库用来配置开发工具设置; ★ 项目管理器用来创建和维护项目;
★ 集成的MAKE工具可以汇编、编译和连接用户的嵌入式应用; ★ 所有开发工具的设置都是以对话框的形式出现的; ★ 具有真正的源代码级的对CPU和外围器件的调试器;
★ 高级GDI接口用来在目标硬件上进行软件调试以及和Monitor-51进行通信; ★ 与开发工具手册、器件数据手册和用户指南有直接的链接。 1.C51 编译器和A51汇编器
源代码由μVision4创建,并被C51编译成A51汇编。编译器和汇编器从源代码生成可重定位的目标文件。
Keil C51编译器完全遵照ANSIC语言标准,支持C语言的所有标准特性。另外,直接支持80C51结构的几个特性被添加里面。
Keil A51宏汇编器支持80C51及其派生系列的全部指令集。 2.LIB51库管理器
LIB51库管理器允许从由编译器或汇编器生成的目标文件创建目标库。库是一种被特别地组织过并在以后可以被连接重用的对象模块。当连接器处理一个库时,仅仅那些被使用的目标模块才被真正使用。
3.BL51连接器/定位器
BL51连接器/定位器利用从库中提取的目标模块和由编译器或汇编器生成的目标模块创建一个绝对地址的目标模块。一个绝对地址目标模块或文件包含不可重定位的代码和数据。所有的代码和数据被安置在固定的存储器单元中。
此绝对地址目标文件可以用来: 写入EPROM或其他存储器件。
通过μVision4调试器来模拟和调试。 通过仿真器来测试程序。 4.OH51目标文件转换器
OH51目标文件转换器可以把前面编译连接好的目标文件转换成能写入EPROM中的HEX文件。
5. μVision4调试器
μVision4源代码级调试器是一个理想的快速、可靠的程序调试器。此调试器包含一个高速模拟器,能够模拟整个8051系统,包括片上外围器件和外部硬件。当从器件库中选择器件时,这个器件的特性将自动配置。
μVision4调试器为在实际目标板上测试程序提供了以下2种方法: 安装MON51目标监控器到目标系统并且通过Monitor-51接口下载程序; 利用高级的GDI(AGDI)接口,把μVision4调试器绑定到目标系统。 6.Monitor-51
μVision4调试器支持用Monitor-51进行目标板调试。此监控程序驻留在目标板的存储器里,它利用串口和μVision4调试器进行通信。利用Monitor-51,μVision4调试器可以对目标硬件实行源代码级的调试。
7.RTX-51实时操作系统
RTX-51实时操作系统是一个针对8051系统的多任务核。RTX-51实时内核从本质上简化了对实时事件反应速度要求高的复杂应用系统的设计、编程和调试。RTX-51实时内核是完全集成到C51编译器中的,从而方便使用。任务描述表和操作系统的连接由BL51连接器/定位器自动控制。
附A.1.2安装
了解了Keil μVision4的一些基本概况后,下面开始在计算机上搭建MCU的集成开发环境。操作步骤如下:
首先准备Keil μVision4安装源文件,双击μVision4的Setup安装文件,弹出Keil μVision4安装的欢迎界面,如图A-1所示。
图A-1 Keil μVision4欢迎界面
单击“Next”按钮,弹出“License Agreement”对话框,如图A-2所示。这里显示了一些用户安装的协议和许可的要求,选择“I agree to all the terms of the preceding License Agreement”即可,否则无法进入下一步。
图A-2 “License Agreement”对话框
单击“Next”按钮,弹出“Folder Selection”对话框,如图A-3所示。系统默认安装在“C:\\Keil”文件夹下。在这里,单击“Browse”按钮,可以选择安装的目录。
图A-3 “Folder Selection”对话框
单击“Next”按钮,弹出”Customer Information”对话框,如图A-4所示。此时,用户需要输入用户名、公司名称和E-mail,缺一不可。
图A-4 用户信息输入
单击”Next”按钮,下面便开始自动安装。
Keil μVision4安装完成后,弹出安装完成对话框,如图A-5所示。这里的几个选项的含义如下。
图A-5 完成安装
Show Release Notes:显示安装的版本注释信息。
Add Example projects to the recently used project list:添加一个示例程序到当前项目列表中。
Retain currentμVision configuration:保持当前的设置(如果是第一次安装,则不存在这个选项)。
最后,单击\"Finish\"按钮,便可以结束Keil μVision4集成开发环境的安装。需要注意的是,刚刚安装完的版本是试用版(Evaluation Version),代码长度有2KB限制。
附A.1.3 Keil μVision4集成开发环境界面
安装完成后,会在桌面上出现Keil μVision4程序的图标,并在“开始”程序里增加“Keil μVision4”程序项。从“开始”程序里选择“Keil μVision4”程序项或者直接双击桌面上的Keil μVision4程序图标,即可启动Keil μVision4。启动Keil μVision4后,如果是第一次运行,则打开工程项目“Hello”,如图A-6所示。
图A-6 Keil μVision4集成开发环境界面
这里可以看到,Keil μVision4集成开发环境具有典型的Windows界面风格。整个编程界面主要包括菜单栏、工具栏、项目管理区、源代码工作区和输出信息窗口。另外,还有一些功能窗口将在后面逐步介绍。下面我们将带领读者逐一认识Keil μVision4集成开发环境的主要组成部分。
附A.1.4 Keil μVision4菜单命令
Keil μVision4的菜单栏提供了项目操作、编辑操作、编译调试及帮助等各种常用操作。所有的操作基本上都可以通过菜单命令来实现。为了快速执行Keil μVision4的许多功能,有些菜单命令在工具栏上还具有工具条。为了更快速执行一些功能,Keil μVision4提供了比工具栏上的工具条更为快捷的操作,即快捷键。在Keil μVision4集成开发环境中不仅提供了常用功能的默认快捷键,同时用户也可以根据自己的需要自定义快捷键。下面就菜单命令、工具条、快捷键分别进行介绍。
1.File菜单
File菜单和标准的Windows软件的File菜单类似,提供了项目和文件的操作功能。File菜单各个命令的功能如表A-1所示。
表A-1 File菜单
菜单命令 New Open Close Save Save as Save all Device Database License Management Print Setup 工具条 快捷键 Ctrl+N Ctrl+O 功能说明 创建一个新的空白文件 打开一个已存在的文件 关闭当前打开的文件 保存当前打开的文件 当前文件另存为 保存所有打开的文件 打开器件库 产品注册管理 设置打印机 Ctrl+S Print Print Preview 1 .. 10 Exit Ctrl+P 打印当前文件 打印预览 列出最近打开的源文件或文本文件 退出KeilμVision4 2.Edit菜单 Edit菜单提供了常用的代码编辑操作命令。Edit菜单各个命令的功能如表A-2所示。
表A-2 Edit菜单
菜单命令 Undo Redo Cut Copy Paste Navigate Backwards Navigate Forwards Insert/Remove Bookmark Go to Next Bookmark Go to Previous Bookmark Clear All Bookmarks Find Replace Find in Files Incremental Find Outlining Advanced Configuration 工具条 快捷键 Ctrl+Z Ctrl+Y Ctrl+X Ctrl+C Ctrl+V Ctrl+Shift+- Ctr+- Ctrl+F2 F2 Shift+F2 Ctrl+F Ctrl+H Ctrl+I 取消上次操作 重复上次操作 剪切选定的内容 复制选定的内容 粘贴已复制的内容 光标移动到使用Find或go to line命令的前一行 光标移动到使用Find或go to line命令的后一行 设置/取消当前行的标签 光标移动到下一个标签 光标移动到上一个标签 在当前文件中查找 替换 渐进式寻找 源代码概要显示模式 各种高级编辑命令 颜色、字体等高级配置 功能说明 Ctrl+Shift+F2 清除当前文件的所有标签 Ctrl+Shift+F 在多个文件中查找 3.View菜单
View菜单提供了在源代码编辑和仿真调试过程中,各个窗口和工具栏的显示和隐藏命令。View菜单各个命令的功能如表A-3所示。
表A-3 View菜单
菜单命令 Status Bar Toolbars Project Window Books Window Functions Window Templates Window Source Browser Window Build Output Window Find in Files Window Full Screen Command Window 工具条 功能说明 显示/隐藏状态条 显示/隐藏工具栏 显示/隐藏项目管理窗口 显示/隐藏参考书窗口 显示/隐藏函数窗口 显示/隐藏模板窗口 显示/隐藏资源浏览器窗口 显示/隐藏输出信息窗口 显示/隐藏在所有文件中查找文本窗口 显示/隐藏全屏显示窗口 调试模式下的菜单命令 显示/隐藏命令行窗口 Disassembly Window Symbols Window Registers Window Call Stack Window Watch Windows Memory Windows Serial Windows Analysis Windows Trace System Viewer Toolbox Window Periodic Window Update 显示/隐藏反汇编窗口 显示/隐藏字符变量窗口 显示/隐藏寄存器窗口 显示/隐藏堆栈窗口 显示/隐藏变量子菜单观察窗口 显示/隐藏存储器子菜单窗口 显示/隐藏串行口观察子菜单窗口 显示/隐藏分析子菜单窗口 显示/隐藏跟踪子菜单窗口 显示/隐藏外设子菜单窗口 显示/隐藏自定义工具条窗口 在程序运行时刷新调试窗口 4.Project菜单
Project菜单提供了MCU项目的创建、设置和编译等命令。Project菜单各个命令的功能如表A-4所示。
表A-4 Project菜单
菜单命令 New µVision Project... New Multi-Project Workspace... Open Project... Close Project Export Manage Remove object Options for object Clean target Build target Rebuild all target files Batch Build... Translate file Stop build 1 .. 10 工具条 快捷键 功能说明 创建新项目 创建多项目工作空间 打开一个已存在的项目 关闭当前项目 导出当前一个或多个项目为μVision3格式 管理项目的包含文件、库的路径及多项目工作空间 为当前项目选择一个MCU类型 从当前项目中移除选择的文件或项目组 设置当前文件、项目或项目组的配置选项 清除编译过程中创建的中间文件 编译文件并生成应用文件 重新编译所有文件并生成应用文件 批量编译文件并生成应用文件 停止编译当前项目 列出最近打开的项目(最多10个) Alt+F7 Select Device for Target name... F7 Ctrl+F7 编译当前文件 5.Flash菜单
Flash菜单提供了下载程序、擦除MCU程序存储器等操作。这里的命令需要外部的编程器支持才可以使用。Flash菜单各个命令的功能,如表A-5所示。
表A-5 Flash菜单
菜单命令 Download Erase Configure Flash Tools... 工具条 功能说明 下载MCU程序 擦除程序存储器 打开配置工具 6.Debug菜单
Debug菜单中的命令大多用于仿真调试过程中,提供了断点、调试方式及逻辑分析等功能。Debug菜单各个命令的功能如表A-6所示。
表A-6 Debug菜单
菜单命令 Start/Stop Debug Session Reset CPU Run Stop Step Step over Step out Run to Cursor line Show Next Statement Breakpoints Insert/Remove Breakpoint Enable/Disable Breakpoint Disable All Breakpoints Kill All Breakpoints OS Support Execution Profiling Memory Map Inline Assembly Function Editor (Open Ini File) Debug Settings 工具条 快捷键 Ctrl+F5 功能说明 开始/停止仿真调试模式 复位CPU(MCU) 运行程序,直到遇到一个断点 停止运行程序 单步执行程序,遇到子程序则进入 单步执行程序,跳过子程序 程序执行到当前函数的结束 程序执行到光标所在行 显示下一条指令 打开断点对话框 设置/取消当前行的断点 使能/禁止当前行的断点 禁用所有断点 打开查看事件、任务及系统信息的子菜单 打开一个带有配置选项的子菜单 打开存储器空间配置对话框 对某一行进行重新汇编,可以修改汇编代码 编辑调试函数和调试配置文件 设置调试参数 F5 F11 F10 Ctrl+F11 Ctrl+F10 Ctrl+B F9 Ctrl+F9 Ctrl+Shift+F9 取消所有断点 7.Peripherals菜单
Peripherals菜单提供了MCU各种硬件资源的仿真对话框。这里的所有命令都只在仿真调试环境下才显示并可以使用,而且显示的资源内容随用户选择的MCU型号的不同而不同。这里列出一些常用到的Peripherals菜单命令的功能,如表A-7所示。
表A-7 Peripherals菜单
菜单命令 Interrupt I/O Ports Serial Timer Watchdog A/D Converter D/A Converter I²C Controller CAN Controller 功能说明 打开中断仿真对话框 打开并行端口仿真对话框 打开串口仿真对话框 打开定时器仿真对话框 打开看门狗仿真对话框 打开A/D转换器仿真对话框 打开D/A转换器仿真对话框 打开I2C总线控制器仿真对话框 打开CAN总线控制器仿真对话框 8.Tools菜单
Tools菜单提供了一些第三方软件的支持,例如PC-Lint。用户需要额外安装相应的软件才可以使用。Tools菜单一般使用得比较少,这里仅列出各个命令的功能,如表A-8所示。
表A-8 Tool菜单
菜单命令 Set-up PC-Lint Lint Lint All C-Source Files Customize Tools Menu... 功能说明 配置PC-Lint程序 用PC-Lint程序处理当前编辑的文件 用PC-Lint程序处理项目中所有的C源代码文件 自定义工具菜单 9.SVSC菜单
SVSC菜单提供了程序的版本控制,该菜单下仅包括“Configure Version Control”一个命令,用于配置软件版本。
另外,Windows菜单下提供了对工作区窗口布局的管理,Help菜单提供了一些帮助信息,这里不再具体介绍。
附A.2 Keil μVision4汇编语言程序的调试方法
Keil μVision4集成开发环境中包括一个项目管理器,它可以使基于80C51内核的MCU应用系统设计变得简单。要创建一个应用,需要按下列步骤操作:
1.启动Keil μVision4,新建一个项目文件并从器件库中选择一个器件; 2.新建一个源文件并把它加入到项目中; 3. 设置目标硬件选项;
4.编译项目并生成可以编程到程序存储器的HEX文件; 5. 软件模拟调试及下载到MCU中进行仿真调试。
下面通过一个实例,详细介绍如何在Keil μVision4集成开发环境中调试80C51系列MCU的汇编语言程序。
【例A-1】假设晶振频率为11.0592MHz,将MCU片外RAM中40H—5FH单元中的内容全部移到片内相同地址区域,并将原数据区全部清零。
附A.2.1启动Keil μVision4并创建一个项目
双击桌面Keil μVision4程序图标或单击开始菜单中的Keil μVision4程序项,启动Keil μVision4集成开发环境。
要新建一个项目文件,可以从Keil μVision4的Project菜单中选择“New Project”项,打开“Create New Project”对话框,如图A-7所示。
图A-7 “Create New Project”对话框
在此对话框的“文件名”栏中输入项目文件名。建议为每一个项目建立一个独立的文件夹。首先,在下拉列表中选择要保存的位置,最好选择逻辑盘D或E(不要保存在系统盘C,避免因系统重新安装而丢失文件)。单击“新建文件夹”,,得到一个空文件夹,给该文件夹重命名为“test”(文件夹的名字最好能够体现项目名称)。双击该文件夹,在“文件名(N)”栏中输入项目的名称,如“movedata”,创建一个文件名为“movedata.uvproj”的新项目文件。
单击“保存(S)”按钮,将弹出“Select Device for Target ‘Target 1’”对话框,提示为项目选择一个MCU。在该对话框中,“Data base”列表框中显示出各个MCU的生产商。首先找到选用的MCU生产商,单击前面的“+”号,显示出Keil μVision4所支持的该公司的MCU型号列表,单击其中选定的MCU型号。如本例中,选择Atmel公司的型号为AT89S52的MCU,如图A-8所示。
图A-8 “Select Device for Target ‘Target 1’”对话框
单击“OK”按钮,弹出如图A-9所示的对话框,提示是否将标准8051启动代码复制到项目文件夹中并将该文件添加到项目中去。
图A-9 复制启动代码提示对话框
在Keil μVision4中,启动代码在复位目标系统后立即被执行。启动代码主要实现以下功能:
★ 清除内部数据存储器; ★ 清除外部数据存储器; ★ 清除外部页存储器;
★ 初始化small模式下的可重入栈和指针; ★ 初始化large模式下的可重入栈和指针; ★ 初始化compact模式下的可重入栈和指针; ★ 初始化8051硬件栈指针;
★ 传递初始化全局变量的控制命令或者在没有初始化全局变量时给main函数传递命令。
在每一个启动文件中,提供了可供用户自己修改有来控制程序执行的汇编常量。如果只是调试简单程序,可以选择“否(N)”,如果项目复杂可选择“是(Y)”。用户可根据需要修改启动代码,但一般不建议修改启动代码。
附A.2.2新建一个源文件并把它加入到项目中
从“File”菜单中选择“New”项新建一个源文件,或者单击工具栏上的按钮一个空白的编辑窗口,用户可以输入程序源代码。输入以下程序源代码:
ORG 0000H LJMP MAIN ORG 0040H MAIN:
MOV SP,#70H
MOV R0,#40H
MOV R1,#20H LOOP: MOV P2,#00H; 软件仿真时必须有 MOVX A,@R0 MOV @R0,A MOV A,#00H MOVX @R0,A INC R0
DJNZ R1,LOOP END
,打开
从“File”菜单中选择“Save”项或者单击工具栏中的保存按钮,将文件保存为想要的名字。如果使用汇编语言编写程序,则文件的后缀名是:.asm或者.a51,如test.asm,如图A-10所示(如果使用C语言编写程序,则文件的后缀名是:.c,以下步骤与汇编语言程序相同)。保存后,Keil μVision4将高亮显示汇编语言语法字符,如图A-11所示。
图A-10 保存源文件 图A-11 保存后程序显示界面
源文件创建完成后,就可以将它加入到项目中(如不加入,则无法对此文件操作)。Keil μVision4提供了几种方法让用户把源文件加入到项目中。
(1)在“Project Workspace”(项目管理器)窗口中单击“Target 1”前面的“+”号,
展开下一层的“Source Group 1”文件夹,在“Source Group 1”文件夹上单击鼠标右键,弹出快捷菜单,如图A-12所示。从弹出的快捷菜单中单击“Add Files to Group ‘Source Group 1’…”项,弹出“Add Files to Group ‘Source Group 1’…”对话框,如图A-13所示。
图A-12 将源文件加入到项目中
图A-13 “Add Files to Group ‘Source Group 1’…”对话框
在该对话框中,默认的文件类型是“C Source file (*.c)”。若使用汇编语言进行设计,则需要从“文件类型”下拉列表框中选择“Asm Source file (*.S*;*.src;*.a*)”文件类型。这样,以.asm为扩展名的汇编语言程序文件才会出现在文件列表框中。从文件列表框中选择要加入的文件并双击即可添加到项目中;也可以单击选中文件,然后单击“Add”按钮将该文件加入项目中。添加文件后,对话框不会自动关闭,而是继续等待添加其它文件,用户可单击“Close”按钮,关闭对话框。当给项目添加文件成功后,项目管理器的“Source Group 1”文件夹前面会出现“+”号,单击它可看到test.asm文件已经包含在项目中了。双击它即可打开进行修改。
附A.2.3设置目标硬件选项
Keil μVision4允许用户为目标硬件设置选项。可以通过单击工具条图标、菜单“Project”的“Options for Target ‘Target 1’…”项或者在“Project Workspace”窗口的“Target 1”上单击鼠标右键,打开“Options for Target ‘Target 1’…”对话框。在各选项卡中,可以修改与目标硬件及所选MCU的片上集成器件的所有参数,如图A-14所示。
图A-14 Options for Target ‘Target 1’对话框
主要设置CPU的时钟频率、编译器的存储模式等。晶振频率设置应与实际使用的晶振频率相同。如果仅进行软件模拟调试,则采用默认设置即可。
附A.2.4编译项目并生成可以编程到程序存储器的HEX文件
单击工具栏中的“Rebuild”图标,可以编译所有的源文件并生成应用。当程序中有语法错误时,Keil μVision4将在“Build Output”窗口显示错误或者警告信息。双击一行错误提示信息,将打开此信息对应的文件,并定位到语法错误处,如图A-15所示。
在错误信息上双击鼠标,光标会自动定位到出现该错误的程序行上。例如,如图A-15所示,出现“test.asm(15): error A45: UNDEFINED SYMBOL (PASS-2)”(未定义符号)错误信息,双击该信息,光标定位到出现该错误的行上。用户很容易发现错误原因是将标号“LOOP”错写成“LOO”,漏掉字母P。由输入引起的用户常犯的编译错误还有:错将数字0输成字母o,使用中文输入法输入了全角逗号(,)和冒号(:),大于9FH(如A8H)的十六进制数忘记在前面加上数字0(正确写法0A8H)等。根据错误信息提示,修改程序中出现的错误,直到编译成功为止。一旦编译成功,则显示如图A-16所示信息。提示信息最后一行为“\"movedata\" - 0 Error(s), 0 Warning(s).”,不但没有错误,而且最好也没有警告。
需要注意的是,Keil μVision4默认是不生成HEX文件的。这时就需要设置目标硬件选项中的“Output”选项卡,选中“Create HEX File”前面的复选框,如图A-17所示。
图A-15 编译出现错误信息时的提示
图A-16 编译成功提示信息 图A-17 生成HEX文件的选项
附A.2.5软件模拟调试及下载到MCU中进行仿真调试
一旦编译成功,就可以进行程序的仿真调试了。对于程序的调试,有两种方式:一种是软件模拟仿真调试,另一种就是下载到硬件仿真器或者MCU中进行在线仿真调试。一般情
况下,首先使用软件模拟仿真调试,通过之后,再用硬件仿真器或者直接下载到MCU中进行在线仿真调试。由于软件模拟仿真调试与在线仿真调试方法基本相同,所以就以软件模拟仿真调试为例,介绍程序的调试方法。
为了对前面编写的程序能够在不连接硬件仿真器或者MCU的情况下进行仿真调试(即软件模拟),需对Keil μVision4做一下设置。按照2.3设置目标硬件选项打开“Options for Target ‘Target 1’…”对话框,选中“Debug”页,如图A-18所示。一般情况下,如果没有进行硬件仿真,则“Use Simulator”单选框是默认选中的,此时,进行软件模拟调试,其它选项不做修改;否则,则选中“Use:”单选框,并在其下拉框中选择相应的硬件驱动,并点击“Settings”按钮对目标仿真硬件进行设置。
图A-18 设置“Options for Target ‘Target 1’…”对话框中的“Debug”页 通过以上设置,就可以进行软件模拟调试了。 单击工具栏中的“Start/Stop Debug Session”按钮,或者从“Debug”菜单中选中“Start/Stop Debug Session”项(其快捷键为Ctrl+F5),,开始模拟调试过程。在调试过程中,可以进行如下操作:
1.连续运行 单击工具栏中的按钮速运行。
2.停止程序运行
,或者“Debug”菜单中的“Run”(快捷键F5),可以使程序全
当程序全速运行时,可以单击工具栏中的按钮,或者“Debug”菜单中的“Stop”,
使程序停止运行。
3.复位CPU
当程序运行过一次以上后,累加器A、某些寄存器或者其它资源的值修改了,而再次运行需要恢复到初始状态,这时就需要执行复位CPU的命令。单击工具栏中的按钮“Debug”菜单中的“Reset CPU”,可以使MCU恢复到初始状态。
4.单步运行
,或者
单击工具栏中的按钮,或者“Debug”菜单中的“Step”(快捷键F11),可以执行一行程序。如果遇到函数调用,则进入函数内部并单步运行。
5.单步跳过函数运行 单击工具栏中的按钮,或者“Debug”菜单中的“Step Over”(快捷键F10),可以执行一行程序。如果遇到函数调用,则将函数调用看做一行程序运行,不进入函数内部运行。
6.运行到当前函数的结束 这种情况出现在单步运行后进入到函数内部运行程序,通过单击工具栏中的按钮或者“Debug”菜单中的“Step Out”(快捷键Ctrl+F11),以运行到当前函数的结束。
7.运行到光标行
,
单击工具栏中的按钮,或者“Debug”菜单中的“Run to Cursor Line”(快捷键Ctrl+F10),可以执行到光标所在的程序行。
8.设置断点 在要设置断点的程序行上双击鼠标左键,或者单击工具栏上的按钮,或者“Debug”菜单中的“Insert/Remove Breakpoint”(快捷键F9),可以在当前行上插入或者删除断点。只要在当前行上设置了断点,则在当前行的最左边显示一个红色的小方块。连续运行程序后,执行到该行时,程序会暂停运行。此时用户可以查看程序运行的一些中间状态和结果(累加器A、工作寄存器、SFR、数据存储器等)。
9.查看寄存器
当进入调试状态后,Keil μVision4集成开发环境中左侧的项目管理器,变成寄存器查
看器。如图A-19所示。用户可以通过这个窗口观察工作寄存器、部分SFR的内容。
图A-19 观察寄存器的内容
10.查看变量及堆栈
在调试状态中,在Keil μVision4集成开发环境中的右下侧会出现如图A-20所示的窗口,即调用堆栈和变量查看窗口(使用C语言编程调试的时候常用)。
图A-20 调用堆栈和变量查看窗口
11.查看存储器
在图A-20中单击Memory1选项卡则在在Keil μVision4集成开发环境中的右下侧会出现如图A-21所示的窗口,即存储器查看窗口。
图A-21 存储器查看窗口
默认情况下,想查看内部RAM(片内数据存储器)中的内容,需在“Address”编辑框中输入“D:0”并按回车键即可。拖动窗口的左边框可以调整窗口的大小,经过调整,最佳的显示范围如图A-22所示。
图A-22 片内数据存储器查看窗口
可以通过“View”菜单中的“Memory Windows”项,添加存储器查看窗口,这样可通过不同的窗口查看不同存储器的内容。例如,可再增加一个窗口查看外部RAM中的内容。如图A-23 所示,在在“Address”编辑框中输入“X:0”并按回车键即可。
图A-23 片外数据存储器查看窗口
要改变某个地址单元中的内容,可在上面双击鼠标左键即可修改;或者在要修改内容的单元上单击鼠标右键,弹出菜单,选择“Modify Memory at …”修改。通过弹出菜单,还可修改进制、有符号数、无符号数、ASCII码等
“Address”编辑框一般输入格式如下:
X:XXXX
其中X为:
D,查看内部RAM; X,查看外部RAM;
I,查看间接访问的内部RAM; C,查看程序ROM。
XXXX为:查看的起始地址(0000H~FFFFH)。 12.查看外部设备
单击菜单“Peripherals”可选择查看所选MCU集成的不同外部设备。例如: (1)“Interrupt”打开中断向量表窗口,在窗口里显示了所有的中断向量。如图A-24所示。对选定的中断向量可以用窗口下面的复选框进行设置。
图A-24 中断向量表窗口
(2)“I/O-Ports”:打开I/O端口(P0~P3)的观察窗口,在窗口里显示了程序运行时的端口状态。可以随时查看并修改端口的状态,从而模拟外部的输入。例如,要查看P2口的状态,可打开P2口的观察窗口,如图A-25所示。当运行到第10行时,则如图A-26所示。图中标有“√”的复选框表示这一位的值是1,没有的为0。对于不同的MCU,可能图A-25、图A-26的显示略有不同。
图A-25 刚进入调试状态时P2口的查看窗口 图A-26 运行到程序行第10行时P2口的查看窗口
(3)“Serial”:打开串行口的观察窗口,可以随时修改窗口里显示的不同状态。 (4)“Timer”:打开定时器的观察窗口,可以随时修改窗口里显示的不同状态。
除此以外,对于不同公司生产的MCU,在“Peripherals”菜单中会出现很多与该型号
MCU相关的外部设备资源菜单项。
掌握了上述的操作过程,就可以进行基本的程序调试工作了。只有不断调试程序,才能逐步积累经验,增强对MCU的使用,做到灵活运用,熟练掌握。
附A.3 Keil C51语言
虽然MCU上使用的C语言都是直接针对具体硬件的,但任何一家公司开发的C语言,都必须符合ANSI C的标准,或者说,要与ANSI C兼容。因此,不论哪一家公司为MCU开发的C语言,其主要部分必然要与ANSI C保持一致,不同的只是非ANSI C的扩展部分,Keil C51也不例外。
Keil C51是一个兼容ANSI C的编译器,为了支持80C51系列MCU加入了一些扩展的内容。C51编译器与ANSI C相比,扩展的内容包括:数据类型、存储器类型、存储模式、指针及函数(包括:定义函数的重入性、指定函数的寄存器组、指定函数的存储模式及定义中断服务程序)。
阅读本书的读者请注意,本书没有详细介绍标准的C语言,只是介绍Keil C51对ANSI C的扩展。对于通用的C语言部分,如果需要,请查阅相关介绍C语言的教材。下面详细介绍Keil C51对ANSI C的扩展部分。
附A.3.1数据类型
Keil C51编译器支持的各种规格的数据类型列于表A-8。除了这些数据类型以外,变量可以组合成结构、联合及数组。
表A-8 KeilC51支持的数据类型
数据类型 signed char unsigned char signed short unsigned short signed int unsigned int signed long unsigned long float bit sbit sfr sfr 16 位数 8 8 16 16 16 16 32 32 32 1 1 8 16 字节 1 1 2 2 2 2 4 4 4 数值范围 -128到+127 0到255 -32768到+32767 0到65535 -32768到+32767 0到65535 1.175494E-38到3.402823E+38 0或1 0或1 1 2 0或255 0或65535 数据表A-1所列的数据类型中,关键字bit、 sbit 、sfr 和sfr16 等四种类型在ANSI C中是没有的,是Keil C51编译器中新增加的。其中,关键字bit用于操作80C51中的位寻址区,而关键字sbit、sfr和sfr16用于操作80C51的特殊功能寄存器SFR。
例如,下面的表达式: sfr P0 = 0x80; /* 定义 80C51 P0 口的特殊功能寄存器 */ 声明了一个变量P0,并且把它和位于0x80(80C51的P0口)处的特殊功能寄存器联系在一起。
1.bit类型
bit 数据类型用于定义操作位寻址区的变量,可用于变量声明、参数列表、函数声明和函数返回值等。所有的 bit 变量存放在80C51 内部存储区的位寻址区。因为这个区域只有16 字节长,所以最多只能声明128个位变量。
一个bit 变量的声明与其它数据类型相似,例如: static bit gbFlag = 0; /* 位变量 */ bit bFunc ( /* 位函数 */ bit bFlag1, /* 位变量 */ bit bFlag2 ) /* 位变量 */
{
︙ ︙ return(0) /* 位返回值 */
}
bit 变量的声明中,可包含存储器类型。但是因为 bit 变量存储在80C51的内部数据区,只能使用data 和idata 存储类型,不能使用别的存储类型。例如:
int data iBase; /* 在在直接访问数据区定义一个整型变量iBase */ char idata cAry[4]; /* 在间接访问数据区定义一个数组iBase */ bit mybit0 = iBase ^ 0; bit mybit15 = iBase ^ 0; bit bAry07 = cAry[0] ^ 7; bit bAry37 = cAry [3] ^ 7; bit 变量和 bit 声明有以下限制:
(1)如果在函数中禁止使用中断(#pragma disable)或者函数中包含有明确的寄存器组切换(using n),则该函数不能返回一个位值。否则,在编译时会产生编译错误。
(2)一个位不能被声明为一个指针,如bit *bPtr;是错误的。
(3)不能声明使用一个 bit 类型的数组,如bit bArr[5];是错误的。 2.sfr类型
sfr和C语言的其它类型变量声明是一样的。例如: sfr P0 = 0x80; /* P0口,地址为80H */ sfr P1 = 0x90; /* P1口,地址为90H*/ sfr P2 = 0xA0; /* P2口,地址为0A0H */ sfr P3 = 0xB0; /* P3口,地址为0B0H*/
P0、P1、P2 和P3 是声明的SFR名。在等号(=)后指定的地址必须是一个常数值不允许用带操作数的表达式。标准的80C51系列支持SFR地址从0x80 到0xFF。
3.sfr16
Keil C51编译器提供的sfr16数据类型,可以将两个8位的SFR作为一个16位的SFR来访问。访问该16位的SFR只能是低字节跟着高字节,即将低字节的地址用作sfr16声明的地址。例如:
sfr16 T2 = 0xCC; /*定义Timer2的16位数据寄存器, TL2的地址为0CCH,TH2
的地址为0CDH */
在这个例子中,定时器T2的16位的数据寄存器被声明为16 位SFR。当然,这个16位的数据寄存器可以声明为2个8位的数据寄存器。如下所示:
sfr TL2 = 0xCC; /*定义Timer2的16位数据寄存器的低8位, TL2的地址为
0CCH */ sfr TH2 = 0xCD; /*定义Timer2的16位数据寄存器的高8位, TH2的地址为 0CDH */
sfr16 声明和 sfr 声明遵循相同的原则。任何符号名可用在 sfr16 的声明中。等号(=)指定的地址,必须是一个常数值。不允许使用带操作数的表达式,而且必须使用SFR的低位和高位字节中的低位字节的地址。
4.sbit类型
在80C51系列MCU中,经常需要访问SFR中的某些位,这时需使用关键字sbit,利用它可以定义可位寻址的对象。定义方法有如下三种。
(1)sbit 位变量名 = 位地址
这种方法将位的绝对地址赋给位变量,位地址必须位于0x80~0xFF之间。例如: sbit OV = 0xD2; sbit CY = 0xD7;
(2)sbit 位变量名 = SFR名 ^ 位位置 当可位寻址的位位于SFR中的时候,可采用此方法。“位位置”是一个0~7之间的常数。例如:
sfr PSW = 0xD0; sbit OV = PSW ^ 2; sbit CY = PSW ^ 7;
(3)sbit 位变量名 = 字节地址 ^ 位位置
这种方法以字节地址作为基地址,该字节地址必须位于0x80~0xFF之间。“位位置”是一个0~7之间的常数。例如:
sbit OV = 0xD0 ^ 2; sbit CY = 0xD0 ^ 7;
附A.3.2存储器类型
80C51的存储区域有两个特奌:
★ 程序存储器和数据存储器是截然分开的;
★ 特殊功能寄存器与内部数据存储器是统一编址的。
C51编译器支持80C51的这种存储器结构,能够访问80C51的所有存储器空间。针对80C51存储空间的多样性,提出了修饰存储空间的修饰符,用以指明所定义的变量应分配在什么样的存储空间,如表A-9所示。
表A-9 存储空间类型说明符
存储器类型 code data idata bdata xdata pdata 描述 程序空间(64KB);通过MOVC@A+DPTR访问。 直接访问的内部数据存储器;访问速度最快(128字节)。 间接访问的内部数据存储器;可以访问所有的内部存储器空间(256字节)。 可位寻址的内部数据存储器;可用字节方式也可用位方式访问(16字节)。 外部数据存储器(64KB);通过MOVX@DPTR访问。 分页的外部数据存储器(256字节);通过MOVX@Ri访问。 1.程序存储区
程序的代码(CODE)存储区是只读的,不能写入。硬件决定最多可能有64KB的程序存储区。用code标识符来访问片内、片外统一编址的程序存储区,寻址范围为0000H~
FFFFH。在此空间存放程序代码、数据和表格。用间接寻址的方式访问程序存储区数据,如“MOVC A,@A+DPTR”或“MOVC A,@A+PC”。
2.内部数据存储区
内部的数据存储区是可读、可写的。80C51系列最多可有256字节的内部数据存储区。内部数据区可以分成三个不同的存储类型data、idata和bdata。
data存储类型标识符通常是指低128字节的内部数据区,为片内直接寻址的RAM空间,寻址范围为0~127。在此空向内存取速度最快。
idata存储类型标识符是指全部256个字节的内部存储区,为片内间接寻址的RAM空间,寻址范围为0~255。寻址方式为“MOV @Ri”。由于只能间接寻址,访问速度比直接寻址慢。bdata存储类型标识符是指可位寻址的16字节内部存储区(20H~2FH),位地址范围为0~127。
本空间允许按字节和按位寻址。在本区域可以声明可位寻址的数据类型。 3.外部数据存储区
外部数据存储区是可读、可写的。可通过一个数据指针加载一个地址来间接访问外部数据区。因此,访问外部数据存储区比访问内部数据存储区来得慢。
外部数据存储区最多可有64KB。这些地址不一定都用来作为数据存储区。因为,硬件设计可能把外围设备影射到该存储区。
编译器提供两种不同的存储类型来访问外部数据——xdata和pdata。 xdata存储类型标识符是指外部数据存储区(64KB)内的任何地址,寻址范围为0000H~FFFFH。寻址方式为“MOVX @DPTR”。
pdata 存储类型标识符仅指一页或256字节的外部数据存储区,寻址范围为00H~FFH。寻址方式为“MOVX @Ri”。
在定义变量时,通过指明存储器类型,可以将所定义的变量存储在指定的存储区域中。访问内部数据存储器将比访问外部数据存储器快的多。因此,应该把频繁使用的变量放置在内部数据存储器中,把很少使用的变量放在外部数据存储器中。
在变量的声明中,可以包括存储器类型和signed或unsigned属性。例如: char data var1; char code text[ ] = \"ENTER PARAMETER\"; unsigned long xdata array[100]; float idata x,y,z; unsigned int pdata dimension; unsigned char xdata vector[10][4][4]; char bdata flags;
如果在变量的定义中,没有包括存储器类型,将自动选用默认的存储器类型。
附A.3.3存储器模式
如果省略存储器类型,系统则会按编译模式SMALL,COMPACT或LARGE所规定的默认存储器类型去指定变量的存储区域。无论什么存储模式都可以声明变量在任何的8051存储区范围,然而把最常用的命令如循环计数器和队列索引放在内部数据区可以显著的提高系统性能。还有要指出的就是变量的存储种类与存储器类型是完全无关的。
1.SMALL模式
SMALL存储模式把所有函数变量和局部数据段放在8051系统的内部数据存储区这使访问数据非常快,但SMALL存储模式的地址空间受限。在写小型的应用程序时,变量和数据放在data内部数据存储器中是很好的因为访问速度快,但在较大的应用程序中data区最
好只存放小的变量、数据或常用的变量(如循环计数、数据索引),而大的数据则放置在别的存储区域。
2.COMPACT模式
COMPACT存储模式中所有的函数和程序变量和局部数据段定位在8051系统的外部数据存储区。外部数据存储区可有最多256字节(一页),在本模式中外部数据存储区的短地址用R0/R1。
3.LARGE
LARGE存储模式所有函数和过程的变量和局部数据段都定位在8051系统的外部数据区外部数据区最多可有64KB,这要求用DPTR数据指针访问数据。
一般情况下,应该使用小(SMALL)模式,它产生最快,最紧凑,效率最高的代码。在定义变量时,最好要指定存储器类型。只有当应用不可能在SMALL模式下操作时,才需要往上增加你的存储模式。
附A.3.4指针
C51编译器支持用星号(*)进行指针声明。可以用指针完成在标准C语言中有的所有操作。由于80C51及其派生系列所具有的独特结构,C51编译器支持两种不同类型的指针:通用指针和存储器指针。
1.通用指针
通用或未定型的指针的声明和标准C语言中一样。如: char *s; /* string ptr */ int *numptr; /* int ptr */ long *state; /* long ptr */ 通用指针需要三个字节来存储。第一个字节用来表示存储器类型,第二个字节是指针的高字节,第三字节是指针的低字节。
通用指针可以用来访问所有类型的变量,而不管变量存储在哪个存储空间中。因而许多库函数都使用通用指针。通过使用通用指针,一个函数可以访问数据,而不用考虑它存储在什么存储器中。通用指针很方便,但是也很慢。在所指向目标的存储空间不明确的情况下,它们用的最多。
2.存储器指针
存储器指针或类型确定的指针在定义时要包含一个存储器类型说明,并且总是指向此说明的特定存储器空间。例如:
char data *str; /* 指向data 区域的字符串 */ int xdata *numtab; /* 指向xdata 区域的 int */ long code *powtab; /* 指向code 区域的 long */ 正是由于存储器类型在编译时已经确定,通用指针中用来表示存储器类型的字节就不再需要了。指向idata,data,bdata和pdata的存储器指针使用一个字节来保存;指向code和xdata的存储器指针用两个字节来保存。
由此可见,使用存储器指针比通用指针效率要高,速度要快。当然,存储器指针的使用不是很方便。只有在所指向目标的存储空间明确并不会变化的情况下,才用它。
附A.3.5函数
1.重入函数
函数的嵌套调用是指当一个函数正被调用尚未返回时,又被本函数或其它函数再次调用的情况,只有等到后次调用返回到了本次,本次被暂时搁置的程序才得以恢复接续原来的正
常运行,直到本次返回。允许被嵌套调用的函数必须是可重入函数,即函数应具有可重入性。
通常情况下,C51函数一般是不能被递归调用的。这是由于函数参数和局部变量是存储在固定的地址单元中的。重入函数需要使用重入堆栈,这种堆栈是在存储模式所指的空间内从顶端另行分配的一个非覆盖性的堆栈。该堆栈将被嵌套调用的每层参数及局部变量一直保留到由深层返回到本层,而又终止本层的返回。
在一个基本函数的基础上添加reentrant说明,从而使它具有重入特性。如:
int calc (char i, int b) reentrant { int x; x = table [i]; return (x * b);
}
在实时应用中以及中断服务程序代码和非中断程序代码必须共用一个函数的场合中,经常用到重入函数。
需要注意的是, 不应将全部程序声明为重入函数。把全部程序声明为重入函数将增加目标代码的长度并减慢运行速度。应该选择哪些必须的函数作为重入函数。
2.函数使用指定的寄存器组 using n
函数使用指定寄存器组的定义性说明如下:
viod 函数标识符(形参表)using n
其中 n=0~3为寄存器组号,对应80C51中的四个寄存器组。函数使用了using n后,C51编译器自动在函数的汇编码中加入如下的函数头段和尾段:
{
push psw mov psw,#与寄存器组号n有关的常量 ┆ pop psw }
应该注意的是,using n不能用于有返回值的函数。因为,C51的返回值是放在寄存器中的,而返回前寄存器组却改变了,将会导致返回值发生错误。
3.函数使用指定的存储模式
针对80C51存储空间的多样性,提出了修饰存储空间的修饰符,用以指明所定义的变量应分配在什么样的存储空间,其定义性格式为:
类型说明符 函数标识符(形参表) 存储模式修饰符{small,compact,large} 其中,修饰符可用small、compact、large三者中的一个。 存储模式为本函数的参数和局部变量指定的存储空间,在指定了存储模式之后,该空间将再也不随编译模式而变。如:
extern int func (int i, int j) large; /* 修饰为大模式 */
4.中断服务程序
C51编译器允许用C语言创建中断服务程序。只需关心中断号和寄存器组的选择。编译器自动产生中断向量和程序的入栈及出栈代码。
在函数声明时包括interrupt m,将把所声明的函数定义为一个中断服务程序。其格式为:
viod 函数标识符(viod) interrupt m 其中, m=0~31,
0对应于外部中断0; 1对应于定时器0中断; 2对应于外部中断1; 3对应于定时器1中断; 4对应于串行口中断; 其它为预留。
从定义中可以看出,中断的函数必须是无参数、无返回值的函数。如:
unsigned int interruptcnt;
unsigned char second; void timer0 (void) interrupt 1 using 2 { if (++interruptcnt == 4000) /* 计数到 4000 */
{
second++; /* 秒计数器 */
interruptcnt = 0; /* 清除中断计数器 */ } }
附A.4 C语言与汇编语言的混合编程
C51语言编程与汇编语言编程各有所长。使用C51语言,开发速度快,可读性、可维护性、可移植性都好;而使用汇编语言,则可以更为充分地利用芯片的软、硬件资源,使得程序代码的执行效率高。为了发挥C51语言与汇编语言两种语言各自的优势,希望能够实现它们的混合编程。这一点特别适用于要求占用空间小、有严格时间限制的子程序设计,这类子程序总是希望用汇编语言来编写,然后由C51语言主程序来调用;或者直接在C51语言中直接嵌入汇编语言。
附A.4.1 Keil C51调用汇编函数
通过一个例子,介绍在C51程序中调用汇编函数的一种方法。在这个例子里,外部函数的入口参数是一个字符型变量和一个位变量,返回值是一个整型变量。例中,先用C51写出这个函数的主体,然后用SRC控制指令编译产生ASM文件,进一步修改这个ASM文件就得到我们所要的汇编函数。该方法让编译器自动完成各种段的安排,提高了汇编程序的编写效率。
1.建立项目
按写普通C51程序方法,建立项目,在里面导入main.c文件和cfunc.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); }
//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); }
2.设置文件选项
在项目管理器窗口中,在将要得到汇编代码的C文件“cfunc.c”上单击右键,弹出菜单,选择“Options for File ‘cfunc.c’”,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使复选框由灰色变成黑色(有效)状态。在选择时需注意,该选项有3种状态,未选中、无效(灰色)和有效(黑色),如图A-27所示。
图A-27 “Options for File ‘cfunc.c’”选项
3.向项目中添加库文件
根据选择的编译模式,把相应的库文件(如 Small 模式时,是 Keil\\C51\\Lib\\C51S.Lib)加入工程中,该文件必须作为项目的最后文件。
4.生成汇编语言文件并调整项目文件
编译这个项目后将会产生一个CFUNC.SRC的文件,将这个文件改名为CFUNC.A51(也可以通过编译选项直接产生CFUNC.A51文件),然后在项目里去掉库文件(如C51S.Lib)和CFUNC.c,而将CFUNC.A51添加到项目里。
; .\\cfunc.SRC generated from: cfunc.c ; COMPILER INVOKED BY:
; C:\\Keil\\C51\\BIN\\C51.EXE cfunc.c BROWSE DEBUG OBJECTEXTEND SRC(.\\cfunc.SRC) NAME CFUNC
PR_AFUNCCFUNC SEGMENT CODE
BI_AFUNCCFUNC SEGMENT BIT OVERLAYABLE PUBLIC _AFUNCBIT PUBLIC _AFUNC
RSEG BI_AFUNCCFUNC _AFUNCBIT:
v_bflag041: DBIT 1 ; //CFUNC.c文件 ;
; #define uchar unsigned char ; #define uint unsigned int
;
; uint AFUNC(uchar v_achr , bit v_bflag) RSEG PR_AFUNCCFUNC _AFUNC:
USING 0
; SOURCE LINE # 6
;---- Variable 'v_achr040' assigned to Register 'R7' ---- ; {
; SOURCE LINE # 7 ; uchar tmp_vchr; ; uint tp_vint; ;
; tmp_vchr = v_achr;
; SOURCE LINE # 11
;---- Variable 'tmp_vchr042' assigned to Register 'R5' ---- MOV R5,AR7
; tp_vint = (uint)v_bflag;
; SOURCE LINE # 12 MOV C,v_bflag041 CLR A RLC A
;---- Variable 'tp_vint043' assigned to Register 'R6/R7' ---- ; return tmp_vchr + (tp_vint << 8); ; SOURCE LINE # 13 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 # 14 C0001: RET
; END OF _AFUNC END 5.编译项目
再次编译这个项目,到此已经得到汇编函数的主体,修改函数里面的汇编代码就得到所需的汇编函数了。
附A.4.2 在Keil C51中直接嵌入汇编语言
1.C51中嵌入汇编语言程序的格式
要在 C51 文件中嵌入汇编语言程序,需要按照如下格式加入:
#pragma ASM 汇编语言程序 #pragma ENDASM
在上例中,将main.c文件内容修改成如下所示:
//main.c文件
#include \"reg52.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); #pragma asm
MOV P1,mvintrslt042
MOV P2,mvintrslt042+01H #pragma endasm }
即将调用函数AFUNC()得到的返回值通过P1口、P2口输出。
2.设置文件选项
在项目管理器窗口中,在将要得到汇编代码的C文件“main.c”上单击右键,弹出菜单,选择“Options for File ‘main.c’”,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使复选框由灰色变成黑色(有效)状态。
3.向项目中添加库文件
根据选择的编译模式,把相应的库文件(如 Small 模式时,是 Keil\\C51\\Lib\\C51S.Lib)加入工程中,该文件必须作为项目的最后文件。
4、编译并生成目标代码
编译整个项目,即可得到用户需要的目标代码。
使用此方法可以在C51源代码的任意位置嵌入汇编语言程序。但是,需要注意的是,在直接使用形参时,在不同的优化级别下产生的汇编代码可能有所不同。
附A.5 C语言程序举例
例A.1(汇编语言见例4.1) #include void delay(unsigned int time); void main(void) { P1 = 0x80; //P1.7写“1”,作为输入口线 while(1) { while(key); //检测P1.7是否为0,是,则按键按下 delay(12500); //延时,去除按键抖动 while(key); //检测P1.7是否为0,是,则确认按键按下 while(!key); //检测按键是否抬起 led = !led; //LED点亮或熄灭 } } void delay(unsigned int time) { while(time--) { _nop_(); } } 例A.2(汇编语言见例4.2) #include void timer1(void) interrupt 3 { TH1 = 0xFE; //重新设置初值 TL1 = 0x0C; square = !square; //定时1ms时间到,输出取反 } 例A.3(汇编语言见例4.3) #include EA = 1; //允许中断 ET0 = 1; //允许T0中断 TR0 = 1; //启动T0 while(1); //等待中断 } void timer0(void) interrupt 1 { square = !square; //定时200μs时间到,输出取反 } 例A.4(汇编语言见例4.4) #include void display(int value) { //以机器周期个数的形式显示正脉冲宽度 } 例A.5(汇编语言见例4.5) #include unsigned long distance _at_ 0x30; void main(void) { IT0 = 0; //设置下降沿触发方式 PX0 = 1; //置外部中断0高优先级 EX0 = 1; //允许外部中断0 EA = 1; //开CPU中断 distance = 0; //初始化里程计数器 while(1); //等待中断 } void ex_int0() interrupt 0 { distance += 2; //里程计数器+2 } 例A.6(汇编语言见例4.6) #include #define uchar unsigned char uchar code table[]={0x03,0x9F,0x25,0x0D,0x99,0x49,0x41,0x1F,0x01,0x09}; void delay(uchar time); void main() { uchar X,Y; //定义要显示的变量X,Y取值为0~9的整数 SCON = 0X00; //串行口工作在方式0 X = 9; //给X赋值 Y = 0; //给Y赋值 SBUF = table[X]; //通过串口显示X delay(20); //延时 SBUF = table[Y]; //通过串口显示Y while(1); } void delay(uchar time) { while(time--); } 例A.7(汇编语言见例4.7) 甲机: #include #define NUM 16 //定义发送字节数 bit finish; //定义发送一个字节成功标志,finishi = 0表示发送成功 unsigned char data trans[NUM] _at_ 0x40; //定义发送数组及数组首地址 unsigned char i; void init(void); void main(void) { init(); for(i = 0;i < NUM;i ++) { finish = 1; //表示处于发送状态 ACC = trans[i]; //发送字节送入累加器A TB8 = P; //校验位送入TB8 SBUF = ACC; //发送字节进入发送缓冲器,开始发送 while(finish); //等待发送成功 } ES = 0; while(1); } void init(void) { SCON = 0xd0; //置工作方式2并允许接收 TMOD = 0x20; //置定时器方式2,自动重装载 TH1 = 0xfd; //波特率设置 TL1 = 0xfd; TR1 = 1; //启动定时器 EA = 1; //CPU开中断 ES = 1; //允许串行口中断 } void serial(void) interrupt 4 { while(TI) { TI = 0; //发送完成,清发送中断 } while(RI) { RI = 0; //清接收中断 ACC = SBUF; //接收到的数据送入累加器A if(ACC == 0x00) //接收正确,清发送标志 { finish = 0; } else //接收不正确,重新发送 { ACC = trans[i]; TB8 = P; SBUF = ACC; } } } 乙机: #include #define NUM 16 //定义接收字节数 bit finish; //定义接收一个字节成功标志,finishi = 0表示接收成功 unsigned char data recei[NUM] _at_ 0x40; //定义接收数组及数组首地址 unsigned char i; void init(void); void main(void) { init(); for(i = 0;i < NUM;i ++) { finish = 1; //表示处于接收状态 while(finish); //等待接收成功 } ES = 0; while(1); } void init(void) { SCON = 0xd0; //置工作方式2并允许接收 TMOD = 0x20; //置定时器方式2,自动重装载 TH1 = 0xfd; //波特率设置 TL1 = 0xfd; TR1 = 1; //启动定时器 EA = 1; //CPU开中断 ES = 1; //允许串行口中断 } void serial(void) interrupt 4 { while(TI) { TI = 0; //发送完成,清发送中断 } while(RI) { RI = 0; //清接收中断 ACC = SBUF; //接收到的数据送入累加器A if(RB8 == P) //接收正确 { recei[i] = SBUF; //保存接收到的数据 SBUF = 0x00; //发送接收成功标志 finish = 0; //清接收标志 } else //接收到的数据不正确 { SBUF = 0xaa; //发送接收不正确标志 } } } 因篇幅问题不能全部显示,请点此查看更多更全内容