学习单元三 建立软件实训环境(1)——Keil软件的安装与使用

目前比较流行的51单片机开发软件有德国Keil公司推出的Keil软件。该软件提供了包括C编译器、宏汇编、链接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境uVision IDE(即Integrated Development Environment),将这些部分组合在一起。通过Keil软件,既可以对汇编语言源程序(ASM文件)进行汇编,也可对C语言源程序进行编译,链接目标模块与库模块以产生一个目标文件,最后生成HEX文件以便对程序进行调试等。目前该软件使用的版本一般为uVision2或uVision3,统称为uVision。Keil软件为英文界面,要求初学者有较好的英文基础。

一、Kei1软件的安装

可以从Keil公司及其代理公司处购得Keil软件。如果想试用,可以到Keil公司的网站(http:/www.Keil.com)下载试用版本Eval版本。下载得到的Keil软件是一个压缩包,解压后双击其中的Setup.exe即可安装。

二、Kei1软件的使用

Keil软件的使用过程如下:

双击桌面uVision图标,即可进入Keil软件的集成开发环境,可以看到一个标准的Windows程序窗口。该程序窗口又由多个子窗口组成,如图2-6所示。为了较为全面地了解窗口的组成,该图显示了尽可能多的子窗口,但在初次进入uVision IDE时,只能看到工程管理窗口、源程序窗口和输出窗口。

图2-6 uVision IDE的窗口组成

工程管理窗口有3个选项卡,分别是Files、Regs和Books,这三个选项卡分别显示当前项目的文件结构、CPU的寄存器及部分特殊功能寄存器的值(在进入系统调试时才出现)和所选CPU的附加说明文件。如果是第一次启动Keil,那么这三个选项卡全是空的。图2-6中还有内存窗口、外围设备对话框等,这些窗口和对话框在进入系统调试后才能看到。

1.新建工程

在单片机项目开发中,Keil使用工程(Project)这一概念将所有与项目开发相关的文件都加在一个工程中,只能对工程而不能对单一的源程序进行编译/汇编和连接。

在图2-6中选择“Project—New Project...”,出现创建新工程的对话框,如图2-7所示,要求给定一个工程名称如“ONE LED FLASH”并保存。一般应把工程建立在与源文件相同的文件夹如“D:\ONE LED”中。

图2-7 新建工程对话框

注意:工程名不用加扩展名(已有默认的扩展名uv2),单击“保存”即可。

2.为工程选择目标器件

进入下一步,为新建工程选择目标单片机,如图2-8所示。

图2-8 选择目标单片机

Keil支持所有400余种以8051/80C51为内核的单片机系列。可以根据需要来选择合适的单片机,也可任选一种单片机,这里选择Atmel公司的AT89S51:在图2-8所示左侧的Date base列表框中单击Atmel展开,选择其中的AT89S51。在其右边的DescriPtion显示区域中有关该单片机特性的一般性描述。在图2-8中单击“确定”按钮出现图2-9所示对话框。

图2-9 对话框

图2-9中提示是否将8051启动代码文件(Startup.a51)拷贝到工程文件夹并添加到工程中,点击按钮“否”后返回主界面。

3.为工程添加源程序

新建工程后返回到主界面时,在工程窗口的文件页中,出现了“Target1”,前面有“+”号,点击“+”号展开,可以看到下一层的“Source Group1”,这时的工程还是一个空的工程,里面什么文件也没有,需要编写源程序或为工程添加源程序。

1)编写并保存源程序

uVision IDE内集成有一个文本编辑器,可以在uVision集成环境中直接进行源程序的输入,输入的源程序在源程序窗口显示。该文本编辑器支持对汇编或C语言中关键字以变色显示。在图2-6中选择“File—New...”,在源程序窗口出现一个新的文件输入窗口,在该窗口输入源程序,如【例2-1】中的汇编语言源程序。输入完毕之后,选择“File—Save/Save As”,给该源程序文件取名如“ONE LED FLASH.ASM”或“Hujinde.ASM”(文件名可任意取)并保存在文件夹如“D:\ONE LED”中,如图2-10所示。

