希望帮我画出这个单片机程序的程序如何用word制作流程图图,和给程序语句注释和说明(希望详细些)。,程序在详情里

基于51单片机制作的红外计数器全套资料分享给大家 包含电路原理图+PCB  源程序等资料

E18-D80NK-N是E18-D80NK的升级版改动部分主要是内部电路板和外部连线。传感器外部接线在末端增加了杜邦头,方便用户使用 E18-D80NK-N这是一种集发射与接收于一体的光电传感器,发射光经过调制后发出接收头对反射光进行解调输出。有效的避免叻可见光的干扰透镜的使用,也使得这款传感器最远可以检测80厘米距离的问题(由于红外光的特性不同颜色的物体,能探测的最大距離也有不同;白色物体最远黑色物体最近) 。


检测障碍物的距离可以根据要求通过尾部的电位器旋钮进行调节 该传感器具有探测距离遠、受可见光干扰小、价格便宜、易于装配、使用方便等特点,可以广泛应用于机器人避障、流水线计件等众多场合

红外计数器电路原悝图如下:

  请注意,原理图上有些接线的地方是用网络标号连接的意思就是名字相同的两个点就是连接在一起的相当导线的作用,图上所有的VCC要接在一起所有的GND要接在一起。

请看原理图焊接不要看仿真图焊接。

wrod格式里面的原理图是复制出来的有一点点变行变形,麻煩大家注意一下尽量看其他三种格式的图焊接,

如果论文里面的原理图和原理图文件夹内的图不一样的话请大家以原理图文件夹内的為准,原理图文件夹的图是和实物配套的可以自己截图或复制,然后粘贴到论文里面去

51单片机红外计数器元件清单:


1.在电子技术飞速發展的今天,电子产品的人性化、智能化和自动化的发展已经越来越成熟了其发展的前景不可估量。工业生产中常常需要自动统计产品嘚数量计数器在这里发挥了的他的重要作用。基于单片机的光电计数器采用由单片机控制及光电传感器原理实现对物件的数目统计光電式传感器是将光电信号转化为电信号的一种传感器,它的理论基础是光电效应
2.通过对光电计数器实物的设计和制作,全面提高自身的素质在此设计过程中还要充分发挥自己的逻辑思维能力,自己的动手能力和遇到问题的解决能力在设计过程中将会用到多门学科的理論知识,涉及到模拟电子技术知识、数字电子技术知识、广电传感技术知识、单片机技术及C语言编程知识和Protel绘图知识等是对我们以前所學知识的一个全面的复习和巩固,更重要的是培养了自我分析问题和处理问题的能力计数器在人们日常生活中也是非常普遍应用的,随著智能化、自动化的不断普及人们急需一种自动计数的装置,因此研究这一课题还是有着非常现实的意义

  随着科学技术的发展,如今嘚产品自动计数器很多都是采用非接触式的计数触发方式早已开发出了多种型号的专用检测芯片,而利用51系列单片机为控制单元、辅以哆种外围硬件搭配而成的计数装置已成为现在自动计数器应用领域的潮流而如何提高的计数器的实时性、抗干扰性、稳定性是现在国内外计数器生产厂家研究的主要课题。

如果没有装KEIL软件

或者到开发资料里安装KEIL软件











  1. * 功能 : 小延时

  2. * 输入 : 输入的命令值









  3.  //倒计时器按键操作开始 暂停





  4. …………限于本文篇幅 余下代码请从51黑下载附件…………

第一章 MPC100B-I型单片机实验箱简介

1.2.1单片機最小系统

第二章 集成开发环境介绍

2.1.2快速入门适用范围

2.4.1 不使用MedWin集成开发环境项目管理方式

2.4.2 使用MedWin集成开发环境项目管理方式

第三章 单片机最尛系统

实验一、单片机最小系统的熟悉

实验一 多字节、多进制加减运算实验

实验二、中断实验――中断优先级控制及中断保护

实验三、定時器实验――低频脉冲计数器

实验四、双机串行通讯实验

实验五、SRAM外部数据存储器扩展实验

实验六、矩阵式键盘输入实验

实验七、8255可编程並行I/O扩展接口实验

单片机原理及应用实验指导书

第一章 MPC100B-I型单片机实验箱简介1.1系统组成

整个单片机实验系统由实验箱、功能扩展模块、单片機专用开发工具、实验指导书以及配套软件光盘等部分组成

实验系统的基础平台采用箱式结构,外形美观大方、便于携带、利于教学操作方便。

1.2.1单片机最小系统

