當前位置:首頁 > 公眾號精選 > 玩轉(zhuǎn)嵌入式
[導讀]本文將先大概講一下模塊化的方法和注意事項,最后將以初學者使用最廣的keil c編譯器為例,給出模塊化編程的詳細步驟。

/*
在大學時,我是用51單片機入門的,課堂上所學習的單片機C語言編程和單片機項目編程,兩者千差萬別。課堂上的編程以語法為主,完全不知道如何應用,比較茫然。做單片機項目時,比較具體用到什么內(nèi)容查閱什么內(nèi)容。印象中,第一個項目搞了很久才出來,之后一發(fā)不可收拾,但都是局限于課程設計、畢業(yè)設計的層面上。一個.c文件走遍天下,那時候沒有模塊化和移植的概念。直到遇到了那個博士,說我的代碼比較亂,移植性太差( 點這里查看我的自我介紹 )。但是,也不理解什么是移植。后來,漸漸的認識到,編程是要模塊化的。每一個功能都可以寫成一個.c和.h,這樣以后在用的時候,可以把.c和.h復制過去,稍微修改一下接口和參數(shù)就可以了。

南京的天氣 依然很熱,太陽很大,今天沒上班休假一天,把小孩上幼兒園所需要的體檢材料、銀行卡等辦理一下。給大家推薦一首老歌《夏天的風》,期待絲絲涼爽。
*/
我們 在一個項目小組做一個相對較復雜的工程時,意味著你不再獨自單干。 而是和小組成員分工合作 ,這就要求小組成員各自負責一部分工程。 比如你可能只是負責通訊或者顯示這一塊。 這個時候,你就應該將自己的這一塊程序?qū)懗梢粋€模塊,單獨調(diào)試,留出接口供其它模塊調(diào)用。 最后,小組成員都將自己負責的模塊寫完并調(diào)試無誤后,由項目組長進行組合調(diào)試。 像這些場合就要求程序必須模塊化。 模塊化的好處是很多的,不僅僅是便于分工,它還有助于程序的調(diào)試,有利于程序結(jié)構(gòu)的劃分,還能增加程序的可讀性和可移植性。
初學者往往搞不懂如何模塊化編程,其實它是簡單易學,而且又是組織良好程序結(jié)構(gòu)行之有效的方法之一。本文將先大概講一下模塊化的方法和注意事項,最后將以初學者使用最廣的keil c編譯器為例,給出模塊化編程的詳細步驟。
模塊化程序設計應該理解以下概述: 模塊即是一個.c文件和一個.h文件的結(jié)合,頭文件(.h)中是對于該模塊接口的聲明 。
這一條概括了模塊化的實現(xiàn)方法和實質(zhì):將一個功能模塊的代碼單獨編寫成一個.c文件,然后把該模塊的接口函數(shù)放在.h文件中.舉例:假如你用到液晶顯示,那么你可能會寫一個液晶驅(qū)動模塊,以實現(xiàn)字符、漢字和圖像的現(xiàn)實,命名為: led_device.c,該模塊的.c文件大體可以寫成:
   
#include …

//定義變量
unsigned char value;//全局變量

//定義函數(shù)
//這是本模塊第一個函數(shù),起到延時作用,只供本模塊的函數(shù)調(diào)用,所以用到static關(guān)鍵字修飾
/********************延時子程序************************/
static void delay (uint us) //delay time
{}
//這是本模塊的第二個函數(shù),要在其他模塊中調(diào)用
/*********************寫字符程序**************************
** 功能:向LCD寫入字符
** 參數(shù):dat_comm 為1寫入的是數(shù)據(jù),為0寫入的是指令
content 為寫入的數(shù)字或指令
******************************************************/
void wr_lcd (uchar dat_comm,uchar content)
{}
……
……
/***************************** END Files***********************************/

注:?此處只寫出這兩個函數(shù),第一個延時函數(shù)的作用范圍是模塊內(nèi),第二個,它是其它模塊需要的。為了簡化,此處并沒有寫出函數(shù)體.
.h文件中給出模塊的接口.在上面的例子中, 向LCD寫入字符函數(shù):wr_lcd (uchar dat_comm,uchar content)就是一個接口函數(shù),因為其它模塊會調(diào)用它,那么.h文件中就必須將這個函數(shù)聲明為外部函數(shù)(使用extrun關(guān)鍵字修飾),另一個延時函數(shù):void delay (uint us)只是在本模塊中使用(本地函數(shù),用static關(guān)鍵字修飾),因此它是不需要放到.h文件中的。
.h文件格式如下:
   
