• 沒有找到結果。

—— C51 单片机应用与 C 语言程序设计

N/A
N/A
Protected

Academic year: 2022

Share "—— C51 单片机应用与 C 语言程序设计"

Copied!
165
0
0

加載中.... (立即查看全文)

全文

(1)

高等职业教育创新实践教材 工程对象教学法系列教材

C51 单片机应用与 C 语言程序设计

—— 基于机器人工程对象的项目实践

秦志强 等 编著

电子工业出版社

(2)

内容提要

本教材以两轮智能移动机器人工程项目为主线,通过循序渐进的构建智能机 器人的智能控制器和传感器电路,将单片机外围接口特性、内部结构原理、应用 设计方法和 C 语言程序设计等知识通过先项目实践、后总结归纳的方式传授给 学生,彻底打破了传统的教学方法和教学体系结构,解决了单片机原理与应用,

以及 C 语言程序设计等核心专业基础课程抽象与难学的老大难问题。

本书可作为中等职业教育和高等职业教育的《单片机技术与应用》以及《嵌

入式 C 语言程序设计》两门课程的学习教材和教学参考书,也可以作为本科院

校工程训练、电子制作的实践教材和相应专业课程的实验配套教材,同时还可以

供广大希望从事嵌入式系统开发和 C 语言程序设计的学生或者个人自学使用。

(3)

前 言

本书可作为高职高专院校工程类专业二年级及以上学生学习单片机原理与应用的主导教材,也 可以作为大学二年级及以上工程类专业学生学习单片机原理与应用的辅助教材,还可以供其他机器 人爱好者使用。使用者只需要有初级的编程基础和简单的计算机操作和基础的英语,不需要专业的 C 语言基础。

本书的任务是要让每一个学习单片机原理与应用的学生或者个人都能够以教育机器人作为工 程对象,让他们在开发自己的教育机器人过程中学习和掌握单片机的基本原理与应用系统的开发技 能,包括:

z C51 系列单片机的 C 语言编程环境和使用方法;

z 单片机的输入接口、使用方法和 C 程序设计;

z 单片机的输出接口、使用方法和 C 程序设计;

z 单片机的接口电气特性和外围电路;

z 单片机的串口通讯、应用与 C 程序设计;

z 单片机与 LCD 的连接与 C 编程;

z 基础传感器原理和用 C51 编程实现机器人基本智能的实现方法等。

本书在编写过程当中非常注意的一点,就是寓教于乐,兴趣为先。将传统的学习单片机原理与 应用(即先理论讲解,然后实验验证)的模式,改变为先实验和实践如何应用,然后再归纳单片机 原理(即先实践,后归纳)的模式,并以机器人作为贯穿实践过程的典型工程对象,使整个教学和 学习过程充满挑战和乐趣,大大提高学习效率。同时在学习和实践的过程中,还可以培养学生的系 统世界观和方法论。

熟练掌握本教材的学生或者个人,可以继续《高级机器人制作》的课程。

通过本课程的学习和实践,可以引领学生或者个人进入神奇的信息技术世界和机器人世界。

本书的完成,编者首先要感谢东南大学的张文锦教授,是他的建议促成了本书的成文;其次要 感谢深圳市德普施科技有限公司的邓莹和阮科,邓莹对本书的最后完成付出了巨大的努力,阮科一 一验证了本书的所有项目;还要感谢德普施科技的前员工刘庆秋,是他最早帮助起草了本书的初稿。

编者 2007 年 10 月

(4)

目 录

前 言...I

第一章 C51 单片机编程环境与机器人智能 ...1

单片机与 C51 系列单片机...1

机器人与 C51 单片机...3

任务一 获得软件...4

任务二 安装软件...5

任务三 硬件连接...5

任务四 你的第一个程序...6

printf 函数 ...12

C 语言数据类型 ...14

常量 ...14

变量 ...14

运算符 ...15

表达式 ...15

任务五 做完实验关断电源 ...16

工程素质和技能归纳 ...16

科学精神的培养 ...16

第二章 单片机输出接口与伺服电机控制 ...17

C51 单片机的输入/输出接口 ...17

任务一 单灯闪烁控制 ...18

while 语句 ...20

任务二 机器人伺服电机控制信号...21

任务三 计数并控制循环次数...23

for 语句 ...23

任务四 用你的计算机来控制机器人的运动 ...27

scanf 函数...28

工程素质和技能归纳 ...29

科学精神的培养 ...29

第三章 C 语言函数与机器人巡航控制 ...31

任务一 基本巡航动作 ...31

任务二 匀加速/减速运动...36

任务三 用函数调用简化运动程序...38

任务四 高级主题――用数组建立复杂运动 ...43

字符型数据 ...43

数组 ...45

switch 语句 ...48

工程素质和技能归纳 ...50

科学精神的培养 ...50

第四章 单片机输入接口与机器人触觉导航 ...51

触觉导航与单片机输入接口 ...51

任务一 安装并测试机器人胡须 ...51

位操作符 ...53

(5)

if 语句 ...54

?操作符 ...54

任务二 通过胡须导航 ...57

关系与逻辑运算符...58

任务三 机器人进入死区后的人工智能决策 ...62

工程素质和技能归纳 ...66

科学精神的培养 ...66

第五章 C51 输入/输出接口与红外线导航 ...67

使用红外线发射和接收器件探测道路 ...67

任务一 搭建并测试 IR 发射和探测器对...68

任务二 探测和避开障碍物 ...72

任务三 高性能的 IR 导航 ...76

do…while 语句 ...78

任务四 俯视的探测器 ...79

工程素质和技能归纳 ...84

科学精神的培养 ...84

第六章 机器人的距离检测...85

用同样的 IR LED/探测电路检测距离 ...85

任务一 定时/计数器的运用 ...85

任务二 测试扫描频率 ...89

任务三 尾随小车...92

任务四 跟踪条纹带 ...98

工程素质和技能归纳 ...101

科学精神的培养 ...101

第七章 机器人中 UART 的应用 ...102

串口控制寄存器 SCON ...103

RS232 电平与 TTL 电平转换 ...103

任务一 编写串口通信程序 ...104

串口工作流程...108

工程素质和技能归纳 ...109

科学精神的培养 ...109

第八章 LCD 应用编程及与机器人的集成技术 ... 110

任务一 认识 LCD 显示器 ... 110

任务二 编写 LCD 模块驱动程序 ... 113

指针 ... 117

任务三 用 LCD 显示机器人运动状态 ... 118

C 语言的编译预处理 ... 118

工程素质和技能归纳 ...125

科学精神的培养 ...125

第九章 多传感器智能机器人...126

多传感器智能机器人的设计目标 ...126

任务一 多传感器信息与 C 语言结构体的使用和编程 ...126

结构体 ...126

任务二 智能机器人的行为控制策略和编程 ...133

(6)

工程素质和技能归纳 ...138

科学精神的培养 ...138

附录A C 语言概要归纳...139

附录B 微控制器原理归纳 ...149

附录C 无焊锡面包板 ...153

附录D LCD 模块电路 ...156

附录E 本讲义所使用机器人零配件清单...157

(7)

第一章 C51单片机编程环境与机器人智能

单片机与C51系列单片机

什么是单片机?

一台能够工作的计算机要有这样几个部份:CPU(Central Processing Unit,中央处理单 元:进行运算、控制)、RAM(Random Access Memory,随机存储器:数据存储)、ROM(Read Only Memory,只读存储器:程序存储)、输入/输出设备(串行口、并行口等)。在个人计算 机上这些部份被分成若干块芯片或者插卡,安装一个称之为主板的印刷线路板上。而在单片 机中,这些部份全部被做到一块集成电路芯片中,所以就称为单片机。

学习单片机有必要吗?

与我们经常使用的个人计算机、笔记本电脑相比,单片机的功能是很小的,那学它干啥 吗?实际生活中并不是任何需要计算机的场合都要求计算机有很高的性能,比如空调温度的 控制,冰箱温度的控制等都不需要很复杂高级的计算机。应用的关键是看是否够用,是否有 很好的性能价格比。