最小系统板是本实验装置的核心模块它提供了51单片机的一个最小工作系统,并充分地扩展了单片机总线和I/O口鉯便于与其他模块的电路连接最小系统板包括的外围电路有:时钟电路、复位电路、P0口锁存电路、拨码开关电路。最小系统板上扩展的I/Oロ包括P0、P1、P2、P3口的每个管脚独立都引出,作为数据总线(P0口)地址总线低8位(P0口经锁存后的输出),地址总线高8位用8针的双排插针座引出;控制总线(WR,

由于最小系统板的独立式可更换设计使得本实验装置可通过改变这个最小系统板来进行不同家族单片机的实验教学,洳AVRPIC,96家族单片机等非常灵活和方便。

基础板是本实验装置的实验基础其内容围绕单片机教学大纲。

它由以下21个模块组成:

(1)发光②极管阵列模块;

(2)键盘模块(独立式与矩阵式两用键盘);

(3)动态数码管扫描显示模块;

(4)静态串行显示模块;

(5)8155扩展实验模塊;

(6)8255扩展实验模块;

(7)RS232接口实验模块;

(8)波形信号发生器模块;

(9)外部SRAM实验模块(62256);

(13)V/F转换实验模块;

(15)F/V转换实验模块;

(18)微处理器监控模块;

(19)电源模块(±12V/±5V);

(20)芯片自由扩展实验模块(2组);

(21)电位器调节模块(2组);

(22)接口转换模块


第二章 集成开发环境介绍
2.1 MedWin概述

MedWin是万利电子有限公司Insight~系列仿真开发系统的高性能集成开发环境。集编辑、编译/汇编、在线及模拟调试为┅体VC风格的用户界面,内嵌自主版权的宏汇编器和连接器并完全支持Franklin/KeilC扩展OMF格式文件,支持所有变量类型及表达式配合Insight系列仿真器,是您开发80C51系列单片机的理想开发工具

2.内嵌自主版权的宏汇编器A51和连接器L51,并支持Keil/Franklin编译、连接工具

3.具有分别独立控制项目文件的笁程项目管理器

4.在工程项目管理下实现多模块和混合语言编程调试

5.VC风格的窗口停驻、窗口切分和工作簿模式界面

6.在线编辑、编译/汇编、连接下载运行和错误关联定位

7.符合编程语言语法的彩色文本显示

8.完全的表达式分析,支持所有数据类型变量的观察

9.无须点擊的感应式鼠标提示功能

10.外部功能部件编程向导

11.不限制打开数据区观察窗口的数目

12.调试状态下用户程序自动重装功能

13.提供真实的軟件模拟仿真开发环境

2.1.2快速入门适用范围

为使MedWin集成开发环境能够正常地运行您必须提供以下软硬件环境:

1.586以上的PC及兼容机

2.100M以上的剩餘硬盘空间

3.分辨率为800x600以上的显示器

4.具有SPP功能的打印机接口

6.尽量减少DOS自动批处理中关于系统路径及环境变量的设置,或删除DOS下的批处悝文件Autoexec.bat文件

以下分别是安装MedWin集成开发环境的方法:

第一步:从光盘上安装或从因特网下载安装

3.在文件夹内点击setup。

3.将文件释放到C:\Windows\Temp或其它文件夹

5.在文件夹内点击setup安装MedWin集成开发环境。

第二步:安装文件路径设置

为了便于管理和服务建议安装MedWin集成开发环境时,使鼡以下缺省路径安装:

1.中文版缺省安装目标路径:C:\Manley\PMedWin

2.英文版缺省安装目标路径:C:\Manley\MedWin

注意安装MedWin集成开发环境路径选择

MedWin集成开發环境完成安装后,如果是第一次安装请您务必注意:必须根据系统提示,设置工作目录、编译/汇编器、连接器的路径、环境以及注冊仿真器如果是升级安装,则MedWin集成开发环境启动后出现启动向导菜单或直接打开项目文件

1由安装MedWin集成开发环境,直接启动MedWin

4.如果已经连接仿真器屏幕上出现MedWin的初始画面,进入启动MedWin的第三步

5.如果没有连接仿真器屏幕上出现端口选择画面,进入启动MedWin的第二步

第二步:选择進入在线仿真或模拟仿真

1.由于仿真器供电电源通讯电缆连接问题,请检查并使仿真器工作正常后点击对话框中[仿真器]按钮,进入在线汸真状态

2.如果确认需要进行模拟仿真,点击[模拟仿真]按钮进入模拟仿真状态。

经过以上步骤屏幕上出现MedWin集成开发环境的初始画面,並渐渐消失出现MedWin集成开发环境的系统画面。

进入MedWin集成开发环境后系统首先提示默认的工作目录为C:\MedWin。我们建议用户根据需要选择合適的工作目录例如将工作目录改为D:\WorkDir(用户可自行定义目录名称)。注意:不得使用长文件名作为工作目录!

在MedWin集成开发环境工作目录提示對话框中更改MedWin集成开发环境工作目录的步骤,必须在Windows环境下进行:

1.打开Windows的资源管理器

2.选择本地磁盘(D:)

3.单击鼠标右键选择新建文件夾,命名为WorkDir

当建立WorkDir文件夹后在MedWin集成开发环境点中,点击当前工作目录左侧按钮在开发环境工作目录对话框中选择驱动器D盘后,打开文件夹(目录)WorkDir确认后,工作目录设置即告完成系统将提示进行下一步设置:编译/汇编/连接器的配置。

第四步:配置编译/汇编/连接器及環境

MedWin集成开发环境初始默认编译/汇编/连接环境为使用万利电子自主版权的宏汇编和连接器使用默认设置,汇编连接信息和错误关联均为中文提示

当选择用户设置编译/汇编/连接环境时,必须注意选择合适的路径和程序如

系统头文件、库文件的环境为:

1.编译器系统头文件路径为C:\C51\INC

2.编译器系统库文件路径为C:\C51\LIB

MedWin集成开发环境默认源程序的扩展名为:

1.C源程序文件的扩展名为.C

2.汇编源程序文件的扩展名為.ASM

当您已经存在编译器/汇编器/连接器时,建议按照下列目录存放:

用户可按照上面提示的路径设置编译器/汇编器/连接器及环境洳编译器/汇编器/连接器存放在其它目录,则必须在输入框中设置合适的路径或点击按钮选择相应的文件及路径

当您使用Keil编译器时,其连接器应设为BL51.exe当您使用Franklinl编译器时,其连接器应设为L51.exe

第五步:设置文本编辑器

设置文本编辑器可以用于设置文本窗口的前景字符的颜銫、背景颜色、字体和编辑文件类型。

MedWin集成开发环境提供了以下两种方式开发用户应用程序:

1.不使用MedWin集成开发环境项目管理方式——對源程序文件直接进行汇编/连接,兼容传统开发习惯

2.使用MedWin集成开发环境项目管理方式——可进行多模块、混合语言编程的,同样也適合单模块程序的开发

用户无论是单模块或多模块的程序开发,我们都建议使用项目管理方式管理应用程序

2.4.1 不使用MedWin集成开发环境项目管理方式

不使用MedWin集成开发环境项目管理方式,只能对单模块方式下的应用程序开发具有很大的局限性。以下是不使用MedWin集成开发环境项目管理方式开发应用程序的步骤:

第一步:关闭当前项目文件

命令[项目管理/关闭当前项]

不使用MedWin集成开发环境项目管理方式开发应用程序用戶必须关闭已经打开的项目,此时MedWin集成开发环境关闭界面上所有的窗口因为当打开项目文件后,MedWin集成开发环境默认所有编译/汇编、产苼代码的过程都是对项目或项目所包含的文件进行的

第二步:在文件莱单下打开应用程序

命令[文件/新建][文件/打开]

单模块方式下的文件調试可以按照以下方法新建或打开文件:

1.点击[文件/新建],输入文件名和扩展名新建文件

2.点击[文件/打开],选择文件捡取框中的文件将其打开

命令[项目管理/编译/汇编]

MedWin集成开发环境根据文件的扩展名自动对当前激活的文件选择调用外部编译器或汇编器:

1.如果当前文件嘚扩展名为ASM或系统定义的扩展名,编译/汇编命令调用外部汇编命令对当前文件汇编

2.如果当前文件的扩展名为C或系统定义的扩展名编譯/汇编命令调用外部C编译命令对当前文件编译执行[项目管理|编译/汇编]命令后产生的结果显示在消息框中。

如果需要设置文件编译/汇編的命令行参数可以选择[项目管理1文件属性]命令设置。

MedWin集成开发环境调用外部命令编译/汇编后产生的结果显示在消息窗口中,消息窗口可由热键Ctrl+9激活当编译/汇编发生错误时,消息窗口中的错误信息自动与源文件关联提示出错的位置。在消息窗口中错误提示处双擊鼠标左键或键入回车可将错误信息与源文件的错误位置关联:

1.如果编译/汇编没有错误,可进入第五步操作

2.如果编译/汇编出现錯误在修改源文件后重复进行第三步操作

第五步:产生代码并装入仿真器调试

命令[项目管理/产生代码][项目管理/产生代码并装入]

产生代碼或产生代码并装入命令对经过编译/汇编无误后产生的OBJ文件进行连接产生用于下载的代码。此命令自动地对修改过的源程序进行编译或彙编对没有修改过的程序将越过编译或汇编过程,然后连接所有的OBJLIB文件,再装载代码到仿真器完成调试程序所需的准备工作。装载唍成后出现“Loadingprogram“(项目名)”…Completed'’的字样。

2.4.2 使用MedWin集成开发环境项目管理方式

使用MedWin集成开发环境项目管理方式开发程序适用于各种方式下的單模块和多模块方式应用程序。以下是使用MedWin集成开发环境项目管理方式开发应用程序的步骤:

1.建立一个新的项目进入第一步——新建项目文件

2.打开已经存在的项目文件进入第三步——打开项目文件

命令[项目管理/新建项目文件]

MedWin集成开发环境的项目器是按项目名称管理的項目管理器内的项目名称不可以相同。在项目名称输入栏内用户必须输入项目名称,并且项目名不得超过8个字符不可以使用汉字以及“-,?*,/”等DOS文件名所不可以使用的字符也不可以输入盘符和路径!

高级设置是用户程序所处路径与当前工作目录不同时,选择存放项目攵件路径的过程通常,当用户源程序文件存放在当前工作目录时不需要进行高级设置。如果源程序文件不在当前工作目录时用户必須选择高级设置,将项目文件存放到用户源程序所处的目录

如果源文件与项目文件不在同一目录时,将会出现不能进行源文件调试的情況发生

选择添加文件选项,表示在项目建立后会自动打开文件捡取框供用户选择文件添加到项目管理器中。

存储器属性Small/Compact/Large此属性為C编译器所需,如果项目文件中包含C程序用户必须设置存储器属性。

RAM尺寸:128/256此属性为连接器所需的控制项。系统默认128字节

系统默認标准80C51汇编选项,此选项为汇编器所需的控制项选中时,汇编器默认SFR为80C51

如果项目所使用的不足标准的80C51,用户必须定义其使用的SFR例:

項目头文件路径为该项目所定义的头文件所处的路径。该路径与系统定义的头文件路径在DOS环境设置的关系是:

set c51inc=(系统定义的头文件路径)(项目萣义的头文件路径)

项目库文件路径为该项目所定义的库文件所处的路径该路径与系统定义的库文件路径在DOS环境设置的关系是:

setc51lib=(系统定义嘚库文件路径)(项目定义的库文件路径)

在项目的编译和连接过程中,MedWin自动加入以上环境变量的设置通常用户不需要设置项目定义的头文件囷库文件路径,只有在高级应用时才需设置

建立新的项目后,系统默认为产生调试信息能够进行源文件调试。

关于编译/汇编程序的哽多的文件属性可在[项目管理I文件属性]中设置。如果在多模块调试中需要关闭一个或多个文件的源文件调试可在文件属性对话框中,汾别关闭产生调试信息选项

命令[项目管理/添加文件项]

添加文件命令用于将各类文件添加至项目管理器,新建项目文件后集成环境会自动咑开添加文件对话框提供选择。请根据:

1.如果文件存在在文件捡取框中选择并打开文件添加

2.如果文件不存在,必须输入文件名(包含扩展名)建立新文件

新建文件后,键入命令[项目管理|添加文件项]将新建文件添加到项目中。

命令:[项目管理/打开项目文件]

对于已经存茬的项目文件可以直接打开项目文件进入第四步操作。

第四步:编辑编译/汇编/连接

命令:[项目管理/编译/汇编]

MedWin集成开发环境根据攵件的扩展名,自动对激活的文件选择调用外部编译器或汇编器:

1.如果当前文件的扩展名为ASM或系统定义的扩展名编译/汇编命令调用外部汇编命令对当前文件汇编

2.如果当前文件的扩展名为C或系统定义的扩展名,编译/汇编命令调用外部C编译命令对当前文件编译文件编譯/汇编的命令行参数由[项目管理1文件属性]确定

文件经过编译/汇编后的结果显示于消息窗口,出现错误后错误信息与文件关联在消息窗口中错误之处双击鼠标左键或键入回车,即可将错误与文件关联:

1.如果没有错误进入第六步操作

2.如果出现错误,修改文件后重複进行第四步操作

第六步产生代码并装人仿真器

命令[项目管理/产生代码]

命令[项目管理/产生代码并装入]

命令[项目管理/重新产生全部代码]

产生玳码或产生代码并装入命令对经过编译/汇编无误后产生的OBJ文件进行连接,产生用于下载的代码此命令对修改过的源程序自动进行编譯或汇编,否则将越过编译或汇编过程进行连接并装载代码到仿真器,完成调试文件所需的准备工作

1.使用产生代码命令,项目管理器会自动判别文件是否需要重新编译/汇编提高调试效率

2.使用产生代码并装入命令,项目管理器会自动判别文件是否需要重新编译/彙编并将连接产生的代码下载到仿真器,提供调试运行

使用重新产生全部代码命令项目管理器会对所有文件重新编译/汇编,并将连接产生的代码下载到仿真器提供调试运行

MedWin集成开发环境配合|nsight系列仿真器,具有强大的断点功能:可以在编辑和调试状态下设置或清除断點并且在退出集成开发环境时自动保存断点信息。

MedWin集成开发环境的断点标记根据系统状态和所处窗口类型,标记如下:

1.在编辑状态丅源程序文本窗口内的白色箭头——预设断点,退出时保存

2.在调试状态下,源程序文本窗口内的白色箭头——无效断点退出时保存

3.在调试状态下,源程序文本窗口内的黄色箭头——有效地址断点退出时保存

4.在调试状态下,源程序文本窗口内的蓝绿色箭头——臨时断点退出时不保存

5.在反汇编本窗口内设置的断点,标记为红色光带——地址断点退出时不保存

根据系统状态和所处窗口类型,MedWin集成开发环境的断点颜色变化规律如下:

编辑状态下在源程序窗口内设置断点,标记为白色再次设置断点,白色断点标记被清除

调試状态下,在源程序窗口内设置断点标记为黄色,再次设置断点黄色断点标记被清除。

调试状态下在源程序窗口内蓝绿色断点处设置断点,标记为黄色再次设置断点,黄色断点标记被清除

调试状态下,在与源程序断点关联的反汇编窗口红色光带处设置断点在反彙编窗口内清除断点,同时将源程序断点标记为白色再次设置断点,反汇编窗口标记为红色光带源程序的白色断点再次标记为黄色。

3.源程序文本上设置断点

(1)编辑状态设置断点

当用户在编写源程序的过程中根据调试需求,可以直接在源程序文本的任何地方设置或清除断点如果设置了断点,将以白色箭头标记在源程序文本左侧的灰色状态栏内断点属性为预设断点。对于在源程序文本上设置的断點系统退出时全部自动保存。

(2)调试状态设置断点

在调试状态下可以直接在源程序文本的有效行上设置断点(当文本左侧的灰色状态欄内出现小圆点时,表示当前程序为有效行)设置后,断点以黄色箭头标记在文本左侧的灰色状态栏内如果没有出现小圆点,则不可以設置断点在编辑状态下设置的断点,经过编译/汇编如果预设的断点编译后是有效行,则断点标记为黄色如果不是有效行,断点标記为白色对于在源程序文本上设置的断点,系统退出时全部自动保存

4.反汇编窗口内设置断点

断点在反汇编窗口内以红色光带表示,鈳以任意设置地址断点如果设置的断点与源程序有效行地址关联,则与源程序相关联的地址处设置断点在源程序窗口内出现蓝绿色箭頭。

如果在源程序断点相关联的反汇编窗口红色光带处设置断点将清除窗口内的断点光带,同时将源程序断点标记为白色再次设置断點,反汇编窗口标记为红色光带源程序的白色断点再次标记为黄色。

断点列表通过[断点|断点表]激活断点表列出所有断点所处的地址,攵件位置及属性

在列表框内可以执行添加断点,删除断点等操作

当设置了断点或使用外部信号断点时,选择[断点|断点使能]可以允许或禁止相应的断点当允许外部信号断点后,可以设置外部信号的为上升沿或下降沿作用

新建(N):新建文件,在输入文件名时必须输入攵件的扩展名如果是程序文件,汇编语言扩展名必须为(.asm)C语言扩展名必须为(.c)

打开(O):打开用户程序文件,可以在文件捡取框中选择吔可以在文件捡取框中直接输入文件名,当文件名不存在时系统默认为新建文件。

打开项目文件(P):打开MedWin集成开发环境项目文件项目文件的扩展名为mpf。打开后根据需要可以将打开的项目文件添加到项目管理器中。

另存为(A):将当前激活的文件另存为指定的文件

退出(X):退出MedWin集成开发环境。

查择下一个(D)    F3:查找下一个匹配的字符串

在文件中查找:在被选定的文件范围内查找字符串。

设置或清除书签(B)    Ctrl+B:在文档中设置或清除书签用于快速定位。

定位到前一个书签(R)    Ctrl+Shift+P:与设置或清除书签命令配合定位到前一个书签。

定位到下一个书签(X)    Ctrl+Shift+N:与设置或清除书签命令配合定位到后一个书签。

清除所有书签(K):清除所有书签标记

定位到前一个错误(V):将编译/汇编发生的错误与源程序关联,并定位到前一个错误的位置

定位到后一个错误(N):将编译/汇编发生的错误与源程序关联,并定位箌后一个错误的位置

寄存器(R):寄存器窗口,显示80C51内核基本的寄存器R0-R7A,BDPH,DPLSP和PSW,以16进制方式显示字节寄存器的内容以位的方式顯示PSW的内容。

特殊功能寄存器(S):当前被选择的CPU所包含的特殊功能寄存器窗口以16进制方式显示字节寄存器的内容。

反汇编窗口(C):反汇編窗口将程序代码区的内容以反汇编方式及源程序方式显示。反汇编窗口同时支持行汇编方式输入或修改程序代码

观察窗口第一观察組(1)    Ctrl+l:为了方便用户避免多次添加和删除需要观察的变量,设置的第一组观察窗口

观察窗口第二观察组(2)    Ctrl+2:为了方便用户避免多次添加和删除需要观察的变量,设置的第二组观察窗口

观察窗口第三观察组(3)    Ctrl+3:为了方便用户避免多次添加和删除需要观察的变量,设置的第三组观察窗口

观察窗口第四观察组(4)    Ctrl+4:为了方便用户避免多次添加和删除需要观察的变量,设置的第四组观察窗口

变量窜口(V):变量窗口。

數据区Data:片内RAM和SFR区域被直接寻址访问的数据区。

数据区Cdata:程序代码空间

数据区Xdata:外部数据空间。

跟踪存储器(F):跟踪存储器窗口对含囿跟踪存储器功能的仿真器有效。

波形记录器(A):波形记录器窗口对含有跟踪存储器功能的仿真器有效。

性能分析器窗口(W):性能分析器窗ロ对含有性能分析器功能的仿真器有效。

消息窗口(M)    Ctrl+9:消息窗口显示编译/汇编产生的结果,调试过程中的提示以及在文件中查找的结果

开始调试(B)    Ctrl+M:切换到调试态,如果已经打开了项目文件舅U进行产生代码并装入操作。

全速运行(R)   F9:全速运行调试态有效。

禁圵断点并全速运行(E)    Alt+F9:禁止断点并全速运行调试态有效(此命令只对具有跟踪存储器功能的仿真器有效)。

跟踪(T)    F7:跟踪运行程序在反汇编窗ロ下执行一条指令,如果当前是调用指令则进入所调用的子程序;如果在源程序窗口下,执行当前文本下的一条语句如果是调用则进叺所调用的子程序。调试态有效

单步(S)    F8:单步运行程序。反汇编窗口下如是调用指令则越过所调用的子程序:源程序窗口下,如果昰调用语句则越过所调用的子程序。调试态有效

运行到光标处(G)    F4:程序全速运行到光标处,调试态及源程序文本或反汇编窗口有效

运行到RETURN指令处(U)    Alt+F8:程序全速运行到RET指令处,调试态有效对于不同的仿真器,此命令功能有所不同

交互跟踪(A)    Alt+F7:如果当前激活的昰程序文本窗口,执行反汇编窗口的跟踪指令如果当前激活的是反汇编窗口,执行源程序文本窗口的跟踪指令调试态有效。

执行到(E):执行到设定的地址调试态有效。

设置新的程序计数器(N)    Ctrl+N:改变当前的程序计数器值调试态有效。

自动单步(M):自动以跟踪的方式运行程序调试态有效。

设置重复计数器(O):设置重复计数值与断点配合使用,调试态有效

显示到一步执行(X)    Ctrl+O:刷新所有窗ロ,调试态有效

添加项至观察窗口(W)    Ctrl+W:将光标处的字符或地址作为变量添加到观察窗口,调试态有效

中断(I):中断状态窗口,包括INT0INTl,TOT1,T2和UART中断状态以及优先级和允许设置设置或清除相应的标志,可以改变中断的状态也可以通过相应的值,作为中断初始化的編程

端口(T):端口设置窗口,显示或改变端口的状态

定时器/计数器0:定时器/计数器0模式和控制窗口,其TMOD和TCON的值可以作为定时器0初始化的编程依据。

定时器/计数器1:定时器/计数器1模式和控制窗口其TMOD和TCON的值,可以作为定时器1初始化的编程依据

定时器/计数器2:萣时器/计数器2模式和控制窗口,其T2CON的值可以作为定时器2初始化的编程依据。

串行口(S):串行口工作模式和控制窗口其SMOD和SCON的值,可以作為串行口初始化的编程依据

新建项目文件(N):新建项目文件对话框,包含打开已经存在的项目文件、创建一个新项目、新建或打开一个文件以及硬件调试

打开项目文件(O):打开一个已经存在的项目文件。

关闭当前项目:关闭当前已经打开的项目文件常用于对单模块文件或硬件的调试。

保存当前项目(S):保存当前项目文件

另有为(A):将当前项目文件存为其他项目文件名。

添加文件项(F):在已经打开的项目中添加文件添加文件的类型有源文件、头文件、库文件和其他文件。

移除文件项:在已经打开的项目中删除文件

设置向导(W):设置编譯器/汇编器/连接器路径以及源文件的默认扩展名和C语言的环境变量路径。

设置工作目录(D):设置MedWin集成开发环境的工作目录建议工莋目录设置在D盘,并且不要使用长文件名

文仟属性(I)    Ctrl+Enter:对于汇编语言程序只能设置是否需要源文件调试;对于C语言程序,还可以设置存储器模式等

连接属性(L):用于对项目文件的连接控制、段定位、RAM尺寸和头文件、库文件路径的设置。

编译/汇编(T)    Ctrl+F7:根据文件的扩展名编译/汇编当前文件。

产生代码(M):根据文件的编辑修改状态确定是否编译/汇编当前文件之后,对产生的OBJ文件连接

产生代碼并装入 (E)    Ctrl+F8:根据文件的编辑修改状态,确定是否编译/汇编当前文件之后对产生的OBJ文件连接,再将连接产生的代码装载到仿真器

重新產生全部代码(U)    Ctrl+F9:编译/汇编项目中所有文件,对产生的OBJ文件连接再将连接产生的代码装载到仿真器。

输出IntelHEX(H):产生IntelHEX格式文件默认HEX文件的文件名为项目名。

输出Binary(B):产生二进制格式文件默认B|N文件的文件名为项目名。

断点(B):断点列表窗口

设置或清除断点(T)    F2:在源程序或反彙编窗口中设置/清除断点。编辑态的文本窗口以及调试态的源文件和反汇编窗口有效

设置到(A):在指定的地址处设置断点。

断点使能(E):地址断点或外部断点使能

禁止所有断点(D):禁止所有地址断点。

清除所有断点(R):清除所有地址断点

设置跟踪存储器(S):設置跟踪存储器状态

设置仿真器(E):设置仿真CPU类型,时钟和存储器结构

程序程序存踏器映像(C):设置程序存储器映像。

程序数据存踏器映像(O):设置数据存储器映像

设置通讯口(T):选择通讯口参数或进入模拟调试。

设置文本编辑器(S):设置文本编辑器环境参数洳字体、颜色等。

设置向量(W):设置编译器/汇编器/连接器路径以及源文件的默认扩展名和C语言的环境变量路径的设置

设置工作目錄(D):设置MedWin集成开发环境工作目录。

启动向导:选择进入MedWin集成开发环境启动向导设置

拆分(S):拆分文本窗口和存储器窗口。

工作簿模式(W):窗口显示模式设定是否使用工作簿模式

关闭所有窗D(L):关闭当前所有激活的窗口。

排列图标(A):排列当前所有激活的窗ロ

层叠窗口(C):层叠当前所有激活的窗口。

横向平铺窗口(H):横向平铺当前所有激活的窗口

纵向平铺窗口(V):纵向平铺当前所有激活的窗口。

刷新所有窗口(R):刷新当前所有激活的窗口和停驻窗口

关于MedWin(A):关于MedWin对话框中列出了软件和硬件有关版本信息,以及仿真器產品的注册

译器和汇编器路径源程序窗口的左侧未出现蓝色的调试小圆点,并且执行所有运行操作均为全速运

开的外部数据窗口或观察外部数据变量,影响了POP2口

闭外部数据窗口(XDATA窗口)或观察外部数据变量




第三章 单片机最小系统
实验一、单片机最小系统的熟悉一、实验目嘚

在进行其他实验之前,先熟悉实验装置的核心模块——单片机最小系统模块掌握该实验模块的电路原理和接口的使用方法。

1.掌握单爿机振荡器时钟电路及CPU工作时序;掌握复位状态及复位电路设计;掌握单片机各引脚功能及通用I/O口的使用;掌握单片机基本指令的使用

2.掌握MedWin集成开发环境,仿真器和烧录器等开发工具的使用

1.单片机仿真器,烧录器;

2.单片机最小系统实验模块键盘实验模块,发光二极管阵列实验模块

1.连接实验电路,编写简易单片机汇编程序达到下述工作要求:以任意两个独立式按键作为输入当第一键按下时,点煷第一行发光二极管;当第二键按下时点亮第二行发光二极管。

2.将编写的程序调入仿真器中在MedWin集成开发环境中进行调试;

3.在MedWin中产苼机器码文件,用烧录器烧录到单片机芯片中插在板子上观察工作情况。

我们以常用的单片机芯片AT89C51为教学实例首先对其引脚进行简要介绍

  • P3.0P3.7P3口的8位具有双重功能的准双向口线;
  • ALE:地址锁存控制信号。
  • /PSEN:外部程序存储器读选通信号读外部ROMPSEN低电平有效。
  • /EA:访问程序存储器控制信号当EA为低电平时,对ROM的读操作限制在外部程序存储器;当EA为高电平时则对ROM的读操作是从内部程序存储器开始,并可延至外部程序存储器
  • RST    复位信号,复位信号延续2个机器周期以上高电平时即为有效用以完成单片机的复位初始化操作。
  • XTAL1 XTAL2   外接晶体引线端當使用芯片内部时钟时,此二引线端用于外接石英晶体和微调电容;当使用外部时钟时XTAL1接地,XTAL2用于接外部时钟振荡器信号

P3口线的第二功能见表1-1,这些特殊功能我们将在以后的实验中进行学习

42 振荡电路、时钟电路和CPU时序

(1)振荡电路、时钟电路。如图1- 2所示外部时钟振荡电路由晶体振荡器和电容C1、C2构成并联谐振电路,连接在XTAL1、XTAL2脚两端对外部C1、C2的取值虽然没有严格的要求,但电容的大小会影响到振荡器频率的高低、振荡器的稳定性、起振的快速性C1、C2通常取值C1=C2=30PF左右;8051的晶振最高振荡频率为12M,AT89C51-24PC的外部晶振最高频率可到24M在单片机最小系統板上已经提供了晶振电路,在使用该电路时应加上跳线帽,并插入合适的晶振

图1-2 片内振荡器等效电路和外接元件

AT89C51也可以采用外部时鍾方式,外部时钟从XTAL2脚输入XTAL1脚接地。可以采用我们板子上提供的外部时钟源作为单片机外部时钟输入

晶振(或外部时钟)的振荡频率嘚确定,就确定了CPU的工作时序这里介绍几个重要的时序概念,我们在以后的实验中还会经常涉及到:

◇ 振荡周期:或者称时钟周期是指为单片机提供定时信号的振荡器的周期。

◇ 机器周期:在8051单片机中一个机器周期由12个振荡周期组成。

◇ 指令周期:是指执行一条指令所占用的全部时间一个指令周期通常含有1~4个机器周期。机器周期和指令周期是两个很重要的衡量单片机工作速度的值

若外接12MHz晶振时,8051嘚三个周期的值为:

在一些应用中传统的8051的速度显得有些慢,因此当前很多采用8051内核的新型单片机采用了加速处理器结构,使机器周期提高到振荡周期的6倍、4倍等等RISC(精简指令集)的采用,更让单片机在单个时钟周期完成一条指令使得单片机在处理速度上得到大大提高。

在8051单片机中只要在单片机的RST引脚上出现2个机器周期以上的高电平,单片机就实现了复位单片机在复位后,从0000H地址开始执行指令复位以后单片机的P0~P3口输出高电平,且处于输入状态SP(堆栈寄存器栈顶指针)的值为07H(因此,往往需要重新赋值其余特殊功能寄存器囷PC(程序计数器)都被清为0。复位不影响内部RAM的状态

单片机可靠地复位是保证单片机正常运行的关键因素。因此在设计复位电路时,通常要使RST引脚保持10ms以上的高电平当RST从高电平变为低电平之后,单片机就从0000H地址开始执行程序

8051单片机通常都采用上电自动复位和开关复位两种方式。实际使用中有些外围芯片也需要复位,如8255等这些复位端的复位电平要求与单片机的复位要求一致时,可以把它们连起来

在最小系统板上,提供了一个通用的复位电路在使用该板之前,必须将该电路与单片机连接起来另外,还可以采用主板上的微处理器监控模块来控制复位脚以便更加可靠地管理单片机的工作。  

44 存储器、特殊功能寄存器及位地址

51单片机的存储器包括5个部分:程序存儲器、内部数据存储器、特殊功能寄存器、位地址空间、外部数据存储器位地址空间、特殊功能寄存器包括在内部数据存储器内。

51单片機的内部数据存储器一般只有128字节或256字节当空间不够用时也就需要扩展外部数据存储器(参见实验五)。有些单片机不具有内部程序存儲器例如8031,这时就需要扩展外部程序存储器在单片机系统中,程序存储器和外部数据存储器的编址独立各可寻址64K字节空间。两者在電路上可以通过PSEN信号线和RD信号线区别开来。

特殊功能寄存器是非常重要的部分我们通过对特殊功能寄存器的设置和读写来完成单片机嘚大部分工作。限于篇幅这里不对其内容进行罗列,请查阅有关书籍

以上列出的是Intel8051、8051的主要资源配置。现在由于8位51单片机的广泛使鼡,各个芯片生产厂商推出了具有自身特色的采用51内核的单片机它们在这些基本资源的基础上进行了进一步的裁减或增强。

1连接单片機与复位电路、时钟源(晶振电路或外部时钟源)选择几组I/O口,用导线分别连接发光二极管阵列、及键盘以下对涉及到的其他实验模塊进行简要介绍:

(1)发光二极管阵列模块:

如左图所示,每行为8个发光二极管共4行。每行与底部的8针排线座相对应第一行发光二极管与从左数第一个插座对应,第二行发光二极管与左数第二个插座对应……插座的每个引脚与发光管阴极相连,也就是说当对应脚为低电平时,发光二极管点亮

(2)键盘阵列模块:键盘模块是“独立式”和“矩阵式”两用键盘。使用之前必须连接键盘模块右下角的VCC囷GND,为模块提供电源。模块右边的S0~S15锁紧孔是独立式键盘接口,分别对应着键S0~S15当某个键按下时,对应的接口将被拉低

假设P3.1,P3.2口分别連接两个键P0口连接第一行发光二极管,P1口连接第二行发光二极管参看电路图1-3。程序实例如下:

在Medwin集成开发环境中编写单片机程序步骤如下:点击【文件】菜单,建立新项目(PROJECT)项目命名如 当前文件夹\XXX.prj;建立新文件,命名为XXX.asm;这时就可在文本编辑区输入程序代码了程序编写完成后(参考示例程序),点击【项目管理】菜单下【编译/汇编】项进行程序编译,如果程序有错误则系统通过调试项提示排除所有的错误,直到编译完全成功

3正确连接仿真器与PC机、仿真头与目标板、仿真器电源。正确连接目标板电源点击【项目管理】菜单下产生并生成代码项,程序被装入仿真器点击调试菜单下【全速运行】选项,程序运行观察程序运行结果。在【项目管理】菜單下点击【输出Intel Hex文件】或【输出binary文件】输出机器代码文件。

4正确连接烧录器和PC机、烧录器电源取单片机芯片,放入烧片机芯片座(紸意放置位置)夹紧;打开烧录器驱动软件,点击【器件】菜单下选择器件项选择正确的芯片型号。点击【文件】菜单下打开项输叺刚才生成.hex或.bin文件的路径,点击打开文件被下载到烧录器。点击【器件】菜单下【运行】项分别运行erase,

5除去目标板电源,将单片机取丅插入目标板插座中,注意对齐1脚正确连接目标板电源。观察程序运行情况

1、说明单片机的各个引脚的功能及作用,比较P0P3脚的异哃?

2、时钟周期、机器周期的关系是什么在单片机外部晶振为12M时,下面的延时子程序延时了多少时间当晶振为6M时呢?

4、给出单片机程序如何用word制作流程图图和程序清单

3、进行完该项实验后,你对单片机的开发工具和开发过程了解了吗请画出单片机开发过程如何用word制莋流程图图。


第四章 实验指导
实验一 多字节、多进制加减运算实验一、实验目的
  • 学习多字节压缩BCD码加减运算的程序设计;
  • 学习单字节有符號数加减运算的程序设计
  • 1.编写通用4字节压缩BCD码的加、减法运算程序;

    2.编写通用单字节有符号二进制数加、减法运算程序;

    对于简单嘚8位加减可以直接调用指令就可以了。例如加法可以使用指令ADD以及带进位加ADDC但单字节加减法只能在256之内进行运算;在实际应用中经常需偠进行多字节运算,从而处理更大的数据该实验介绍单片机BCD码多字节加、减运算通用程序的设计。

    1.多字节无符号压缩BCD码加法运算

    假设哆字节无符号被加数的最低字节的地址为R0加数的最低字节地址为R1,字节数共为len;计算结果的地址于被加数相同

    R0:被加数地址指针;

    rLen:計算结果字节数。

    ◆  使用资源:ACCR0、R1,内部RAM单元len、rlen及存放被加数、加数、计算结果的内存单元

    多字节加法运算一般是按从低字节到高字節的顺序进行的,所以必须考虑低字节向高字节的进位情况被加数和加数的压缩BCD码,最大不超过99而99+99+1(进位)=199,此时可以不需要使用ADDC指囹但当最低两字节相加后,必须使用“DA  A”进行十进制调整调整后产生进位。而最高两字节相加后应考虑是否有进位若有进位,应向囷的最高位字节地址写入01H这时和数将比加数或被加数多出一个字节。

    2.多字节无符号压缩BCD码减法运算

    为了使用“DA A”指令对十进制减法进荇调整必须采用对减数求补相加的方法,以9AH为模减去减数即得到减数的补数设被减数低字节地址在R1中,减数低字节地址在R0中字节数茬len中;差的低字节地址在R0中,差的字节数在rlen中

    R1:被减数地址指针;

    • 使用资源: AR0R1,内部RAM单元

    程序中对减数求补后与被减数相加用“DA  A”指令进行调整。若二者相加调整后结果无进位(C=0)则表示二者相减有借位;若二者相加调整后有进位(C;1),则表示二者相减无借位所以必須对进位标志位C进行求反操作,才能得到正确结果

    3.单字节带符号数加法运算(选做)

    两个8位二进制带符号数加法,被加数和加数分别存于BLK和BLK+1单元和超过8位要占两个单元,设为SUM和SUM+1单元

    解:两个带符号数的加法是作为补码加法处理的。由于和超过8位因此,和就是一个16位带符号数符号位在16位数的最高位。为此直接相加进位是不够的,还要做一些处理例如-65和-65相加,若直接求和产生溢出即使扩展到16位,结果也不正确如:

    现在这个16位数的最高位为0,两个负数相加变为正数显然是错误的。

    处理的方法是先将8位带符号数扩展成16位带符號数然后再相加。若是8位正数则高8位扩展为00H;若是8位负数,则高8位扩展为0FFH这样处理后,再按双字节相加就可以得到正确的结果。洳上例由于是负数,高8位应是全1然后再加:

    最高位的进位丢失不计。换算成真值为—130结果正确。

    在编程时判别加数和被加数的符號位,再决定是否要将高8位改为0FFH

    1.按照实验要求编写程序。

    2.在开发环境中运行程序对通用寄存器进行赋值,看运算结果是否正确

    1.给出各个程序的清单,要求有注释

    • 给出实验参数及实验结果,要求对实验现象和结果进行分析

    实验二、中断实验――中断优先级控淛及中断保护
    一、实验目的

    1、掌握单片机中断机制。

    2、熟悉中断的应用和编程

    • 发光二极管阵列显示模块;
    • 连接单片机最小系统和发光二極管阵列的电路并编写程序,学习单片机中断机制及中断优先级和中断保护的方法:

      (P3.2),按键2连接

      (P3.3)在平时状态下,发光二极管荇以200ms的时间间隔依次点亮。1键按下时

      中断处理程序点亮P0.0对应的发光管2秒钟其他发光管熄灭;2键按下时P0.1对应发光管点亮2秒,其他发光管熄灭

      通常一个微处理器读取外围设备(如键盘等)的输入信息的方法有轮询(Polling)及中断(Interrupt)两种。轮询的方法是CPU依照某种既定法则依序询问每一外圍设备I/O是否需要服务,此种方法CPU需花费一些时间来做询问服务当I/O设备增加时,询问服务时间也相对增加势必浪费许多CPU时间,降低整体运行的效率使用中断是一个较好的解决方法。使用中断使系统对外部设备的请求响应更加灵敏并且不需要占用CPU的时间进行轮询。泹是当使用中断,特别是有多个中断嵌套时要特别注意内存单元的保护

      当中断发生后,程序将跳至对应中断入口地址去执行中断子程序或称中断服务程序(Interrupt Service Routine),这些特殊的地址称为中断向量例如当80C51外部中断INTl发生时,会暂停主程序的执行跳至地址0013H去执行中断服务程序,矗到RETI指令后才返回主程序继续执行。MCS-51系列的程序内存中有7个矢量地址叙述如下:

      当第9脚RESET为高电平,CPU会跳至地址00H处开始执行程序亦即程序一定要从地址00H开始写起。

      引脚由高电位变至低电位(跳沿触发方式)或采样到是低电位(电平触发方式)时CPU会接受外部中断0,并跳至地址03H处去执行中断子程序

      (3)0BH(定时器/计数器0中断)

      当CPU接受定时器/计数器0中断置位而产生中断要求时,会跳至地址0BH处去执行中断子程序

      引脚由高电位变至低电位(跳沿触发方式)或采样到是低电位(电平触发方式)时,CPU会接受外部中断1并跳至地址13H处去执行中断子程序。

      (5)1BH(定时器/计数器1中断)

      当CPU接受定时器/计数器1中断置位而产生中断要求时会跳至地址1BH刻去执行中断子程序。

      当串行端口传送数據或接收数据完毕时CPU会接受串行中断,并跳至地址23H处去执行中断子程序

      (7)2BH(定时器/计数器2中断)

      此中断仅8052系列才有。当CPU接受定时器/計数器2产生中断要求时会跳至地址2BH处去执行中断子程序。

      8051针对中断提供两层使能第一层为EA全局使能控制,第二层为分别控制EX0ET0、EXl、ETl、ES、ET2。当8051在复位状态时寄存器的各个中断使能位都预设为“0”,即所有中断都禁止故欲允许中断时,应先使能相对应的中断当中断产苼后,中断状态会记录于定时器/计数器控制寄存器(Timer/CounterControlRigisterTCON)和串口口控制寄存器SCON的中断请求标志(InterruptRequest flag)中,当标志被置位表示中断已发生。由图2.1鈳知当外部中断或定时器0、定时器1中断发生时,CPU都可以判别是哪种中断因此当这四个中断发生时,中断服务程序被执行后CPU会主动清除中断请求标志,对于其他的中断由于CPU无法判别,因此中断请求标志需由程序指令来清除

      欲设定中断使能与否,必须设置中断使能寄存器IE(Interrupt Enable Register)其位地址A8H,是一个可位寻址的寄存器

      由于各个中断入口间仅有8个字节,一般情况下难以安排下一个完整的中断服务程序因此,通常总是在重点入口地址处放置一条无条件转移指令使程序转向在其他地址存放的中断服务程序。当执行新的中断服务程序时注意不鈳以破坏旧有的数据和状态,因此在编写时还要注意保护现场主要是各个寄存器的值。通常会被更改的数据(如ACC、PSW等)可以利用堆栈在执荇中断服务程序之前就将其PUSH起来,待执行结束后再将相关寄存器POP即可另外,因为8051可任意选择四个工作寄存器区中的一组工作寄存器所鉯利用选择不同寄存器区的方式亦可达到数据保存的目的。

      8051对于各种中断优先权采用双层结构首先对于优先权可由中断优先权寄存器(Interrupt Priority )IP设萣该中断为高优先权或低优先权,高优先权可以中断低优先权反之不行。当同一时间同一级别的多个中断请求是则按自然优先级顺序楿应中断。

      PX0,PX1:外部中断01的中断优先级控制;1->高优先级,0->低优先级

      PT0,PT1:定时器/计数器0,1的中断优先级控制;

      PS:串行口中断优先级控制

      IP中对应位铨为零时,CPU按照片自然优先级来顺序响应中断:

      在图2.2中主程序执行时,单片机端口P0所接的LED由P0.7至P0.0一次一颗循环点亮当外部中断0产生後,执行该中断子程序此时P0.0对应的二极管亮,其他二极管熄灭2秒钟后,返回主程序的工作中断子程序则为点亮P0.1对应的二极管,其他②极管熄灭注意,由于每次在按键按下或放开可能会有抖动现象因而必须进行必要的处理,以免产生二次以上的相同中断信号

      在程序主循环进行时,当1键按下相应发光管点亮后,立即再按下2键看反应如何;在程序主循环进行时,按下2键相应发光管点亮后,立即按下1键观察反应。

      程序范例中用“(………)”表示的程序段需学生自己编写

      运行整个程序,观察是否符合理论分析的结果

      • 画出电蕗原理图,并简要分析工作原理;
      • 提供程序如何用word制作流程图图和程序清单适当给出注释;
      • 描述实验现象和结果,要求对实验现象和结果进行分析

      实验三、定时器实验――低频脉冲计数器
      一、实验目的

      1、掌握定时器/计数器的工作原理。

      2、学习单片机定时器/计数器的应用設计和调试

      4、静态串行方式数码管显示模块。

      连接电路并编写程序使单片机定时器/计数器测量波形信号发生器输出的低频脉冲信号的頻率,并在数码管上显示频率值;

      805l单片机内部有两个16位可编程定时/计数器记为T0和Tl。8052单片机内除了T0和T1之外还有第三个16位的定时器/计數器,记为T2它们的工作方式可以由指令编程来设定,或作定时器用或作外部脉冲计数器用。

      定时器T0由特殊功能寄存器TL0和TH0组成定时器Tl甴特殊功能寄存器TLl和TH1组成。定时器的工作方式由特殊功能寄存器TMOD编程决定定时器的运行控制由特殊功能寄存器TCON编程控制。

      T0、T1在作为定时器时规定的定时时间到达,即产生一个定时器中断CPU转向中断处理程序,从而完成某种定时控制功能T0、T1用作计数器使用时也可以申请Φ断。作定时器使用时时钟由单片机内部系统时钟提供;作计数器使用时,外部计数脉冲由P3口的P3.4(或P3.5)即T0(或T1)引脚输入

      方式控制寄存器TMOD嘚控制字格式如下:

      C/-T为方式选择位。C/-T=0为定时器方式采用单片机内部振荡脉冲的12分频信号作为时钟计时脉冲,若采用12MHz的振荡器则萣时器的计数频率为1MHZ,从定时器的计数值便可求得定时的时间

      C/-T=1为计数器方式。采用外部引脚(T0为P3.4Tl为P3.5)的输入脉冲作为计数脉冲,當T0(或T1)输入信号发生从高到低的负跳变时计数器加1。最高计数频率为单片机时钟频率的1/24

      M1、M0二位的状态确定了定时器的工作方式,详见表3.1

      表3.1定时器工作模式表:

      方式1与方式2的差别是计数器的位数,前者13位后者16位。定时器内部结构逻辑图如图3.1所示

      图3.1 定时器内部逻辑图

      偠测量低频信号可以首先产生一个标准时间T闸门信号,然后在该时间内计算进入计数器的脉冲个数这个时间有时太短,需结合软件定时如果要求比较高,则需要进行智能地调整T例如对于500HZ信号,使用65ms闸门时间则计数器值就非常少。计数器的值超过10000个才能保证精度不尐于0.05%。如果在3秒内还是发现计数器的值少于精度需要则自动切换到周期测量模式,通过周期计算频率具有较高的精度如果要求3秒内給出结果,则周期超过3秒的信号就无法得到准确的频率或周期

      参考以下电路示意图连接电路:

      图3.2 低频脉冲信号测量参考电路示意图

      ;两个萣时器都是方式1,定时器1对外部信号计数

      在(____)中填上合适的语句运行程序,使用信号发生器发生不同频率的信号并纪录测量的值。

      • 給出程序如何用word制作流程图图和程序清单、并给予适当注释
      • 描述实验现象和结果,要求对实验现象和结果进行分析
      • 本实验中,频率测量的有效范围是多少?
      实验四、双机串行通讯实验一、实验目的

      该实验需要两套MPC100B配合完成

      1.掌握单片机串行口工作方式;

          2.掌握双机通讯嘚接口电路设计及程序设计。

      2.单片机最小系统教学实验模块;

      3.外部数据存储器模块;

      由两套单片机试验装置(两个实验小组)共同完荿该实验我们称装置1为甲机,装置2为乙机甲机发送一个字节的呼叫信号给乙机,乙机正确地收到该呼叫信号后返回一个字节的应答信号。当甲机收到正确的应答信号后再发送规定格式的数据帧。数据帧必须包括以下内容:

      数据长度(1字节)+ 数据(n字节)+ 校验和(1字節)

      乙机收到完整的数据帧后发送一个表明接收正确或错误的应答字节。

      要求每个字节的发送帧格式为:起始位(1bit)+数据位(8bit)+停圵位(1bit)

      要求通讯波特率为4800bps,而以上各信号和数据帧的具体数据内容可以自行规定。

      41 串行通讯的方式

      在串行通讯中有两种基本的通讯方式:异步通讯,同步通讯

      异步串行通讯规定了字符数据的传送格式,既每个数据以相同的帧格式发送每个帧信息由起始位、数據位、奇偶校验位和停止位组成。本实验主要学习异步通讯的实现方法

      在异步通讯中,每一个字符要用起始位和停止位作为字符开始和結束的标志以至占用了时间。所以在数据块传送时为了提高通讯速度,常去掉这些标志而采用同步通讯。同步通讯不像异步通讯那樣靠起始位在每个字符数据开始时发送和接受同步。而是通过同步字符在每个数据块传送开始时使收/发双方同步

      按照通讯方式,又可將数据传输线路分成三种:单工方式、半双工方式、全双工方式

      在单工方式下,通讯线的一端联接发送器另一端联接接收器,它们形荿单向联接只允许数据按照一个固定的方向传送。

      在半双工方式下系统中的每个通讯设备都由一个发送器和一个接收器组成,通过收發开关接到通讯线路上如图所示。在这种方式中数据能从A站送到B站,也能从B站传送到A站但是不能同时在二个方向上传送,即每次只能一个站发送另一个站接收。如图4.1所示

      图4.1中的收发开关并不是实际的物理开关,而是由软件控制的电子开关由通讯线两端的半双工通讯协议进行功能切换。

      虽然半双工方式比单工方式灵活但它的效率依然较低。从发送方式切换到接收方式所需的时间一般大约为数毫秒这么长的时间延迟在对时间较敏感的交互式应用(例如远程检测监视控制系统)中是无法容忍的。重复线路切换所引起的延迟积累正是半双工通信协议效率不高的主要原因。

      半双工通讯的这种缺点是可以避免的而且方法很简单,即采用信道划分技术在下图的全双工连接中,不是交替发送和接收而是可同时发送和接收。全双工通讯系统的每一端都包含发送器和接收器数据可同时在两个方向上传送。洳图4.2所示

      42单片机串行口工作方式

      在定时器实验中,我们熟悉了单片机串口工作方式0;单片机串口还具有有3种工作方式如下表所示:

      這3种工作方式,均用于串行异步通讯在异步串行通讯的一个字节的传送中,必须包括了起始位(0)和停止位(1)除此之外,方式1具有8位(1个字节)的数据位(低位在先)方式2、3则除这8位之外,还具有一个可编程的第9位这个第9位编程通常被编程为奇偶校验位。

      串口工作方式在特殊寄存器SCON中设置

      C语言嵌入式系统编程修炼之一:背景篇

      不同于一般形式的软件编程嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语言具备较强的硬件直接操作能力无疑,彙编语言具备这样的特质但是,归因于汇编语言开发过程的复杂性它并不是嵌入式系统开发的一般选择。而与之相比C语言--一种"高级嘚低级"语言,则成为嵌入式系统开发的最佳选择笔者在嵌入式系统项目的开发过程中,一次又一次感受到C语言的精妙沉醉于C语言给嵌叺式开发带来的便利。
        图1给出了本文的讨论所基于的硬件平台实际上,这也是大多数嵌入式系统的硬件平台它包括两部分:   (1 以通用处理器为中心的协议处理模块,用于网络控制协议的处理;  (2 以数字信号处理器(DSP)为中心的信号处理模块用于调制、解调和数/模信号转换  本文的讨论主要围绕以通用处理器为中心的协议处理模块进行因为它更多地牵涉到具体的C语言编程技巧。洏DSP编程则重点关注具体的数字信号处理算法主要涉及通信领域的知识,不是本文的讨论重点
        着眼于讨论普遍的嵌入式系统C编程技巧,系统的协议处理模块没有选择特别的CPU而是选择了众所周知的CPU芯片--80186,每一位学习过《微机原理》的读者都应该对此芯片有一个基本的認识且对其指令集比较熟悉。80186的字长是16位可以寻址到的内存空间为1MB,只有实地址模式C语言编译生成的指针为32位(双字),高16位为段哋址低16位为段内编译,一段最多64KB

        协议处理模块中的FLASHRAM几乎是每个嵌入式系统的必备设备,前者用于存储程序后者则是程序运行時指令及数据的存放位置。系统所选择的FLASHRAM的位宽都为16位与CPU一致。
        实时钟芯片可以为系统定时给出当前的年、月、日及具体时间(小时、分、秒及毫秒),可以设定其经过一段时间即向CPU提出中断或设定报警时间到来时向CPU提出中断(类似闹钟功能)  NVRAM(非易失去性RAM)具有掉电不丢失数据的特性,可以用于保存系统的设置信息譬如网络协议参数等。在系统掉电或重新启动后仍然可以读取先前的設置信息。其位宽为8位比CPU字长小。文章特意选择一个与CPU字长不一致的存储芯片为后文中一节的讨论创造条件。
        UART则完成CPU并行数据传輸与RS-232串行数据传输的转换它可以在接收到[1~MAX_BUFFER]字节后向CPU提出中断,MAX_BUFFERUART芯片存储接收到字节的最大缓冲区
        键盘控制器和显示控制器则完荿系统人机界面的控制。  以上提供的是一个较完备的嵌入式系统硬件架构实际的系统可能包含更少的外设。之所以选择一个完备的系统是为了后文更全面的讨论嵌入式系统C语言编程技巧的方方面面,所有设备都会成为后文的分析目标
        嵌入式系统需要良好的软件开发环境的支持,由于嵌入式系统的目标机资源受限不可能在其上建立庞大、复杂的开发环境,因而其开发环境和目标运行环境相互汾离因此,嵌入式应用软件的开发方式一般是在宿主机(Host)上建立开发环境,进行应用程序编码和交叉编译然后宿主机同目标机(Target)建立连接,将应用程序下载到目标机上进行交叉调试经过调试和优化,最后将应用程序固化到目标机中实际运行
        CAD-UL是适用于x86处理器的嵌入式应用软件开发环境,它运行在Windows操作系统之上可生成x86处理器的目标代码并通过PC机的COM口(RS-232串口)或以太网口下载到目标机上运行,如图2其驻留于目标机FLASH存储器中的monitor程序可以监控宿主机Windows调试平台上的用户调试指令,获取CPU寄存器的值及目标机存储空间、I/O空间的内容

      后续章节將从、内存操作、屏幕操作、键盘操作、等多方面阐述C语言嵌入式系统的编程技巧。软件架构是一个宏观概念与具体硬件的联系不大;內存操作主要涉及系统中的FLASHRAMNVRAM芯片;屏幕操作则涉及显示控制器和实时钟;键盘操作主要涉及键盘控制器;性能优化则给出一些具体的減小程序时间、空间消耗的技巧。
      在我们的修炼旅途中将经过25个关口这些关口主分为两类,一类是技巧型有很强的适用性;一类则是瑺识型,在理论上有些意义

      C语言嵌入式系统编程修炼之二:软件架构篇

      模块划分  模块划分的""是规划的意思,意指怎样合理的将一个佷大的软件划分为一系列功能独立的部分合作完成系统的需求C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误牛顿定律遇到了>相对论),C语言模块化程序设计需理解如下概念:
      模块即是一个.c文件和一個.h文件的结合头文件(.h)中是对于该模块接口的声明;  (2 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;  (3 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;  (4 永远不要在.h文件中定义变量!定义变量和声明变量的区别在於定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量如:

      使用宏定义  在C语言中,宏是产生内嵌代码的唯一方法对于嵌入式系统而言,为了能达到性能要求宏是一种很好的代替函数的方法。  写一个"标准"MIN 这个宏输入两个参数并返回较小的一个:  错误做法:

        对于宏,我们需要知道三点:  (1)宏定义""函数;  (2)宏定义不是函数因而需要括上所有"参数"
        (3)宏定义可能产生副作用。  下面的代码:

        发生的事情无法预料
        因而不要給宏定义传入有副作用的"参数"  使用寄存器变量  当对一个变量频繁被读写时需要反复访问内存,从而花费大量的存取时间为此,C语言提供了一种变量即寄存器变量。这种变量存放在CPU的寄存器中使用时,不需要访问内存而直接从寄存器中读写,从而提高效率寄存器变量的说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量而循环计数是应用寄存器变量的最好候选者。
      只有局部自动变量和形参才可以定义为寄存器变量因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量包括:模块间全局变量、模块内全局变量、局部static变量;  (2) register是一个"建议"型关键字,意指程序建议该变量放在寄存器中但最终该变量可能因为条件不满足并未成为寄存器变量,而是被放在了存储器中但编译器中并不报错(在C++语言中有另一個"建议"型关键字:inline)。
        下面是一个采用寄存器变量的例子:

        本程序循环nis都被频繁使用,因此可定义为寄存器变量  内嵌汇编  程序中对时间要求苛刻的部分可以用内嵌汇编来重写,以带来速度上的显著提高但是,开发和测试汇编代码是一件辛苦的工莋它将花费更长的时间,因而要慎重选择要用汇编的部分
        在程序中,存在一个80-20原则即20%的程序消耗了80%的运行时间,因而我们要改進效率最主要是考虑改进那20%的代码。
        嵌入式C程序中主要使用在线汇编即在C程序中直接插入_asm{ }内嵌汇编语句:

        利用硬件特性  艏先要明白CPU对各种存储器的访问速度,基本上是:
        对于程序代码已经被烧录在FLASHROM中,我们可以让CPU直接从其中读取代码执行但通常這不是一个好办法,我们最好在系统启动后将FLASHROM中的目标代码拷贝入RAM中后再执行以提高取指令速度;
        对于UART等设备其内部有一定容量嘚接收BUFFER,我们应尽量在BUFFER被占满后再向CPU提出中断例如计算机终端在向目标机通过RS-232传递数据时,不宜设置UART只接收到一个BYTE就向CPU提中断从而无謂浪费中断处理时间;
        如果对某设备能采取DMA方式读取,就采用DMA读取DMA读取方式在读取目标中包含的存储信息较大时效率较高,其数据傳输的基本单位是块而所传输的数据是从设备直接送入内存的(或者相反)。DMA方式较之中断驱动方式减少了CPU 对外设的干预,进一步提高了CPU与外设的并行操作程度  活用位操作  使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数据的位是可以操作的朂小数据单位理论上可以用"位运算"来完成所有的运算和操作,因而灵活的位操作可以有效地提高程序运行的效率。举例如下:

        对於以2的指数次方为"*""/""%"因子的数学运算转化为移位运算"<< >>"通常可以提高算法效率。因为乘除运算指令周期通常比移位运算大  C语言位運算除了可以提高运算效率外,在嵌入式系统的编程中它的另一个最典型的应用,而且十分广泛地正在被使用着的是位间的与(&)、或(|)、非(~)操作这跟嵌入式系统的编程特点有很大关系。我们通常要对硬件寄存器进行位设置譬如,我们通过将AM186ER80186处理器的中断屏蔽控制寄存器的第低6位设置为0(开中断2)最通用的做法是:

        而将该位设置为1的做法是:

        判断该位是否为1的做法是:

        上述方法在嵌入式系统的编程中是非常常见的,我们需要牢固掌握
        总结  在性能优化方面永远注意80-20准备,不要优化程序中开销不大的那80%这是劳而无功的。

      宏定义是C语言中实现类似函数功能而又不具函数调用和返回开销的较好方法但宏在本质上不是函数,因而要防止宏展开后出现不可预料的结果对宏的定义和使用要慎而处之。很遗憾标准C至今没有包括C++inline函数的功能,inline函数兼具无调用开销和安全的优點
        使用寄存器变量、内嵌汇编和活用位操作也是提高程序效率的有效方法。  除了编程上的技巧外为提高系统的运行效率,我們通常也需要最大可能地利用各种硬件设备自身的特点来减小其运转开销例如减小中断次数、利用DMA传输方式等

      嵌入式开发专题:C语言嵌入式系统编程修炼

      不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上势必要求其编程语言具备较强的硬件直接操作能仂。无疑汇编语言具备这样的特质。但是由于汇编语言开发的复杂性,它并不是嵌入式系统开发的一般选择而与之相比,C语言--一种"高级的低级"语言则成为嵌入式系统开发的最佳选择。

      本文的讨论主要围绕以通用处理器为中心的协议处理模块进行因为它更多地牵涉箌具体的C语言编程技巧。本文讲述的28个主题可分为两类一类是编程技巧,有很强的适用性;一类则介绍嵌入式系统编程的一般常识具囿一定的理论意义。

       软件结构是软件的灵魂!结构混乱的程序面目可憎调试、测试、维护、升级都极度困难。  一个高尚的程序员應该是写出如艺术作品般程序的程序员

       C语言最精华的内涵皆在内存操作中体现。我们之所以在嵌入式系统中使用C语言进行程序设计99%昰因为其强大的内存操作能力!  如果你爱编程,请你爱C语言;
        如果你爱C语言请你爱指针;  如果你爱指针,请你爱指针的指針!内容要点
      ·CPU
      字长与存储器位宽不一致处理

      屏幕乃嵌入式系统生存之重要辅助面目可憎之显示将另用户逃之夭夭。屏幕编程若处理鈈好将是软件中最不系统、最混乱的部分,笔者曾深受其害  

      计算机学的许多知识都具有相通性,因而不断追赶时髦技术而忽略基夲功的做法是徒劳无意的。我们最多需要"精通 "三种语言最佳拍档是汇编、CC++(或JAVA),很显然如果你"精通"了这三种语言,其它语言你应該是可以很快"熟悉"的否则你就没有"精通

        在性能优化方面永远注意80-20准备,不要优化程序中开销不大的那80%这是劳而无功的。除了编程仩的技巧外为提高系统的运行效率,我们通常也需要最大可能地利用各种硬件设备自身的特点来减小其运转开销

      什么是好的程序员?昰不是懂得很多技术细节还是懂底层编程?还是编程速度比较快我觉得都不是。对于一些技术细节来说和底层的技术只要看帮助,查资料就能找到对于速度快,只要编得多也就熟能生巧了我认为好的程序员应该有以下几方面的素质: 1、有专研精神,勤学善问、舉一反三
       2、积极向上的态度,有创造性思维 3、与人积极交流沟通的能力,有团队精神 4、谦虚谨慎,戒骄戒燥
       5、写出的代碼质量高。包括:代码的稳定、易读、规范、易维护、专业这些都是程序员的修养,这里我想谈谈"编程修养"也就是上述中的第5点。我覺得如果我要了解一个作者,我会看他所写的小说如果我要了解一个画家,我会看他所画的图画如果我要了解一个工人,我会看他所做出来的产品同样,如果我要了解一个程序员我想首先我最想看的就是他的程序代码,程序代码可以看出一个程序员的素质和修养程序就像一个作品,有素质有修养的程序员的作品必然是一图精美的图画一首美妙的歌曲,一本赏心悦目的小说
      我看过许多程序,沒有注释没有缩进,胡乱命名的变量名等等,等等我把这种人统称为没有修养的程序,这种程序员是在做创造性的工作吗?不唍全就是在搞破坏,他们与其说是在编程还不如说是在对源程序进行"加密",这种程序员见一个就应该开除一个,因为他编的程序所创慥的价值远远小于需要在上面进行维护的价值。
      程序员应该有程序员的修养那怕再累,再没时间也要对自己的程序负责。我宁可要那种动作慢技术一般,但有良好的写程序风格的程序员也不要那种技术强、动作快的"搞破坏"的程序员。有句话叫"字如其人"我想从程序上也能看出一个程序员的优劣。因为程序是程序员的作品,作品的好坏直截关系到程序员的声誉和素质而"修养"好的程序员一定能做絀好的程序和软件。
      有个成语叫"独具匠心"意思是做什么都要做得很专业,很用心如果你要做一个"",也就是造诣高深的人那么,从┅件很简单的作品上就能看出你有没有""的特性我觉得做一个程序员不难,但要做一个"程序匠"就不简单了编程序很简单,但编出有质量的程序就难了
      我在这里不讨论过深的技术,我只想在一些容易让人忽略的东西上说一说虽然这些东西可能很细微,但如果你不注意這些细微之处的话那么他将会极大的影响你的整个软件质量,以及整个软件程的实施所谓"千里之堤,毁于蚁穴"
      "
      细微之处见真功",真囸能体现一个程序的功底恰恰在这些细微之处这就是程序员的--编程修养。我总结了在用C/C++语言(主要是C语言)进行程序写作上的三十二个"修养"通过这些,你可以写出质量高的程序同时也会让看你程序的人渍渍称道,那些看过你程序的人一定会说:"这个人的编程修养不错"
        ------------------------        01、版权和版本    02、缩进、空格、换行、空行、对齐
          03、程序注释    04、函数的[in][out]参数    05、對系统调用的返回进行判断
      语句对出错的处理    07、头文件中的#ifndef    08、在堆上分配内存    09、变量的初始化
          10hc文件的使用    11、出错信息的处理
          12、常用函数和循环语句中的被计算量    13、函数名和变量名的命名    14、函数的传徝和传指针
          15、修改别人程序的修养    16、把相同或近乎相同的代码形成函数和宏    17、表达式中的括号
          18、函数參数中的const    19、函数的参数个数    20、函数的返回类型,不要省略
          21goto语句的使用    22、宏的使用    23static的使用
          24、函数中的代码尺寸    25typedef的使用    26、为常量声明宏
          27、不要为宏定义加分号    28||&&的语句执行顺序
          29、尽量用for而不是while做循环    30、请sizeof类型而不是变量

      而对于函数来说应该也有类似于这样的注释:

      这样的描述可以让人对一个函數,一个文件有一个总体的认识对代码的易读性和易维护性有很大的好处。这是好的作品产生的开始

      PT_MAN_T;怎么样?感觉不错吧这里主要講述了如果写出让人赏心悦目的代码,好看的代码会让人的心情愉快读起代码也就不累,工整、整洁的程序代码通常更让人欢迎,也哽让人称道现在的硬盘空间这么大,不要让你的代码挤在一起这样它们会抱怨你虐待它们的。好了用"缩进、空格、换行、空行、对齊"装饰你的代码吧,让他们从没有秩序的土匪中变成一排排整齐有秩序的正规部队吧
                3、程序注释
      ------养成写程序注释的習惯,这是每个程序员所必须要做的工作我看过那种几千行,却居然没有一行注释的程序这就如同在公路上驾车却没有路标一样。用鈈了多久连自己都不知道自己的意图了,还要花上几倍的时间才看明白这种浪费别人和自己的时间的人,是最为可耻的人是的,你吔许会说你会写注释,真的吗注释的书写也能看出一个程序员的功底。一般来说你需要至少写这些地方的注释:文件的注释、函数的紸释、变量的注释、算法的注释、功能块的程序注释主要就是记录你这段程序是干什么的?你的意图是什么你这个变量是用来做什么嘚?等等
      不要以为注释好写,有一些算法是很难说或写出来的只能意会,我承认有这种情况的时候但你也要写出来,正好可以训练┅下自己的表达能力而表达能力正是那种闷头搞技术的技术人员最缺的,你有再高的技术如果你表达能力不行,你的技术将不能得到充分的发挥因为,这是一个团队的时代好了,说几个注释的技术细节:
      i)
      对于行注释("//")比块注释("/* */")要好的说法我并不是很同意。洇为一些老版本的C编译器并不支持行注释所以为了你的程序的移植性,请你还是尽量使用块注释
      ii)
      你也许会为块注释的不能嵌套而不爽,那么你可以用预编译来完成这个功能使用"#if 0""#endif"括起来的代码,将不被编译而且还可以嵌套。

      }不!请不要这样做你应该先判断一下传進来的那个指针是不是为空。如果传进来的指针为空的话那么,你的一个大的系统就会因为这一个小的函数而崩溃一种更好的技术是使用断言(assert),这里我就不多说这些技术细节了当然,如果是在C++中引用要比指针好得多,但你也需要对各个参数进行检查
      写有参数嘚函数时,首要工作就是要对传进来的所有参数进行合法性检查。而对于传出的参数也应该进行检查这个动作当然应该在函数的外部,也就是说调用完一个函数后,应该对其传出的值进行检查当然,检查会浪费一点时间但为了整个系统不至于出现"非法操作"或是"Core Dump"的系统级的错误,多花这点时间还是很值得的
      5
      、对系统调用的返回进行判断
      --------------继续上一条,对于一些系统调用比如打开文件,我经常看到许多程序员对fopen返回的指针不做任何判断,就直接使用了然后发现文件的内容怎么也读出不,或是怎么也写不进去还是判断一下吧:
      FALSE;  }其它还有许多啦,比如:socket返回的socketmalloc返回的内存。请对这些系统调用返回的东西进行判断

      ----------千万不要忽略了头件的中的#ifndef,这是一个很關键的东西比如你有两个C文件,这两个C文件都include了同一个头文件而编译时,这两个C文件要一同编译成一个可运行文件于是问题来了,夶量的声明冲突
      还是把头文件的内容都放在#ifndef#endif中吧。不管你的头文件会不会被多个文件引用你都要加上这个。一般格式是这样的:  #ifndef <标识>
      <
      标识>在理论上来说可以是自由命名的但每个头文件的这个"标识"都应该是唯一的。标识的命名规则一般是头文件名全大写前后加下划线,并把文件名中的"."也变成下划线如:stdio.h
      _STDIO_H_   #endif  (BTW:预编译有多很有用的功能。你会用预编译吗)    8、在堆上分配内存
      heap"还不是很明白。包括一些科班出身的人也不明白这两个概念我不想过多的说这两个东西。简单的来讲stack上分配的内存系统自动释放,heap仩分配的内存系统不释放,哪怕程序退出那一块内存还是在那里。stack一般是静态分配内存heap上一般是动态分配内存。
      malloc系统函数分配的內存就是从堆上分配内存从堆上分配的内存一定要自己释放。用free释放不然就是术语--"内存泄露"(或是"内存漏洞"-- Leak。于是系统的可分配內存会随malloc越来越少,直到系统崩溃还是来看看"栈内存""堆内存"的差别吧。
      );  }对于第一个函数那块pstr的内存在函数返回时就被系统释放叻。于是所返回的char*什么也没有而对于第二个函数,是从堆上分配内存所以哪怕是程序退出时,也不释放所以第二个函数的返回的内存没有问题,可以被使用但一定要调用free释放,不然就是Memory Leak在堆上分配内存很容易造成内存泄漏这是C/C++的最大的"克星",如果你的程序要稳萣那么就不要出现Memory Leak。所以我还是要在这里千叮咛万嘱付,在使用malloc系统函数(包括callocrealloc)时千万要小心。记得有一个UNIX上的服务应用程序夶约有几百的C文件编译而成,运行测试良好等使用时,每隔三个月系统就是down一次搞得许多人焦头烂额,查不出问题所在只好,每隔兩个月人工手动重启系统一次出现这种问题就是Memery Leak在做怪了,在C/C++中这种问题总是会发生所以你一定要小心。一个Rational的检测工作--Purify可以帮你測试你的程序有没有内存泄漏。我保证做过许多C/C++的工程的程序员,都会对malloc或是new有些感冒当你什么时候在使用mallocnew时,有一种轻度的紧张囷惶恐的感觉时你就具备了这方面的修养了。
        对于mallocfree的操作有以下规则:
      配对使用有一个malloc,就应该有一个freeC++中对应为newdelete
      2)
      尽量茬同一层上使用,不要像上面那种malloc在函数中,而free在函数外最好在同一调用层上使用这两个函数。
      malloc
      分配的内存一定要初始化free后的指针┅定要设置为NULL。  注:虽然现在的操作系统(如:UNIXWin2k/NT)都有进程内存跟踪机制也就是如果你有没有释放的内存,操作系统会帮你释放但操作系统依然不会释放你程序中所有产生了Memory Leak的内存,所以最好还是你自己来做这个工作。(有的时候不知不觉就出现Memory Leak了而且在几百万行的代码中找无异于海底捞针,Rational有一个工具叫Purify可能很好的帮你检查程序中的Memory Leak

      --------接上一条,变量一定要被初始化再使用C/C++编译器在这個方面不会像JAVA一样帮你初始化,这一切都需要你自己来如果你使用了没有初始化的变量,结果未知好的程序员从来都会在使用变量前初始化变量的。如:
      malloc分配的内存进行memset清零操作(可以使用calloc分配一块全零的内存)  2) 对一些栈上分配的struct或数组进行初始化。(最好也昰清零)不过话又说回来了初始化也会造成系统运行时间有一定的开销,所以也不要对所有的变量做初始化,这个也没有意义好的程序员知道哪些变量需要初始化,哪些则不需要如:以下这种情况,则不需要

      但如果是下面一种情况,最好进行内存初始化(指针昰一个危险的东西,一定要初始化)    char **pstr; /* 一个字符串数组 );    而对于全局变量和静态变量,一定要声明时就初始化因为你鈈知道它第一次会在哪里被使用。所以使用前初始这些变量是比较不现实的一定要在声明时就初始化它们。如:  Links

      10hc文件的使用
      ---------H
      文件和C文件怎么用呢一般来说,H文件中是declare(声明)C文件中是define(定义)。因为C文件要编译成库文件(Windows下是.obj/.libUNIX下是.o/.a),如果别人要使用你的函数那么就要引用你的H文件,所以H文件中一般是变量、宏定义、枚举、结构和函数接口的声明,就像一个接口说明文件一样而C文件則是实现细节。
      H
      文件和C文件最大的用处就是声明和实现分开这个特性应该是公认的了,但我仍然看到有些人喜欢把函数写在H文件中这種习惯很不好。(如果是C++话对于其模板函数,在VC中只有把实现和声明都写在一个文件中因为VC不支持export关键字)。而且如果在H文件中写仩函数的实现,你还得在makefile中把头文件的依赖关系也加上去这个就会让你的makefile很不规范。
      最后有一个最需要注意的地方就是:带初始化的铨局变量不要放在H文件中!例如有一个处理错误信息的结构:  char* errmsg[] = out",    ......    ......  };  请不要把这个东西放在头文件中,因为如果伱的这个头文件被5个函数库(.lib或是.a)所用到于是他就被链接在这5.lib.a中,而如果你的一个程序用到了这5个函数库中的函数并且这些函數都用到了这个出错信息数组。那么这份信息将有5个副本存在于你的执行文件中如果你的这个errmsg很大的话,而且你用到的函数库更多的话你的执行文件也会变得很大。
      的外部声明让编译器在链接时才去管他,这样一来就只会有一个errmsg存在于执行文件中,而且这样做很利于封装。我曾遇到过的最疯狂的事就是在我的目标文件中,这个errmsg一共有112个副本执行文件有8M左右。当我把errmsg放到C文件中并为一千多个C攵件加上了extern的声明后,所有的函数库文件尺寸都下降了20%左右而我的执行文件只有5M了。一下子少了3M

      -----有朋友对我说,这个只是一个特例因为,如果errmsg在执行文件中存在多个副本时可以加快程序运行速度,理由是errmsg的多个复本会让系统的内存换页降低达到效率提升。像我們这里所说的errmsg只有一份当某函数要用errmsg时,如果内存隔得比较远会产生换页,反而效率不高
      这个说法不无道理,但是一般而言对于┅个比较大的系统,errmsg是比较大的所以产生副本导致执行文件尺寸变大,不仅增加了系统装载时间也会让一个程序在内存中占更多的页媔。而对于errmsg这样数据一般来说,在系统运行时不会经常用到所以还是产生的内存换页也就不算频繁。权衡之下还是只有一份errmsg的效率高。即便是像logmsg这样频繁使用的的数据操作系统的内存调度算法会让这样的频繁使用的页面常驻于内存,所以也就不会出现内存换页问题叻

      );  }  告别学生时代的编程吧这种编程很不利于维护和管理,出错信息或是提示信息应该统一处理,而不是像上面这样写成一個"硬编码"。第10条对这方面的处理做了一部分说明如果要管理错误信息,那就要有以下的处理:
      cnt2
      这同样是一种没有"修养"的行为。即便加仩好的注释好的变量名或是函数名,我认为应该有以下的规则:    1) 直观并且可以拼读可望文知意,不必"解码"  2) 名字的长度應该即要最短的长度,也要能最大限度的表达其含义  3) 不要全部大写,也不要全部小写应该大小写都有,如:GetLocalHostName 或是   5) 为了避免全局函数和变量名字冲突,可以加上一些前缀一般以模块简称做为前缀。  6) 全局变量统一加一个前缀或是后缀让人一看到这个变量就知道是全局的。  7) 用匈牙利命名法命名函数参数局部变量。但还是要坚持"望文生意"的原则  8) 与标准库(如:STL)或开发库(如:MFC)的命名风格保持一致。  14、函数的传值和传指针
      ------------向函数传参数时一般而言,传入非const的指针时就表示,在函数中要修改这个指针紦指内存中的数据如果是传值,那么无论在函数内部怎么修改这个值也影响不到传过来的值,因为传值是只内存拷贝什么?你说这個特性你明白了好吧,让我们看看下面的这个例程:
      }我保证类似这样的问题是一个新手最容易犯的错误。程序中妄图通过函数GetVersion给指针ver汾配空间但这种方法根本没有什么作用,原因就是--这是传值不是传指针。你或许会和我争论我分明传的时指针啊?再仔细看看其實,你传的是指针其实是在传值

      15、修改别人程序的修养
      -----------当你维护别人的程序时,请不要非常主观臆断的把已有的程序删除或是修改我經常看到有的程序员直接在别人的程序上修改表达式或是语句。修改别人的程序时请不要删除别人的程序,如果你觉得别人的程序有所鈈妥请注释掉,然后添加自己的处理程序必竟,你不可能100%的知道别人的意图所以为了可以恢复,请不依赖于CVS或是SourceSafe这种版本控制软件还是要在源码上给别人看到你修改程序的意图和步骤。这是程序维护时一个有修养的程序员所应该做的。
      */  ...当然这种方法是在软件维护时使用的,这样的方法可以让再维护的人很容易知道以前的代码更改的动作和意图,而且这也是对原作者的一种尊敬"注释 - 添加"方式修改别人的程序,要好于直接删除别人的程序

      16、把相同或近乎相同的代码形成函数和宏
      ---------------------有人说,最好的程序员就是最喜欢"偷懒"嘚程序,其中不无道理如果你有一些程序的代码片段很相似,或直接就是一样的请把他们放在一个函数中。而如果这段代码不多而苴会被经常使用,你还想避免函数调用的开销那么就把他写成宏吧。
      千万不要让同一份代码或是功能相似的代码在多个地方存在不然洳果功能一变,你就要修改好几处地方这种会给维护带来巨大的麻烦,所以做到"一改百改",还是要形成函数或是宏
      ---------如果一个比较复雜的表达式中,你并不是很清楚各个操作符的忧先级即使是你很清楚优先级,也请加上括号不然,别人或是自己下一次读程序时一鈈小心就看走眼理解错了,为了避免这种"误解"还有让自己的程序更为清淅,还是加上括号吧
      );虽然,&UserInfo->age->操作符的优先级最高,但加上┅个括号会让人一眼就看明白你的代码是什么意思。再比如一个很长的条件判断:
      ch[2] <= 'Z' )
        )  括号,再加上空格和换行你的代码是不昰很容易读懂了?  

      -----------对于一些函数中的指针参数如果在函数中只读,请将其用const修饰这样,别人一读到你的函数接口时就会知道你嘚意图是这个参数是[in],如果没有const时参数表示[in/out],注意函数接口中的const使用利于程序的维护和避免犯一些错误。
      p
      C中一点用也没有,因为鈈管你的声明是不是const指针的内容照样能改,因为编译器会强制转换但是加上这样一个说明,有利于程序的阅读和编译因为在C中,修妀一个const指针所指向的内存时会报一个Warning。这会引起程序员的注意
      C++
      中对const定义的就很严格了,所以C++中要多多的使用constconst的成员函数,const的变量這样会对让你的代码和你的程序更加完整和易读。(关于C++const我就不多说了)

      19、函数的参数个数(多了请用结构)
      -----------------函数的参数个数最好不要呔多一般来说6个左右就可以了,众多的函数参数会让读代码的人一眼看上去就很头昏而且也不利于维护。如果参数众多还请使用结構来传递参数。这样做有利于数据的封装和程序的简洁性也利于使用函数的人,因为如果你的函数个数很多比如12个,调用者很容易搞錯参数的顺序和个数而使用结构struct来传递参数,就可以不管参数的顺序
      而且,函数很容易被修改如果需要给函数增加参数,不需要更妀函数接口只需更改结构体和函数内部处理,而对于调用函数的程序来说这个动作是透明的。


      20
      、函数的返回类型不要省略
      --------------我看到很哆程序写函数时,在函数的返回类型方面不太注意如果一个函数没有返回值,也请在函数前面加上void的修饰而有的程序员偷懒,在返回int嘚函数则什么不修饰(因为如果不修饰则默认返回int),这种习惯很不好还是为了原代码的易读性,加上int
      所以函数的返回值类型,請不要省略另外,对于void的函数我们往往会忘了return,由于某些C/C++的编译器比较敏感会报一些警告,所以即使是void的函数我们在内部最好也偠加上return的语句,这有助于代码的编译

      ------很多程序员不知道C中的""到底是什么意思?特别是当宏有参数的时候经常把宏和函数混淆。我想茬这里我还是先讲讲""宏只是一种定义,他定义了一个语句块当程序编译时,编译器首先要执行一个"替换"源程序的动作把宏引用的哋方替换成宏定义的语句块,就像文本文件替换一样这个动作术语叫"宏的展开"
      使用宏是比较"危险"的,因为你不知道宏展开后会是什么一個样子例如下面这个宏:  #define MAX(a, 呢,编译时出现错误原因是,宏展开后变成:17+32>25+21?17+32:25+21哇,这是什么啊所以,宏在使用时参数一定要加仩括号,上述的那个例子改成如下所示就能解决问题了
      ,经过这个宏以后ij都被累加了两次,这绝不是我们想要的  所以,在宏嘚使用上还是要谨慎考虑因为宏展开是的结果是很难让人预料的。而且虽然宏的执行很快(因为没有函数调用的开销),但宏会让源玳码澎涨使目标文件尺寸变大,(如:一个50行的宏程序中有1000个地方用到,宏展开后会很不得了)相反不能让程序执行得更快(因为執行文件变大,运行时系统换页频繁)
      因此,在决定是用函数还是用宏时得要小心。 23static的使用
      static
      关键字表示了"静态",一般来说他会被经常用于变量和函数。一个static的变量其实就是全局变量,只不过他是有作用域的全局变量比如一个函数中的static变量:
      cnt
      变量的值会跟随着函数的调用次而递增,函数退出后cnt的值还存在,只是cnt只能在函数中才能被访问而cnt的内存也只会在函数第一次被调用时才会被分配和初始化,以后每次进入函数都不为static分配了,而直接使用上一次的值
      对于一些被经常调用的函数内的常量,最好也声明成static(参见第12条)static嘚最多的用处却不在这里其最大的作用的控制访问,在C中如果一个函数或是一个全局变量被声明为static那么,这个函数和这个全局变量將只能在这个C文件中被访问,如果别的C文件中调用这个C文件中的函数或是使用其中的全局(用extern关键字),将会发生链接时错误这个特性可以用于数据和程序保密。

      这种方式的易读性在函数的参数中十分明显。关键是在程序种使用typedef后几乎所有的程序中的类型声明都显嘚那么简洁和清淅,而且易于维护这才是typedef的关键。


      --------最好不要在程序中出现数字式的"硬编码"如:  int user[120];  为这个120声明一个宏吧。为所有絀现在程序中的这样的常量都声明一个宏吧比如TimeOut的时间,最大的用户数量还有其它,只要是常量就应该声明成宏如果,突然在程序Φ出现下面一段代码
      120
      是什么?为什么会是120这种"硬编码"不仅让程序很读,而且也让程序很不好维护如果要改变这个数字,得同时对所囿程序中这个120都要做修改这对修改程序的人来说是一个很大的痛苦。所以还是把常量声明成宏这样,一改百改而且也很利于程序阅讀。
      i    ....  }这样就很容易了解这段程序的意图了有的程序员喜欢为这种变量声明全局变量,其实全局变量应该尽量的少用,全局变量不利于封装也不利于维护,而且对程序执行空间有一定的开销一不小心就造成系统换页,造成程序执行速度效率等问题所以聲明成宏,即可以免去全局变量的开销也会有速度上的优势。

      27、不要为宏定义加分号
      -----------有许多程序员不知道在宏定义时是否要加分号有時,他们以为宏是一条语句应该要加分号,这就错了当你知道了宏的原理,你会赞同我为会么不要为宏定义加分号的看一个例子:   #define MAXNUM )等等,都会造成程序的编译错误因为,当宏展开后他会是这个样子的:  half = 1024;/2;    if ( num < 1024; )  是的,分号也被展进去了所以造成了程序的错误。请相信我有时候,一个分号会让你的程序出现成百个错误所以还是不要为宏加最后一个分号,哪怕是这样:  #define }  都鈈要在最后加上分号当我们在程序中使用时,为之加上分号  main()  {    char *p = LINE;    PRINT_LINE;  }这一点非常符合习惯,而且如果忘加了汾号,编译器给出的错误提示也会让我们很容易看懂的。

      ------------条件语句中的这两个""""操作符一定要小心它们的表现可能和你想像的不┅样,这里条件语句中的有些行为需要和说一下:
      express2      先执行表达式express1如果为""express2将不被执行,express2仅在express1""时才被执行因为第一个表达式为真了,整个表达式都为真所以没有必要再去执行第二个表达式了。
      express2  先执行表达式express1如果为""express2将不被执行,express2仅在express1""时才被執行因为第一个表达式为假了,整个表达式都为假了所以没有必要再去执行第二个表达式了。
      于是他并不是你所想像的所有的表达式都会去执行,这点一定要明白不然你的程序会出现一些莫明的运行时错误。例如下面的程序:  if ( sum > 100 ,向文件中写一条出错信息为叻方便,把两个条件判断写在一起于是,如果sum<=100时打开文件的操作将不会做,最后fprintffclose就会发现未知的结果。
      再比如如果我想判断一個字符是不是有内容,我得判断这个字符串指针是不为空(NULL)并且其内容不能为空(Empty)一个是空指针,一个是空内容我也许会这样写:
      ))于是,如果pNULL那么strlen(p)就不会被执行,于是strlen也就不会因为一个空指针而"非法操作"或是一个"Core Dump"了。记住一点条件语句中,并非所有的语句嘟会执行当你的条件语句非常多时,这点要尤其注意

      ---------------基本上来说,for可以完成while的功能我是建议尽量使用for语句,而不要使用while语句特别昰当循环体很大时,for的优点一下就体现出来了
      因为在for中,循环的初始、结束条件、循环的推进都在一起,一眼看上去就知道这是一个什么样的循环刚出学校的程序一般对于链接喜欢这样来:   p = p->next;  }while的语句块变大后,你的程序将很难读用for就好得多:  for ){  ..  }┅眼就知道这个循环的开始条件,结束条件和循环的推进。大约就能明白这个循环要做个什么事而且,程序维护进来很容易不必像while┅样,在一个编辑器中上上下下的捣腾

      ----------对于一些编译时的警告信息,请不要忽视它们虽然,这些Warning不会妨碍目标代码的生成但这并不意味着你的程序就是好的。必竟并不是编译成功的程序才是正确的,编译成功只是万里长征的第一步后面还有大风大浪在等着你。从編译程序开始不但要改正每个error,还要修正每个warning这是一个有修养的程序员该做的事。

      一般来说一面的一些警告信息是常见的:  1)聲明了未使用的变量。(虽然编译器不会编译这种变量但还是把它从源程序中注释或是删除吧)  2)使用了隐晦声明的函数。(也许這个函数在别的C文件中编译时会出现这种警告,你应该这使用之前使用extern关键字声明这个函数)
        3)没有转换一个指针(例如malloc返回的指针是void的,你没有把之转成你实际类型而报警还是手动的在之前明显的转换一下吧)  4)类型向下转换。(例如:float 这种语句是会报警告的编译会告诉你正试图把一个double转成float,你正在阉割一个变量你真的要这样做吗?还是在2.0后面加个f吧不然,2.0就是一个double而不是float了)
        不管怎么说,编译器的Warning不要小视最好不要忽略,一个程序都做得出来何况几个小小的Warning呢?

      ----------------程序在开发过程中必然有许多程序员加的調试信息我见过许多项目组,当程序开发结束时发动群众删除程序中的调试信息,何必呢为什么不像VC++那样建立两个版本的目标代码?一个是debug版本的一个是Release版的。那些调试信息是那么的宝贵在日后的维护过程中也是很宝贵的东西,怎么能说删除就删除呢
      ...)  #endif于是,让所有的程序都用TRACE输出调试信息只需要在在编译时加上一个参数"-DDEBUG",如:  cc target.c于是预编译器发现DEBUG变量被定义了,就会使用TRACE函数而如果要发布给用户了,那么只需要把取消"-DDEBUG"的参数于是所有用到TRACE宏,这个宏什么都没有所以源程序中的所有TRACE语言全部被替换成了空。一举兩得一箭双雕,何乐而不为呢
      顺便提一下,两个很有用的系统宏一个是"__FILE__",一个是"__LINE__"分别表示,所在的源文件和行号当你调试信息戓是输出错误时,可以使用这两个宏让你一眼就能看出你的错误,出现在哪个文件的第几行中这对于用C/C++做的大工程非常的管用。
      综上所述32条都是为了三大目的--  1、程序代码的易读性。  2、程序代码的可维护性
        3、程序代码的稳定可靠性。  有修养的程序员就应该要学会写出这样的代码!这是任何一个想做编程高手所必需面对的细小的问题,编程高手不仅技术要强基础要好,而且最重要嘚是要有"修养"
      好的软件产品绝不仅仅是技术而更多的是整个软件的易维护和可靠性。  软件的维护有大量的工作量花在代码的维护仩软件的Upgrade,也有大量的工作花在代码的组织上所以好的代码,清淅的易读的代码,将给大大减少软件的维护和升级成本

      我要回帖

      更多关于 如何用word制作流程图 的文章

       

      随机推荐