6666|1

6366

帖子

4914

TA的资源

版主

楼主
 

详解怎样对自己的程序进行模块化管理 [复制链接]

刚开始写程序的时候,我们都是把所有的C程序写在一个C文件中。首先包含所需的头文件,后面进行宏定义和全局变量声明以及函数的声明。然后是一个一个的子程序,最后写主函数。这样在写一些简单的程序的时候还是有一定的好处的,操作简单。但是当你的程序写的比较大的时候,这种写法的弊端就显现出来了,你不能快速定位到你要的模块。如果对于还没习惯于用Ctrl+F的新手来说,意味着就要频繁的上下拉去搜寻要找的程序。不仅繁琐,也不是一种好的管理自己的程序的方法。

分模块管理自己的程序是指把实现一种功能的子程序进行单独建立一个C文件,并且每个C文件都做一个头文件,其他函数调用这些子程序时只要加载它的头文件就可以调用。假设你现在要做一个计算器,你可以把键盘输入模块建一个C文件,液晶显示部分做一个C文件,计算器功能也写在一个C文件中,并写出相应的头文件。

以工程的形式分模块来管理自己程序的好处有这些:首先,这样分模块管理的工程在移植的时候可以只进行简单的修改就移植到其他的工程里面。其次在阅读程序的时候,别人很容易判别要找的函数在那个模块里面,这有一个前提,你必须要对这些模块化的程序命名要一目了然。再者,管理自己的项目也显得很方便,在多人合作完成一个项目的时候,不同的队员分工去写相应的功能模块和头文件,最后整合在一起,这是相当大的优势。





下面以按键模块为例进行模块化的设置,先给出KeyBoard的C文件:

#include                //下面的程序中使用了52的寄存器名称,故要加载
#include"KeyBoard.h"                                 //自定义键盘输入的库,有键盘的变量和函数声明
#include"Global.h"                //全局变量定义头文件
//----------------------------------------------------------------------------
//下面的子函数按正常的形式书写
//-----------------------------------------------------------------------------

/****************************************************
*函数名称:ScanKey                                            
*入口参数:无                    
*出口参数:temp
*函数功能:对矩阵键盘进行扫描,返回键值                                       
****************************************************/

uchar ScanKey( void )
{
    uchar temp;
    temp = KEYNULL;
   
    KeyBoard = ~0x10;   // 1110 1111
    if( !LINE1 ) temp = 1 ;
    if( !LINE2 ) temp = 2 ;
    if( !LINE3 ) temp = 3 ;
    if( !LINE4 ) temp = 4 ;

    KeyBoard = ~0x20;   // 1101 1111
    if( !LINE1 ) temp = 5 ;
    if( !LINE2 ) temp = 6 ;
    if( !LINE3 ) temp = 7 ;
    if( !LINE4 ) temp = 8 ;

    KeyBoard = ~0x40;    // 1011 1111
    if( !LINE1 ) temp = 9 ;
    if( !LINE2 ) temp = 10 ;
    if( !LINE3 ) temp = 11 ;
    if( !LINE4 ) temp = 12 ;

    KeyBoard = ~0x80;    // 0111 1111
    if( !LINE1 ) temp = 13 ;
    if( !LINE2 ) temp = 14 ;
    if( !LINE3 ) temp = 15 ;
    if( !LINE4 ) temp = 16 ;

    return temp ;
}

/****************************************************
*函数名称:ReadKey                                            
*入口参数:无                    
*出口参数:KeyNum
*函数功能:响应用户按的一个键, 返回一个正确的键值                                       
****************************************************/

uchar ReadKey( void )
{
        uint j,k;
        for( ; ScanKey( ) !=  KEYNULL ; );    // 等待用户松开按键 即无返回键值
loop:     for( ; ScanKey( ) ==  KEYNULL ; );                         // 等待用户重新按键
        k = ScanKey( );                         // 第一次读入按键值
        for ( j= 0; j< 7000; j++ );        // 延时,起到消抖作用
        if ( k == ScanKey( ) )         // 和第二次读回来比较,如果相等
        return ( k ) ;            // 返回键值,如果不等继续等键值
        else
        goto loop ;
}


模块化的关键是头文件的设置,下面拿键盘模块对应的头文件来说明:

注:开头的这两句和最后一句一定要有,如果是定义其他的函数,比如LCD1602,则这样定义一下即可:#ifndef _ LCD1602_h  #define _ LCD1602_h

#ifndef _KeyBoard_h                    //条件编译,编译过就不再向下编译
#define _KeyBoard_h

/****************************************************
*                    宏定义
****************************************************/

#define uchar unsigned char                //常用数据类型的缩写宏定义
#define uint unsigned int
#define KeyBoard       P1                // 定义P1口为键盘
#define KEYNULL        0

/****************************************************
*            引脚定义
****************************************************/

sbit  LINE1 = P1^0 ;                    //对模块使用的引脚进行定义
sbit  LINE2 = P1^1 ;
sbit  LINE3 = P1^2 ;
sbit  LINE4 = P1^3 ;


/****************************************************
*            函数声明
****************************************************/

uchar ScanKey( void );                    //这个部分对模块内的函数进行声明
uchar ReadKey( void );

#endif                     

主函数的处理如下:

#include
#include"KeyBoard.h"            //矩阵键盘扫描头文件
#include"Global.h"            //全局变量定义头文件
#include"LCD1602.h"            //液晶显示头文件
#include"DisplayTime.h"        //时间显示头文件
#include"KeyProcess.h"        //按键处理头文件


void main( void )
{   

    //各类的初始化
    while( 1 )
    {            
        KeyNum = ReadKey(  );
        DateTimeProcess( KeyNum );        //时间显示功能函数
        CalculatorProcess( KeyNum  );         //计算功能执行函数
        Lock( KeyNum );            //锁定功能执行函数        
    }
}
   
  
最容易犯错的是关于全局变量的处理,比如上面主函数中的KeyNum,它是在多个模块中都有使用,是一个全局变量,关于全局变量在模块化的工程中的处理问题是一个难点。

它首先要在模块中进行声明,后面还要在头文件中定义为外部调用extern uchar KeyNum。我个人的习惯是把这些外部调用的定义写在一个Global.h的头文件中。如下所示:


#ifndef _Global_h
#define _Global_h

/****************************************************
*                    宏定义
****************************************************/

#define uchar unsigned char
#define uint unsigned int
#define TRUE 1
#define FALSE 0
#define INPUT_PASSWORD  11
#define ADJUST_PASSWORD  12
#define CHECK  14
sbit Buzzer = P3^7;

/****************************************************
*    全局变量引用
****************************************************/

extern uchar KeyNum;
extern uchar Sign;
extern uchar Week;
extern uchar Day;
extern uchar Month;
extern uchar Year_L;
extern uchar Year_H;
extern double Sum;   
extern double Num1;
extern double Num2;
extern uchar Minute;
extern uchar Hour;
extern uchar Status;
extern bit LockFlag ;
extern uchar AlarmHour ;
extern uchar AlarmMinute ;
extern bit AlarmFlag;

#endif
此帖出自单片机论坛

最新回复

果然很详细值得学习,此方法不管是对存储管理还是备份来说都是不错的  详情 回复 发表于 2010-3-16 15:05
点赞 关注
 

回复
举报

21

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
果然很详细值得学习,此方法不管是对存储管理还是备份来说都是不错的
此帖出自单片机论坛
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表