单片机凭借体积小、质量轻、价格便宜等优势,已经渗透到我们生活的各个领域:导弹 的导航装置、飞机上各种仪表的控制、工业自动化过程的实时控制和数据处理、广泛使用的 各种智能IC 卡、民用豪华轿车的安全保障系统、录象机、摄象机、全自动洗衣机、程控玩 具、电子宠物等等。更不用说自动控制领域的机器人、智能仪表、医疗器械了。

因此,单片机的学习、开发与应用将造就一批计算机应用、嵌入式系统设计与智能化控 制的科学家、工程师,同时,学习使用单片机也是了解通用计算机原理与结构的最佳选择。

C51 系列单片机

一提到单片机,你就会经常听到这样一些名词:MCS51、8051、C51 等等,它们之间究 竟是什么关系呢?

MCS51 是指由美国 INTEL 公司生产的一系列单片机的总称。这一系列单片机包括了好 些品种,如8031,8051,8751 等,其中 8051 是最典型的产品,该系列单片机都是在 8051 的基础上进行功能的增、减、改变而来的,所以人们习惯于用 8051 来称呼 MCS51 系列单 片机。

INTEL 公司将 MCS51 的核心技术授权给了很多其它公司,所以有很多公司在做以 8051 为核心的单片机,当然,功能或多或少有些改变,以满足不同的需求。其中较典型的一款单 片机AT89C51(简称 C51)由于由美国 ATMEL 公司以 8051 为内核开发生产。本教材使用 的AT89S52 单片机是在此基础上改进而来。

AT89S52 是一种高性能、低功耗的 8 位单片机,内含 8k 字节 ISP(In-system Programmable,

系统在线编程)可反复擦写1000 次的 FLASH 只读程序存储器,器件采用 ATMEL 公司的高 密度、非易失性存储技术制造,兼容标准MCS51 指令系统及其引脚结构。在实际工程应用 中,功能强大的AT89S52 已成为许多高性价比嵌入式控制应用系统的解决方案。

什么是单片机的位数?

现在市场上闹得沸沸扬扬的微软新推出的系统VISTA64位操作系统;大家常用的系 统,如WINDOWS XPWINDOWS 2003等,是32位操作系统;这里你将用到的单片机AT89S528位的,而有些厂家生产的单片机则是16位的。那么,这些位数:6432168代表 什么意义呢?

简单地说,这些位数指的是 能一次处理的数据的最大长度。当然,这里的位是指

(8)

二进制的位,而非十进制的位。AT89S528位的单片机,意味着它如果要处理16位数据 的话就应该分两次处理。

嵌入式系统

嵌入式系统是指嵌入到工程对象中能够完成某些相对简单或者某些特定功能的计算机 系统。与从8位机迅速向16位、32位、64位过渡的通用计算机系统相比,嵌入式系统有其 功能的特殊要求和成本的特殊考虑,从而决定了嵌入式系统在高、中、低端系统三个层次共 存的局面。在低端嵌入式系统中,8位单片机从20世纪70年代初期诞生至今还一直在工业 生产和日常生活中广泛使用。

嵌入式系统嵌入到对象系统中,并在对象环境下运行。与对象领域相关的操作主要是对 外界物理参数进行采集、处理,对对象实现控制,并与操作者进行人机交互等。

鉴于嵌入式低端应用对象的有限响应要求、嵌入式系统低端应用的巨大市场以及8位机

具有的计算能力,可以预测在未来相当长的时间内,8位机仍然是嵌入式应用中的主流机型。

早期的单片机应用程序开发通常需要仿真机、编程机等配套工具,要配置这些工具需要 一笔不小的投资。本教材采用的AT89S52,不需要仿真机和编程机,只需运用 ISP 电缆就可 以对单片机的FLASH 反复擦写 1000 次以上,因此使用起来特别方便简单,尤其适合初学 者使用,而且配置十分灵活,可扩展性特别强。

In-system ProgrammableISP,系统在线编程)

In-system programmable 是指用户可把已编译好的程序代码通过一条“下载线”直接写

入到器件的编程(烧录)方法,已经编程的器件也可以用ISP方式擦除或再编程。ISP所用

的“下载线”并非不需要成本,但相对于传统的“编程器”成本已经大大下降了。通常FLASH

型芯片会具备ISP下载能力。

本教材将引导你如何运用 AT89S52 作为机器人的大脑制作一款教育机器人,并采用 C 语言对AT89S52 进行编程,使机器人实现下述四个基本智能任务:

1.安装传感器以探测周边环境;

2.基于传感器信息做出决策;

3.控制机器人运动(通过操作带动轮子旋转的电机);

4.与用户交换信息;

通过这些任务的完成,使你在无限的乐趣之中,不知不觉地掌握 C51 单片机原理与应 用开发技术,以及C 语言程序设计技术,轻松走上嵌入式系统开发之路。

为了方便单片机微控制器与电源、ISP 下载电缆、串口线以及各种传感器和电机的连接,

需要制作一个电路板,并将单片机插在教学板上,如图 1-1 所示。本教材将此电路板叫做教 学板。

(9)

图 1-1 C51 单片机教学板

机器人与C51单片机

图 1-2 所示的是本教材使用的机器人工程对象,它采用 AT89S52 单片机作为大脑,通过 教学板安装在机器人底盘上。本教材将以此机器人作为典型工程对象,完成上节提到的机器 人所需具备的四种基本能力,使机器人具有基本的智能。本教材假设你已经学习过《基础机 器人制作》课程,并已经组装好该机器人的机械套件和伺服电机,且已经调试好了机器人伺 服电机的零点。如果没有学习过《基础机器人制作与编程》,也不要紧,可以在边学习该课 程的同时,参考《基础机器人制作与编程》的相关章节,在后面的任务中,如果需要,会给 出相关指引。

(10)

图 1-2 采用 C51 单片机的机器人

本章首先通过以下步骤告诉你如何安装和使用 C51 单片机的 C 语言编程开发环境,如何 开发第一个简单机器人程序,并在机器人上如何运行你写的这个程序。本章的具体任务包括:

z 寻找并安装开发编程软件

z 连接机器人到电池或者供电的电源

z 连接单片机教学板 ISP 接口到计算机,以便编程 z 连接单片机教学板串行接口到计算机,以便调试和交互

z 运用 C 语言初次编写少量的程序,运用编译器编译生成可执行文件,然后下载到单片 机上,通过串口观察机器人上的单片机教学板的执行结果

z 完成后断开电源

任务一 获得软件

在本课程的学习中,你将反复用到三款软件:Keil uVision2 IDE 集成开发环境、SL ISP 下载软件、串口调试软件等。

1.Keil uVision2 IDE 集成开发环境

该软件是德国 KEIL 公司出品的 51 系列单片机 C 语言集成开发系统。如果你已经学习过

《基础机器人制作与编程》,并掌握了 PBASIC 语言编程思想和基本技能,你将会发现,C 语言在语法结构上更加灵活,功能更加强大,但同时学习和理解起来也稍困难些。

你可以在KEIL公司的网站www.keil.com上获得该软件的安装包。

2.SL ISP软件下载工具

该软件是广州天河双龙电子有限公司推出的一款ISP下载软件,使用该软件你可以将可 执行文件下载到你的机器人单片机上。该软件的使用需要你的计算机有并行口。

(11)

3.串口调试软件

此软件是用来显示单片机与计算机的交互信息的。在硬件上,你的计算机至少要有串口 或 USB 接口来与单片机教学板的串口连接。

教材光盘中提供了该软件的绿色版本,无需安装即可使用。

教你一招,如何从互联网上获得你想要的东西:

当今互联网如此发达,以至我们可以“万事”不求人。熟练运用互联网的搜索引擎,你 也可以做个“百事通”。

如今有两大著名的搜索引擎,国内的www.baidu.com,全球的www.google.com,只要你输 入关键字,你就可以找到相关的任何东西;比如你想找Keil软件,你可以先打开google网,

