任务4 第一个程序
第一个C语言程序将告诉AT89S52单片机控制器,让它在执行程序时发送一条信息给PC(或笔记本电脑)。
创建与编辑你的第一个程序
双击Keil uVision IDE的图标,启动Keil uVision IDE程序,你会得到如图1-7所示的Keil uVision2 IDE的主界面。通过用Project菜单中的New Project命令建立项目文件,过程如下。
图1-7 Keil uVision2 IDE的主界面
(1)单击Project,会出现如图1-8所示的菜单画面,然后选择“New Project”,将出现如图1-9所示的对话框。
图1-8 Project菜单
图1-9 Create New Project对话框
(2)在文件名中输入“HelloRoBot”,保存在你想保存的位置(如D:\C51单片机应用与C语言程序设计\程序\Chapter 1),可不用加后缀名,单击“保存”按钮,会出现如图1-10所示的窗口。
图1-10 单片机型号选择窗口
(3)这里要求我们选择芯片的类型,Keil uVision2 IDE几乎支持所有的51核心单片机,并以列表的形式给出。本书使用的是ATMEL公司的AT89S52,在Keil uVision2 IDE提供的数据库(Data base)列表中找到此款芯片,然后单击“确定”按钮,会出现如图1-11所示的窗口,询问你是否加载8051启动代码,在这里我们选择“否”,不加载。(如果选择“是”,对你的程序没有任何影响。若你感兴趣,可选择“是”,看看编译器加载了哪些代码。)之后会出现如图1-12所示画面,此时即得到了项目文件。
图1-11 是否加载8051启动代码提示窗口
图1-12 目标工程窗口
项目文件创建后,这时只有一个框架,紧接着需要向项目文件中添加程序文件内容。Keil uVision2支持C语言程序。可以是已经建立好的程序文件,也可以是新建的程序文件。如果是建立好的程序文件,则直接用后面的方法添加;如果是新建立的程序文件,则先将程序文件.c存盘后再添加。
单击按钮(或通过“File→New”操作),为该项目新建一个C语言程序文件,保存后弹出如图1-13所示的对话框,将文件保存在项目文件夹中,在文件类型中填写.c(这里.c为文件扩展名,表示此文件类型为C语言源文件),因为下面将采用C语言编写第一个程序。
图1-13 C语言源文件保存对话框
例程:HelloRoBot.c
#include<uart.h> int main(void) { uart_Init();//串口初始化 printf("Hello,this is a message from your Robot\n"); while(1); }
将该例程输入Keil uVision IDE的编辑器,并以文件名HelloRoBot.c保存。下一步就是添加该文件到目标工程项目了,其具体添加过程如下。
(1)单击图1-12中的“+”,将出现如图1-14所示的列表。
图1-14 添加C语言文件到目标工程
(2)右键单击“Source Group 1”,在出现的菜单下选择“Add File To Group→Source Group 1”,出现Add Files to Group Source“Group1”对话框。在该对话框中选择需要添加的程序文件,如刚才建立的HelloRoBot.c,单击“Add”按钮,把所选文件添加到项目文件中。一次可添加多个文件。
(3)程序文件添加到项目文件后,这时图1-14中“Source Group 1”的前面将出现一个“+”号;单击它将出现刚才添加的源文件名,如图1-15所示(注意,图中显示的文件名是刚才输入的文件名)。
图1-15 添加了C语言文件的目标工程
双击源文件即可显示源文件的编辑界面。
下面来生成下载需要的可执行文件。要生成可执行的.hex文件,需要对目标工程“Target 1”进行编译设置,右键单击“Target 1”,选择“Option for target‘Target 1’”。单击“output”,选择其中的“Create HEX Fi:”,如图1-16所示,单击“确定”按钮关闭设置窗口。单击Keil uVision IDE快捷工具栏中的按钮,Keil的C编译器开始根据要生成的目标文件类型对目标工程项目中的C语言源文件进行编译。编译过程中,可以观察到源文件中有没有错误产生,如果没有错误产生,在IDE主窗口的下面出现如图1-17所示的提示信息,表明已成功生成了可执行文件,并存储在C语言源程序存储的目录中,文件名就是HelloRoBot.hex。
图1-16 设置目标工程的编译输出文件类型
图1-17 编译过程的输出提示信息
下载可执行文件到单片机
单击ISP下载软件图标,打开ISP下载软件窗口,如图1-18所示,并将通信参数设置成图中所示的参数。
图1-18 ISP软件下载窗口
第一个列表框为接口类型选择列表,该下拉列表中提供了许多接口类型:串口COM1~COM16、并口LPT1~LPT3,以及USB接口等。本书使用并口LPT1。
第二个列表框为下载速度选择列表,该列表内容与接口类型紧密相连。接口不同,该列表就提供不同内容的下载速度。若选择LPT1,则提供了5种下载速度:TURBO模式、FAST模式、NORMAL模式、SLOW模式和TURBO SLOW模式。在这5种模式下,程序下载速度依次减小。本书中的例程使用的是第一个模式:TURBO模式,下载速度最快。
第三个列表框为单片机型号选择列表。
如何快速地认识新的软件?
面对一款新的软件,你可能有一种无从下手的感觉:这个是干什么的?那个又是干什么的?其实,软件本身就提供了问题的答案。
每一款软件都提供了帮助文档。如SL ISP软件界面的右上角有个“问号”按钮,单击它就弹出一系列的选项,这些选项就对该款软件做出了大致的解释,有助你快速掌握软件的使用。
单击“Flash”按钮,选择要下载的可执行hex文件——HelloRoBot.hex,选择后单击编程开始下载。如果下载成功,则在窗口下面显示“完成次数:x次”,否则显示“失败次数:x次”。
如果芯片是第二次下载程序,先选中“擦除”复选框。
举一反三
如果你学习过《基础机器人制作与编程》这本书,并已经掌握了采用BASIC Stamp系列单片机模块的PBASIC语言开发技能,请你与刚才介绍的C语言编程过程进行比较,看看有何不同。并思考一下,这些不同对于初学者而言各有何优缺点。是不是复杂很多?
用串口调试软件查看单片机输出信息
打开串口调试终端,选择串口“COM1”后单击“打开串口”按钮,在“接收区”内你看到了什么?什么也没有!为什么呢?因为从把执行文件成功下载到单片机的那个时刻开始,程序就开始运行了:单片机已经向PC发送了信息。你错过了接收。怎么办呢?
在机器人教学板上提供了“Reset”按钮,它可以让下载到单片机内的程序重新运行一次。按下“Reset”按钮,是不是出现如图1-19所示的画面呢?
图1-19 串口调试终端
HelloRoBot.c是如何工作的
要讲清楚C语言的第一个程序是如何工作的,要比BASIC语言复杂很多。因为C语言是一个非常庞大的系统,是为开发大型程序而准备的。即使是最小的一个程序,其框架结构也很复杂。
例程中第一行代码是HelloRoBot.c所包含的头文件。该头文件在编译过程中用来将下面程序中需要用到的标准数据类型和由C语言编译器提供的一些标准输入/输出函数、中断服务函数等包括进来,生成可执行代码。头文件中可以嵌套头文件,同时也可以直接定义一些常用的功能函数。本例程中的头文件uart.h在本书的后续任务中都要用到,它其中就包含了本例程中,以及后面的例程中都要用到的uart_Init()函数的定义和实现。
下面先介绍函数的概念。一个较大的C语言程序一般分成若干个模块,每个模块实现一定的功能,我们称之为函数。任何一个C语言程序本身就是一个函数,该函数必须以main函数作为程序的起点,通常称之为主函数。主函数可以调用任何子函数,子函数之间也可以相互调用(但是不可以调用主函数)。函数定义的一般格式为:
函数返回值的类型 函数名(形式参数1,形式参数2,……)
第二行就是程序的入口main函数。main前面的int是指定main的函数返回值类型为整数类型,括号中void或无内容表示没有形式参数。每个函数的主体都要用“{}”括起来(反思一下同PBASIC语言编程的区别)。
函数的具体应用将在第3讲介绍。
main函数主体中有两行语句:第一行是串口初始化函数uart_Init(),用来规定单片机串口是如何与PC通信的。有兴趣的读者可以打开uart.h头文件,看看该函数是如何实现的。如果其中有很多内容不懂,不要紧,记住这个函数的功能就行,以后慢慢学习和理解。这行语句中“//”后的是注释。注释是一行会被编译器忽视的文字,不被编译,仅仅为了让人阅读程序方便。函数体中的第二行语句printf命令是单片机通过串口向PC发送一条信息。
printf函数
printf函数称为格式输出函数,其功能是按用户指定的格式,把指定的数据显示输出。该函数是C语言提供的标准输出函数,定义在C语言的标准函数库中,要使用它,必须包括定义标准函数库的头文件stdio.h。由于在uart.h头文件中包括了stdio.h,因此本例程无须另外包括该头文件。printf函数的一般形式为:
printf(“格式控制字符串”,输出列表);
格式控制字符串可由格式字符串和非格式字符串组成。
格式字符串是以%开头的字符串;输出列表在格式输出时才用到,它给出了各个输出项,要求与格式字符串在数量和类型上一一对应。
非格式字符串在输出时原样输出,在显示中起提示作用。例程中用到的就是非格式字符串。
“\n”是一个向调试终端发送回车命令的控制符。也就是说,当单击“Reset”按钮再次运行程序时,将在下一行显示“Hello,this is a message from your Robot”;如果没有“\n”,则会在上一语句中的结尾,即“Robot”后面接着显示。
while(1);的作用
while是C语言里的循环控制语句,它的具体语法将在第2讲里介绍,这里讲解为何要加上这个循环。
hex文件是加载在单片机Flash存储器上的,并且是从头开始往下加载。当你把hex文件加载上去时,填满了整个Flash空间吗?当然没有!那么,当程序执行完printf函数之后,它还将向下执行,但后面的空间并没有存放程序代码,这时程序会乱运行,也就是发生了跑飞现象。
加上while(1);语句,让程序一直停止在这里,就是为了防止程序跑飞。
该你了——例程:HelloRoBotYourTurn.c
#include<uart.h> int main(void) { int i; uart_Init(); i=7*11; printf("What's 7 X 11?\n"); printf("The answer is:%d\n",i); while(1); }
按照上述方法建立新的项目,输入程序HelloRoBotYourTurn.c并运行,查看输出结果,是否与图1-20一样?
图1-20 例程HelloRoBotYourTurn.c输出结果
HelloRoBotYourTurn.c是如何工作的
在介绍main函数内容之前,先讲解一下C语言的一些基本知识。
C语言数据类型
C语言有5种基本数据类型:字符、整型、单精度实型、双精度实型和空类型。这些数据类型的长度和范围会因处理器的类型和C语言编译程序的实现而有所不同,对于Keil51产生的目标文件,表1-1给出了两种书中常用的数据长度和范围。
表1-1 常用数据类型的长度和范围
标志符
在C语言中,标志符是对变量、函数名和其他各种用户定义对象的命名。标志符的长度可以是一个或多个字符。绝大多数情况下,标志符的第一个字符必须是字母或下画线,随后的字符必须是字母、数字或下画线(某些C语言编译器可能不允许下画线作为标志符的起始字符)。表1-2是一些正确或错误标志符命名的实例。
表1-2 正确或错误标志符命名实例
常量
C语言中的常量是不接受程序修改的固定值,常量可以为任意数据类型,如下例所示。
char 'a'、'9' int 21、-234
变量
在程序中可以改变的量称为变量。一个变量应该有一个名字(标志符),在内存中占据一定的存储单元,在该存储单元中存放变量的值。请注意区分变量名和变量值这两个不同的概念。所有C语言变量必须在使用之前定义。定义变量的一般形式是:
type variable_list;
这里的type必须是有效的C数据类型,variable_list(变量表)可以由一个或多个由逗号分隔的多个标志符名构成。下面给出一些定义的范例:
int i,j,k; char'x','y','z';
注意:C语言中变量名与其类型无关。
运算符
C语言有3大运算符:算术、关系与逻辑、位操作。另外,C语言还有一些特殊的运算符,用于完成一些特殊的任务。
算术运算符
表1-3给出了C语言允许的算术运算符。在C语言中,运算符“+”、“-”、“*”和“/”的用法与大多数计算机语言的相同,几乎可以用于C语言内定义的任何数据类型。
表1-3 算术运算符
表达式
表达式由运算符、常量及变量构成。C语言的表达式遵循一般代数规则。
C语言规定,任何表达式在其末尾加上分号就构成为语句。
赋值运算符
赋值运算符记为“=”。由“=”连接的式子称为赋值表达式,其后加分号构成赋值语句,其一般形式为:
变量=表达式;
现在,来看看main函数是如何工作的。
int i;
定义了一个整型变量i。i即是变量的标志符,分号表示结束。
uart_Init();
与上一个例程一样,规定单片机串口如何与PC通信。
i=7*11;
将表达式“7*11”的值赋给变量i,也就是说变量i的值为77。
printf("What's 7*11?\n");
输出“What’s 7*11?”,这里printf的用法与上一个例程一样。
printf("The answer is:%d\n",i);
这里用到了printf函数的格式字符串输出。%d是指定输出数据的类型为十进制整数。printf函数首先输出“The answer is:”;然后它遇到了“%d”,表示将后面输出列表中的变量以十进制的形式输出,即将变量i以“77”的形式输出;最后的输出结果即为:The answer is:77
最后一条语句while(1);也起到与在上例中同样的作用:防止程序跑飞。