图2-10 编写并保存源程序

图2-10所示的窗口中,左边为工程管理窗口,右边为源程序编辑窗口。在源程序编辑窗口中输入【例2-1】中的源程序,至于该程序是怎样编写出来的,先暂且不管。【例2-1】程序中的每一行由两部分组成:分号左边的是指令代码,分号及分号右边部分是指令的注释,注释部分可以不录入。

在输入程序时要注意以下几点:

1)程序中有冒号(:)、分号(;)以及圆点(.)等符号,这些符号必须在半角方式下录入,否则,编译/汇编时就会报错。

2)程序中的空格可以是一个或多个空格,输入指令时按Tab键可实现空格的录入。

3)程序中,“MAIN”、“DELAY”、“LOOP1”为指令标号,即指令的符号地址。标号以冒号结尾,其后的冒号不可丢去。为便于阅读,标号一般顶头输入,而指令一般退几格后录入。

4)不区分大小写,即字符的大小写等价。

需注意:给源程序取名时必须要加上扩展名,一般汇编程序均以“ASM”为扩展名。

(2)为工程添加源程序

要把刚才编写好的源程序如“ONE LED FLASH.ASM”加入到工程中,需点击“Source Group1”使其反白显示,然后,点击鼠标右键,出现一个下拉菜单,如图2-11所示。

选中图2-11中的“Add files to Group'Source Group1”,出现一个对话框,要求寻找源程序文件,如图2-12所示。注意,该对话框下面的“文件类型”默认为C source file(*. c),也就是以C为扩展名的文件,而我们的文件是以asm为扩展名的,所以在列表框中找不到asm源文件,要将文件类型改掉,点击对话框中“文件类型”后的下拉列表,找到并选中“Asm Source File(*.s*; *.src; *.a*),这样,在列表框中就可以找到源程序asm文件了,双击源程序asm文件,将文件加入项目。

图2-12 寻找源程序文件

注意:在asm文件加入项目后,该对话框并不消失,等待继续加入其他文件,但初学者时常会误认为操作没有成功而再次双击同一文件,这时会出现一对话框,提示你所选文件已在列表中,此时应点击“确定”,返回前一对话框,然后点击“Close”即可返回主界面。返回后,点击“Source Group 1”前的加号,会发现源程序asm文件已在其中。双击源程序文件名,即可打开该源程序。

图2-11 将源程序添加到工程

4.设置工程的配置参数

工程建立好后,还要设置工程的配置参数,以满足要求。首先点击左边Project窗口中的Target 1,使其反白显示,然后,点击鼠标右键,出现一个下拉菜单,如图2-13所示。

图2-13 设置工程配置参数

选中图2-13中的“Options for target‘target1'”,即出现工程设置对话框中的Target页面,如图2-14所示。工程设置对话框比较复杂,共有10个页面,但绝大部分设置项取默认值就行了。

图2-14 工程设置对话框中的Target页面

(1)设置对话框中的Target页面,如图2-14。

1)Xtal后面的数值是晶振频率值,默认值是所选目标单片机的最高可用频率值。该数值与最终产生的目标代码无关,仅用于软件模拟调试时显示程序执行时间。正确设置该数值可使显示时间与实际所用时间一致,一般将其设置成与你的硬件所用晶振频率相同,如果没必要了解程序执行的时间,也可以不设。

2)Memory Model用于设置RAM使用情况有三个选择项:Small是所有变量都在单片机的内部RAM中;Compact是可以使用一页外部扩展RAM; Larget则是可以使用全部外部的扩展RAM。

3)Code Rom Size用于设置ROM空间的使用,同样也有三个选择项。

Small模式:只用低于2k的程序空间;

Compact模式:单个函数的代码量不能超过2k,整个程序可以使用64k程序空间;

Larget模式:可用全部64k空间。

4)Use on-chip ROM选择项确认是否仅使用片内ROM(注意:选中该项并不会影响最终生成的目标代码量)。

