当前位置:首页 > 单片机 > 单片机
[导读]  好的开始是成功的一半  通过上一章的学习,我想你已经掌握了如何在程序中释放CPU了。希望能够继续坚持下去。一个良好的开始是成功的一半。我们今天所做的一切都是为了在单片机编程上做的更好。在谈论今天的主题

  好的开始是成功的一半

  通过上一章的学习,我想你已经掌握了如何在程序中释放CPU了。希望能够继续坚持下去。一个良好的开始是成功的一半。我们今天所做的一切都是为了在单片机编程上做的更好。

在谈论今天的主题之前,先说下我以前的一些经历。在刚开始接触到C语言程序的时候,由于学习内容所限,写的程序都不是很大,一般也就几百行而矣。所以所有的程序都完成在一个源文件里面。记得那时候大一参加学校里的一个电子设计大赛,调试了一个多星期,所有程序加起来大概将近1000行,长长的一个文件,从上浏览下来都要好半天。出了错误简单的语法错误还好定位,其它一些错误,往往找半天才找的到。那个时候开始知道了模块化编程这个东西,也尝试着开始把程序分模块编写。最开始是把相同功能的一些函数(譬如1602液晶的驱动)全部写在一个头文件(.h)文件里面,然后需要调用的地方包含进去,但是很快发现这种方法有其局限性,很容易犯重复包含的错误。

而且调用起来也很不方便。很快暑假的电子设计大赛来临了,学校对我们的单片机软件编程进行了一些培训。由于学校历年来参加国赛和省赛,因此积累了一定数量的驱动模块,那些日子,老师每天都会布置一定量的任务,让我们用这些模块组合起来,完成一定功能。而正是那些日子模块化编程的培训,使我对于模块化编程有了更进一步的认识。并且程序规范也开始慢慢注意起来。此后的日子,无论程序的大小,均采用模块化编程的方式去编写。很长一段时间以来,一直有单片机爱好者在QQ上和我一起交流。有时候,他们会发过来一些有问题的程序源文件,让我帮忙修改一下。同样是长长的一个文件,而且命名极不规范,从头看下来,着实是痛苦,说实话,还真不如我重新给他们写一个更快一些,此话到不假,因为手头积累了一定量的模块,在完成一个新的系统时候,只需要根据上层功能需求,在底层模块的支持下,可以很快方便的完成。而不需要从头到尾再一砖一瓦的重新编写。藉此,也可以看出模块化编程的一个好处,就是可重复利用率高。

  下面让我们揭开模块化神秘面纱,一窥其真面目。

  C语言源文件 *.c

  提到C语言源文件,大家都不会陌生。因为我们平常写的程序代码几乎都在这个XX.C文件里面。编译器也是以此文件来进行编译并生成相应的目标文件。作为模块化编程的组成基础,我们所要实现的所有功能的源代码均在这个文件里。理想的模块化应该可以看成是一个黑盒子。即我们只关心模块提供的功能,而不管模块内部的实现细节。好比我们买了一部手机,我们只需要会用手机提供的功能即可,不需要知晓它是如何把短信发出去的,如何响应我们按键的输入,这些过程对我们用户而言,就是是一个黑盒子。

  在大规模程序开发中,一个程序由很多个模块组成,很可能,这些模块的编写任务被分配到不同的人。而你在编写这个模块的时候很可能就需要利用到别人写好的模块的借口,这个时候我们关心的是,它的模块实现了什么样的接口,我该如何去调用,至于模块内部是如何组织的,对于我而言,无需过多关注。而追求接口的单一性,把不需要的细节尽可能对外部屏蔽起来,正是我们所需要注意的地方。

  C语言头文件 *.h

  谈及到模块化编程,必然会涉及到多文件编译,也就是工程编译。在这样的一个系统中,往往会有多个C文件,而且每个C文件的作用不尽相同。在我们的C文件中,由于需要对外提供接口,因此必须有一些函数或者是变量提供给外部其它文件进行调用。

  假设我们有一个LCD.C文件,其提供最基本的LCD的驱动函数

LcdPutChar(char cNewValue) ; //在当前位置输出一个字符