//聲明全局變量
extern unsigned char value;
//聲明接口函數(shù)
extern void wr_lcd (uchar dat_comm,uchar content); //向LCD寫入字符
……
/***************************** END Files***********************************/
這里注意三點:
  1. 在keil 編譯器中,extern這個關(guān)鍵字即使不聲明,編譯器也不會報錯,且程序運行良好,但不保證使用其它編譯器也如此。強烈建議加上,養(yǎng)成良好的編程規(guī)范。
  2. .c文件中的函數(shù)只有其它模塊使用時才會出現(xiàn)在.h文件中,像本地延時函數(shù)static void delay (uint us)即使出現(xiàn)在.h文件中也是在做無用功,因為其它模塊根本不去調(diào)用它,實際上也調(diào)用不了它(static關(guān)鍵字的限制作用)。
  3. 注意本句最后一定要加分號”;”,相信有不少同學遇到過這個奇怪的編譯器報錯: error C132: 'xxxx': not in formal parameter list,這個錯誤其實是.h的函數(shù)聲明的最后少了分號的緣故。
模塊的應用:?假如需要在LCD菜單模塊lcd_menu.c中使用液晶驅(qū)動模塊lcd_device.c中的函數(shù)void wr_lcd (uchar dat_comm,uchar content),只需在LCD菜單模塊的lcd_menu.c文件中加入液晶驅(qū)動模塊的頭文件lcd_device.h即可.
   
#include“l(fā)cd_device.h //包含液晶驅(qū)動程序頭文件,之后就可以在該.c文件中調(diào)用//lcd_device.h中的全局函數(shù),使用液晶驅(qū)動程序里的全局//變量(如果有的話)。

//調(diào)用向LCD寫入字符函數(shù)
wr_lcd (0x01,0x30);

//對全局變量賦值
value=0xff;

某模塊提供給其他模塊調(diào)用的外部函數(shù)及數(shù)據(jù)需在.h文件中以extern關(guān)鍵字聲明。
這句話在上面的例子中已經(jīng)有體現(xiàn),即某模塊提供給其它模塊調(diào)用的外部函數(shù)和全局變量需在.h 中文件中冠以extern 關(guān)鍵字聲明,下面重點說一下全局變量的使用。使用模塊化編程的一個難點(相對于新手)就是全局變量的設定,初學者往往很難想通模塊與模塊公用的變量是如何實現(xiàn)的,常規(guī)的做法就是本句提到的,在.h文件中外部數(shù)據(jù)冠以extern關(guān)鍵字聲明。比如上例的變量value就是一個全局變量,若是某個模塊也使用這個變量,則和使用外部函數(shù)一樣,只需在使用的模塊.c文件中包含#include“l(fā)cd_device.h”即可。
另一種處理模塊間全局變量的方法來自于嵌入式操作系統(tǒng)uCOS-II,這個操作系統(tǒng)處理全局變量的方法比較特殊,也比較難以理解,但學會之后妙用無窮,這個方法只需用在頭文件中定義一次。方法為:
在定義所有全局變量(uCOS-II將所有全局變量定義在一個.h文件內(nèi))的.h頭文件中:
   
#ifdef xxx_GLOBALS
#define xxx_EXT
#else
#define xxx_EXT extern
#endif
.H 文件中每個全局變量都加上了xxx_EXT的前綴。xxx 代表模塊的名字。
該模塊的.C文件中有以下定義:
   
#define xxx_GLOBALS
#include "includes.h"
當編譯器處理.C文件時,它強制xxx_EXT(在相應.H文件中可以找到)為空,(因為xxx_GLOBALS已經(jīng)定義)。所以編譯器給每個全局變量分配內(nèi)存空間,而當編譯器處理其他.C 文件時,xxx_GLOBAL沒有定義,xxx_EXT 被定義為extern,這樣用戶就可以調(diào)用外部全局變量。為了說明這個概念,可以參見uC/OS_II.H,其中包括以下定義:
   
#ifdef OS_GLOBALS
#define OS_EXT
#else
#define OS_EXT extern
#endif
OS_EXT INT32U OSIdleCtr;
OS_EXT INT32U OSIdleCtrRun;
OS_EXT INT32U OSIdleCtrMax;
同時,uCOS_II.H 中有以下定義:
   