然后输入关键字“Keil 下载”,你就可以找到很多相关的网站。如 http://bbs.mcu123.net/bbs/dispbbs.asp?boardID=7&ID=6352&page=1

任务二 安装软件

到目前为止,你已从网站上,或从教材配套光盘中获得了软件安装包。在教材配套光盘 中提供了几个文件夹,它们分别是 Keil uVision2 安装包、ISP 软件安装包、串口调试终端、

头文件和本书例程的源码。

软件的安装很简单,与你安装的其他软件过程一样。

安装 Keil uVision2

1. 执行 Keil uVision2 安装程序,选择安装 Eval Version 版进行安装

2. 在 后 续 出 现 的 窗 口 中 全 部 选 择 Next 按 钮 , 将 程 序 默 认 安 装 在 C:\Program Files\Keil 文件目录下

3. 将光盘“头文件”文件夹中的文件拷贝到 C:\Program Files\Keil\C51\INC 文件夹 里

Keil uVision IDE 软件安装到你的电脑上的同时,会在你的计算机桌面建立一个快捷 方式。

安装 ISP 下载软件与此类似。

任务三 硬件连接

C51 教学板(或者说机器人大脑)需要连接电源以便运 行,同时也需要连接到 PC 机(或笔记本电脑)以便编程和 交互。以上接线完成后,你就可以用编辑器软件来对系统进 行测试。下面将告诉你如何完成上述硬件连接任务。

串口的连接

机器人教学板通过串口电缆连接到 PC 机(或笔记本电 脑)上以便与用户交互。如果你的计算机有串行接口,直接 使用串口连接电缆。如果没有,此时需要使用 USB 转串口适 配器,如图 1-3 所示。你只需将该串口线一端的串口连接到 你的机器人教学板,而另一端连接到计算的 USB 口上。

如果你使用的是 Windows98 操作系统,在使用该适配器 调试程序前,你还需要给适配器安装驱动程序,相关步骤请 按照适配器硬件和软件安装说明书进行。如果是 Windows2000 以上的操作系统,则通常可以直接使用,无需安装驱动程序。

1-3 USB 转串口适配器

图 1-4 ISP 下载线

(12)

机器人程序通过连接到 PC 机或者笔记本电脑的并口上的 ISP 下载线来下载到教学板上 的单片机内。图 1-4 所示为 ISP 下载线。下载线一端连接到 PC 机或者笔记本的并行接口上,

而另一端(小端)连接到教学板上的程序下载口上。

电池的安装

本教材使用的机器人采用五号碱性电池给机器人电机和教学板供电,在继续下面的任务 前,请先检查机器人底部电池盒内是否已经装好电池,并是否有正常的电压输出。如果没有,

请更换新的电池。更换过程中,确保每颗电池都按照塑料盒子里面标记的电池极性(“+”

和“-”)方向装入。

给教学板和单片机进行通电检查

教学底板上有一个三位开关(见图 1-5),当开 关拨到“0”位断开教学底板电源。无论你是否将电 池组或者其它电源连接到教学底板上,只要三位开 关位于“0”位,那么设备就处于关闭状态。

现在将三位开关由“0”位拨至“1”位,打开 教学板电源,如图 1-6 所示。检查教学底板上标有

“Pwr”的绿色 LED 电源指示灯是否变亮。如果没有,

检查电池盒里的电池和电池盒的接头是否已经插到 教学板的电源插座上。

开关“2”你将会在后续章节中用到。将开关拨至“2”后,电源不仅要结教学板供电,

同时还会给机器人的执行机构——伺服电机供电,同样的,此时绿色 LED 电源指示灯仍然会 变亮。

任务四 你的第一个程序

你编写和测试的第一个 C 语言程序将告诉 AT89S52 单片机控制器,让它在执行程序时发 送一条信息给 PC 机(或笔记本电脑)。

创建与编辑你的第一个程序

双击 Keil uVision IDE 的图标,启动 Keil uVision IDE 程序,你会得到图 1-7 所示的 Keil uVision2 IDE 的主界面。通过用 Project 菜单中的 New Project 命令建立项目文件,

过程如下:

1-5 处于关闭状态的三位开关

1-6 处于 1 位状态的三位开关

(13)

1-7 Keil uVision IDE的主界面

1. 点击 Project,会出现图 1-8 所示的菜单画面,然后选择“New Project”,将出 现图 1-9 所示对话框。

1-8 Project菜单画面 图1-9 Create New Project对话框

2. 在文件名中输入如“HelloRoBot”,保存在你想保存的位置(如 D:\中级机器人制 作与编程\程序\Chapter 1),可不用加后缀名,点击“保存”,后会出现图 1-10 所示的窗口。

3. 这里要求我们选择芯片的类型,Keil uVision2 IDE 几乎支持所有的 51 核心单片 机,并以列表的形式给出。本教材使用的是 Atmel 公司的 AT89S52,在 Keil uVision2 IDE 提供的数据库(Data base)列表中找到此款芯片,然后点击确定,会出现图 1-11 所示的窗口,询问你是否加载 8051 启动代码,在这里我们选择“否”,不加载。

(如果你选择“是”,对你的程序没有任何影响。若你感兴趣,可选择“是”,看 看编译器加载了哪些代码。)之后会出现图 1-12 画面,此时即得到了项目文件。

(14)

图 1-10 单片机型号选择窗口

图 1-11 是否加载 8051 启动代码提示窗口 图 1-12 目标工程窗口 项目文件创建后,这时只有一个框架,紧接着需要向项目文件中添加程序文件内容。Keil uVision2 支持 C 语言程序。可以是已经建立好的程序文件,也可以是新建的程序文件。如 果是建立好了的程序文件,则直接用后面的方法添加;如果是新建立的程序文件,则先将程 序文件.c 存盘后再添加。

点 击 按 钮 ( 或 通 过

“File->New”操作)为该项目新 建一个 C 语言程序文件,保存后 弹出图 1-13 所示的对话窗口,将 文件保存在项目文件夹中,在文 件类型中填写.C(这里.C 为文件 扩展名,表示此文件类型为 C 语 言源文件),因为你下面将采用 C 语言编写第一个程序。

例程:HelloRoBot.c

#include<uart.h>

int main(void) {

uart_Init(); //串口初始化

printf("Hello,this is a message from your Robot\n");

while(1);

}

1-13 C语言源文件保存对话框

(15)

是添加该文件到目标工程项目了,其具体添加过程如下:

1. 单击图 1-12 中的“+”,将出现图 1-14 所示的列表;

2. 然后右键点击“Source Group 1”,在出现的菜单下选择“Add File To Group

“Source Group 1”, 出现 Add Files to Group Source ‘Group1’对话框。在 该对话框中选择需要添加的程序文件,如刚才建立 HelloRoBot.c,单击 Add 按钮,

把所选文件添加到项目文件中。一次可添加多个文件。

3. 程序文件添加到项目文件中去后,这时上图中“Source Group 1”的前面将出现一 个“+”号;单击它将出现刚才添加的源文件名,如图 1-15 所示(注意:图中显示 的文件名是刚才输入的文件名)。

图1-14 添加C语言文件到目标工程 图1-15 添加了C语言文件的目标工程

双击源文件即可显示源文件的编辑界面。

下面来产生下载需要的可执行文件。要产生可执行的.Hex文件,需要对目标工程“Target 1”进行编译设置,右键点击“Target 1”,选择“Option for target ‘Target 1’”。

点击“output”,选择其中的“Create HEX File”,如图 1-16 所示,点击确定关闭设置窗 口。然后点击 Keil uVision IDE 快捷工具栏中的 ,Keil 的 C 编译器开始根据要生成的 目标文件类型对目标工程项目中的 C 语言源文件进行编译。编译过程中,可以观察到源文件 中有没有错误产生,如果没有错误产生,在 IDE 主窗口的下面出现如图 1-17 的提示信息,

表明已成功生成了可执行文件,并存储在 C 语言源程序存储的目录中,文件名就是 HelloRoBot.Hex。

(16)