而在我们的另外一个文件中需要调用此函数,那么我们该如何做呢?

  头文件的作用正是在此。可以称其为一份接口描述文件。其文件内部不应该包含任何实质性的函数代码。我们可以把这个头文件理解成为一份说明书,说明的内容就是我们的模块对外提供的接口函数或者是接口变量。同时该文件也包含了一些很重要的宏定义以及一些结构体的信息,离开了这些信息,很可能就无法正常使用接口函数或者是接口变量。但是总的原则是:不该让外界知道的信息就不应该出现在头文件里,而外界调用模块内接口函数或者是接口变量所必须的信息就一定要出现在头文件里,否则,外界就无法正确的调用我们提供的接口功能。因而为了让外部函数或者文件调用我们提供的接口功能,就必须包含我们提供的这个接口描述文件----即头文件。同时,我们自身模块也需要包含这份模块头文件(因为其包含了模块源文件中所需要的宏定义或者是结构体),好比我们平常所用的文件都是一式三份一样,模块本身也需要包含这个头文件。

下面我们来定义这个头文件,一般来说,头文件的名字应该与源文件的名字保持一致,这样我们便可以清晰的知道哪个头文件是哪个源文件的描述。

  于是便得到了LCD.C的头文件LCD.h 其内容如下。

#ifndef_LCD_H_#define_LCD_H_externLcdPutChar(charcNewValue);#endif

  这与我们在源文件中定义函数时有点类似。不同的是,在其前面添加了extern 修饰符表明其是一个外部函数,可以被外部其它模块进行调用。

#ifndef_LCD_H_#define_LCD_H_#endif

  这个几条条件编译和宏定义是为了防止重复包含。假如有两个不同源文件需要调用LcdPutChar(char cNewValue)这个函数,他们分别都通过#include “Lcd.h”把这个头文件包含了进去。在第一个源文件进行编译时候,由于没有定义过 _LCD_H_ 因此 #ifndef _LCD_H_ 条件成立,于是定义_LCD_H_ 并将下面的声明包含进去。在第二个文件编译时候,由于第一个文件包含时候,已经将_LCD_H_定义过了。因此#ifndef _LCD_H_ 不成立,整个头文件内容就没有被包含。假设没有这样的条件编译语句,那么两个文件都包含了extern LcdPutChar(char cNewValue) ; 就会引起重复包含的错误。

  不得不说的typedef

  很多朋友似乎了习惯程序中利用如下语句来对数据类型进行定义

#defineuintunsignedint#defineucharunsignedchar

然后在定义变量的时候 直接这样使用

uintg_nTimeCounter=0;

  不可否认,这样确实很方便,而且对于移植起来也有一定的方便性。但是考虑下面这种情况你还会 这么认为吗?

#definePINTunsignedint*//定义unsignedint指针类型PINTg_npTimeCounter,g_npTimeState;

  那么你到底是定义了两个unsigned int 型的指针变量,还是一个指针变量,一个整形变量呢?而你的初衷又是什么呢,想定义两个unsigned int 型的指针变量吗?如果是这样,那么估计过不久就会到处抓狂找错误了。庆幸的是C语言已经为我们考虑到了这一点。typedef 正是为此而生。为了给变量起一个别名我们可以用如下的语句

typedefunsignedintuint16;//给指向无符号整形变量起一个别名uint16typedefunsignedint*puint16;//给指向无符号整形变量指针起一个别名puint16

  在我们定义变量时候便可以这样定义了:

uint16g_nTimeCounter=0;//定义一个无符号的整形变量puint16g_npTimeCounter;//定义一个无符号的整形变量的指针

  在我们使用51单片机的C语言编程的时候,整形变量的范围是16位,而在基于32的微处理下的整形变量是32位。倘若我们在8位单片机下编写的一些代码想要移植到32位的处理器上,那么很可能我们就需要在源文件中到处修改变量的类型定义。这是一件庞大的工作,为了考虑程序的可移植性,在一开始,我们就应该养成良好的习惯,用变量的别名进行定义。

如在8位单片机的平台下,有如下一个变量定义

