0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何對(duì)一個(gè)大的項(xiàng)目進(jìn)行模塊化編程

GReq_mcu168 ? 來(lái)源:未知 ? 作者:胡薇 ? 2018-06-11 17:06 ? 次閱讀

當(dāng)你在一個(gè)項(xiàng)目小組做一個(gè)相對(duì)較復(fù)雜的工程時(shí),意味著你不再獨(dú)自單干。你需要和你的小組成員分工合作,一起完成項(xiàng)目,這就要求小組成員各自負(fù)責(zé)一部分工程。比如你可能只是負(fù)責(zé)通訊或者顯示這一塊。這個(gè)時(shí)候,你就應(yīng)該將自己的這一塊程序?qū)懗梢粋€(gè)模塊,單獨(dú)調(diào)試,留出接口供其它模塊調(diào)用。最后,小組成員都將自己負(fù)責(zé)的模塊寫完并調(diào)試無(wú)誤后,由項(xiàng)目組長(zhǎng)進(jìn)行組合調(diào)試。像這些場(chǎng)合就要求程序必須模塊化。模塊化的好處是很多的,不僅僅是便于分工,它還有助于程序的調(diào)試,有利于程序結(jié)構(gòu)的劃分,還能增加程序的可讀性和可移植性。

初學(xué)者往往搞不懂如何模塊化編程,其實(shí)它是簡(jiǎn)單易學(xué),而且又是組織良好程序結(jié)構(gòu)行之有效的方法之一.

本文將先大概講一下模塊化的方法和注意事項(xiàng),最后將以初學(xué)者使用最廣的keil c編譯器為例,給出模塊化編程的詳細(xì)步驟。

模塊化程序設(shè)計(jì)應(yīng)該理解以下概述:

(1) 模塊即是一個(gè).c 文件和一個(gè).h 文件的結(jié)合,頭文件(.h)中是對(duì)于該模塊接口的聲明;

這一條概括了模塊化的實(shí)現(xiàn)方法和實(shí)質(zhì):將一個(gè)功能模塊的代碼單獨(dú)編寫成一個(gè).c文件,然后把該模塊的接口函數(shù)放在.h文件中.舉例:假如你用到液晶顯示,那么你可能會(huì)寫一個(gè)液晶驅(qū)動(dòng)模塊,以實(shí)現(xiàn)字符、漢字和圖像的現(xiàn)實(shí),命名為: led_device.c,該模塊的.c文件大體可以寫成:

注:此處只寫出這兩個(gè)函數(shù),第一個(gè)延時(shí)函數(shù)的作用范圍是模塊內(nèi),第二個(gè),它是其它模塊需要的。為了簡(jiǎn)化,此處并沒(méi)有寫出函數(shù)體.

.h文件中給出模塊的接口.在上面的例子中,向LCD寫入字符函數(shù):wr_lcd (uchar dat_comm,uchar content)就是一個(gè)接口函數(shù),因?yàn)槠渌K會(huì)調(diào)用它,那么.h文件中就必須將這個(gè)函數(shù)聲明為外部函數(shù)(使用extrun關(guān)鍵字修飾),另一個(gè)延時(shí)函數(shù):void delay (uint us)只是在本模塊中使用(本地函數(shù),用static關(guān)鍵字修飾),因此它是不需要放到.h文件中的。

.h文件格式如下:

這里注意三點(diǎn):

1. 在keil 編譯器中,extern這個(gè)關(guān)鍵字即使不聲明,編譯器也不會(huì)報(bào)錯(cuò),且程序運(yùn)行良好,但不保證使用其它編譯器也如此。強(qiáng)烈建議加上,養(yǎng)成良好的編程規(guī)范。

2. .c文件中的函數(shù)只有其它模塊使用時(shí)才會(huì)出現(xiàn)在.h文件中,像本地延時(shí)函數(shù)static void delay (uint us)即使出現(xiàn)在.h文件中也是在做無(wú)用功,因?yàn)槠渌K根本不去調(diào)用它,實(shí)際上也調(diào)用不了它(static關(guān)鍵字的限制作用)。

