首页资源分类编程语言C/C++ > 模块化编程教程

模块化编程教程

已有 454692个资源

下载专区

上传者其他资源

文档信息举报收藏

标    签: 模块化

分    享:

文档简介

非常好的教程,适合初学者的

文档预览

一坚科技 STC15 单片机实战指南(C 语言版) 从 51 单片机 DIY、四轴飞行器到优秀产品设计 残弈悟恩(小刘老师) 2016 官 方 淘 宝 店 铺 : HTTP://FSMCU.TAOBAO.COM STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 郑重声明 《STC15 单片机实战指南(C 语言版)》 一书以一坚科技研发的飛天三號(FSST15-V1.0) 实验板为硬件平台,以《深入浅出玩转 STC15 单片机》为配套视频,由清华大学出版社权 威出版,并且分别是 STC 官方大学计划和高校高性能联合实验室推荐教程和视频,版权归 作者和清华大学出版社所有。 本资料以个人学习、工作经验以及宏晶科技单片机技术为素材,以单片机初学者、单 片机项目开发者为对象,教大家如何走进单片机,继而达到开发工程项目为目的。限于时 间和水平关系,资料中难免有过失之处,望各位高手批评指教,多多拍砖,拍累了,你们 休息,我继续上路。 现已连载的方式免费共享于各大电子网站,供单片机新手们参考学习,可以自由下载 传阅,但未经作者许可,不得用于任何商业目的,转载请注明出处:残弈悟恩 (www.ieeBase.net)。最终完整版以出版社所出书籍为准。 飛天三號单片机实验板 ~ 1 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 飛天三號(FSST15-V1.0)实验板 STC 高性能联合实验室专用试验箱学生实验补充开发板 STC 官方推荐书籍《STC15 单片机实战指南》配套开发板 STC 大学计划推荐视频《深入浅出玩转 STC15 单片机》配套开发板 让爱充满大地——花 1 秒时间,拯救 1 个人,传递 1 份爱 声明:只是残弈悟恩爱心的喷发,我得不到一分钱,各位不要多想,谢谢! 你知道吗?在非洲北边的某个地区,每一秒都有许许多多的人正在挨饿,每一天至少有 一位儿童死于营养不足。你的一次点击就能让某位穷人得到 1.1 杯食物。当然你可以不相信 有这样的链接或者是骗点击什么的。事实上,网站确实是帮穷人得 1.1 杯食物的,只要你点 进去点击一下中间的黄色按钮,就会出来一系列介绍各种商品的网页(绝对免费的并且不会 下载任何软件,也不会有电脑病毒),同时也会有人因为您的一次点击而得到 1.1 杯食物, 食物是由商家提拱的,但爱心却是您献出的。如果你觉得残弈悟恩在忽悠大家,你不妨可以 在网上查一下是真与假。 看到这本资料的朋友多数都是电子爱好者、单片机初学者,或者干电子这一行的,管你 是穷学生还是穷工人,只要能上网,只要愿花一秒种就可以了。人生在世,有两件事不能等: 一、孝顺;二、行善。无论你是 LED 小灯、普通灯泡也好,还是荧光灯也罢,最重要就是 要懂得用自身的光去照耀别人,光的强度并不重要。 点击链接: http://www.thehungersite.com/clickToGive/home.faces?siteId=1&link=ctg_ths_home_from_ths_thankyou_sitenav 飛天三號单片机实验板 ~ 2 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 第 4 章 排兵布阵 步步扣杀:模块化编程 当一个项目小组做一个相对比较复杂的工程时,就需要小组成员分工合作,一起完成项 目,意味着不再是某人独自单干,而是要求小组成员各自负责一部分工程。比如你可能只是 负责通讯或者显示某一块,这个时候,就应该将自己的这一块程序写成一个模块,单独调试, 留出接口供其它模块调用。最后,小组成员都将自己负责的模块写完并调试无误后,由项目 组长进行综合调试,像这些场合就要求程序必须模块化。模块化的好处非常多,不仅仅是便 于分工,它还有助于程序的调试,有利于程序结构的划分,还能增加程序的可读性和可移植 性。 对于刚入门的读者来说,还没发现前面第三章程序的不完美之处,程序当然没有错的, 只是在移植、管理方面,存在一些不足,因为部分程一遍又一遍、一页又一页的再重复,鉴 于这些不足之处,从本书的开始阶段,笔者就计划将模块化编程列为一项艰巨而有持久的战 役,请读者做好准备,跟随笔者一起揭开这块神秘的面纱,一窥其真面目。 4.1 夯实基础——数值的换算以及逻辑运算 单片机的学习中,常常需要和数值、逻辑运算打交道,原因是单片机在某种程度上可以 归结到数字电路中,读者应该知道,数字电路中,数值的计算、存储和取值都是以“0”和 “1”的形式来体现。所以在真正开始单片机学习之前,掌握基本的数值运算和逻辑运算是 必不可少的。 4.1.1 各进制之间的换算关系 二进制、十进制、十六进制。八进制,不常用。敢不敢相信,大家看到电影、图片或听 到歌曲在电脑中都是用二进制存储的,同理,学习单片机时,在 12864 上所显示的图片也是 二进制存储的。因为电脑、单片机这些“大傻瓜”不认识别的,只认识“1 和 0”,所以得学 好二进制,乍一听,好像不是很难,1、0 小学生都认识的。 十进制数有 0~9,共 10 个,逢十进一;二进制数 0、1 共两个,逢二进一;十六进制数 有 0~9、外加 A~F(a~f),总共 16 个数,逢十六进一。二进制书写前需加 0b,十六进制需 加 0x。十六进制数是和四为一,就是 4 个二进制组成一个十六进制数,于是它的每一位有 0b0000~0b1111 共计 16 个值。这三个数之间对于关系见表 4-1 所示。 借此为大家推荐一本王玮编著的《感悟设计——电子设计的经验与哲理》一书,我甚是 喜欢,他里面写的好多东西,确实值得我们好好学习,虽然有些内容我还没涉及到,但这丝 毫不影响我对这本书的喜欢。这本书除了理论、经验,还有他发明的指算(二、十进制之间 的转换)。我就引用到这里,希望能和大家共勉。 一只手掌 5 个手指,假设我们规定拇指、食指、中指、无名指、小指分别代表 1、2、4、 8、16 这 5 个数(顺序倒过来或搅乱也可以,规定好就行),那么,在 0~31 以内的各个整数 都可以通过手指的屈伸来表示了。例如划拳(民间喝酒的一种方法)出的二,就是十进制数 5(1+4)。通常做的“OK 手势”表示的就是 28(4+8+16),如此等等。这么一说大家可能会 觉得没意思,但当大家用熟练了,就会觉得很好玩。 表 4-1 部分二进制、十进制、十六进制之间的对应关系 十进制 二进制 十六进制 0 0b0000 0000 0x00 1 0b0000 0001 0x01 2 0b0000 0010 0x02 3 0b0000 0011 0x03 飛天三號单片机实验板 ~ 3 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 4 5 6 7 8 9 10 11 12 13 14 15 …… 255 0b0000 0100 0b0000 0101 0b0000 0110 0b0000 0111 0b0000 1000 0b0000 1001 0b0000 1010 0b0000 1011 0b0000 1100 0b0000 1101 0b0000 1110 0b0000 1111 …… 0b1111 1111 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F …… 0xFF 4.1.2 数字电路和 C 语言中的逻辑运算 二进制的逻辑运算,又称其为布尔运算。无论 C 语言中,还是数字电路中,逻辑运算不 可缺。在逻辑范畴中,只有“真”和“假”。先来目睹一下 C 语言中的逻辑运算,“0”为“假”, “非 0”为真,不要理解为只有 1 是“真”,2、-43、100 同样也是真。 (1)逻辑运算(是按整体运算),通常叫做逻辑运算符。 &&(and):逻辑与,只有同为真时结果才为真,近似于乘法。 ||(or):逻辑或,只有同为假时结果才为假,近似于加法。 !(not):逻辑非,条件为真,结果为假,近似于相反数。 (2)逻辑运算(按每个位来运算),通常叫做位运算符。 &:按位与,变量的每一位都参与(下同),例如:A = 0b0101 1010,B = 0b1010 1010, 则 A & B = 0b0000 1010。 |:按位或。则 A | B = 0b1111 1010。 ~:按位取反。则~A = 0b1010 0101。 ^: 按位异或,异或的意思是,如果运算双方的值不同(即相异),则结果为真,双方 值相同则结果为假。这样 A^B = 0b1111 0000。 数字电路的逻辑运算。读者以后看资料或数据手册时,经常会遇到一些逻辑运算符号, 笔者列举到这里,以便读者以后查阅。所有符合如表 4-2 所示。 表 4-2 数字逻辑运算符合 序号 运算名称 国际标准符合 国外流行符号 1 与门 2 或门 3 非门 飛天三號单片机实验板 ~ 4 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 4 或非门 5 与非门 6 同或门 7 异或门 集电极开漏 OC 门 8 ---- 漏极开漏 OD 门 4.2 简述单片机开发流程 世间万物、世间琐事,无一不需要遵循流程,更无一离不开工具。做单片机项目同样要 遵循一定的开发流程,同样需要一定的工具,由于单片机的生产厂家不同、型号不同、开发 工具有所不同,从而导致开发流程有别,但基本的软件开发环境必须掌握,开发语言的基础 必须有,硬件环境不可缺,接下来简要说明一下单片机的开发流程,顺带介绍各个环节所需 的开发工具。具体项目的开发流程和注意事项,我们将会在第 20 章学习,因为对于刚入门 的菜鸟来说,谈项目开发有点不切实际,因此读者还是先打好基础,后面章节有更精彩的知 识等着你。 1.产品需求 根据市场需求或公司安排,确定开发什么产品。开发人员需要和产品需求方沟通,明确 客户的需求,对即将开发的产品有一个总体的印象。 2.产品立项 这时产品已经确定要开发,需要立项,开发人员可能要填写立项相关文件,这个可能涉 及项目奖金哦… 3.设计机构 一般由高级系统架构师完成整个产品的系统设计,并做系统结构框图。接着选择处理器, 是 8 位、16 位还是 32 位的,之后软(软件指上位机应用软件,不是单片机内部程序)、硬 件分工,确定各个工程师的任务。 4.攻关技术难点 这里需要技术牛人(软硬通吃)出马了,就是把整个系统比较难或不能确定的部分,先 进行研究实验,以确认不会因为这些部分导致项目无法实现。 5.硬件设计 根据功能确定显示(用液晶还是数码管)、存储器(空间大小)、定时器、中断、通信 (RS-232、RS-485、USB)、打印、A/D、D/A 及其他 I/O 口操作。接着绘制原理图、结构图、 PCB。最后选购元器件、焊接电路板、组装、测试。这部分是硬件工程师的强项了。 飛天三號单片机实验板 ~ 5 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 6.软件设计 终于要编程了,到单片机工程大显身手的时候了。建立数学模型,确定算法及数据结构; 进行资源分配及结构设计;绘制流程图,结合流程图设计并编写各子程序模块;最后仿真、 调试、固化。 7. 样机联试 这时软、硬件结合起来调试。测试硬件系统各个模块工作是否正常,软件运行是否稳定、 能否满足要求;进行一些老化、高低温测试,震动实验等。 8.小批量试产 这时,产品都搞完了,不过开发人员需要提供测试报告、使用说明等文档;制定产生工 艺流程,形成工艺,进入小批量生产;接着送样或投放市场,让客户检验是否是金子;依客 户反映来升级产品。 9.产品量产 产品量产,并销售于市场,若有问题,一般由售后来处理,若搞不定,还得开发人员出 马,毕竟开发人员熟悉产品呀。 4.3 Keil5 的进阶应用——建模 由于这里的模块化编程是基于 Keil5 的,因此这里先来讲述 Keil5 中如何“建模”,或者 说自己的工程如何管理比较妥当。接下来通过以下几个步骤来讲述 Keil5 的模块化编程。 1.新建工程文件夹 读者可能看到这个标题有点“发疯”,新建文件夹谁不会啊,还需讲解吗?可对于新手 来说,还真不一定。 在你项目管理盘符(E:\...)中新建文件夹,并命名为:Ex05_模块化编程之 LED 小灯(当 然也可以是别的)。打开此文件夹,接着在下面再新建四个文件夹,分别命名为:“FsBSP”、 Project、STCLib、USER,之后再在 FsBSP 文件夹下新建两个文件夹,并分别命名为:“inc”、 “src”,这么多文件夹有何用,稍等自有答案。 2.新建工程 打开 Keil5 软件,选择 Project→New uVision Project 菜单项,此时弹出工程保存对话 框,这里定位到上面刚新建的工程文件下(如:E:\ ...),输入工程名:Fs_AllLedFlash,具 体建立过程请参考第二章内容,此时的工程只是一个有“骨架”,没有“血肉”空架子,如 图 4-1,接下来只待我们来填补。 飛天三號单片机实验板 ~ 6 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 图 4-1 Fs_AllLedFlash 空工程图 3.新建并拷贝文件 这里读者需要注意的是,以下文件我们即可以在 Keil5 中直接新建或者加入,这种方法 我们前面一直在用,这里我们先在以上新建的文件夹中直接新建我们所需的文件,具体过程 如下(顺序前后当然可以调换): (1)在“E:\ Ex05_模块化编程之 LED 小灯\FsBSP\inc”文件夹下新建两个文件,分别命 名为:“FsBSP_AllLedFlash.h”、“FsBSP_Delay.h”,意思是,我们将以后所有例程与板级支持包 (Board Surport Packet)有关的头文件全部放置到这个文件夹下。读者需要注意的是,这里 文件的后缀名一定是“.h”,也即头文件,后面自有细述。 (2)在“E:\ Ex05_模块化编程之 LED 小灯\FsBSP\src”文件夹下新建两个文件,分别命 名为:“FsBSP_AllLedFlash.c”、“FsBSP_Delay.c”,意思是,我们将以后所有例程与板级支持包 有关的头文件全部放置到这个文件夹下。读者需要注意的是,这里文件的后缀名一定是“.c”, 也即源文件,这样,一个“.c”文件就对应一个“.h”文件,就可方便、快捷的管理我们的 驱动源码。以后所有的“.c”和“.h”文件,我们将在 Keil5 中建立,只需保存到这两个文件 下即可。 (3)从 X:\Keil5\C51\INC\STC(X 代表读者的 Keil5 安装盘符)文件下拷贝“STC15.H” 文件到“E:\ Ex05_模块化编程之 LED 小灯\STCLib”文件下。这是 STC 单片机所用的头文件, 前面我们有所讲述。 (4)在“E:\Ex05_模块化编程之 LED 小灯\USER”文件下新建文件“main.c”,这就是我 们要用的主函数文件,名称可以随意,但是扩展名一定为“.c”文件。 4.利用 Manage components Items 对话框来管理工程 单价如图 4-1 箭头所指的工具按钮,打开“Manage components Items”工程组件对话框, 如图 4-2 所示。双击如图 4-2 所示的小“1”处,修改“Project Targets”为“FsST15”;同样 双击修改其第一个“Groups”为“FsBSP”,紧接着单击小“2”新建一个组,并命名为“USER”。 飛天三號单片机实验板 ~ 7 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 图 4-2 “Manage components Items”工程组件对话框 选中“FsBSP”组,单击小“3”所示的“Add Files”按钮,进入文件添加选择对话框, 这里我们先定位到我们的“E:\ Ex05_模块化编程之 LED 小灯\FsBSP\src”文件夹下,之后选 中这两个文件,接着单击“Add”按钮;用同样的方法,选中“USER”组件,并添加“main.c” 文件,这样就可得到如图 4-3 所示的工程界面图。 图 4-3 建立好的工程界面图 这样模块化工程的建立就讲述完毕,接下来的任务就是编写代码,也就是让各个“.c”、 “.h”能表里如一,别表面风光、内心空虚。 5.源文件路径的添加 如果说我们没有用模块化编程,而是简简单单的一个“.c”文件,那么此时只需添加文 件的内容就是,可在进行模块化编程,特别是多个文件夹下存放文件时,有很重要的一步, 那就是路径的添加,接下来分两步来说明。 (1)打开“Target Options”对话框,并选择“C51”选项卡,此时界面如图 4-4 所示, 飛天三號单片机实验板 ~ 8 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 注意笔者添加的圆角矩形框 。 图 4-4 Options 路径添加对话框 (2)单击矩形框后的“<2>”处的浏览选项按钮,进入“Floder Setup”对话框。接着 单击如图 4-5(左图)所示的“New”处,此时该对话框会变成右图所示的摸样,接着单击 路径浏览框,这时会弹出如图 4-5 所示的浏览文件夹,这里定位到自己的“inc”文件夹路径 下(E:\ Ex05_模块化编程之 LED 小灯\FsBSP\inc),用同样的方式,分别添加“src”、“STCLib”、 “USER”文件夹的路径,接着单击“确定”按钮,最后再单击“OK”按钮,这样路径就添 加完成了,添加完整的路径如图 4-6 所示。 图 4-5 新建路径对话框 飛天三號单片机实验板 ~ 9 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 图 4-6 路径选择框 4.4 单片机之模块化编程 接下来的这部分,对于初学者,或许比较难理解,因为涉及面广,内容复杂且比较零散, 但如果读者能掌握这部分,那对于以后的编程将有很大的帮助和提升。 4.4.1 借说明开头 说明一:模块即是一个.c 和一个.h 的结合,头文件(.h)是对该模块的声明。 说明二:某模块提供给其他模块调用的外部函数以及数据需在所对应的.h 文件中冠以 extern 关键字来声明。 说明三:模块内的函数和变量需在.c 文件开头处冠以 static 关键字声明。 说明四:永远不要在.h 文件中定义变量。 先解释一下说明中的两个关键词:定义和声明。相信读者都是学过 C 语言的,本应该对 这两个词理解的很透彻,可笔者在培训时发现,好多人都搞不清楚,都是凭着感觉写的,高 兴了就用定义,不高兴了就用声明,这样做当然是不对的,换句话说是错的。 强势 X 入广告:什么是定义和声明? 所谓的定义就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名 字,这个名字就是我们经常所说的变量名或者对象名。但注意,这个名字一旦和这块内存匹 配起来(可以想象是这个名字嫁给了这块空间,没有要彩礼啊),它们就同生共死,终生不 离不弃,并且这块内存的位置也不能被改变。一个变量或对象在一定的区域内(比如函数内) 只能被定义一次,如果定义多次,编译器会提示你重复定义同一个变量或对象。 什么是声明?声明确切的说应该有两重含义: (1)告诉编译器,这个名字已经匹配到一块内存上了(伊人已嫁,吾将何去何从?何 以解忧,唯有稀粥),下面的代码用到变量或对象是在别的地方定义的。声明可以出现多次。 (2)告诉编译器,我这个名字我先预定了,别的地方再也不能用它来作为变量名或对 象名。比如你在图书馆自习室的某个座位上放了一本书,表明这个座位已经有人预订,别人 再也不允许使用这个座位。其实这个时候你本人并没有坐在这个座位上。这种声明最典型的 例子就是函数参数的声明,例如:void fun(int i, char c)。 那他们的区别也很清晰了。记住,定义声明最重要的区别:定义创建了对象并为这个对 飛天三號单片机实验板 ~ 10 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 象分配了内存,声明没有分配内存(一个抱伊人,一个喝稀粥,^_^)。 4.4.2 用实践解释 有了以上的概述,接下来就请读者跟随残弈悟恩一起享受模块化编程这顿大餐吧。说明 一概括了模块化的实现方法和实质:将一个功能模块的代码单独编写成一个.c 文件,然后把 该模块的接口函数放在.h 文件中。这里就分别来介绍一下它们里面究竟该包括什么,又不该 包括什么。 1.源文件.c 提到 C 语言源文件,大家都不会陌生。因为我们平常写的程序代码几乎都在这个.C 文 件里面。编译器也是以此文件来进行编译并生成相应的目标文件。作为模块化编程的组成基 础,所有要实现功能源代码均在这个文件里。理想的模块化应该可以看成是一个黑盒子,即 只关心模块提供的功能,而不予理睬模块内部的实现细节。好比读者买了一部手机,只需会 用手机提供的功能即可,而不需要知晓它是如何把短信发出去的,又是如何响应按键输入的, 这些过程对用户而言,就是一个黑盒子。 在大规模程序开发中,一个程序由很多个模块组成,很可能,这些模块的编写任务被分 配给不同的人。例如当读者在编写模块时很可能需要用到别人所编写模块的接口,这个时候 读者关心的是它的模块实现了什么样的接口,该如何去调用,至于模块内部是如何组织、实 现的,读者无需过多关注。特此说明,为了追求接口的单一性,把不需要的细节尽可能对外 屏蔽起来,只留需要的让别人知道。 Eg:不知读者发现了,第 3 章共用了一个比较多的函数“DelayMS()”,那这里就先拿这个 函数来开刀,写个 FsBSP_Delay.c 源文件,具体代码如下: #include "FsBSP_Delay.h" /* ****************************************************************** * 函数名称:DelayMS() * 入口参数:ms,要延时的 ms 数 * 出口参数:无 * 函数功能:不精确延时 ***************************************************************** */ void DelayMS(unsigned int ms) { unsigned int i; do{ i = MAIN_Fosc / 13000; while(--i); }while(--ms); } 这里需要向读者介绍的是,MAIN_Fosc 在头文件中用宏定义来完成,也即我们所选择的 CPU 运行频率,这个前面有所介绍;再者,为何除以 1300,这个是经验和测试值,读者不 需过多理会。 2.头文件.h 谈及到模块化编程,必然会涉及到多文件编译,也就是工程编译。在这样的一个系统中, 往往会有多个 C 文件,而且每个 C 文件的作用不尽相同。在我们的 C 文件中,由于需要对外 飛天三號单片机实验板 ~ 11 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 提供接口,因此必须有一些函数或变量需提供给外部其它文件进行调用。例如上面新建的 FsBSP_Delay.c 文件,提供最基本的延时功能函数。 void DelayMS(unsigned int ms); 而在另外一个文件(eg: FsBSP_AllLedFlash.c)中需要调用此函数,那该如何做呢? 兵来将挡、水来土淹,头文件的作用正是在此。具体过程是先创建一个 FsBSP_Delay.h 头文 件,在该头文件中对 DelayMS()函数进行封装(声明),这种封装的内容不应包含任何实质 性的函数代码。有了这样一个封装好的接口文件,每当 FsBSP_AllLedFlash.c 文件需要调用 DelayMS()函数时,直接在 FsBSP_AllLedFlash.c 中包含 FsBSP_Delay.h 头文件即可。读者 可将头文件形象的理解为连接 FsBSP_Delay.c 和 FsBSP_AllLedFlash.c 的桥梁。同时该文件 也可以包含一些宏定义以及结构体的信息,离开了这些信息,很可能就无法正常使用接口函 数或者是接口变量。但是总的原则是:不该让外界知道的信息就不应该出现在头文件里,而 外界调用模块内接口函数或者是接口变量所必须的信息就一定要出现在头文件里,否则外界 就无法正确调用。因而为了让外部函数或者文件调用我们提供的接口功能,就必须包含我们 提供的这个接口描述文件----头文件。同时,我们自身模块也需要包含这份模块头文件(因 为其包含了模块源文件中所需要的宏定义或者是结构体),好比三方协议,除了给学校、公 司有之外,自己总得留一份吧。下面我们来定义这个头文件,一般来说,头文件的名字应该 与源文件的名字保持一致,这样便可清晰的知道哪个头文件是哪个源文件的描述。 于是便得到了 FsBSP_Delay.c 如下的 FsBSP_Delay.h 头文件,具体代码如下: #ifndef __FsBSP_Delay_H__ #define __FsBSP_Delay_H__ #define MAIN_Fosc 22118400 extern void DelayMS(unsigned int ms); #endif 这里详细解释以下三点。 (1)“.c”源文件中不想被别的模块调用的函数、变量就不要出现在.h 文件中。 (2)“.c”源文件中需要被别的模块调用的函数、变量就声明现在.h 文件中。例如 void DelayMS(unsigned int ms)函数,这与以前所写源文件中的函数声明有些类似,为何没说一样 了,因为前面加了修饰词 extern,表明是一个外部函数。通俗点,就像公交车一样,谁都 可以坐(没钱人当然坐不了,例如残弈悟恩)。 特别提醒,在 Keil5 编译器中,extern 这个关键字即使不声明,编译器也不会报错,且 程序运行良好,但不保证使用其它编译器也如此。因此,强烈建议加上,养成良好的编程习 惯也不是一件坏事。 (3)1、2、8 行是条件编译和宏定义,目的是为了防止重复定义。假如有两个不同的 源文件都需要调用 void DelayMS(unsigned int ms)这个函数,他们分别都通过#include “FsBSP_Delay.h”把这个头文件包含进去。在第一个源文件进行编译时候,由于没有定义 过 __FsBSP_Delay_H__ , 因 此 #ifndef __FsBSP_Delay_H__ 条 件 成 立 , 于 是 定 义 __FsBSP_Delay_H__并将下面的声明包含进去。在第二个文件编译时,由于第一个文件包含 的时候,已经将__FsBSP_Delay_H__定义过了。因而此时#ifndef __FsBSP_Delay_H__不成立, 整个头文件内容就不再被包含。假设没有这样的条件编译语句,那么两个文件都包含了 extern void DelayMS(unsigned int ms),就会引起重复包含的错误。 特别说明,可能新手们看到 DELAY 前后的这些“__”、“_”时,又会模糊一阵,事实 飛天三號单片机实验板 ~ 12 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 上这又是一只“纸老虎”,看着吓人,一捅就破。举几个例 子:FsBSP_Delay_H __、 FsBSP_Delay_H、FsBSP_Delay_H、____ FsBSP_Delay_H、__ FsBSP_Delay_H,经调试,这些版 本都是对的,所以,请读者自便,怎么高兴怎么来,残弈悟恩这么(__FsBSP_Delay_H__) 写也只是出于编程习惯。 3.位置决定思路—变量 上面说明四中提到,变量不能定义在.h 中,是不是有点危言耸听的感觉,都不敢用全局 变量,其实也没这么严重。对于新手来说,这或许是一个难点,再难也有解决的办法啊,世 上无难事,只怕有心人嘛。解决这个问题的良方当然可以借鉴嵌入式操作系统——uCOS-Ⅲ, 该操作系统处理全局变量的方法比较特殊,也比较难理解,但学会之后妙用无穷啊。感兴趣 的读者可以自行研究一下。 残弈悟恩依个人的编程习惯,介绍一种处理方式。概括的讲,就是在.c 中定义变量,之 后在该.c 源文件所对应的.h 中声明即可。注意,一定要给它带上一顶“奴隶帽”,即变量声 明前加一修饰词——extern,这样无论“他”走到哪里,别人都可以指示“他”干活,想怎 么修改就怎么修改,但读者用“他”时,可别太过分,“他”也是人,累了会生病。同理, 滥用全局变量会使程序的可移植性、可读性变差。接下来用两段代码来比较说明全局变量的 定义和声明。 电脑爆炸式的代码: module 1.h // 编写一个.h unsigned char uaVal = 0; // 在模块 1 的.h 文件中定义一个变量 uaVal /* ============================================================ */ module1 .c // 编写一个.c #include "module1.h" // .c 模块 1 中包含模块 1 的.h /* ============================================================*/ module2 .c #include "module1.h" // .c 模块 2 中包含模块 1 的.h 以上程序的结果是在模块 1、2 中都定义了无符号 char 型变量 uaVal,uaVal 在不同的模 块中对应不同的内存地址。如果这个世间都这么写程序,那电脑就会爆炸,当然是夸张的修 辞手法。 推荐式的代码: module 1.h // 编写一个.h extern unsigned char uaVal; // 在.h 中声明 uaVal /* ============================================================ */ module1 .c #include "module1.h" unsigned char uaVal = 0; // .c 模块 1 中包含模块 1 的.h // 在模块 1 的.h 文件中定义一个变量 uaVal /* ============================================================ */ module2 .c #include "module1.h" // 在模块 2 的.h 文件中定义一个变量 uaVal 这样如果模块 1、2 操作 uaVal 的话,对应的是同一块内存单元。 4.符号决定出路—头文件之包含 以上模块化编程中,要大量的包含头文件。学过 C 语言的都知道,包含头文件的方式有 两种,一种是“”,第二种是“”xx.h””,那何时用第一种,又何时用第二种,可能 读者会从什么相对路径啊、绝对路径啊、系统的用什么、工程中的用什么,当然如果你知道, 飛天三號单片机实验板 ~ 13 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 肯定是一件好事,若读者不好理解的话,那请记下笔者的一句话:自己写的用双引号,不是 自己写的用尖括号。 5.模块的分类 一个嵌入式系统通常包括两类模块(注意:是两类,不是两个)。 (1)硬件驱动模块。一种特定硬件对应一个模块。 (2)软件功能模块。其模块的划分应满足低耦合、高内聚的要求。 低耦合、高内聚这是软件工程中的概念。简单说是六个字,但是所涉及的内容比较多, 笔者就不过多讲解了,若读者感兴趣,可以自行查阅资料,慢慢理解、总结、归纳其中的奥 秘,笔者这里简单补充两点。 1)内聚和耦合 内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述 的是模块内的功能联系。 耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂 程度、进入或访问一个模块的点以及通过接口的数据。 理解了以上两个词的含义之后,那“低耦合、高内聚”就好理解了,通俗点讲,模块与 模块之间少来往,模块内部多来往。当然对应到程序中,就不是这么简单,这需要大量的编 程和练习才能掌握其真正的内涵,这就留给读者去慢慢研究吧。 2)硬件驱动模块和软件功能模块的区别 所 谓硬 件驱 动模 块是 指所 写的 驱动 (也 就是 .c 文 件) 对应 一个 硬件 模块 。例 如 FsBSP_AllLedFlash.c 是用来驱动 LED 灯的,smg.c 是用来驱动数码管的,lcd.c 是用来驱动 LCD 液晶的,key.c 是用来检测按键的,等等,将这样的模块统称为硬件驱动模块。 所谓的软件功能模块是指所编写的模块只是某个功能的实现,而没有所对应的硬件模 块。例如 FsBSP_Delay.c 是用来延时的,main.c 是用来调用各个子函数的。这些模块都没有 对应的硬件模块,只是起某个功能而已。 4.5 模块化编程的应用实例 上面的内容,对于初学者估计不好接受,但请大家相信,只要渡过了这个难关,以后的 编程之路会比较轻快。同时,读者要对 Keil5 的工程建立过程,必须掌握,且必须熟悉。其 过程大致分为:新建文件夹→建立工程→保存工程→新建文件→添加文件到工程→添加文件 夹路径→编写程序→编译→下载程序,至于仿真,我们后续再叙。 12 颗 LED 的闪烁例程,对于 C 语言基础好,并且如果只建立了一个“.c”文件的读者来 说是很简单,可如果分成几个模块来完成,估计在工程的建立和工程的管理方面,读者会比 较难以接受,那这里先将所有的源码写出,读者照上面讲述的,多建立几次工程,多写几遍 程序,在 FSST15 开发板上多运行几次,相信会有量变到质变的奇迹发生,各部分源码如下。 (一) main.c 的源码。 1. #include "FsBSP_AllLedFlash.h" 2. void main(void) 3. { 4. LedGPIO_Init(); // 调用函数,对其 GPIO 口进行初始化 5. while(1) 6. { 7. AllLedFlash(); // 调用函数,实现所有 LED 小灯的闪烁 飛天三號单片机实验板 ~ 14 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 8. } 9. } (二) FsBSP_Delay.c 的源码。 1. #include "FsBSP_Delay.h" 2. 3. /************************************************************ 4. * 函数名称:DelayMS() 5. * 入口参数:ms,要延时的 ms 数 6. * 出口参数:无 7. * 函数功能:不精确延时 8. *********************************************************** */ 9. void DelayMS(unsigned int ms) 10. { 11. unsigned int i; 12. do{ 13. i = MAIN_Fosc / 13000; 14. while(--i); 15. }while(--ms); 16. } (三) FsBSP_Delay.h 的源码。 17. #ifndef __FsBSP_Delay_H__ 18. #define __FsBSP_Delay_H__ 19. 20. #define M AIN_Fosc 22118400 // 根据主 CPU 的运行频率来定 21. 22. extern void DelayMS(unsigned int ms); // 声明函数,以便外部调用 23. 24. #endif (四) FsBSP_AllLedFlash.c 的源码。 25. #include "FsBSP_AllLedFlash.h" 26. 27. /* *********************************************************** 28. * 函数名称:LedGPIO_Init() 29. * 入口参数:无 30. * 出口参数:无 31. * 函数功能:初始化所用端口 32. *********************************************************** */ 33. void LedGPIO_Init(void) 34. { 35. P3M0 = 0x00; 36. P3M1 = 0x00; 37. } 38. /* *********************************************************** 39. * 函数名称:AllLedFlash() 飛天三號单片机实验板 ~ 15 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 40. * 入口参数:无 41. * 出口参数:无 42. * 函数功能:所有的 LED 闪烁 43. *********************************************************** */ 44. void AllLedFlash(void) 45. { 46. P7 = 0xFF; // 熄灭所有的小灯 47. LED9 = 1; 48. LED10 = 1; 49. LED11 = 1; 50. LED12 = 1; 51. DelayMS(1000); 52. P7 = 0x00; // 延时一段时间 // 点亮所有的小灯 53. LED9 = 0; 54. LED10 = 0; 55. LED11 = 0; 56. LED12 = 0; 57. DelayMS(1000); // 延时一段时间 58. } (五) FsBSP_AllLedFlash.h 的源码。 59. #ifndef __FsBSP_AllLedFlash_H__ 60. #define __FsBSP_AllLedFlash_H__ 61. 62. #include "stc15.h" 63. #include "FsBSP_Delay.h" // 程序用到了一些端口,所以包含此头文件 // 程序用到延时函数,所以包含此头文件 64. 65. sbit LED9 = P5^4; // 定义端口,以便位操作 66. sbit LED10 = P5^5; 67. sbit LED11 = P3^6; 68. sbit LED12 = P3^7; 69. 70. void LedGPIO_Init(void); 71. void AllLedFlash(void); 72. 73. #endif 这样,一个完整的模块化编程实例已讲述完毕,读者需要做的是,将其编译,待生成可 执行文件“xx.hex”文件之后,下载到单片机中,这时就会发现 FSST15 开发板的 12 颗 LED 小灯开始闪烁,同时也说明模块化编程的步骤你已基本掌握,接下来要做的就是由量变到质 变,读者应不断以这种方式建立工程,不断编写程序,不断下载到单片机中调试、运行,反 复重复这些过程。冰冻三尺,非一日之寒的道理大家都懂,可又有多少人能做到呢? 该章的最后,为读者讲述一下 FsBSP_AllLedFlash.h 文件中的第 9 行(sbit LED11 = P3^6;), 别的道理类似,这里不赘,为了更好的讲述该行,我们先来看看第 4 行(#include "stc15.h")。 头文件的作用和编写形式上面已做过讲述,这里着重看看“stc15.h”头文件的具体内容。打 开工程,将鼠标放到编辑界面的 #include< stc15.h>处,单击鼠标右键并选择“ Open 飛天三號单片机实验板 ~ 16 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu document< STC15.h >”打开该头文件,具体操作如图 4-7 所示。 图 4-7 打开 STC15.h 头文件 其内容如下,鉴于篇幅原因,这里只保留了很小的一部分做讲解用,具体内容读者自己 可以打开该头文件去参阅。 1. STC15.h 2. #ifndef __STC15F2K60S2_H_ 3. #define __STC15F2K60S2_H_ 4. /* BYTE Registers */ 5. sfr P0 = 0x80; 6. sfr P1 = 0x90; 7. sfr P2 = 0xA0; 8. sfr P3 = 0xB0; 9. /* BIT Registers */ 10. /* IE */ 11. sbit EA = IE^7; 12. sbit ET2 = IE^5; 13. sbit ES = IE^4; 14. sbit ET1 = IE^3; 15. sbit EX1 = IE^2; 16. sbit ET0 = IE^1; 17. sbit EX0 = IE^0; 从上面代码中可以看到,该头文件定义了 STC15 系列单片机内部所有的功能寄存器,用 到了两个关键字 sfr 和 sbit,如第 5 行的 sfr P0 = 0x80,意思是把单片机内部地址 0xA0 处的这个寄存器重新起名 P0,P0 口有 8 位(0x80~0x87),读者理解为 8 个房间好了。但这 8 位(0x80~0x87)与 P0 毫无关系,当操作 P0 口时,实质是在操作(0x80~0x87)这 8 位寄 存器。形象点,某位读者叫张三,笔者给张三一个苹果,事实上苹果给到了张三这个人,而 不是张三这两个字。这样,如果写一句:P0 = 0x00,就等价于将从地址 0x80 开始的 8 个寄 存器全部清零,之后单片机内部又通过数据总线(不管是一根钢管也好,还是一根毛发也罢, 反正是一种线)将这 8 位寄存器与 I/O 口相连,最后操作这些寄存器就可达到控制 I/O 的目 的了。 为了让读者再明白点,笔者再举个形象的例子。P0~P7 就相当于 0(地下室)、1、2...7 楼层,一层楼房又分 8 个房间,例如房号有:001、103、205、307 等,这些房号类似于单 片机中寄存器(关于寄存器,后续章节将会讲述到)的地址 0x80、0x86 等,或者是所取的 别名 P0.0、P0.6 等,接着房间里面可以住男的(高电平),也可以住女的(低电平),同理, 这些寄存器中可以存“1”或者“0”。这样 62 个房间(7 层*8 个房间+1 层*6 个房间)刚好 飛天三號单片机实验板 ~ 17 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 就对应 62 个寄存器,最后将这 62 个寄存器用某种特殊的线连接到 62 个 I/O 口上,继而实 现了通过控制寄存器达到控制 I/O 口的目的。 接着再看看第 9 行的 sbit。就是将 IE 寄存器(他也是对应一个地址,单片机也并不认 PSW)的最高位重新命名为 EA,以后要开总中断时,就直接可以写 EA = 1;意思是将 EA 所 对应的的最高位置 1(写高电平)。这些知识点,如果读者立马能记忆、能理解,那最好不 过。如果暂时不能理解,读者要做的就是不断的重复、模仿和照抄。这些方法听起来,对于 做学问来说,感觉不够科学和研究,但笔者告诉读者,这是刚开始学习单片机、编程等最有 效的办法,也是笔者一直强调的由量变到质变的具体化。 4.6 课后习题 (1)熟练掌握各进制数之间的换算。 (2)了解单片机的开发流程,并在以后的学习中加以锻炼和提高。 (3)能够熟练用 Keil5 软件建立模块化工程模板,并能对所建工程做到简洁、规范。 (4)深刻理解“.c”和“.h”文件的作用和对应关系,以及所用变量的灵活定义。 (5)能够独立将第三章的所有实例编写成模块化形式,并对其进行管理、存档,以便 建立属于自己的函数库,为以后的学习提供便捷和方便。 飛天三號单片机实验板 ~ 18 ~ 电子工程师基地: www.ieeBase.net STC15 单片机实战指南(C 语言版) 微信号:xymbmcu 作者简介 刘 平,热爱电子技术,乐于分享、善于交流。先后在珠海、深圳等地主持研发通信类、 LCD 显示屏等多款产品,在嵌入式软件和硬件电路设计方面有丰富的项目实战经验,现创立 了深圳一坚电子和沃达智能科技。 现已编著有《深入浅出玩转 51 单片机》一书,研发有与书籍配套的飛天一號(MGMC-V2.0) 51 单片机试验板,并录制有与书籍、实验板完全配套的《31 天环游单片机》视频,此系列 产品已获得广大网友的好评和在校师生的肯定。 除此之外,自主研发有飛天二號(FSST32-V1.0)STM32 实验板和本书配套的飛天三號 (FSST15-V1.0)实验板,以及基于 STM32 平台的四轴飞行器飞蜓一号,基于 STC15 平台的 四轴飞行器飞蜓二號。 可以个人、公司的形式,承接各类嵌入式(全志、MTK 等)、单片机(51 单片机、STM32、 STM8、PIC、Atmel、松翰、合泰等)、蓝牙、WiFi、PCBA、Layout、APP、网页、商城等软硬 件项目。 联系人:小刘 邮箱 :625408362@qq.com 电话 :151-1239-9952 单片机技术交流群:143406243 先后获得以下各大网站称号: EDN China(电子技术设计)网站——博客专家(并荣膺 2015 年度最佳博主奖) China AET(电子技术应用)网站——网络名师 Elecfans (电子发烧友) 网站——社区之星 Moore8 (摩尔吧) 网站——金牌讲师 飛天三號单片机实验板 ~ 19 ~ 电子工程师基地: www.ieeBase.net

Top_arrow
回到顶部
EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。