图1-16 设置目标工程的编译输出文件类型

图 1-17 编译过程的输出提示信息 下载可执行文件到单片机

点击 ISP 下载软件图标,打开 ISP 下载软件窗口如图 1-18 所示,并将通信参数设置成 图中所示的参数。

第一个为接口类型选择窗口,该窗口的下拉列表中提供了许多接口类型:串口 COM1~

COM16、并口 LPT1~LPT3 以及 USB 接口等。教材使用并口 LPT1。

第二个为下载速度选择窗口,该窗口内容与接口类型紧密相连。不同的接口,该窗口就 提供不同内容的下载速度。若选择 LPT1,则提供了五种下载速度:TURBO 模式、FAST 模式、

NORMAL 模式、SLOW 模式和 TURBO SLOW 模式。在这五种模式下,程序下载速度依次减小。教 材中的例程使用的是第一个模式 TURBO 模式,下载速度最快。

第三个为单片机型号选择窗口。

如何快速地认识新的软件?

面对一款新的软件,你可能有一种无从下手的感觉:这个是干嘛的?那个又是干嘛的?

其实,软件本身就提供了问题的答案。

每一款软件都提供了帮助文档。如 SL ISP 软件界面的右上角有个“问号”按钮,单击 它就弹出一系列的选项,这些选项就对该款软件做出了大致的解释,有助你快速掌握软件的 使用。

点击“Flash”,选择要下载的可执行 HEX 文件——HelloRoBot.Hex,选择后点击编程 开始下载。如果下载成功,则下面显示“完成次数:x 次”,否则显示“失败次数: x 次”。

(17)

1-18 ISP 软件下载窗口

举一反三

如果你已经学习过《基础机器人制作与编程》,并已经掌握了采用 BASIC Stamp 系列单 片机模块的 PBASIC 语言开发技能,请你与刚才介绍的 C 语言编程过程进行比较,看看有何 不同。并思考一下,这些不同对于初学者而言各有何优缺点。

用串口调试软件查看单片机输出信息

打开串口调试终端,选择串口“COM1”后点击“打开串口”,在“接收区”内你看到了 什么?什么也没有!为什么呢?因为从你把执行文件成功下载到单片机的那个时刻开始,程 序就开始运行了:单片机已经向 PC 机发送了信息。你错过了接收。怎么办呢?

在机器人教学板上给你提供了“Reset”按钮,它可以让下载到单片机内的程序重新运 行一次。按下“Reset”按钮,是不是出现如图 1-19 所示画面呢?

(18)

图 1-19 串口调试终端 HelloRoBot.c 是如何工作的?

例程中第一行代码是 HelloRoBot.c 所包含的头文件。该头文件在编译过程中用来将下 面程序中需要用到的标准数据类型和由 C 语言编译器提供的一些标准输入/输出函数、中断 服务函数等包括进来,生成可执行代码。头文件中可以嵌套头文件,同时也可以直接定义一 些常用的功能函数。本例程中的头文件 uart.h 在本教材的后续任务中都要用到,它其中就 包含了本例程中以及后面例程中都要用到的 uart_Init()函数的定义和实现。

下面先讲一下函数的概念。一个较大的 C 语言程序一般分成若干个模块,每个模块实现 一定的功能,我们称之为函数。任何一个 C 语言程序本身就是一个函数,该函数必须以 main 函数作为程序的起点,通常称之为主函数。主函数可以调用任何子函数,子函数之间也可以 相互调用(但是不可以调用主函数)。函数定义的一般格式为:

函数返回值的类型 函数名(形式参数1,形式参数2………..

第二行就是程序的入口 main 函数。main 前面的 int 是指定 main 的函数返回值类型为 整数类型,括号中 void 或无内容表示没有形式参数。每个函数的主体都要用“{ }”括起来

(反思一下同 PBASIC 语言编程的区别)。

函数的具体讲解将在第三章。

main 函数主体中有两行语句:第一行是串口初始化函数 uart_Init( ),用来规定单片 机串口是如何与 PC 通信的。有兴趣的读者可以打开 uart.h 头文件,看看该函数是如何实现 的,如果其中有很多内容不懂,不要紧,记住这个函数的功能就行,以后慢慢学习和理解。

这行语句中“//”后的是注释。注释是一行会被编译器忽视的文字,因为注释是为了给人阅 读。函数体中的第二行语句 printf 命令是单片机通过串口向 PC 机发送一条信息。

printf函数

printf 函数称为格式输出函数,其功能是按用户指定的格式,把指定的数据显示输出。

该函数是 C 语言提供的标准输出函数,定义在 C 语言的标准函数库中,要使用它,必须包括 定义标准函数库的头文件 stdio.h。由于在 uart.h 头文件中包括了 stdio.h,因此本例程无 需另外包括该头文件。printf 函数的一般形式为:

(19)

printf(“格式控制字符串”,输出列表);

格式控制字符串可由格式字符串和非格式字符串两种组成。

格式字符串是以%开头的字符串;输出表列在格式输出时才用到,它给出了各个输出项,

要求与格式字符串在数量和类型上一一对应。

非格式字符串在输出时原样输出,在显示中起提示作用。例程中用到的就是非格式字符 串。

“\n”是一个向调试终端发送回车命令的控制符。也就是说,当你按下“Reset”按钮 再次运行程序时,将在下一行显示“Hello,this is a message from your Robot”;如果 没有“\n”,则会在上一语句中的结尾,即“Robot” 后面接着显示。

while(1);的作用

while 是 C 语言里的循环控制语句,它的具体语法将在第二章里介绍,这里向你讲解为 何要加上这个循环。

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 一样?

(20)

HelloRoBotYourTurn.c 是如何工作的?

在介绍 main 函数内容之前,先向你讲解一下 C 语言的一些基本知识。

C语言数据类型

C 语言有 5 种基本数据类型:字符、整型、单精度实型、双精度实型和空类型。这些数 据类型的长度和范围会因处理器的类型和C 语言编译程序的实现而有所不同,对于 KEIL51 产生的目标文件,表1-1 给出了两种教材中常用的数据长度和范围。

1-1 常用数据类型的长度和范围

类型 长度(单位 bit) 范围

char 8 -128~+127 即-27~(27-1)

int 16 -32768~+32767 即-215~(215-1)

float 32 -3.4X10-38~3.4X1038

标识符

在 C 语言中,标识符是对变量、函数名和其他各种用户定义对象的命名。标识符的长 度可以是一个或多个字符。绝大多数情况下,标识符的第一个字符必须是字母或下划线,随 后的字符必须是字母、数字或下划线(某些 C 语言编译器可能不允许下划线作为标识符的 起始字符)。表1-2 是一些正确或错误标识符命名的实例。

1-2 正确或错误标识符命名实例

正确形式 错误形式

count 2count test23 hi!there high_balance high..balance

常量

C 语言中的常量是不接受程序修改的固定值,常量可以为任意数据类型,如下例所示:

char ‘a’、‘9’

int 21、-234

变量

在程序中可以改变的量称为变量。一个变量应该有一个名字(标识符),在内存中占据 一定的存储单元,在该存储单元中存放变量的值。请注意区分变量名和变量值这两个不同的 概念。所有C 语言变量必须在使用之前定义。定义变量的一般形式是:

type variable_list;

这里的 type 必须是有效的 C 数据类型,variable_list(变量表)可以由一个或多个由逗 号分隔的多个标识符名构成。下面给出一些定义的范例:

int i,j,k;

char ‘x’,’y’,’z’;

注意,C 语言中变量名与其类型无关。

(21)

运算符

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 X 11?\n");

输出“What’s 7 X 11?”,这里 printf 的用法与上一个例程一样。

printf("The answer is :%d\n",i);

这里用到了 printf 函数的格式字符串输出。%d 是指定输出数据的类型为十进制整数。

printf 函数首先输出“The answer is :”;然后它遇到了“%d”,表示将后面输出列表中的 变量以十进制的形式输出,即,将变量 i 以“77”的形式输出;最后的输出结果即为:The answer is :77

最后一条语句:while(1);也起到与在上例中同样的作用:防止程序跑飞。

该你了——利用 printf()函数输出图形

通过两个例程的学习,你应该大概掌握了 printf()函数的使用。除此之外,你还可以 利用它来做一些有趣的事,比如你如何让竖线“|”在屏幕上顺时针或逆时针转动输出呢?

参考下面的程序代码:

printf("|");

delay_nms(500);

(22)

delay_nms(500);

printf("-");

delay_nms(500);

printf("\");

delay_nms(500);

printf("|");

delay_nms(500);

当然,你还可以显示静态图形,如:

*

* *

* * *

* * * *

* * *

* *

* 其实很简单,仔细想想!

任务五 做完实验关断电源

把电源从教学底板上断开很重要,原因有几点:首先,如果系统在不使用时没有消耗电 能,电池可以用的更久;其次,在以后的试验中,你将在教学底板上的面包板上搭建电路,

搭建电路时,应使面包板断电。如果是在教室,老师可能会有额外的要求,比如断开串口电 缆,把教学底板存放到安全的地方等等。总之,你做完试验后最重要的一步是断开电源。

断开电源比较容易,只要三位开关拨到左边的 0 位即可。

工程素质和技能归纳

1. C51 系列单片机 Keil uVision IDE(集成开发环境)软件和 ISP 下载软件的下载和 安装

2. 机器人用 C51 教学板与计算机或者笔记本的连接

3. 如何在集成开发环境中创建目标工程文件,并添加和编辑 C 语言源程序 4. C 语言程序的编译和下载

5. 串口调试终端的使用

6. C 语言基本知识:基本数据类型、常量、变量、运算符、表达式 7. printf 格式输出函数的使用

科学精神的培养

1. 比较 Keil uVision IDE 与 BASIC Stamp 系列开发环境的优缺点,找出它们的共同 特点

2. 比较第一个 C 语言程序与第一个 PBASIC 程序的异同,找出它们的共同点 3. 比较 BASIC Stamp 的 PBASIC 调试指令和 Keil C 的输出指令 printf 的异同 4. 查找 C 语言的标准输入输出库函数,了解 printf 的总体功能。本章中到了它的两

个格式符和控制符

(23)

第二章 单片机输出接口与伺服电机控制

本章教你如何用单片机 AT89S52 的输入/输出接口来连接、测试和控制机器人伺服电机。

为此,你需要理解和掌握用单片机输入/输出接口控制伺服电机方向、速度和运行时间的相 关原理和编程技术。

C51单片机的输入/输出接口

控制机器人伺服电机以不同速度运动是通过让单片机的输入/输出(I/O)口输出不同的 脉冲序列来实现的。51 系列单片机有 4 个 8 位的并行 I/O 口:P0、P1、P2 和 P3。这 4 个接 口,既可以作为输入,也可以作为输出;可按 8 位处理,也可按位方式(1 位)使用。图 2-1 是单片机 AT89S52 的引脚定义图,这是一个标准的40 引脚双列直插式集成电路芯片。

图2-1 单片机AT89S52引脚I/O定义图

说到这里,你或许马上就会问,单片机如何知道它的引脚端口是作为输入还是输出呢?

这与单片机各I/O 接口的内部结构有关,而且每个 8 位并行 I/O 口的使用方式也不太一 样。后面的章节会根据机器人控制的需要逐步介绍它们的原理和使用方法。本章主要介绍如 何用P1 口来完成机器人伺服电机的控制。P1 口作为输出时,使用非常简单,可以直接对该 端口的位进行操作而不需额外设置,只需向该端口的各个位输出你想输出的高低电平信号即 可。

AT89S52引脚

如图2-1所示,AT89S52共有40根引脚,其中32根是I/O端口引脚。在这32根引脚

中,有29根具备两种用途(用圆括号写出),既可作为I/O端口,也可作为控制信号或地

址及数据线。

(24)

任务一 单灯闪烁控制

为了验证P1 口的输出电平是不是由你编写的程序输出的电平,可以采用一个非常简单 有效的办法,就是在你想验证的端口位接一个发光二极管。当你输出高电平时,发光二级管 灭;输出低电平时,发光二极管亮。

在本任务中,使用P1 端口的第一脚(记为 P1_0)来控制发光二极管以 1HZ 的频率不 断闪烁。

LED电路元件

1. 红色发光二极管2个 2. 470Ω电阻2个

LED电路搭建

如果你已经学习过《基础机器人制作》这本教材,你肯定对图2-2 所示电路很熟悉。按 照图2-2 上边所示电路,在智能机器人教学板的面包板上搭建起实际电路。实际搭建好的电 路参考图2-2 下边的照片。实际搭建电路时注意:

z 确认发光二极管的短针脚(阴极)插入通过电阻与 P1_0 相连 z 确认发光二极管的长针脚(阳极)插入“VCC”插口

2-2 发光二极管与I/O脚P1_0的连接 例程:HighLowLed.c

z 接通板上的电源

z 输入、保存、下载并运行程序 HighLowLed.c(整个过程请参考第一章)

z 观察与 P1_0 连接的 LED 是否每隔一秒发光、关闭一次

#include<BoeBot.h>

#include<uart.h>

int main(void) {

uart_Init(); //初始化串口

(25)

while(1) {

P1_0=1; // P1_0 输出高电平 delay_nms(500); //延时 500ms P1_0=0; // P1_0 输出低电平

delay_nms(500); //延时 500ms }

}

HighLowLed.c是如何工作的?

与第一章程序相比,本例程多使用了一个头文件BoeBot.h,在该头文件中定义了两个延 时函数:void delay_nms(unsigned int i)与 void delay_nus(unsigned int i)。

无符号整型数据 unsigned int

与第一章讲到的整型数据int 相比,无符号整型数据 unsigned int 只有一个区别:数据的 取值范围从-32768~+32767 变为 0~65535,也就是说它只能取非负整数。

delay_nms( )是毫秒级的延时,而 delay_nus( )是微秒级的延时。如果你想延时 1 秒钟,

可以使用语句 delay_nms(1000); 1 毫秒的延时则用 delay_nus(1000)来完成。

注意:上述的延时函数是在外部晶振为12MHZ的情况下设计的,如果外部晶振频率不

12MHz,调用这两个函数所产生的真正延时就会发生变化。

晶振的作用

单片机要能工作,就必须有一个标准时钟信号,而晶振就是为单片机提供标准时钟信号。

uart_Init();串口初始化函数,在头文件 uart.h 中实现,具体内容将在后面章节讲解。

调用printf 是为了在程序执行前给调试终端发送一条提示信息,告诉你现在程序开始执 行了,并告诉你随后程序将开始干什么。这在你以后的编程开发过程中是一个良好的习惯,

将非常有助于你提高程序的调试效率。代码段:

while(1) {

P1_0=1; // P1_0 输出高电平 delay_nms(500); //延时 500ms P1_0=0; // P1_0 输出低电平 delay_nms(500); //延时 500ms }

是本例程的功能主体。首先看两个大括号中的代码:先给P1_0 脚输出高电平,由赋值 语句P1_0=1 完成,然后调用延时函数 delay_nms(500),让单片机微控制等待 500 毫秒,再 给P1_0 脚输出低电平,即 P1_0=0,然后再次调用延时函数 delay_nms(500)。这样就完成了 一次闪烁。在程序中,你没有看到P1_0 的定义,它已经在由 C 语言为 C51 开发的标准库中 定义好,由头文件uart.h 包括进来。后续章节中将要用到的其它引脚名称和定义都是如此。

注意:在所有计算机系统中,都用 1表示高电平,0表示低电平,所以,P1_0=1就表

示要向该端口输出高电平,而P1_0=0就表示给该端口输出低电平。

例程中两次调用延时函数,让单片机微控制器在给P1_0 引脚端口输出高电平和低电平 之间都延时500ms,即输出的高电平和低电平都保持 500ms。

微控制器的最大优点之一就是它们从来不会抱怨不停地重复做同样的事情。为了让单片 机不断闪烁,你需要把让LED 闪烁一次的几个语句放在 while(1){…}循环里。这里用到了 C 语言实现循环结构的一种形式:

(26)

while语句

while 语句的一般形式如下:

while(表达式) 循环体语句

当表达式为非0 值时,执行 while 语句中的内嵌语句,其特点是先判断表达式,后执行 语句。例程中直接用1 代替了表达式,因此总是非 0 值,所以循环永不结束,也就可以一直 让LED 灯闪烁。

注意:循环体语句如果包含一个以上的语句,就必须用花括号(“{ }”)括起来,以 复合语句的形式出现。如果不加花括号,则 while 语句的范围只到 while 后面的第一个分号 处。例如,本例中 while 语句中如果没有花括号,则 while 语句范只到“P1_0=1;”。

也可以不要循环体语句,如第一章例程中就直接用 while(1);程序将一直停在此处。

时序图简介

时序图反应的是高、低电压信号与时间的关系图。在图2-3 中,时间从左到右增长,高、

低电压信号随着时间在0V 或 5V 间变化。这个时序图显示的是刚才实验中的 1000ms 的高、

低电压信号片段。右边的省略号表示的是这些信号是重复出现的。

2-3 程序 HighLowLed.c 的时序图 该你了——让另一个 LED 闪烁

让另一个连接到P1_1 管脚的 LED 闪烁是一件很容易的事情,把 P1_0 改为 P1_1,重新 运行程序即可。

参考下面的代码段修改程序:

uart_Init();

printf("The LED connected to P1_1 is blinking!");

while(1) {

P1_1=1; // P1_1 输出高电平 delay_nms(500); //延时 500ms P1_1=0; // P1_1 输出低电平 delay_nms(500); //延时 500ms }

运行修改后的程序,确定能让LED 闪烁。

(27)

你也可以让两个LED 同时闪烁。参考下面代码段修改程序:

uart_Init();

printf("The LEDs connected to P1_0 and P1_1 are blinking!\n ");

while(1) {

P1_0=1; // P1_0 输出高电平 P1_1=1; // P1_1 输出高电平 delay_nms(500); //延时 500ms P1_0=0; // P1_0 输出低电平 P1_1=0; // P1_0 输出低电平 delay_nms(500); //延时 500ms }

运行修改后的程序,确定能让两个LED 几乎同时闪烁。

当然,你可以再次修改程序,让两个发光二极管交替亮或灭,你也可以通过改变延时函 数的参数n 的值,来改变 LED 的闪烁频率。

尝试一下!

任务二 机器人伺服电机控制信号

本书所用的机器人伺服电 机与《基础机器人制作》中的电 机相同。回想一下,控制伺服电 机 转 动 速 度 的 信 号 是 不 是 图 2-4、2-5和2-6所示的脉冲信号。

如果没有学习过《基础机器 人制作》,也没有关系。图2-4 所示是高电平持续 1.5ms 低电 平持续 20ms,然后不断重复的 控制脉冲序列。该脉冲序列发给 经过零点标定后的伺服电机,伺 服电机不会旋转。如果此时你的 电机旋转,表明电机需要标定。

此时,还是需要你参考《基础机 器人制作》教材中有关伺服电机 标定的部分。从图 2-4、2-5 和 2-6 可知,控制电机运动转速的 是高电平持续的时间,当高电平 持续时间为1.3ms 时,电机顺时 针全速旋转,当高电平持续时间 1.7ms 时,电机逆时针速旋转。

下面你将看到如何给单片机微 控制器编程使P1 端口的第一脚

(P1_0)来发出伺服电机的控制

2-4 电机转速为零的控制信号时序图

2-5 1.3 ms的控制脉冲序列使电机顺时针全速旋转

2-6 1.7ms的连续脉冲序列使电机逆时针全速旋转

(28)

在进行下面的实验之前,你必须首先确认一下机器人两个伺服电机的控制线是否已经正 确的连接到了C51 单片机教学板的两个专用电机控制接口上。照图 2-7 所示的电机连接原理 图和实际接线图进行检查。如果没有正确连接,也请参照该图重新连接。从图2-7 可知,P1_0 引脚的控制输出用来控制右的伺服电机,而 P1_1 则用来控制左边的伺服电机。

显然这里对微控制器编程发给伺服电机的高、低电平信号必须具备更精确的时间。因为 单片机只有整数,没有小数,所以要生成伺服电机的控制信号,要求具有比delay_nms()

函数的时间更精确的函数,这就需要用另一个延时函数delay_nus(unsigned int n)。前面已经 介绍过,这个函数可以实现更小的延时,它的延时单位是微秒,即千分之一毫秒,参数 n 为延时微秒数。

看看下面的代码片断 while(1)

{

P1_0=1; //P1_0 输出高电平 delay_nus(1500); //延时 1.5ms P1_0=0; //P1_0 输出低电平 delay_nus(20000); //延时 20ms }

如果用这个代码段代替例程 HighLowLed.c 中相应程序片断,它是不是就会输出图 2-4 所示的脉冲信号?肯定是!如果你手边有个示波器,可以用示波器观察P1_0 脚输出的波形 是不是如图2-4 所示。此时,连接到该脚的机器人轮子是不是静止不动。如果它在慢慢转动,

就说明你的机器人伺服电机可能没有经过调整。

同样,用下面的程序片断代替例程HighLowLed.c 中相应程序片断,编译、连接下载执 行代码,观察连接到P1_0 脚的机器人轮子是不是顺时针全速旋转?

while(1) {

P1_0=1; //P1_0 输出高电平 delay_nus(1300); //延时 1.3ms P1_0=0; //P1_0 输出低电平 delay_nus(20000); //延时 20ms }

用下面的程序片断代替例程HighLowLed.c 中相应程序片断,编译、连接下载执行代码,

观察连接到P1_0 脚的机器人轮子是不是逆时针全速旋转?

图2-7 伺服电机与教学底板的连线原理图(左)和实际接线示意图(右)

(29)

{

P1_0=1; //P1_0 输出高电平 delay_nus(1700); //延时 1.7ms P1_0=0; //P1_0 输出低电平 delay_nus(20000); //延时 20ms }

该你了――让机器人的两个轮子全速旋转

刚才是让连接到P1_0 脚的伺服电机轮子全速旋转,下面你自己可以修改程序让连接到 P1_1 机器人轮子全速旋转。

当然,最后你需要修改程序,让机器人的两个轮子都能够旋转。让机器人两个轮子都顺 时钟全速旋转的程序参考下面的程序。

例程:BothServoClockwise.c z 接通板上的电源

z 输入、保存、下载并运行程序 BothServoClockwsie.c(整个过程请参考第一章)

z 观察机器人的运动行为

#include<BoeBot.h>

#include<uart.h>

int main(void) {

uart_Init(); //初始化串口

printf("The LEDs connected to P1_0 and P1_1 are blinking!\n ");

while(1) {

P1_0=1; //P1_0 输出高电平 P1_1=1; //P1_1 输出高电平 delay_nus(1300); //延时 500ms P1_0=0; //P1_0 输出低电平 P1_1=0; //P1_1 输出低电平 delay_nms(20); //延时 20ms }

}

注意上述程序用到了两个不同的延时函数,效果与前面例子一样。运行上述程序时,你 是不是对机器人的运动行为感到惊讶!如果你已经学习过《基础机器人制作》,你就不会对 此感到惊讶;如果没有学过,后面的章节将会介绍为何会这样。

任务三 计数并控制循环次数

任务二中已经通过对 C51 编程实现对机器人伺服电机的控制,为了让微控制器不断发 出控制指令,你用到了以while(1)开头的死循环(即永不结束的循环)。不过在实际的机器 人控制过程中,你会经常要求机器人运动一段给定的距离或者一段固定的时间。这时就需要 你能控制代码执行的次数。

for语句

(30)

for(表达式1;表达式2;表达式3) 语句 它的执行过程如下:

1) 先求解表达式 1

2) 求解表达式 2,若其值为真(非 0),则执行 for 语句中指定的内嵌语句,然后执 行下面第3)步;若其值为假(0),则结束循环,转到第 5)步

3) 求解表达式 3

4) 转回上面第 2)步继续执行

5) 循环结束,执行 for 语句下面的一个语句

for 语句最简单的应用形式也就是最易理解的形式如下:

for(循环变量赋初值;循环条件;循环变量增/减值) 语句

例如,下面是一个用整型变量myCounter 来计数的 for 循环程序片断。每执行一次循环,

它会显示myCounter 的值。

for(myCounter=1; myCounter<=10; myCounter++) {

printf(“%d”,myCounter);

delay_nms(500);

}

在这里,向你介绍新的算术运算符。

自增和自减

C 语言有两个很有用的运算符——自增和自减,“++”和“--”。

运算符“++”是操作数加 1,而“--”是操作数减 1,换句话说:“x=x+1;”同“x++”;

“x=x-1;”同“x--;”。

myCounter ++的作用就相当于 myCounter = myCounter +1,只不过这样用起来更 简洁。这也是C 语言的特点,灵活简洁。

该你了――不同的初始值和终值以及计数步长

你可以修改表达式3 来使 myCounter 以不同步长计数,而不是按 9, 10, 11…来计,你可 以让它每次增加2(9, 11,13…)或增加 5 (10, 15, 20…)或任何你想要的步进,递增或递减都可 以。下面的例子是每次减3。

for(myCounter=21; myCounter>=9; myCounter=myCounter-3) {

printf(“%d\n”,myCounter);

delay_nms(500);

}

for循环控制电机的运行时间

到目前为止,你已经理解了脉冲宽度控制连续旋转电机速度和方向的原理。控制电机速 度和方向的方法是非常简单的。控制电机运行的时间也非常简单,那就是用for 循环。

下面是for 循环的例子,它会使电机运行几秒钟。

for(Counter=1;Counter<=100;i++) {

P1_1=1;

delay_nus(1700);

P1_1=0;

delay_nms(20);

}

(31)

delay_nus(1700)持续 1.7 ms,delay_nms(20)持续 20ms,其他语句的执行时间很少,可忽略。

那么for 循环整体执行一次的时间是:1.7 ms + 20 ms = 21.7ms,本循环执行 100 次,即就是 21.7ms 乘以 100,时间=100*21.7ms =100*0.0217 秒=2.17 秒。

假如你要让电机运行4.34 秒,for 循环必须执行上面两倍的时间。

for(Counter=1;Counter<=200;i++) {

P1_1=1;

delay_nus(1700);

P1_1=0;

delay_nms(20);

}

例程:ControlServoRunTimes.c

z 输入、保存并运行程序 ControlServoRunTimes.c

z 验证是否与 P1_1 连接的电机逆时针旋转 2.17 秒,然后与 P1_0 连接的电机旋转 4.34 秒

#include<BoeBot.h>

#include<uart.h>

int main(void) {

int Counter;

uart_Init();

printf("Program Running!\n");

for(Counter=1;Counter<=100;Counter++) {

P1_1=1;

delay_nus(1700);

P1_1=0;

delay_nms(20);

}

for(Counter=1;Counter<=200;Counter++) {

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

}

while(1);

}

假如你想让两个电机同时运行,给与 P1_1 连接的电机发出 1.7ms 的脉宽,给与 P1_0 连接的电机发出1.3ms 的脉宽,现在每通过循环一次要用的时间是:

1.7ms – 与 P1_1 连接的电机

(32)

20 ms – 中断持续时间

--- --- 一共是23 ms

如果你想使机器人运行一段确定的时间,可以计算如下:

脉冲数量=时间/0.023 秒=时间/0.023 假如你想让电机运行3 秒,计算如下:

脉冲数量=3 / 0.023= 130

现在,你可以将for 循环中作如下修改,程序如下:

for(counter=1;counter<=130;i++) {

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

}

例程:BothServosThreeSeconds.c

下面是一个使电机向一个方向旋转3 秒,然后反向旋转的例子。

z 输入、保存并运行程序 BothServosThreeSeconds.c

#include<BoeBot.h>

#include<uart.h>

int main(void) {

int counter;

uart_Init();

printf("Program Running!\n");

for(counter=1;counter<=130;counter++) {

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

}

for(counter=1;counter<=130;counter++) {

P1_1=1;

(33)

P1_1=0;

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

}

while(1);

}

验证每个机器人是否沿一个方向运行3 秒然后反方向运行 3 秒。你是否注意到当电机同 时反向的时候,它们总是保持同步运行?这将有什么作用呢?

该你了——用 LED 指示电机运动状态

在实际应用中,LED 往往起到状态提示作用,交通红绿灯就是一个典型的应用。你可 以修改程序来模拟交通灯过程:假设 A 灯为交通灯,B 灯为电机运行状态指示灯。模拟过 程如下:

A 灯闪烁,B 灯灭,电机停止运行;

A 灯灭,B 灯亮,电机开始运行;

B 灯闪烁,电机减速运行;

B 灯灭,A 灯闪烁,电机停止运行;如此往复。

你还可以添加更多的LED 来反映电机其他信息,如左右拐弯等。动手实验一下!

任务四 用你的计算机来控制机器人的运动

在工业自动化中,经常需要你的单片机与计算机通讯。一方面,单片机需要读取周边传 感器的信息,并把数据传给计算机;另一方面,计算机需要解释和分析传感器数据,然后把 分析结果或者决策发给单片机以执行某种操作。

在第一章中你已经知道 C51 单片机可以通过串口向计算机发送信息,本章将使用串口 和串口调试终端软件,由你从计算机向单片机发送数据来控制机器人的运动。

在本任务中,你需要编程让C51 单片机从调试窗口接收两个数据:

1. 由单片机发给伺服电机的脉冲个数 2. 脉冲宽度(以微秒为单位)

例程:ControlServoWithComputer.c

z 输入、保存、下载并运行程序 ControlServoWithComputer.c z 验证是否机器人各个轮子的转动是不是同你期望的运动一样

#include<BoeBot.h>

#include<uart.h>

int main(void) {

int Counter;

int PulseNumber,PulseDuration;

uart_Init();

printf("Program Running!\n");

printf(“Please input pulse number:\n”);

(34)

printf(“Please input pulse duration:\n”);

scanf(“%d”,&PulseDuration);

for(Counter=1;Counter<=PulseNumber;Counter++) {

P1_1=1;

delay_nus(PulseDuration);

P1_1=0;

delay_nms(20);

}

for(Counter=1;Counter<=PulseNumber;Counter++) {

P1_0=1;

delay_nus(PulseDuration);

P1_0=0;

delay_nms(20);

}

while(1);

}

ControlServoWithComputer.c 是如何工作的?

单片机通过串口从计算机读取输入的数据,需要用到格式输入函数:

scanf函数

scanf 函数与 printf 函数对应,在 C51 库的 stdio.h 中定义。下面是它的一般形式:

scanf(“格式控制字符串”,地址表列);

“格式控制字符串”的作用与 printf 函数相同,但不能显示非格式字符串,也就是不 能显示提示字符串。

地址表列中给出各变量的地址。地址是由地址运算符“&”后跟变量名组成的。如“&a”

表示变量 a 的地址。这个地址是编译系统在存储器中给变量 a 分配的地址,你不必关心具体 的地址是多少。

变量的值和变量的地址 这是两个不同的概念,例如:

a=123;

那么:a 为变量名,123 是变量的值,&a 则是变量 a 的地址。

scanf(“%d”,&PulseNumber);将会把你输入的十进制整数赋给变量PulseNumber。 程序运行过程(见图 2-8)如下:

(35)

图 2-8 例程运行过程

1. 首先输出“Program Running!”和“Please input pulse number:” 2. 程序处于等待状态,等待你输入数据

3. 将你输入数据给变量PulseNumber;

4. 输出“Please input pulse duration:” 5. 又处于等待状态

6. 将输入数据给变量PulseDuration 7. 电机运转

一次输入多个数据

当要求输入数据比较多时,上述方法是不是很麻烦?下面的代码可以让你一次输入两个 数据,两个数据之间用空格隔开:

printf(“Please input pulse number and pulse duration:\n”);

scanf(“%d %d”,&PulseNumber,&PulseDuration);

想一想,如果要输入三及以上数据,程序代码段该怎样写呢?

工程素质和技能归纳

1. C51 系列单片机的引脚定义和分布

2. 用 C51 单片机的 P1 端口的位输出控制单灯和双灯闪烁,时序图的概念,while 循 环的引入和延时函数的使用

3. 机器人伺服电机的控制脉冲序列,通过给 C51 编程让其输出这些控制脉冲序列 4. 自增运算符的使用

5. for 循环的使用以控制机器人的运动 6. 如何通过串口输入数据控制机器人的运动

科学精神的培养

(36)

2. 比较 C 语言程序与 BASIC 程序的异同,找出它们的共同点 3. 比较 C 语言的 for 循环和 PBASIC 的 for 循环

4. 查找 C 语言的标准输入输出库函数,了解 scanf 的总体功能

(37)

第三章 C语言函数与机器人巡航控制

通过对单片机编程可以使机器人完成各种巡航动作。本章所要介绍的这些巡航动作和编 程技术在后面的章节都会用到。与后面章节唯一不同的是:本章机器人在无感觉的情况下巡 航,而在后面的章节中,机器人将根据传感器检测到的信息进行智能巡航。

本章所要完成的主要任务如下:

1.对单片机编程使机器人做一些基本巡航动作:向前,向后,左转,右转和原地旋转 2.编写程序使机器人由突然启动或停止变为逐步加速或减速运动

3.写一些执行基本巡航动作的函数,每一个函数都能够被多次调用 4.将复杂巡航运动记录在数组中,编写程序执行这些巡航运动

任务一 基本巡航动作

图3-1 定义了机器人的前后左右四个方向:当机器人向前走时,它将走向本页纸的右边;

当向后走时,会走向纸的左边;向左转会使其向纸的顶端移动;向右转它会朝着本页纸的底 端移动。

向前巡航

在前一章实际上你应该已经发现,如果按照图3-1 前进方向的定义,机器人向前走时,

从机器人的左边看,它向前走时轮子是逆时针旋转的;从右边看另一个轮子则是顺时针旋转 的。

回忆一下第二章的内容,发给单片机控制引脚的高电平持续时间决定了伺服电机旋转的 速度和方向。for 循环的参数控制了发送给电机的脉冲数量。由于每个脉冲的时间是相同的,

因而for 循环的参数也控制了伺服电机运行的时间。下面是使机器人向前走三秒钟的程序实 例。

图 3-1 机器人及其前进方向的定义 例程:RobotForwardThreeSeconds.c

z 确保控制器和伺服电机都已接通电源

z 输入、保存、编译、下载并运行程序 RobotForwardThreeSeconds.c

(38)

#include<BoeBot.h>

#include<uart.h>

int main(void) {

int counter;

uart_Init();

printf("Program Running!\n");

for(counter=0;counter<130;counter++)//运行 3 秒 {

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

}

while(1);

}

RobotForwardThreeSeconds.c 是如何工作的?

理解该例程的运行你应该没啥问题:for 循环体中前三行语句使左侧电机逆时针旋转,

接着的三行语句使右侧电机顺时针旋转。因此两个轮子转向机器人的前端,使机器人向前运 动。整个for 循环执行 130 次大约需要 3 秒钟,从而机器人也向前运动 3 秒钟。

关于例程的一点说明

例程中使用printf函数是为了起提示作用。若你觉得串口线影响了机器人的运动,可以

不用此函数;还有一个变向决定方法:让机器人的前端悬空,让伺服电机空转。后续例程也 是同样道理。

该你了――调节距离和速度

z 将 for 循环的循环次数调到 65,你可以使机器人运行时间减少到刚才的一半,运行距 离也是一半

z 以新的文件名存储程序 RobotForwardThreeSeconds.c z 运行程序来验证运行的时间和距离是否是刚才的一半 z 将 for 循环的循环次数调到 260,重复这些步骤

delay_nus 函数的参数 n 为 1700 和 1300 都使电机接近它们的最大速度旋转。把每个 delay_nus 函数的参数 n 设定得更接近让电机保持停止的值――1500,可以使机器人减速。

更改程序中相应的代码片段如下:

P1_1=1;

delay_nus(1560);

P1_1=0;

P1_0=1;

(39)

delay_nus(1440);

P1_0=0;

delay_nms(20);

运行程序,验证一下机器人运行速度是否减慢。

向后走,原地转弯和绕轴旋转

将delay_nus 函数的参数 n 以不同的值组合就可以使机器人以其它的方式运行。例如,

下面的程序片段可以使其向后走:

P1_1=1;

delay_nus(1300);

P1_1=0;

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

下面的程序片段可以使你的机器人原地左转:

P1_1=1;

delay_nus(1300);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

下面的程序可以使你的机器人原地右转:

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

你可以把上述命令组合到一个程序中让机器人向前走、左转、右转以及向后走。

例程:ForwardLeftRightBackward.c

z 输入、保存并运行程序 ForwardLeftRightBackward.c

#include<BoeBot.h>

#include<uart.h>

int main(void) {

int counter;

uart_Init();

printf("Program Running!\n");

(40)

for(counter=1;counter<=65;counter++)//向前 {

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

}

for(counter=1;counter<=26;counter++)//向左转 {

P1_1=1;

delay_nus(1300);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

}