5)Operating项是操作系统选择,Keil提供了两种操作系统:Rtx tiny和Rtx full。关于操作系统是另外一个很大的话题了,通常我们不使用任何操作系统,即使用该项的默认值:None(不使用任何操作系统)。

6)Off Chip Code memory用以确定系统扩展ROM的地址范围。

7)Off Chip xData memory用于确定系统扩展RAM的地址范围,这些选择项必须根据所用硬件来决定,由于新型51单片机一般不用扩展ROM和RAM,所以均不重新选择,按默认值设置。

(2)设置对话框中的OutPut页面,如图2-15所示。

1)按钮“Select Folder for objects”是用来选择最终的目标文件HEX所在的文件夹,默认是与工程文件在同一个文件夹如“D:\ONE LED”中。

2)Name of Executable用于指定最终生成的目标文件HEX的名字,默认与工程的名字相同。

3)Creat Hex file用于生成可执行代码文件HEX,默认情况下该项未被选中,如果要将程序写入单片机,就必须选中该项,这一点是初学者易疏忽的,在此特别提醒注意。选中Debug information将会产生调试信息,这些信息用于调试,如果需要对程序进行调试,应当选中该项。Browse information是产生浏览信息,该信息可以用菜单view-→Browse来查看,这里取默认值。

图2-15 工程设置对话框中的OutPut页面

工程设置对话框中的其他各页面与C51编译选项、BL51连接器的连接选项等用法有关,这里均取默认值,不作任何修改。

5.编译与连接工程

在设置好工程后,即可进行编译、连接。编译、连接及工程设置工具条见图2-16。

图2-16 编译、连接及工程设置工具条

(1)编译或汇编当前文件:若当前文件是汇编语言程序文件,则使用A51汇编器对汇编语言程序进行汇编处理;若当前文件是C语言源程序文件,则使用C51编译器进行编译处理。最终得到目标代码HEX文件。

(2)建立目标文件:根据汇编或编译得到的目标文件,并调用有关库模块,连接产生绝对地址的目标文件。如果在上次汇编或编译过后又对源程序作了修改,将先对源程序进行汇编或编译,然后再连接。

(3)重建全部:对工程中所有文件进行重新编译、汇编处理,然后再进行连接以产生目标代码。使用该按钮可以防止由于一些意外情况(如系统日期不正确)而造成源文件与目标代码不一致。

(4)停止建立:在建立目标文件的过程中,可以单击该按钮停止这一工作。

(5)工程设置:该按钮用于对工程进行设置,其效果如同选择“Project—Option for target‘target1'”。

以上建立目标文件的操作也可以通过选择菜单“Project—Translate …”、“Project—Build target”、“Project—Rebuild All target files”和“Project—Stop Build”进行。

编译过程中的信息将出现在输出窗口中的Build页中,如图2-17所示。如果源程序中有语法错误,会有错误报告出现,双击该行,可以定位到出错的位置。对源程序反复修改之后,如在输出窗口显示“…0 Error(s),0 Warning(s)”,提示获得了名为“ONE LED FLASH.hex”的目标文件,该文件即可被写入到单片机中。

图2-17 编译后Build页中的信息提示

6.观察数据和程序运行结果

前面我们学习了如何建立工程、汇编、连接工程,并获得目标代码,但是做到这一步仅仅代表源程序没有语法错误,至于源程序中存在着的其他错误,必须通过调试才能发现并解决,事实上,除了极简单的程序以外,绝大部分的程序都要通过反复调试才能得到正确的结果,因此,调试是软件开发中重要的一个环节,下面将介绍常用的调试命令、在线汇编、各种设置断点进行程序调试的方法,并通过实例介绍这些方法的使用。

1.Keil的调试命令、在线汇编与断点设置

(1)常用调试命令。在对工程成功地进行汇编、连接以后,按Ctrl+F5或者使用菜单“Debug-→Start/Stop Debug Session”即可进入调试状态,Keil内建了一个仿真CPU用来模拟执行程序,该仿真CPU功能强大,可以在没有硬件和仿真器的情况下进行程序的调试,下面将要学的就是该模拟调试功能。不过在学习之前必须明确,模拟毕竟只是模拟,与真实的硬件执行程序肯定还是有区别的,其中最明显的就是时序,软件模拟是不可能和真实的硬件具有相同的时序的,具体的表现就是程序执行的速度和各人使用的计算机有关,计算机性能越好,运行速度越快。