3.注意本句最后一定要加分號(hào)”;”,相信有不少同學(xué)遇到過(guò)這個(gè)奇怪的編譯器報(bào)錯(cuò): error C132: 'xxxx': not in formal parameter list,這個(gè)錯(cuò)誤其實(shí)是.h的函數(shù)聲明的最后少了分號(hào)的緣故。

模塊的應(yīng)用:假如需要在LCD菜單模塊lcd_menu.c中使用液晶驅(qū)動(dòng)模塊lcd_device.c中的函數(shù)void wr_lcd (uchar dat_comm,uchar content),只需在LCD菜單模塊的lcd_menu.c文件中加入液晶驅(qū)動(dòng)模塊的頭文件lcd_device.h即可.

(2) 某模塊提供給其它模塊調(diào)用的外部函數(shù)及數(shù)據(jù)需在.h 中文件中冠以extern 關(guān)鍵字聲明;

這句話在上面的例子中已經(jīng)有體現(xiàn),即某模塊提供給其它模塊調(diào)用的外部函數(shù)和全局變量需在.h 中文件中冠以extern 關(guān)鍵字聲明,下面重點(diǎn)說(shuō)一下全局變量的使用。使用模塊化編程的一個(gè)難點(diǎn)(相對(duì)于新手)就是全局變量的設(shè)定,初學(xué)者往往很難想通模塊與模塊公用的變量是如何實(shí)現(xiàn)的,常規(guī)的做法就是本句提到的,在.h文件中外部數(shù)據(jù)冠以extern關(guān)鍵字聲明。比如上例的變量value就是一個(gè)全局變量,若是某個(gè)模塊也使用這個(gè)變量,則和使用外部函數(shù)一樣,只需在使用的模塊.c文件中包含#include“l(fā)cd_device.h”即可。

另一種處理模塊間全局變量的方法來(lái)自于嵌入式操作系統(tǒng)uCOS-II,這個(gè)操作系統(tǒng)處理全局變量的方法比較特殊,也比較難以理解,但學(xué)會(huì)之后妙用無(wú)窮,這個(gè)方法只需用在頭文件中定義一次。方法為:

在定義所有全局變量(uCOS-II將所有全局變量定義在一個(gè).h文件內(nèi))的.h頭文件中:

當(dāng)編譯器處理.C文件時(shí),它強(qiáng)制xxx_EXT(在相應(yīng).H文件中可以找到)為空,(因?yàn)閤xx_GLOBALS已經(jīng)定義)。所以編譯器給每個(gè)全局變量分配內(nèi)存空間,而當(dāng)編譯器處理其他.C 文件時(shí),xxx_GLOBAL沒(méi)有定義,xxx_EXT 被定義為extern,這樣用戶就可以調(diào)用外部全局變量。為了說(shuō)明這個(gè)概念,可以參見uC/OS_II.H,其中包括以下定義:

同時(shí),uCOS_II.H 有中以下定義:

#define OS_GLOBALS

#include “includes.h”

當(dāng)編譯器處理uCOS_II.C 時(shí),它使得頭文件變成如下所示,因?yàn)镺S_EXT 被設(shè)置為空。

INT32U OSIdleCtr;

INT32U OSIdleCtrRun;

INT32U OSIdleCtrMax;

這樣編譯器就會(huì)將這些全局變量分配在內(nèi)存中。當(dāng)編譯器處理其他.C 文件時(shí),頭文件變成了如下的樣子,因?yàn)镺S_GLOBAL沒(méi)有定義,所以O(shè)S_EXT 被定義為extern。

extern INT32U OSIdleCtr;

extern INT32U OSIdleCtrRun;

extern INT32U OSIdleCtrMax;

在這種情況下,不產(chǎn)生內(nèi)存分配,而任何 .C文件都可以使用這些變量。這樣的就只需在 .H文件中定義一次就可以了。

(3) 模塊內(nèi)的函數(shù)和全局變量需在.c 文件開頭冠以static 關(guān)鍵字聲明;