#define OS_GLOBALS

#include “includes.h”
當編譯器處理uCOS_II.C 時,它使得頭文件變成如下所示,因為OS_EXT 被設置為空。
   
INT32U OSIdleCtr;

INT32U OSIdleCtrRun;

INT32U OSIdleCtrMax;
這樣編譯器就會將這些全局變量分配在內(nèi)存中。當編譯器處理其他.C 文件時,頭文件變成了如下的樣子,因為OS_GLOBAL沒有定義,所以OS_EXT 被定義為extern。
   
extern INT32U OSIdleCtr;

extern INT32U OSIdleCtrRun;

extern INT32U OSIdleCtrMax;
在這種情況下,不產(chǎn)生內(nèi)存分配,而任何 .C文件都可以使用這些變量。這樣的就只需在 .H文件中定義一次就可以了。
模塊內(nèi)的函數(shù)和全局變量需在.c文件開頭以static關(guān)鍵字聲明。
這句話主要講述了關(guān)鍵字static的作用。Static是一個相當重要的關(guān)鍵字,他能對函數(shù)和變量做一些約束,而且可以傳遞一些信息。比如上例在LCD驅(qū)動模塊.c文件中定義的延時函數(shù)static void delay (uint us),這個函數(shù)冠以static修飾,一方面是限定了函數(shù)的作用范圍只是在本模塊中起作用,另一方面也給人傳達這樣的信息:該函數(shù)不會被其他模塊調(diào)用。下面詳細說一下這個關(guān)鍵字的作用,在C 語言中,關(guān)鍵字static 有三個明顯的作用:
  1. 在函數(shù)體,一個被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過程中維持其值不變。
  2. 在模塊內(nèi)(但在函數(shù)體外),一個被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個本地的全局變量。
  3. 在模塊內(nèi),一個被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。
前兩個都比較容易理解,最后一個作用就是剛剛舉例中提到的延時函數(shù)(static void delay (uint us)),本地化函數(shù)是有相當好的作用的。
不要在.h頭文件中定義變量!
比較一下代碼:
代碼一:
   
/*module1.h*/
int a = 5; /* 在模塊1 的.h 文件中定義int a */
/*module1 .c*/
#include "module1.h" /* 在模塊1 中包含模塊1 的.h 文件 */
/*module2 .c*/
#include "module1.h" /* 在模塊2 中包含模塊1 的.h 文件 */
/*module3 .c*/
#include "module1.h" /* 在模塊3 中包含模塊1 的.h 文件 */
以上程序的結(jié)果是在模塊1、2、3 中都定義了整型變量a,a 在不同的模塊中對應不同的地址元,這個世界上從來不需要這樣的程序。正確的做法是:
代碼二:
   
/*module1.h*/
extern int a; /* 在模塊1 的.h 文件中聲明int a */
/*module1 .c*/
#include "module1.h" /* 在模塊1 中包含模塊1 的.h 文件 */
int a = 5; /* 在模塊1 的.c 文件中定義int a */
/*module2 .c*/
#include "module1.h" /* 在模塊2 中包含模塊1 的.h 文件 */
/*module3 .c*/
#include "module1.h" /* 在模塊3 中包含模塊1 的.h 文件 */
這樣如果模塊1、2、3 操作a 的話,對應的是同一片內(nèi)存單元。
注:
一個嵌入式系統(tǒng)通常包括兩類(注意是兩類,不是兩個)模塊:
  • 硬件驅(qū)動模塊,一種特定硬件對應一個模塊;
  • 軟件功能模塊,其模塊的劃分應滿足低偶合、高內(nèi)聚的要求。
    關(guān)注 微信公眾號『玩轉(zhuǎn)嵌入式』,后臺回復“128”獲取干貨資料匯總,回復“256”加入技術(shù)交流群。
  • 精彩技術(shù)文章推薦


  • 01

    |C語言,動態(tài)展示經(jīng)典排序算法


    02

    |怎么看懂芯片的時序圖?怎么根據(jù)時序圖編程?


    03

    |單片機的Bootloader,可以實現(xiàn)用戶輕松升級程序


    04

    |零基礎(chǔ)如何學習單片機,一位入門者的進階路徑,可參考



免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