进入调试状态后,界面与编缉状态相比有明显的变化,Debug菜单项中原来不能用的命令现在已可以使用了,如图2-18所示。另外,工具栏会多出一个用于运行和调试的工具条,如图2-19所示,Debug菜单上的大部分命令可以在此找到对应的快捷按钮,从左到右依次是复位、运行、暂停、单步、过程单步、执行完当前子程序、运行到当前行、下一状态、打开跟踪、观察跟踪、反汇编窗口、观察窗口、代码作用范围分析、1#串行窗口、内存窗口、性能分析、工具按钮等命令。

图2-18 Debug菜单项

图2-19 用于运行和调试的工具条

学习程序调试,必须明确两个重要的概念,即单步执行与全速运行。全速执行是指一行程序执行完以后紧接着执行下一行程序,中间不停止,这样程序执行的速度很快,并可以看到该段程序执行的总体效果,即最终结果正确还是错误,但如果程序有错,则难以确认错误出现在哪些程序行。单步执行是每次执行一行程序,执行完该行程序以后即停止,等待命令执行下一行程序,此时可以观察该行程序执行完以后得到的结果是否与我们写该行程序所想要得到的结果相同,借此可以找到程序中问题所在。程序调试中,这两种运行方式都要用到。使用菜单“STEP”或相应的命令按钮或使用快捷键F11可以单步执行程序,使用菜单“STEP OVER”或功能键F10可以以过程单步形式执行命令,所谓过程单步,是指将汇编语言中的子程序或高级语言中的函数作为一个语句来全速执行。

按下F11键,可以看到源程序窗口的左边出现了一个黄色调试箭头,指向源程序的第一行(源程序的第一行“ORG 0000 H”是伪指令,即不是可执行的指令,故黄色调试箭头实际指向源程序的第二行),如图2-20所示。每按一次F11,即执行该箭头所指程序行,然后箭头指向下一行,当箭头指向“LCALL DELAY”行时,再次按下F11,会发现,箭头指向了延时子程序“DELAY”的第一行。不断按F11键,即可逐步执行延时子程序。

图2-20 调试窗口

通过单步执行程序,可以找出一些问题的所在,但是仅依靠单步执行来查错有时是困难的,或虽能查出错误但效率很低,为此必须辅之以其他的方法,例如在软件延时程序中的某一行程序要执行几千甚至几万次才能达到延时目的时,如果用按F11的方法来执行完该程序行,显然不合适,为此,可以采取以下一些方法:

第一种方法,用鼠标在子程序的最后一行“RET”点一下,把光标定位于该行,然后用菜单“Debug-→Run to Cursor line”(执行到光标所在行),即可全速执行完黄色箭头与光标之间的程序行。

第二种方法,在进入该子程序后,使用菜单“Debug-→Step Out of Current Function”(单步执行到该函数外),使用该命令后,即全速执行完调试光标所在的子程序或子函数并指向主程序中的下一行程序(这里是“CLR P1.0”行)。

第三种方法,在开始调试时,按F10而非F11,程序也将单步执行,不同的是,执行到“LCALL DELAY”行时,按下F10键,调试光标不进入子程序的内部,而是全速执行完该子程序,然后直接指向下一行“CLR P1.0”。灵活应用这几种方法,可以大大提高查错的效率。

(2)在线汇编。在进入Keil的调试环境以后,如果发现程序有错,可以直接对源程序进行修改,但是要使修改后的代码起作用,必须先退出调试环境,重新进行编译、连接后再次进入调试,如果只是需要对某些程序行进行测试,或仅需对源程序进行临时的修改,这样的过程未免有些麻烦,为此Keil软件提供了在线汇编的能力,将光标定位于需要修改的程序行上,用菜单“Debug-→Inline Assambly…”即可出现如图2-21所示的对话框,在“Enter New”后面的编缉框内直接输入需更改的程序语句,输入完后键入回车将自动指向下一条语句,可以继续修改,如果不再需要修改,可以点击右上角的关闭按钮关闭窗口。