這句話主要講述了關(guān)鍵字static的作用。Static是一個(gè)相當(dāng)重要的關(guān)鍵字,他能對(duì)函數(shù)和變量做一些約束,而且可以傳遞一些信息。比如上例在LCD驅(qū)動(dòng)模塊.c文件中定義的延時(shí)函數(shù)static void delay (uint us),這個(gè)函數(shù)冠以static修飾,一方面是限定了函數(shù)的作用范圍只是在本模塊中起作用,另一方面也給人傳達(dá)這樣的信息:該函數(shù)不會(huì)被其他模塊調(diào)用。下面詳細(xì)說(shuō)一下這個(gè)關(guān)鍵字的作用,在C 語(yǔ)言中,關(guān)鍵字static 有三個(gè)明顯的作用:

1.在函數(shù)體,一個(gè)被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過(guò)程中維持其值不變。

2.在模塊內(nèi)(但在函數(shù)體外),一個(gè)被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問(wèn),但不能被模塊外其它函數(shù)訪問(wèn)。它是一個(gè)本地的全局變

量。

3.在模塊內(nèi),一個(gè)被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個(gè)函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。

前兩個(gè)都比較容易理解,最后一個(gè)作用就是剛剛舉例中提到的延時(shí)函數(shù)(static void delay (uint us)),本地化函數(shù)是有相當(dāng)好的作用的。

(4) 永遠(yuǎn)不要在.h 文件中定義變量!

呵呵,似乎有點(diǎn)危言聳聽的感覺(jué),但我想也不會(huì)有多少人會(huì)在.h文件中定義變量的。

比較一下代碼:

代碼一:

以上程序的結(jié)果是在模塊1、2、3 中都定義了整型變量a,a 在不同的模塊中對(duì)應(yīng)不同的地址元,這個(gè)世界上從來(lái)不需要這樣的程序。正確的做法是:

代碼二:

這樣如果模塊1、2、3 操作a 的話,對(duì)應(yīng)的是同一片內(nèi)存單元。

注:

一個(gè)嵌入式系統(tǒng)通常包括兩類(注意是兩類,不是兩個(gè))模塊:

(1)硬件驅(qū)動(dòng)模塊,一種特定硬件對(duì)應(yīng)一個(gè)模塊;

(2)軟件功能模塊,其模塊的劃分應(yīng)滿足低偶合、高內(nèi)聚的要求。

下面以keil C 編譯器為例,講一下模塊化編程的步驟。

下面這個(gè)程序分為三層,共7個(gè)模塊,共同為主程序服務(wù)(它們之間也會(huì)相互調(diào)用)。

程序的結(jié)構(gòu)圖如下所示:

序主要模塊和功能簡(jiǎn)介:

一、底層驅(qū)動(dòng)

1. 紅外鍵盤:程序通過(guò)紅外鍵盤進(jìn)行操作。紅外鍵盤獨(dú)占定時(shí)器0和外部中斷0,以實(shí)現(xiàn)紅外解碼和鍵盤鍵值的識(shí)別。紅外鍵盤定義了五個(gè)按鍵,分別為上翻、下翻、左翻、右翻和確認(rèn)鍵。

2. LCD液晶顯示:程序主要通過(guò)LCD顯示信息,LCD液晶顯示驅(qū)動(dòng)提供顯示漢字、圖形和ASCII碼的函數(shù)接口??梢匀?、單行顯示漢字,任意位置顯示ASCII碼,還可以全屏、半屏顯示圖形。

二、功能模塊

1. LCD菜單程序:菜單程序可以使人機(jī)交互更加方便、容易。本菜單程序的菜單級(jí)別深度受RAM大小的限制,每增加一級(jí)菜單將多消耗4字節(jié)的RAM。菜單程序主要完成菜單功能函數(shù)的調(diào)度,LCD顯示刷新。

2. 計(jì)算器程序:實(shí)現(xiàn)65536以內(nèi)的加、減、乘、除,超出范圍會(huì)出現(xiàn)溢出,溢出發(fā)生時(shí),LCD顯示“錯(cuò)誤:出現(xiàn)溢出”的錯(cuò)誤提示,同時(shí)本次運(yùn)算被忽略。對(duì)于負(fù)數(shù)會(huì)顯示“-”號(hào),除數(shù)為零時(shí)LCD顯示“錯(cuò)誤:除數(shù)為零”的錯(cuò)誤提示。

3. 開機(jī)次數(shù)記憶程序:主要對(duì)基于IIC總線的EEPROM進(jìn)行讀寫,單片機(jī)每次上電后,將開機(jī)次數(shù)寫入EEPROM.