for(counter=1;counter<=26;counter++)//向右转 {

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

}

for(counter=1;counter<=65;counter++)//向后 {

P1_1=1;

delay_nus(1300);

P1_1=0;

(41)

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

}

while(1);

}

该你了――以一个轮子为支点旋转

你可以使机器人绕一个轮子旋转。诀窍是使一个轮子不动而另一个旋转。例如,保持左 轮不动而右轮从前面顺时针旋转,机器人将以左轮为轴旋转。

P1_1=1;

delay_nus(1500);

P1_1=0;

P1_0=1;

delay_nus(1300);

P1_0=0;

delay_nms(20);

如果你想使它从前面向右旋转,很简单,停止右轮,左轮从前面逆时针旋转。

P1_1=1;

delay_nus(1700);

P1_1=0;

P1_0=1;

delay_nus(1500);

P1_0=0;

delay_nms(20);

这些命令使机器人从后面向右旋转 P1_1=1;

delay_nus(1300);

P1_1=0;

P1_0=1;

delay_nus(1500);

P1_0=0;

delay_nms(20);

最后这些命令使机器人从后面向左旋转 P1_1=1;

delay_nus(1500);

P1_1=0;

P1_0=1;

delay_nus(1700);

P1_0=0;

delay_nms(20);

把ForwardLeftRightBackward.c 另存为 PivotTests.c。

參考文獻

相關文件

通过 HSK(二级)的考生可以用汉语就熟悉的日常话题进行简单而直接的 交流,达到初级汉语优等水平。..

通过 HSK(二级)的考生可以用汉语就熟悉的日常话题进行简单而直接的 交流,达到初级汉语优等水平。..

[r]

,在需求分析过程中应该建立起软件系统的 行为模型。状态转换图 ( 简称为状态图 ) 通

笛卡儿企图通过坐标系给几何引进新方 法, 他的成就远远超出他的期望. 坐标系是数 学中的双刃剑, 使得几何的目的可以通过代 数达到, 反过来,

下图是单脚支撑形式的示意图,支撑脚和地面的接触点 A 与前、后轮和地面之间 的接触点 B 、 C 共同构成三点支撑,在地面形 成△

欣赏有关体育运动 的图片,从艺术的角度 与同学交流自己对这些 运动和画面的感受与理 解,并为这些图片设计

穿插课文之中、形 式多种多样的活动使 我们所学的知识与技 能得到及时的巩固、应 用和内化,它是我们主 动建构知识、拓展能