uint16g_nTimeCounter=0;

  如果移植32单片机的平台下,想要其的范围依旧为16位。

  可以直接修改uint16 的定义,即

typedefunsignedshortintuint16;

  这样就可以了,而不需要到源文件处处寻找并修改。

将常用的数据类型全部采用此种方法定义,形成一个头文件,便于我们以后编程直接调用。

文件名 MacroAndConst.h

其内容如下:

#ifndef_MACRO_AND_CONST_H_#define_MACRO_AND_CONST_H_typedefunsignedintuint16;typedefunsignedintUINT;typedefunsignedintuint;typedefunsignedintUINT16;typedefunsignedintWORD;typedefunsignedintword;typedefintint16;typedefintINT16;typedefunsignedlonguint32;typedefunsignedlongUINT32;typedefunsignedlongDWORD;typedefunsignedlongdword;typedeflongint32;typedeflongINT32;typedefsignedcharint8;typedefsignedcharINT8;typedefunsignedcharbyte;typedefunsignedcharBYTE;typedefunsignedcharuchar;typedefunsignedcharUINT8;typedefunsignedcharuint8;typedefunsignedcharBOOL;#endif

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

单片机是一种嵌入式系统,它是一块集成电路芯片,内部包含了处理器、存储器和输入输出接口等功能。

关键字: 单片机 编写程序 嵌入式

在现代电子技术的快速发展中,单片机以其高度的集成性、稳定性和可靠性,在工业自动化、智能家居、医疗设备、航空航天等诸多领域得到了广泛应用。S32单片机,作为其中的佼佼者,其引脚功能丰富多样,是实现与外部设备通信、控制、数据...

关键字: s32单片机引脚 单片机

在微控制器领域,MSP430与STM32无疑是两颗璀璨的明星。它们各自凭借其独特的技术特点和广泛的应用领域,在市场上占据了重要的位置。本文将深入解析MSP430与STM32之间的区别,探讨它们在不同应用场景下的优势和局限...

关键字: MSP430 STM32 单片机

该系列产品有助于嵌入式设计人员在更广泛的系统中轻松实现USB功能

关键字: 单片机 嵌入式设计 USB

单片机编程语言是程序员与微控制器进行交流的桥梁,它们构成了单片机系统的软件开发基石,决定着如何有效、高效地控制和管理单片机的各项资源。随着微控制器技术的不断发展,针对不同应用场景的需求,形成了丰富多样的编程语言体系。本文...

关键字: 单片机 微控制器

单片机,全称为“单片微型计算机”或“微控制器”(Microcontroller Unit,简称MCU),是一种高度集成化的电子器件,它是现代科技领域的关键组件,尤其在自动化控制、物联网、消费电子、汽车电子、工业控制等领域...

关键字: 单片机 MCU

STM32是由意法半导体公司(STMicroelectronics)推出的基于ARM Cortex-M内核的32位微控制器系列,以其高性能、低功耗、丰富的外设接口和强大的生态系统深受广大嵌入式开发者喜爱。本文将详细介绍S...

关键字: STM32 单片机

在当前的科技浪潮中,单片机作为嵌入式系统的重要组成部分,正以其强大的功能和广泛的应用领域受到越来越多行业的青睐。在众多单片机中,W79E2051以其卓越的性能和稳定的工作特性,成为市场上的明星产品。本文将深入探讨W79E...

关键字: 单片机 w79e2051单片机

单片机,又称为微控制器或微处理器,是现代电子设备中的核心部件之一。它集成了中央处理器、存储器、输入输出接口等电路,通过外部信号引脚与外部设备进行通信,实现对设备的控制和管理。本文将详细介绍单片机的外部信号引脚名称及其功能...

关键字: 单片机 微控制器 中央处理器

随着科技的飞速发展,单片机和嵌入式系统在现代电子设备中的应用越来越广泛。它们不仅提高了设备的智能化水平,还推动了各行各业的创新与发展。在单片机和嵌入式系统的开发中,编程语言的选择至关重要。本文将深入探讨单片机和嵌入式系统...

关键字: 单片机 嵌入式系统 电子设备
关闭
关闭