4. 串口測(cè)試程序:進(jìn)入該程序后,單片機(jī)向電腦發(fā)送字符串“Hello Word!”,發(fā)送數(shù)字24(以字符的形式顯示)。編寫此程序的目的是為了能夠方便的向電腦發(fā)送字符串和變量,便于程序的調(diào)試。串口占用串口資源,與頻率測(cè)量程序共享定時(shí)器1

5. 頻率測(cè)量:復(fù)用定時(shí)器1,占用外部中斷1,實(shí)現(xiàn)5~20KHZ頻率的測(cè)量.

三、主程序

主程序主要完成程序的初始化,LCD菜單顯示,監(jiān)視鍵盤程序并根據(jù)鍵值更新菜單。

步驟為:

1.新建工程。

2.點(diǎn)擊File—New(或者點(diǎn)擊快捷圖標(biāo):),新建一個(gè)文檔。

3.點(diǎn)擊File—Save(或者點(diǎn)擊快捷圖標(biāo):),保存新建的文檔,在文件名后填寫LCD_device.c(液晶驅(qū)動(dòng)模塊: LCD_device,提供顯示漢字、字符和圖像的接口),點(diǎn)擊確定。

在該文檔內(nèi)編寫LCD驅(qū)動(dòng)程序。

4. 點(diǎn)擊File—New(或者點(diǎn)擊快捷圖標(biāo):),再新建一個(gè)文檔。

5. 點(diǎn)擊File—Save(或者點(diǎn)擊快捷圖標(biāo):),保存新建的文檔,在文件名后填寫LCD_device.h(液晶驅(qū)動(dòng)模塊的頭文件,模塊的接口和全局變量在這里聲明(感謝網(wǎng)友楊康佳指正這里的錯(cuò)誤,原文將“聲明”寫成了“定義”,頭文件一般用來(lái)聲明變量和接口的))。點(diǎn)擊確定。在該文檔中整理全局變量和接口函數(shù)。以上步驟之后的效果見下圖:

至此,液晶驅(qū)動(dòng)模塊書寫完畢,可以對(duì)這個(gè)模塊單獨(dú)的調(diào)試。

6.重復(fù)以上步驟2~5,定義 紅外鍵盤模塊:key.c與key.h

菜單模塊:menu.c與menu.h

串口通信模塊:uart_.c與uart.h

計(jì)算器模塊:counter.c與counter.h

頻率測(cè)量模塊:mea_fre.c與mea_fre.h

開機(jī)次數(shù)記憶模塊:eepram.c與eepram.h

7.重復(fù)以上步驟2~3,定義主程序main.c

最終效果如下圖所示:

完成1~7個(gè)步驟后,有些小白就習(xí)慣性的點(diǎn)擊編譯按鈕了,這時(shí)候會(huì)出現(xiàn)兩個(gè)警告信息:

*** WARNING L1: UNRESOLVED EXTERNAL SYMBOL

*** WARNING L2: REFERENCE MADE TO UNRESOLVED EXTERNAL

這是因?yàn)槟阒皇蔷帉懞昧顺绦蚰K,卻沒(méi)有把他們加入到工程的緣故。

解決方法:在Project Workspace框中,右擊Source group 1文件夾,選擇Add Files to Group‘Source Group 1’,在彈出的對(duì)話框中添加你的.c文件即可。

遙想很久很久以前,我也對(duì)上面的兩個(gè)警告有過(guò)親身體會(huì)。那時(shí)候我還在大學(xué),周圍有一大群的好哥們. 現(xiàn)在…想起來(lái)只剩唏噓?。?!

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7575

    瀏覽量

    134070
  • 模塊化編程
    +關(guān)注

    關(guān)注

    4

    文章

    17

    瀏覽量

    7708

原文標(biāo)題:嵌入式C語(yǔ)言之---模塊化編程