图2-21 在线汇编对话框

(3)断点设置。程序调试时,一些程序行必须满足一定的条件才能被执行到(如程序中某变量达到一定的值、按键被按下、串口接收到数据、有中断产生等),这些条件往往是异步发生或难以预先设定的,这类问题使用单步执行的方法是很难调试的,这时就要使用到程序调试中的另一种非常重要的方法——断点设置。断点设置的方法有多种,常用的是在某一程序行设置断点,设置好断点后可以全速运行程序,一旦执行到该程序行即停止,可在此观察有关变量值,以确定问题所在。在程序行设置/移除断点的方法是将光标定位于需要设置断点的程序行,使用菜单“Debug-→Insert/Remove BreakPoint”设置或移除断点(也可以用鼠标在该行双击实现同样的功能);“Debug-→Enable/Disable Breakpoint”是开启或暂停光标所在行的断点功能;“Debug-→Disable All Breakpoint”暂停所有断点;“Debug-→Kill All BreakPoint”清除所有的断点设置。这些功能也可以用工具条上的快捷按钮进行设置。

除了在某程序行设置断点这一基本方法以外,Keil软件还提供了多种设置断点的方法,按“Debug-→Breakpoints…”即出现一个对话框,该对话框用于对断点进行详细的设置,如图2-22所示。

图2-22 断点设置对话框

图2-22中Expression后的编缉框内用于输入表达式,该表达式用于确定程序停止运行的条件,这里表达式的定义功能非常强大,涉及到Keil内置的一套调试语法,这里不作详细说明,仅举若干实例,希望读者可以举一反三。

(1)在Experssion中键入a==0xf7,再点击Define即定义了一个断点,注意,a后有两个等号,意即相等。该表达式的含义是:如果a的值到达0xf7则停止程序运行。除使用相等符号之外,还可以使用>、>=、<、<=、!=(不等于)、&(两值按位与),&&(两值相与)等运算符号。

(2)在Experssion后中键入Delay再点击Define,其含义是如果执行标号为Delay的行则中断。

(3)在Experssion后中键入Delay,按Count后的微调按钮,将值调到3,其意义是当第三次执行到Delay时才停止程序运行。

