---------單片機(jī)模塊化編程初探
哈嘍艾威玩,還記得上課時(shí)給大家講到的單片機(jī)模塊化編程的思想么?有些同學(xué)沒有練習(xí),有些同學(xué)一定也都忘卻了。在這里我重新整理一下,作為我們進(jìn)階學(xué)習(xí)的第一彈內(nèi)容給大家熱熱身咯~
俗話說,不會(huì)模塊化編程的廚師不是好司機(jī)。那么如何進(jìn)行單片機(jī)的模塊化編程呢?且聽我給大家慢慢說來(lái)。
(一)What is單片機(jī)的模塊化編程?
模塊化編程是指將一個(gè)龐大的程序劃分為若干個(gè)功能獨(dú)立的模塊,對(duì)各個(gè)模塊進(jìn)行獨(dú)立開發(fā),然后再將這些模塊統(tǒng)一合并為一個(gè)完整的程序。舉個(gè)栗子,大家都玩過七巧板,我們都知道,七巧板巧在七塊板子隨意組合,可以組成很多種形狀。我讓大家拼出一個(gè)小房子,大家能很輕易的做到,下次我又讓大家用這些拼出來(lái)一個(gè)小鐘樓,大家還是能夠毫無(wú)鴨梨的完成任務(wù)。七塊板還是這七塊板,只不過進(jìn)行了不同的排序罷了,但卻收到了兩種不同的產(chǎn)品。這就是模塊化編程的優(yōu)勢(shì),只要我“七塊板”到手,任你魔高一尺,我則道高一丈。
在單片機(jī)程序里,程序比較小或者功能比較簡(jiǎn)單的時(shí)候,我們不需要采用模塊化編程,但是,當(dāng)程序功能復(fù)雜、涉及的資源較多的時(shí)候,模塊化編程就能體現(xiàn)它的優(yōu)越性了。如前面我們寫過的DS18B20的驅(qū)動(dòng)程序、獨(dú)立按鍵掃描程序和12864程序,每一個(gè)程序都是只用一個(gè)源文件編寫就能完成,但是,當(dāng)您制作一個(gè)12864液晶日歷的時(shí)候,需要用到DS18B20驅(qū)動(dòng)程序、獨(dú)立按鍵掃描程序和12864顯示程序,如果把這三個(gè)程序全部集中在一個(gè)源文件里,將導(dǎo)致主體程序臃腫且雜亂,這樣做并非不可取,只是降低了程序可讀性、可維護(hù)性和代碼的重用率。如果把這三個(gè)程序當(dāng)做三個(gè)獨(dú)立的模塊放到你的主體工程進(jìn)行模塊化編程,效果就不一樣了。
實(shí)際上,模塊化編程就是模塊合并的過程,就是建立每個(gè)模塊的頭文件和源文件并將其加入到主體程序的過程。主體程序調(diào)用模塊的函數(shù)是通過包含模塊的頭文件來(lái)實(shí)現(xiàn),模塊的頭文件和源文件是模塊密不可分的兩個(gè)部分,缺一不可。所以,模塊化編程必須提供每個(gè)模塊的頭文件和源文件。
(二)模塊化編程的好處
想當(dāng)年,我還是一枚單片機(jī)清新小菜的時(shí)候,記得大學(xué)的時(shí)候參加一個(gè)電子設(shè)計(jì)大賽,用單片機(jī)做了個(gè)點(diǎn)陣+數(shù)碼管顯示環(huán)境信息和萬(wàn)年歷的程序,調(diào)試了幾個(gè)星期,所有程序加起來(lái)小1000行,瘦長(zhǎng)且雜亂的一個(gè)程序,編程的規(guī)范性也很差,從上瀏覽下來(lái)都要好半天。出了錯(cuò)誤去問老師,見我這亂七八糟的程序,老師看都懶得看。于是自己調(diào)試,出了一些簡(jiǎn)單的語(yǔ)法錯(cuò)誤還好定位,其它一些錯(cuò)誤,找半天才能找的到。那個(gè)時(shí)候被自己挖的大坑折騰的夠嗆,那段歲月也不堪回首,每每回想起來(lái)(我先去吐會(huì)兒血),仍然會(huì)覺得腦袋里一團(tuán)亂麻。
生活就是這樣,總是在你覺得“山窮水復(fù)疑無(wú)路”的時(shí)候讓你“柳暗花明”“又一村”。一個(gè)偶然的機(jī)會(huì),我接觸到了模塊化編程。于是開始了解,覺得這個(gè)是解決困擾我N久的問題的絕好機(jī)會(huì)。于是果斷開始“模塊化”。每天我都會(huì)寫一些函數(shù)并調(diào)試,比如us級(jí)的延時(shí)函數(shù),ms級(jí)的延時(shí)函數(shù),I2C協(xié)議函數(shù),串口通信函數(shù),1602和12864液晶驅(qū)動(dòng)函數(shù),還有例如DS18B20等各種常用的傳感器驅(qū)動(dòng)函數(shù)等等。由于長(zhǎng)期的積累,我收獲了一大堆非常給力的函數(shù),編程也漸漸變的規(guī)范起來(lái),不再像以前那么難以閱讀了。當(dāng)然這都不是重點(diǎn),重點(diǎn)是,我做單片機(jī)設(shè)計(jì)的速度和效率快了好幾個(gè)數(shù)量級(jí)。
那么具體是怎么實(shí)現(xiàn)的呢,敬請(qǐng)關(guān)注《第一彈---單片機(jī)模塊化編程(二)》且聽我慢慢道來(lái)。
--------單片機(jī)模塊化編程再探
又有幾天沒有跟大家見面啦,真真是有點(diǎn)極為想念呢。在上一帖中,我向大家簡(jiǎn)單的講解了使用單片機(jī)模塊化編程給我們帶來(lái)的好處。而在現(xiàn)實(shí)工作場(chǎng)合,不論是“攻城獅”們還是“程序猿”們,也不論是軟件設(shè)計(jì)還是硬件設(shè)計(jì),模塊化的概念也是大家經(jīng)?;蛘哒f是必須使用的思維了。下面針對(duì)大家的51單片機(jī)課程的學(xué)習(xí),詳細(xì)給大家講講如何將模塊化編程這一概念運(yùn)用到單片機(jī)實(shí)戰(zhàn)當(dāng)中去。還是以大家非常之熟悉的流水燈為例進(jìn)行講解吧。(版主下文中開啟嚴(yán)肅教學(xué)模式,上課大家不要講話,上廁所和有問題的請(qǐng)先舉手示意。)
(一)怎樣使用模塊化編程建立工程
下面以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的LED流水燈作為實(shí)例進(jìn)行詳細(xì)講解。我們都知道,要想實(shí)現(xiàn)簡(jiǎn)單的LED流水燈(這里使用延時(shí)函數(shù),而不使用定時(shí)器中斷法),我們必須要有以下函數(shù):延時(shí)函數(shù)、LED初始化函數(shù)、和LED流水燈實(shí)現(xiàn)函數(shù),這三大類函數(shù)。那么,我們?cè)鯓右阅K的形式來(lái)使用這些函數(shù)呢。下文中作者采用了圖文并茂的形式進(jìn)行分析,這真真是極好的~
(二)LED流水燈例程
1、首先使用Keil uVision新建工程,這里我采用的是Keil uVision4
2、保存創(chuàng)建的工程
3、選擇所使用的芯片
4、點(diǎn)擊ok后,在彈出的對(duì)話框中選擇“否”
5、新建文件,用以編輯函數(shù)和頭文件
6、將新建的這些文件分別重命名并保存
7、將xxx.C的文件添加到工作組中。
8、當(dāng)完成以上步奏之后,我們就可以進(jìn)行具體的函數(shù)編寫了。對(duì)于如何編寫一個(gè).C的C語(yǔ)言文件和一個(gè).h的頭文件,下面我來(lái)具體說明。首先以主函數(shù)main.c為例。如下圖:
我們可以看到,這個(gè)流水燈的主函數(shù)main.c如果用模塊化編程的方法來(lái)實(shí)現(xiàn)的話,干凈整潔了很多。少了我們常見的相關(guān)的延時(shí)函數(shù)delay();以及對(duì)uint和uchar的宏定義。且在主函數(shù)中,直接使用了LED_init();和LED_display();這兩個(gè)函數(shù)。而我們知道,要想在主函數(shù)中使用一個(gè)子函數(shù),必須得在主函數(shù)的前面對(duì)這幾個(gè)子函數(shù)進(jìn)行聲明,可是本段代碼中并沒有出現(xiàn)相關(guān)的語(yǔ)句。取而代之的是,在程序段第二行,多了一句#include “LED.h”,這一句話又有什么樣的特殊功能呢?下面讓我們來(lái)研究一下LED.c和LED.h的廬山真面目。
9、LED.c和LED.h的編寫
從LED.c這個(gè)C文件中,我們可以看出具體對(duì)LED_init();和LED_display();函數(shù)如何實(shí)現(xiàn),在這個(gè)文件中有著具體的描述。那么問題來(lái)了~~LED.c和main.c之間是怎樣產(chǎn)生聯(lián)系的呢?換句話說,當(dāng)我們?cè)诹硗庖粋€(gè)文件中需要調(diào)用其他文件當(dāng)中的某個(gè)函數(shù)的時(shí)候,那么我們?cè)撊绾巫瞿??要想搞清楚這個(gè)問題,是時(shí)候請(qǐng)出LED.h這位大神了。一般來(lái)講xxx.h格式的文件為頭文件,頭文件提供了程序內(nèi)函數(shù)被其他函數(shù)所調(diào)用的接口。我們也可以把他稱為一份“接口描述文件”。
頭文件的文件內(nèi)部不應(yīng)該包含任何實(shí)質(zhì)性的函數(shù)代碼。我們可以把這個(gè)頭文件理解成為一份說明書,說明的內(nèi)容就是我們的模塊對(duì)外提供的接口函數(shù)或者是接口變量。同時(shí)該文件也包含了一些很重要的宏定義以及一些結(jié)構(gòu)體的信息,離開了這些信息,很可能就無(wú)法正常使用接口函數(shù)或者是接口變量。但是總的原則是:不該讓外界知道的信息就不應(yīng)該出現(xiàn)在頭文件里,而外界調(diào)用模塊內(nèi)接口函數(shù)或者是接口變量所必須的信息就一定要出現(xiàn)在頭文件里,否則,外界就無(wú)法正確的調(diào)用我們提供的接口功能。因而為了讓外部函數(shù)或者文件調(diào)用我們提供的接口功能,就必須包含我們提供的這個(gè)接口描述文件----即頭文件。同時(shí),我們自身模塊也需要包含這份模塊頭文件(因?yàn)槠浒四K源文件中所需要的宏定義或者是結(jié)構(gòu)體),好比我們平常所用的文件都是一式三份一樣,模塊本身也需要包含這個(gè)頭文件。
下面我們來(lái)對(duì)LED.h這個(gè)頭文件進(jìn)行說明,一般來(lái)說,頭文件的名字應(yīng)該與源文件的名字保持一致,這樣我們便可以清晰的知道哪個(gè)頭文件是哪個(gè)源文件的描述。
于是便得到了LED.c的頭文件LED.h 其內(nèi)容如下。
#ifndef __LED_H__
#define __LED_H__
extern void LED_init();
extern void LED_display();
#endif
這與我們?cè)谠次募卸x函數(shù)時(shí)有點(diǎn)類似。不同的是,在其前面添加了extern 修飾符表明其是一個(gè)外部函數(shù),可以被外部其它模塊進(jìn)行調(diào)用。
10、下面我們?cè)賮?lái)看delay.c和其頭文件delay.h
我們發(fā)現(xiàn)了一點(diǎn)一樣的地方和一點(diǎn)異樣的地方(這句話讀的我也是醉了)。
一樣的是,對(duì)于頭文件來(lái)講,整體的框架似乎一點(diǎn)也沒有發(fā)生改變,都是下列形式。
#ifndef __DELAY_H__
#define __DELAY_H__
extern ……
…… ……
#endif
這是頭文件的標(biāo)準(zhǔn)編寫格式,其中__DELAY_H__這個(gè)是頭文件的名字,必須大寫,中間的橫線不能少。一般來(lái)說,頭文件的名字應(yīng)該與源文件的名字保持一致,這樣我們便可以清晰的知道哪個(gè)頭文件是哪個(gè)源文件的描述。
而異樣的是,我們?cè)赿elay.c這個(gè)文件中,發(fā)現(xiàn)有”mytype.h”這么個(gè)頭文件。那么這個(gè)是神馬,又能做神馬呢?下面我們來(lái)做一個(gè)簡(jiǎn)單的探討。
11、工程中的mytype.h是個(gè)什么樣的存在
大家可能早就注意到了,這個(gè)mytype到底是何方神圣,在分析之前我們先來(lái)仔細(xì)的“打量一下”。
通過上圖中的一段代碼,我們能夠發(fā)現(xiàn),這好像是對(duì)字符串定義表達(dá)符號(hào)的宏定義,沒錯(cuò),你猜對(duì)了!!細(xì)心的小伙伴又發(fā)現(xiàn)了,我們通常在函數(shù)中的用法跟這并不完全一樣啊,例如我們定義uint和uchar的時(shí)候,在程序中我們是這樣寫的:
#define uint unsigned int
#define uchar unsigned char
那么非常好,這就是兩者不一樣的地方啦。在寫的時(shí)候注意一下就好啦。另外,在對(duì)unsigned int和unsigned char等進(jìn)行宏定義的時(shí)候,我們分別采用了多種字符來(lái)對(duì)其進(jìn)行定義,這樣做的好處是,能夠使得mytype.h這個(gè)頭文件能夠很好的適用于不同的芯片和不同編程風(fēng)格的程序員,也能夠起到方便程序進(jìn)行移植的這么一個(gè)目的。由此可見,如果我們將模塊化編程很好的運(yùn)用在項(xiàng)目開發(fā)中,能夠起到避免冗余工作量和一勞永逸的良好效果。
好了,以上幾段代碼都帶著大家一一分析完畢,現(xiàn)在我們來(lái)進(jìn)行一下編譯,看看能不能一切順利。
12、對(duì)編譯輸出選項(xiàng)進(jìn)行簡(jiǎn)單設(shè)置
按照?qǐng)D中設(shè)置完成后,點(diǎn)擊“OK”按鈕即可。
13、點(diǎn)擊編譯按鈕
14、打開工程文件夾路徑,我們可以查找到輸出的test.hex文件
好了,大功告成了,第一彈宣布結(jié)束。。。
---------單片機(jī)模塊化編程之 探之又探
大家好!由于前幾天工作太忙的緣故,擱淺了《單片機(jī)模塊化編程(三)》的創(chuàng)作,還望大家多多包涵和理解~!其實(shí)我想說的是:“碼”字不易,且“碼”且珍惜。很享受跟大家一起學(xué)習(xí)的樂趣。好了閑話不再多說,緊接著上一帖我們往下走起!
在開始之前我們先來(lái)看一下上一帖中的工程文件夾。。額。。。
上一帖中我們說到了,我是一個(gè)有著強(qiáng)迫癥和密集恐懼癥的雙重“病癥”的患者,當(dāng)然這不是重點(diǎn),重點(diǎn)是看到這樣的工程文件夾。。。。我也是醉的一塌糊涂了。作為一個(gè)凡事都追求分類和條理性的“完美主義攻城獅”來(lái)說,這種混亂的狀況是堅(jiān)決不能出現(xiàn)在我們的世界里的。那么如何才能將這些文件進(jìn)行有秩序的分門別類呢?
首先,讓我們對(duì)文件夾中的文件進(jìn)行解讀。對(duì)于文件夾中的文件,想必大家最熟悉的就是上一帖中重點(diǎn)講述的.c和.h兩類文件了,那么我們就任性一次,不管三七二十一,新建四個(gè)文件夾將其分別放入相應(yīng)的文件夾中。如下圖:
例如文件夾“delay”所展示給大家的那樣。
這樣一來(lái),我們能夠看到在工程文件夾中,文件是少了一些。但是問題來(lái)了,當(dāng)我們?cè)俅蜷_工程的時(shí)候,我們發(fā)現(xiàn),工程中的.c文件成了這個(gè)樣子:
而且再編譯的話也會(huì)出現(xiàn)問題,那么怎么解決這個(gè)問題呢?我們接下來(lái)繼續(xù)探討:
在完成了以上步驟之后,我們可以發(fā)現(xiàn)工程中的三個(gè).c文件的狀態(tài)已經(jīng)恢復(fù)了正常。但這還沒完,由于.h的頭文件也被移動(dòng)到了新的位置,因此我們需要在編譯軟件中對(duì)其路徑進(jìn)行配置。配置方法如下:
之后再點(diǎn)擊編譯按鈕進(jìn)行編譯,即可收到和之前一樣的編譯效果了??墒浅?c和.h之外,我們?cè)谖募A里發(fā)現(xiàn)還有其他的很多文件,這些文件比.c和.h兩類文件更亂更糟心。那么他們又是些什么文件呢?又該怎么處理呢?萊斯夠昂go on!
這些文件絕大部分都是編譯的過程中,產(chǎn)生的中間文件。為了更好的區(qū)分這些文件,我們采用以下辦法。請(qǐng)大家讀圖:
做完了以上步奏,我們發(fā)現(xiàn)雖然根文件夾下干凈了不少,可是還有一些.lst和.bak等后綴的文件存在,這些文件又是哪兒來(lái)的呢?我們?cè)賮?lái)繼續(xù)分析。請(qǐng)大家繼續(xù)讀圖:
在完成了以上兩個(gè)步奏之后,我們發(fā)現(xiàn),我們的文件夾已經(jīng)相對(duì)干凈且有條理了。在完成了以上的步奏之后,我們也可以從中看出在編譯過程中生成的文件主要有.obj、.lst、.hex以及其他文件,其中delay.obj、LED.obj等obj類型的文件是在對(duì)工程中的C文件編譯時(shí)產(chǎn)生的二進(jìn)制文件,大家可以不用理會(huì);而delay.lst、LED.lst等lst類型的文件是在編譯過程中生成的列表文件,些文件均屬于中間文件,我們?cè)趯W(xué)習(xí)過程中可以暫時(shí)將其忽略,不再做進(jìn)一步的細(xì)究。我們要注意的文件是生成的 .hex格式的文件,這個(gè)文件是我們要用的著的文件,也是我們最終要往單片機(jī)內(nèi)部燒寫的文件。
而對(duì)于上面圖中的.bak文件來(lái)講,他們是在工程中所產(chǎn)生的備份文件,是可以刪除的,在這里,為了更加美觀我將其刪除,同樣不會(huì)影響再次編譯的效果。好了,現(xiàn)在我們將沒有進(jìn)行處理的根文件夾與“分門別類”過的文件夾進(jìn)行一下對(duì)比。下面是見證奇跡的時(shí)刻。。。。。請(qǐng)看下圖:
看完了之后我的強(qiáng)迫癥和密集恐懼癥被自己的“機(jī)智”治愈了,這真是太瘋狂啦??!原諒我的自戀,點(diǎn)評(píng)一下這樣的好處吧。
當(dāng)我們對(duì)函數(shù)文件進(jìn)行分類之后,我們發(fā)現(xiàn),當(dāng)再需要建立一個(gè)新的工程的時(shí)候,又需要用到delay.c和delay.h這兩個(gè)文件的時(shí)候,我們就能直接將這個(gè)“delay”文件夾拷貝到新的工程文件根目錄下啦!不需要重新編寫,僅僅需要按照上文的方法再次配置一下路徑就好了!這真的是一勞永逸,坐享其成的好方法??!
好啦,文章寫到這里,想必大家能夠較好的認(rèn)識(shí)模塊化編程的思想了!這回第一彈真真的要結(jié)束了。我也真真的要和大家說再見了?。?!站在教師的角度,還是希望大家多動(dòng)手勤練習(xí),爭(zhēng)取學(xué)到有用的知識(shí)早日成才;站在創(chuàng)客的角度,樓主我在此拋磚引玉獻(xiàn)丑啦,也希望各路大神能夠?qū)⒆约簩氋F的經(jīng)驗(yàn)分享出來(lái),共同照亮我們大家學(xué)習(xí)的道路!謝謝大家~第一彈宣告結(jié)束,么~么~~噠~~~?。?!
聯(lián)系客服
微信登錄中...
請(qǐng)勿關(guān)閉此頁(yè)面