文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    關(guān)于keil 模塊化編程個(gè)小小疑惑

    請(qǐng)問(wèn)模塊化編程中如圖所示,我要將最后個(gè)c文件standby.c添加到main.c中,怎么操作??謝謝
    發(fā)表于 11-13 23:05

    模塊化編程

    大家起來(lái)講解看,,,到底何為模塊化編程???
    發(fā)表于 08-07 21:56

    如何對(duì)Keil 5模塊化進(jìn)行編程

    如何對(duì)Keil 5模塊化進(jìn)行編程?
    發(fā)表于 10-11 06:08

    單片機(jī)模塊化是如何進(jìn)行編程的?

    單片機(jī)模塊化是如何進(jìn)行編程的?
    發(fā)表于 10-21 06:20

    模塊化編程&工程模板設(shè)計(jì)

    ???九層妖塔?起于壘土【藍(lán)橋杯】—{模板Template}—{Part1:模塊化編程&工程模板}、流程圖二、基礎(chǔ)知識(shí)1、編譯預(yù)處理2、變量的定義和說(shuō)明3、`extern`三、操作流程1
    發(fā)表于 11-30 07:18

    到底什么是模塊化編程?

    對(duì)于些接觸單片機(jī)編程不久的小萌新來(lái)說(shuō),模塊化編程這個(gè)概念剛接觸的時(shí)候可能會(huì)很懵,到底什么是模塊化編程
    發(fā)表于 12-03 07:39

    什么是模塊化編程

    1什么是模塊化模塊化編程就是把我們的整個(gè)項(xiàng)目,分成很多模塊(比如
    發(fā)表于 12-06 08:02

    新建模塊化編程工程及常見問(wèn)題匯總

    先跳過(guò)關(guān)于模塊化編程的好處,在這里不多說(shuō)了,因?yàn)檫@已經(jīng)被人說(shuō)爛了,但是有相當(dāng)部分新手是覺(jué)得模塊化編程比較復(fù)雜,但是實(shí)際上,當(dāng)面臨
    發(fā)表于 01-05 07:29

    什么是模塊化編程模塊化編程的注意事項(xiàng)

    、回顧上篇文章,我寫了單片機(jī)的實(shí)戰(zhàn),數(shù)碼管的消影。這篇文章,將對(duì)單片機(jī)的模塊化編程進(jìn)行個(gè)記錄講解。二、什么是
    發(fā)表于 02-23 07:14

    模塊化程序設(shè)計(jì)簡(jiǎn)單解釋

    模塊化程序設(shè)計(jì)模塊化程序設(shè)計(jì)是指在進(jìn)行程序設(shè)計(jì)時(shí)將個(gè)大程序按照功能劃分為若干小程序模塊,每個(gè)小
    發(fā)表于 02-23 06:05

    模塊化編程思想

    模塊化編程思想,充分利用c語(yǔ)言的可移植性,更好的利用c語(yǔ)言。
    發(fā)表于 03-22 15:15 ?16次下載

    【C語(yǔ)言】模塊化編程-通俗易懂

    1什么是模塊化模塊化編程就是把我們的整個(gè)項(xiàng)目,分成很多模塊(比如
    發(fā)表于 11-23 18:06 ?10次下載
    【C語(yǔ)言】<b class='flag-5'>模塊化</b><b class='flag-5'>編程</b>-通俗易懂

    單片機(jī)零基礎(chǔ)入門(8-5)模塊化編程

    、回顧上篇文章,我寫了單片機(jī)的實(shí)戰(zhàn),數(shù)碼管的消影。這篇文章,將對(duì)單片機(jī)的模塊化編程進(jìn)行個(gè)記錄講解。二、什么是
    發(fā)表于 12-31 18:55 ?20次下載
    單片機(jī)零基礎(chǔ)入門(8-5)<b class='flag-5'>模塊化</b><b class='flag-5'>編程</b>

    模塊化通知項(xiàng)目

    電子發(fā)燒友網(wǎng)站提供《模塊化通知項(xiàng)目.zip》資料免費(fèi)下載
    發(fā)表于 12-16 14:01 ?0次下載
    <b class='flag-5'>模塊化</b>通知<b class='flag-5'>項(xiàng)目</b>

    如何模塊化編程

    塊程序?qū)懗?b class='flag-5'>一個(gè)模塊,單獨(dú)調(diào)試,留出接口供其它模塊調(diào)用。 最后,小組成員都將自己負(fù)責(zé)的模塊寫完并調(diào)試無(wú)誤后,由
    的頭像 發(fā)表于 06-22 11:40 ?1186次閱讀