(4)在Experssion后键入Delay,在Command后键入printf(“SubRoutine‘Delay'has been Called\n”)主程序每次调用Delay程序时并不停止运行,但会在输出窗口Command页输出一行字符,即SubRoutine‘Delay'has been Called。其中“\n”的用途是回车换行,使窗口输出的字符整齐。

(5)设置断点前先在输出窗口的Command页中键入DEFINE int I,然后在断点设置时同(4),但是Command后键入printf(“SubRoutine ‘Delay'has been Called %d times\n”, ++I),则主程序每次调用Delay时将会在Command窗口输出该字符及被调用的次数,如SubRoutine‘Delay'has been Called 10 times。

对于使用C源程序语言的调试,表达式中可以直接使用变量名,但必须要注意,设置时只能使用全局变量名和调试箭头所指模块中的局部变量名。

2.Keil程序调试窗口

Keil提供的各种窗口如输出窗口、观察窗口、存储器窗口、反汇编窗口、串行窗口等可以帮助我们观察数据和段程序运行结果。

Keil软件在调试程序时提供了多个窗口,主要包括输出窗口(Output Windows)、观察窗口(Watch&Call Statck Windows)、存储器窗口(Memory Window)、反汇编窗口(Dissambly Window)、串行窗口(Serial Window)等。点击菜单“Debug->Start/Stop Debug Session”进入调试模式后,可以通过菜单View下的相应命令打开或关闭这些窗口,如图2-23所示。

图2-23 菜单View下的程序调试窗口

图2-24是输出窗口、观察窗口和存储器窗口,各窗口的大小可以使用鼠标调整。进入调试程序后,输出窗口自动切换到Command页。该页用于输入调试命令和输出调试信息。对于初学者,可以暂不学习调试命令的使用方法。

图2-24 调试窗口(命令窗口、观察窗口、存储器窗口)

(1)存储器窗口。存储器窗口如图2-25所示。存储器窗口中可以显示系统中各种内存中的值,通过在Address后的编缉框内输入“字母:数字”即可显示相应内存值,其中字母可以是C、D、I、X,分别代表代码存储空间、直接寻址的片内存储空间、间接寻址的片内存储空间、扩展的外部RAM空间,数字代表想要查看的地址。例如输入D:0即可观察到地址0开始的片内RAM单元值、键入C:0即可显示从0开始的ROM单元中的值,即查看程序的二进制代码。该窗口的显示值可以以各种形式显示,如十进制、十六进制、字符型等,改变显示方式的方法是点鼠标右键,在弹出的快捷菜单中选择,该菜单用分隔条分成三部分,其中第一部分与第二部分的三个选项为同一级别,选中第一部分的任一选项,内容将以整数形式显示,而选中第二部分的Ascii项则将以字符型式显示,选中Float项将相邻四字节组成的浮点数形式显示、选中Double项则将相邻8字节组成双精度形式显示。第一部分又有多个选择项,其中Decimal项是一个开关,如果选中该项,则窗口中的值将以十进制的形式显示,否则按默认的十六进制方式显示。Unsigned和Signed后分别有三个选项:Char、Int、Long,分别代表以单字节方式显示、将相邻双字节组成整型数方式显示、将相邻四字节组成长整型方式显示,而Unsigned和Signed则分别代表无符号形式和有符号形式,究竟从哪一个单元开始的相邻单元则与你的设置有关,以整型为例,如果你输入的是I:0,那么00H和01H单元的内容将会组成一个整型数,而如果你输入的是I:1,01H和02H单元的内容全组成一个整型数,以此类推。有关数据格式与C语言规定相同,请参考C语言书籍,默认以无符号单字节方式显示。第三部分的Modify Memory at X:xx用于更改鼠标处的内存单元值,选中该项即出现如图2-26所示的对话框,可以在对话框内输入要修改的内容。

图2-25 存储器数值各种方式显示选择

图2-26 存储器的值的修改

(2)工程窗口寄存器页。图2-27是工程窗口寄存器页的内容,寄存器页包括了当前的工作寄存器组和系统寄存器,系统寄存器组有一些是实际存在的寄存器如A、B、DPTR、SP、PSW等,有一些是实际并不存在或虽然存在却不能对其操作的如PC等。每当程序中执行到对某寄存器的操作时,该寄存器会以反色(蓝底白字)显示,用鼠标单击然后按下F2键,即可修改该值。

图2-27 工程窗口寄存器页

(3)观察窗口。观察窗口是很重要的一个窗口,工程窗口中仅可以观察到工作寄存器和有限的寄存器如A、B、DPTR等,如果需要观察其他的寄存器的值或者在高级语言编程时需要直接观察变量,就要借助于观察窗口了。

(4)外围接口。为了能够比较直观地了解单片机中定时器、中断、并行端口、串行端口等常用外设的使用情况,Keil提供了一些外围接口对话框,通过Peripherals菜单选择,该菜单的下拉菜单内容有Interrupt(中断)、I/O Ports(并行I/O口)、Serial(串行口)、Timer(定时/计数器)这四个外围设备菜单,如图2-28所示。打开这些对话框,列出了外围设备的当前使用情况,各标志位的情况等,可以在这些对话框中直观地观察和更改各外围设备的运行情况。

图2-28 外围设备接口对话框

一般情况下,我们仅在单步执行时才对变量的值的变化感兴趣,全速运行时,变量的值是不变的,只有在程序停下来之后,才会将这些值最新的变化反映出来,但是,在一些特殊场合下我们也可能需要在全速运行时观察变量的变化,此时可以点击“View-→Periodic Window Updata”(周期更新窗口),确认该项处于被选中状态,即可在全速运行时动态地观察有关值的变化。但是,选中该项,将会使程序模拟执行的速度变慢。