欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 11026|回復(fù): 18
打印 上一主題 下一主題
收起左側(cè)

從單片機(jī)初學(xué)者邁向單片機(jī)工程師

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:1 發(fā)表于 2013-6-10 00:24 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
  這個(gè)我從別處貼來51hei的。寫的非常的好。我們用學(xué)單片機(jī)不要停在演示的基礎(chǔ)上。只能讓單片機(jī)完成局部事。這樣我們永遠(yuǎn)不會(huì)走出流水燈地獄!!!

學(xué)習(xí)單片機(jī)也已經(jīng)有幾年了,藉此機(jī)會(huì)和大家聊一下我學(xué)習(xí)過程中的一些經(jīng)歷和想法吧。也感謝一線工人提供了這個(gè)機(jī)會(huì)。
    幾年前,和眾多初學(xué)者一樣,我接觸到了單片機(jī),立刻被其神奇的功能所吸引,從此不能自拔。很多個(gè)日夜就這樣陪伴著它度過了。期間也遇到過非常多的問題,也一度被這些問題所困惑……等到回過頭來,看到自己曾經(jīng)走過的路,唏噓不已。經(jīng)常混跡于論壇里,也看到了很多初學(xué)者發(fā)的求助帖子,看到他們走在自己曾走過的彎路上,忽然想到了自己的那段日子,心里竟然莫名的沖動(dòng),凡此總總,我總是盡自己所能去回帖。很多時(shí)候,都想寫一點(diǎn)什么東西出來,希望對(duì)廣大的初學(xué)者有一點(diǎn)點(diǎn)幫助。但總是不知從何處寫起。今天借一線工人的臺(tái),唱一唱我的戲。“賣弄”也好,“吹噓”也罷,我只是想認(rèn)真的寫寫我這一路走來歷經(jīng)的總總,把其中值得注意,以及經(jīng)驗(yàn)的地方寫出來,權(quán)當(dāng)是我對(duì)自己的一個(gè)總結(jié)吧。而作為看官的你,如果看到了我的錯(cuò)誤,還請(qǐng)一定指正,這樣對(duì)我以及其它讀者都有幫助,而至于你如果從中能夠收獲到些許,那便是我最大的欣慰了。姑妄言之,姑妄聽之。
  一路學(xué)習(xí)過來的過程中,幫助最大之一無疑來自于網(wǎng)絡(luò)了。很多時(shí)候,通過網(wǎng)絡(luò),我們都可以獲取到所需要的學(xué)習(xí)資料。但是,隨著我們學(xué)習(xí)的深入,我們會(huì)慢慢發(fā)現(xiàn),網(wǎng)絡(luò)提供的東西是有限度的,好像大部分的資料都差不多,或者說是適合大部分的初學(xué)者所需,而當(dāng)我們想更進(jìn)一步提高時(shí),卻發(fā)現(xiàn)能夠獲取到的資料越來越少,相信各位也會(huì)有同感,鋪天蓋地的單片機(jī)資料中大部分不是流水燈就是LED,液晶,而且也只是僅僅作功能性的演示。于是有些人選擇了放棄,或者是轉(zhuǎn)移到其他興趣上面去了,而只有少部分人選擇了繼續(xù)摸索下去,結(jié)合市面上的書籍,然后在網(wǎng)絡(luò)上鍥而不舍的搜集資料,再從牛人的只言片語中去體會(huì),不斷動(dòng)手實(shí)踐,慢慢的,也摸索出來了自己的一條路子。當(dāng)然這個(gè)過程必然是艱辛的,而他學(xué)會(huì)了之后也不會(huì)在網(wǎng)絡(luò)上輕易分享自己的學(xué)習(xí)成果。如此惡性循環(huán)下去,也就不難理解為什么初級(jí)的學(xué)習(xí)資料滿天飛,而深入一點(diǎn)的學(xué)習(xí)資料卻很少的原因了。相較于其他領(lǐng)域,單片機(jī)技術(shù)的封鎖更加容易。盡管已經(jīng)問世了很多年了,有價(jià)值的資料還是相當(dāng)?shù)那啡保蟛糠值馁Y料都是止于入門階段或者是簡(jiǎn)單的演示實(shí)驗(yàn)。但是在實(shí)際工程應(yīng)用中卻是另外一回事。有能力的高手無暇或者是不愿公開自己的學(xué)習(xí)經(jīng)驗(yàn)。
    很多時(shí)候,我也很困惑,看到國外愛好者毫不保留的在網(wǎng)絡(luò)上發(fā)布自己的作品,我忽然感覺到一絲絲的悲哀。也許,我們真的該轉(zhuǎn)變一下思路了,幫助別人,其實(shí)也是在幫助自己。啰啰嗦嗦的說了這么多,相信大家能夠明白說的是什么意思。在接下來的一段日子里,我將會(huì)結(jié)合電子工程師之家舉辦的主題周活動(dòng)寫一點(diǎn)自己的想法。盡可能從實(shí)用的角度去講述。希望能夠幫助更多的初學(xué)者更上一層樓。而關(guān)于這個(gè)主題周的最大主題我想了這樣的一個(gè)名字“從單片機(jī)初學(xué)者邁向單片機(jī)工程師”。名字挺大挺響亮,給我的壓力也挺大的,但我會(huì)努力,爭(zhēng)取使這樣的一系列文章能夠帶給大家一點(diǎn)幫助,而不是看后大跌眼鏡。這樣的一系列文章主要的對(duì)象是初學(xué)者,以及想從初學(xué)者更進(jìn)一步提高的讀者。而至于老手,以及那些牛XX的人,希望能夠給我們這些初學(xué)者更多的一些指點(diǎn)哈~@_@





我們首先來看第一章節(jié)


從這一章開始,我們開始邁入單片機(jī)的世界。在我們開始這一章具體的學(xué)習(xí)之前,有必要給大家先說明一下。在以后的系列文章中,我們將以51內(nèi)核的單片機(jī)為載體,C語言為編程語言,開發(fā)環(huán)境為KEIL uv3。至于為什么選用C語言開發(fā),好處不言而喻,開發(fā)速度快,效率高,代碼可復(fù)用率高,結(jié)構(gòu)清晰,尤其是在大型的程序中,而且隨著編譯器的不斷升級(jí),其編譯后的代碼大小與匯編語言的差距越來越小。而關(guān)于C語言和匯編之爭(zhēng),就像那個(gè)啥,每隔一段時(shí)間總會(huì)有人挑起這個(gè)話題,如果你感興趣,可以到網(wǎng)上搜索相關(guān)的帖子自行閱讀。不是說匯編不重要,在很多對(duì)時(shí)序要求非常高的場(chǎng)合,需要利用匯編語言和C語言混合編程才能夠滿足系統(tǒng)的需求。在我們學(xué)習(xí)掌握C語言的同時(shí),也還需要利用閑余的時(shí)間去學(xué)習(xí)了解匯編語言。

1.從點(diǎn)亮LED(發(fā)光二極管)開始
在市面上眾多的單片機(jī)學(xué)習(xí)資料中,最基礎(chǔ)的實(shí)驗(yàn)無疑于點(diǎn)亮LED了,即控制單片機(jī)的I/O的電平的變化。
如同如下實(shí)例代碼一般

void main(void)
{
    LedInit() ;
    While(1)  
    {
        LED = ON ;
        DelayMs(500) ;
        LED = OFF ;
        DelayMs(500) ;
}
}

    程序很簡(jiǎn)單,從它的結(jié)構(gòu)可以看出,LED先點(diǎn)亮500MS,然后熄滅500MS,如此循環(huán)下去,形成的效果就是LED以1HZ的頻率進(jìn)行閃爍。下面讓我們分析上面的程序有沒有什么問題。
看來看出,好像很正常的啊,能有什么問題呢?這個(gè)時(shí)候我們應(yīng)該換一個(gè)思路去想了。試想,整個(gè)程序除了控制LED = ON ; LED = OFF; 這兩條語句外,其余的時(shí)間,全消耗在了DelayMs(500)這兩個(gè)函數(shù)上。而在實(shí)際應(yīng)用系統(tǒng)中是沒有哪個(gè)系統(tǒng)只閃爍一只LED就其它什么事情都不做了的。因此,在這里我們要想辦法,把CPU解放出來,讓它不要白白浪費(fèi)500MS的延時(shí)等待時(shí)間。寧可讓它一遍又一遍的掃描看有哪些任務(wù)需要執(zhí)行,也不要讓它停留在某個(gè)地方空轉(zhuǎn)消耗CPU時(shí)間。

從上面我們可以總結(jié)出
(1)    無論什么時(shí)候我們都要以實(shí)際應(yīng)用的角度去考慮程序的編寫。
(2)    無論什么時(shí)候都不要讓CPU白白浪費(fèi)等待,尤其是延時(shí)(超過1MS)這樣的地方。

下面讓我們從另外一個(gè)角度來考慮如何點(diǎn)亮一顆LED。
先看看我們的硬件結(jié)構(gòu)是什么樣子的。


    我手上的單片機(jī)板子是電子工程師之家的開發(fā)的學(xué)習(xí)板。就以它的實(shí)際硬件連接圖來分析吧。如下圖所示


  一般的LED的正常發(fā)光電流為10~20MA而低電流LED的工作電流在2mA以下(亮度與普通發(fā)光管相同)。在上圖中我們可知,當(dāng)Q1~Q8引腳上面的電平為低電平時(shí),LED發(fā)光。通過LED的電流約為(VCC - Vd)/ RA2 。其中Vd為L(zhǎng)ED導(dǎo)通后的壓降,約為1.7V左右。這個(gè)導(dǎo)通壓降根據(jù)LED顏色的不同,以及工作電流的大小的不同,會(huì)有一定的差別。下面一些參數(shù)是網(wǎng)上有人測(cè)出來的,供大家參考。
紅色的壓降為1.82-1.88V,電流5-8mA,
綠色的壓降為1.75-1.82V,電流3-5mA,
橙色的壓降為1.7-1.8V,電流3-5mA
蘭色的壓降為3.1-3.3V,電流8-10mA,
白色的壓降為3-3.2V,電流10-15mA,
(供電電壓5V,LED直徑為5mm)

74HC573真值表如下:

  通過這個(gè)真值表我們可以看出。當(dāng)OutputEnable引腳接低電平的時(shí)候,并且LatchEnable引腳為高電平的時(shí)候,Q端電平與D端電平相同。結(jié)合我們的LED硬件連接圖可以知道LED_CS端為高電平時(shí)候,P0口電平的變化即Q端的電平的變化,進(jìn)而引起LED的亮滅變化。由于單片機(jī)的驅(qū)動(dòng)能力有限,在此,74HC573的主要作用就是起一個(gè)輸出驅(qū)動(dòng)的作用。需要注意的是,通過74HC573的最大電流是有限制的,否則可能會(huì)燒壞74HC573這個(gè)芯片。

   
  
上面這個(gè)圖是從74HC573的DATASHEET中截取出來的,從上可以看出,每個(gè)引腳允許通過的最大電流為35mA 整個(gè)芯片允許通過的最大電流為75mA。在我們?cè)O(shè)計(jì)相應(yīng)的驅(qū)動(dòng)電路時(shí)候,這些參數(shù)是相當(dāng)重要的,而且是最容易被初學(xué)者所忽略的地方。同時(shí)在設(shè)計(jì)的時(shí)候,要留出一定量的余量出來,不能說單個(gè)引腳允許通過的電流為35mA,你就設(shè)計(jì)為35mA,這個(gè)時(shí)候你應(yīng)該把設(shè)計(jì)的上限值定在20mA左右才能保證能夠穩(wěn)定的工作。
(設(shè)計(jì)相應(yīng)驅(qū)動(dòng)電路時(shí)候,應(yīng)該仔細(xì)閱讀芯片的數(shù)據(jù)手冊(cè),了解每個(gè)引腳的驅(qū)動(dòng)能力,以及整個(gè)芯片的驅(qū)動(dòng)能力)

    了解了相應(yīng)的硬件后,我們?cè)賮砭帉戲?qū)動(dòng)程序。
    首先定義LED的接口
    #define LED  P0
        然后為亮滅常數(shù)定義一個(gè)宏,由硬件連接圖可以,當(dāng)P0輸出為低電平時(shí)候LED亮,P0輸出為高電平時(shí),LED熄滅。
    #define LED_ON()    LED = 0x00  //所有LED亮
    #define LED_OFF()    LED = 0xff  //所有LED熄滅
    下面到了重點(diǎn)了,究竟該如何釋放CPU,避免其做延時(shí)空等待這樣的事情呢。很簡(jiǎn)單,我們?yōu)橄到y(tǒng)產(chǎn)生一個(gè)1MS的時(shí)標(biāo)。假定LED需要亮500MS,熄滅500MS,那么我們可以對(duì)這個(gè)1MS的時(shí)標(biāo)進(jìn)行計(jì)數(shù),當(dāng)這個(gè)計(jì)數(shù)值達(dá)到500時(shí)候,清零該計(jì)數(shù)值,同時(shí)把LED的狀態(tài)改變。
unsigned int g_u16LedTimeCount = 0 ;    //LED計(jì)數(shù)器
unsigned char g_u8LedState = 0 ;      //LED狀態(tài)標(biāo)志, 0表示亮,1表示熄滅

void LedProcess(void)
{
    if(0 == g_u8LedState)  //如果LED的狀態(tài)為亮,則點(diǎn)亮LED
    {
        LED_ON() ;
    }
    else                //否則熄滅LED
    {
        LED_OFF() ;
    }
}


void LedStateChange(void)
{
    if(g_bSystemTime1Ms)            //系統(tǒng)1MS時(shí)標(biāo)到
  {
        g_bSystemTime1Ms = 0 ;
        g_u16LedTimeCount++ ;      //LED計(jì)數(shù)器加一
    if(g_u16LedTimeCount >= 500)  //計(jì)數(shù)達(dá)到500,即500MS到了,改變LED的狀態(tài)。
    {
            g_u16LedTimeCount = 0 ;
            g_u8LedState  = ! g_u8LedState ;
      }
    }
}

上面有一個(gè)變量沒有提到,就是g_bSystemTime1Ms 。這個(gè)變量可以定義為位變量或者是其它變量,在我們的定時(shí)器中斷函數(shù)中對(duì)其置位,其它函數(shù)使用該變量后,應(yīng)該對(duì)其復(fù)位(清0) 。
我們的主函數(shù)就可以寫成如下形式(示意代碼)
void main(void)
{
    while(1)
    {
        LedProcess() ;
        LedStateChange() ;
    }  
}

因?yàn)長(zhǎng)ED的亮或者滅依賴于LED狀態(tài)變量(g_u8LedState)的改變,而狀態(tài)變量的改變,又依賴于LED計(jì)數(shù)器的計(jì)數(shù)值(g_u16LedTimeCount ,只有計(jì)數(shù)值達(dá)到一定后,狀態(tài)變量才改變)所以,兩個(gè)函數(shù)都沒有堵塞CPU的地方。讓我們來從頭到尾分析一遍整個(gè)程序的流程。

程序首先執(zhí)行LedProcess() ;函數(shù)
因?yàn)間_u8LedState 的初始值為0 (見定義,對(duì)于全局變量,在定義的時(shí)候最好給其一個(gè)確定的值)所以LED被點(diǎn)亮,然后退出LedStateChange()函數(shù),執(zhí)行下一個(gè)函數(shù)LedStateChange()
在函數(shù)LedStateChange()內(nèi)部首先判斷1MS的系統(tǒng)時(shí)標(biāo)是否到了,如果沒有到就直接退出函數(shù),如果到了,就把時(shí)標(biāo)清0以便下一個(gè)時(shí)標(biāo)消息的到來,同時(shí)對(duì)LED計(jì)數(shù)器加一,然后再判斷LED計(jì)數(shù)器是否到達(dá)我們預(yù)先想要的值500,如果沒有,則退出函數(shù),如果有,對(duì)計(jì)數(shù)器清0,以便下次重新計(jì)數(shù),同時(shí)把LED狀態(tài)變量取反,然后退出函數(shù)。
由上面整個(gè)流程可以知道,CPU所做的事情,就是對(duì)一些計(jì)數(shù)器加一,然后根據(jù)條件改變狀態(tài),再根據(jù)這個(gè)狀態(tài)來決定是否點(diǎn)亮LED。這些函數(shù)執(zhí)行所花的時(shí)間都是相當(dāng)短的,如果主程序中還有其它函數(shù),則CPU會(huì)順次往下執(zhí)行下去。對(duì)于其它的函數(shù)(如果有的話)也要采取同樣的措施,保證其不堵塞CPU,如果全部基于這種方法設(shè)計(jì),那么對(duì)于不是非常龐大的系統(tǒng),我們的系統(tǒng)依舊可以保證多個(gè)任務(wù)(多個(gè)函數(shù))同時(shí)執(zhí)行。系統(tǒng)的實(shí)時(shí)性得到了一定的保證,從宏觀上看來,就是多個(gè)任務(wù)并發(fā)執(zhí)行。

好了,這一章就到此為止,讓我們總結(jié)一下,究竟有哪些需要注意的吧。

(1)    無論什么時(shí)候我們都要以實(shí)際應(yīng)用的角度去考慮程序的編寫。
(2)    無論什么時(shí)候都不要讓CPU白白浪費(fèi)等待,尤其是延時(shí)(超過1MS)這樣的地方。
(3)    設(shè)計(jì)相應(yīng)驅(qū)動(dòng)電路時(shí)候,應(yīng)該仔細(xì)閱讀芯片的數(shù)據(jù)手冊(cè),了解每個(gè)引腳的驅(qū)動(dòng)能力,
          以及整個(gè)芯片的驅(qū)動(dòng)能力
(4)    最重要的是,如何去釋放CPU(參考本章的例子),這是寫出合格程序的基礎(chǔ)。


附完整程序代碼(基于51hei單片機(jī)開發(fā)板)

#include<reg52.h>

sbit LED_SEG  = P1^4;  //數(shù)碼管段選
sbit LED_DIG  = P1^5;  //數(shù)碼管位選
sbit LED_CS11 = P1^6;  //led控制位
sbit ir=P1^7;
#define LED P0            //定義LED接口
bit  g_bSystemTime1Ms = 0 ;              // 1MS系統(tǒng)時(shí)標(biāo)
unsigned int  g_u16LedTimeCount = 0 ; //LED計(jì)數(shù)器
unsigned char g_u8LedState = 0 ;      //LED狀態(tài)標(biāo)志, 0表示亮,1表示熄滅

#define LED_ON()      LED = 0x00 ;  //所有LED亮
#define LED_OFF()    LED = 0xff ;  //所有LED熄滅

void Timer0Init(void)
{
    TMOD &= 0xf0 ;
    TMOD |= 0x01 ;      //定時(shí)器0工作方式1
    TH0  =    0xfc ;      //定時(shí)器初始值
    TL0  =  0x66 ;
    TR0  = 1 ;
    ET0  = 1 ;
}
void LedProcess(void)
{
    if(0 == g_u8LedState)  //如果LED的狀態(tài)為亮,則點(diǎn)亮LED
    {
        LED_ON() ;
    }
    else                //否則熄滅LED
    {
        LED_OFF() ;
    }
}

void LedStateChange(void)
{
    if(g_bSystemTime1Ms)            //系統(tǒng)1MS時(shí)標(biāo)到
  {
            g_bSystemTime1Ms = 0 ;
            g_u16LedTimeCount++ ;      //LED計(jì)數(shù)器加一
      if(g_u16LedTimeCount >= 500)  //計(jì)數(shù)達(dá)到500,即500MS到了,改變LED的狀態(tài)。
      {
      g_u16LedTimeCount = 0 ;
      g_u8LedState  = ! g_u8LedState    ;
          }
    }
}

void main(void)
{
    Timer0Init() ;
    EA = 1 ;
                    LED_CS11 = 1 ; //74HC595輸出允許
    LED_SEG = 0 ;  //數(shù)碼管段選和位選禁止(因?yàn)樗鼈兒蚅ED共用P0口)
    LED_DIG = 0 ;
    while(1)
    {
          LedProcess() ;
          LedStateChange() ;
    }
}  

void Time0Isr(void) interrupt 1
{
    TH0  =    0xfc ;            //定時(shí)器重新賦初值
    TL0  =  0x66 ;
    g_bSystemTime1Ms = 1 ;    //1MS時(shí)標(biāo)標(biāo)志位置位
}




                                                第三章----模塊化編程初識(shí)


      好的開始是成功的一半
  通過上一章的學(xué)習(xí),我想你已經(jīng)掌握了如何在程序中釋放CPU了。希望能夠繼續(xù)堅(jiān)持下去。一個(gè)良好的開始是成功的一半。我們今天所做的一切都是為了在單片機(jī)編程上做的更好。
在談?wù)摻裉斓闹黝}之前,先說下我以前的一些經(jīng)歷。在剛開始接觸到C語言程序的時(shí)候,由于學(xué)習(xí)內(nèi)容所限,寫的程序都不是很大,一般也就幾百行而矣。所以所有的程序都完成在一個(gè)源文件里面。記得那時(shí)候大一參加學(xué)校里的一個(gè)電子設(shè)計(jì)大賽,調(diào)試了一個(gè)多星期,所有程序加起來大概將近1000行,長(zhǎng)長(zhǎng)的一個(gè)文件,從上瀏覽下來都要好半天。出了錯(cuò)誤簡(jiǎn)單的語法錯(cuò)誤還好定位,其它一些錯(cuò)誤,往往找半天才找的到。那個(gè)時(shí)候開始知道了模塊化編程這個(gè)東西,也嘗試著開始把程序分模塊編寫。最開始是把相同功能的一些函數(shù)(譬如1602液晶的驅(qū)動(dòng))全部寫在一個(gè)頭文件(.h)文件里面,然后需要調(diào)用的地方包含進(jìn)去,但是很快發(fā)現(xiàn)這種方法有其局限性,很容易犯重復(fù)包含的錯(cuò)誤。
而且調(diào)用起來也很不方便。很快暑假的電子設(shè)計(jì)大賽來臨了,學(xué)校對(duì)我們的單片機(jī)軟件編程進(jìn)行了一些培訓(xùn)。由于學(xué)校歷年來參加國賽和省賽,因此積累了一定數(shù)量的驅(qū)動(dòng)模塊,那些日子,老師每天都會(huì)布置一定量的任務(wù),讓我們用這些模塊組合起來,完成一定功能。而正是那些日子模塊化編程的培訓(xùn),使我對(duì)于模塊化編程有了更進(jìn)一步的認(rèn)識(shí)。并且程序規(guī)范也開始慢慢注意起來。此后的日子,無論程序的大小,均采用模塊化編程的方式去編寫。很長(zhǎng)一段時(shí)間以來,一直有單片機(jī)愛好者在QQ上和我一起交流。有時(shí)候,他們會(huì)發(fā)過來一些有問題的程序源文件,讓我?guī)兔π薷囊幌隆M瑯邮情L(zhǎng)長(zhǎng)的一個(gè)文件,而且命名極不規(guī)范,從頭看下來,著實(shí)是痛苦,說實(shí)話,還真不如我重新給他們寫一個(gè)更快一些,此話到不假,因?yàn)槭诸^積累了一定量的模塊,在完成一個(gè)新的系統(tǒng)時(shí)候,只需要根據(jù)上層功能需求,在底層模塊的支持下,可以很快方便的完成。而不需要從頭到尾再一磚一瓦的重新編寫。藉此,也可以看出模塊化編程的一個(gè)好處,就是可重復(fù)利用率高。下面讓我們揭開模塊化神秘面紗,一窺其真面目。
    C語言源文件 *.c
        提到C語言源文件,大家都不會(huì)陌生。因?yàn)槲覀兤匠懙某绦虼a幾乎都在這個(gè)XX.C文件里面。編譯器也是以此文件來進(jìn)行編譯并生成相應(yīng)的目標(biāo)文件。作為模塊化編程的組成基礎(chǔ),我們所要實(shí)現(xiàn)的所有功能的源代碼均在這個(gè)文件里。理想的模塊化應(yīng)該可以看成是一個(gè)黑盒子。即我們只關(guān)心模塊提供的功能,而不管模塊內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。好比我們買了一部手機(jī),我們只需要會(huì)用手機(jī)提供的功能即可,不需要知曉它是如何把短信發(fā)出去的,如何響應(yīng)我們按鍵的輸入,這些過程對(duì)我們用戶而言,就是是一個(gè)黑盒子。
在大規(guī)模程序開發(fā)中,一個(gè)程序由很多個(gè)模塊組成,很可能,這些模塊的編寫任務(wù)被分配到不同的人。而你在編寫這個(gè)模塊的時(shí)候很可能就需要利用到別人寫好的模塊的借口,這個(gè)時(shí)候我們關(guān)心的是,它的模塊實(shí)現(xiàn)了什么樣的接口,我該如何去調(diào)用,至于模塊內(nèi)部是如何組織的,對(duì)于我而言,無需過多關(guān)注。而追求接口的單一性,把不需要的細(xì)節(jié)盡可能對(duì)外部屏蔽起來,正是我們所需要注意的地方。
    C語言頭文件 *.h
        談及到模塊化編程,必然會(huì)涉及到多文件編譯,也就是工程編譯。在這樣的一個(gè)系統(tǒng)中,往往會(huì)有多個(gè)C文件,而且每個(gè)C文件的作用不盡相同。在我們的C文件中,由于需要對(duì)外提供接口,因此必須有一些函數(shù)或者是變量提供給外部其它文件進(jìn)行調(diào)用。
假設(shè)我們有一個(gè)LCD.C文件,其提供最基本的LCD的驅(qū)動(dòng)函數(shù)
    LcdPutChar(char cNewValue) ;  //在當(dāng)前位置輸出一個(gè)字符
而在我們的另外一個(gè)文件中需要調(diào)用此函數(shù),那么我們?cè)撊绾巫瞿兀?
    頭文件的作用正是在此。可以稱其為一份接口描述文件。其文件內(nèi)部不應(yīng)該包含任何實(shí)質(zhì)性的函數(shù)代碼。我們可以把這個(gè)頭文件理解成為一份說明書,說明的內(nèi)容就是我們的模塊對(duì)外提供的接口函數(shù)或者是接口變量。同時(shí)該文件也包含了一些很重要的宏定義以及一些結(jié)構(gòu)體的信息,離開了這些信息,很可能就無法正常使用接口函數(shù)或者是接口變量。但是總的原則是:不該讓外界知道的信息就不應(yīng)該出現(xiàn)在頭文件里,而外界調(diào)用模塊內(nèi)接口函數(shù)或者是接口變量所必須的信息就一定要出現(xiàn)在頭文件里,否則,外界就無法正確的調(diào)用我們提供的接口功能。因而為了讓外部函數(shù)或者文件調(diào)用我們提供的接口功能,就必須包含我們提供的這個(gè)接口描述文件----即頭文件。同時(shí),我們自身模塊也需要包含這份模塊頭文件(因?yàn)槠浒四K源文件中所需要的宏定義或者是結(jié)構(gòu)體),好比我們平常所用的文件都是一式三份一樣,模塊本身也需要包含這個(gè)頭文件。
下面我們來定義這個(gè)頭文件,一般來說,頭文件的名字應(yīng)該與源文件的名字保持一致,這樣我們便可以清晰的知道哪個(gè)頭文件是哪個(gè)源文件的描述。
        于是便得到了LCD.C的頭文件LCD.h 其內(nèi)容如下。
        #ifndef    _LCD_H_
                #define     _LCD_H_
                extern   LcdPutChar(char cNewValue) ;
                #endif

    這與我們?cè)谠次募卸x函數(shù)時(shí)有點(diǎn)類似。不同的是,在其前面添加了extern 修飾符表明其是一個(gè)外部函數(shù),可以被外部其它模塊進(jìn)行調(diào)用。
        #ifndef     _LCD_H_
              #define     _LCD_H_
              #endif

              這個(gè)幾條條件編譯和宏定義是為了防止重復(fù)包含。假如有兩個(gè)不同源文件需要調(diào)用LcdPutChar(char cNewValue)這個(gè)函數(shù),他們分別都通過#include “Lcd.h”把這個(gè)頭文件包含了進(jìn)去。在第一個(gè)源文件進(jìn)行編譯時(shí)候,由于沒有定義過 _LCD_H_ 因此 #ifndef _LCD_H_ 條件成立,于是定義_LCD_H_ 并將下面的聲明包含進(jìn)去。在第二個(gè)文件編譯時(shí)候,由于第一個(gè)文件包含時(shí)候,已經(jīng)將_LCD_H_定義過了。因此#ifndef _LCD_H_ 不成立,整個(gè)頭文件內(nèi)容就沒有被包含。假設(shè)沒有這樣的條件編譯語句,那么兩個(gè)文件都包含了extern  LcdPutChar(char cNewValue) ; 就會(huì)引起重復(fù)包含的錯(cuò)誤。
    不得不說的typedef  
          很多朋友似乎了習(xí)慣程序中利用如下語句來對(duì)數(shù)據(jù)類型進(jìn)行定義
    #define uint  unsigned int  
        #define uchar  unsigned char
    然后在定義變量的時(shí)候 直接這樣使用
  uint  g_nTimeCounter = 0 ;
    不可否認(rèn),這樣確實(shí)很方便,而且對(duì)于移植起來也有一定的方便性。但是考慮下面這種情況你還會(huì) 這么認(rèn)為嗎?
  #define PINT unsigned int *  //定義unsigned int 指針類型
  PINT  g_npTimeCounter, g_npTimeState ;
      那么你到底是定義了兩個(gè)unsigned int 型的指針變量,還是一個(gè)指針變量,一個(gè)整形變量呢?而你的初衷又是什么呢,想定義兩個(gè)unsigned int 型的指針變量嗎?如果是這樣,那么估計(jì)過不久就會(huì)到處抓狂找錯(cuò)誤了。
    慶幸的是C語言已經(jīng)為我們考慮到了這一點(diǎn)。typedef 正是為此而生。為了給變量起一個(gè)別名我們可以用如下的語句
    typedef  unsigned  int    uint16 ;    //給指向無符號(hào)整形變量起一個(gè)別名 uint16
      typedef  unsigned  int  * puint16 ;  //給指向無符號(hào)整形變量指針起一個(gè)別名 puint16
    在我們定義變量時(shí)候便可以這樣定義了:

  uint16    g_nTimeCounter  =  0 ;  //定義一個(gè)無符號(hào)的整形變量
  puint16  g_npTimeCounter  ;    //定義一個(gè)無符號(hào)的整形變量的指針
  在我們使用51單片機(jī)的C語言編程的時(shí)候,整形變量的范圍是16位,而在基于32的微處理下的整形變量是32位。倘若我們?cè)?位單片機(jī)下編寫的一些代碼想要移植到32位的處理器上,那么很可能我們就需要在源文件中到處修改變量的類型定義。這是一件龐大的工作,為了考慮程序的可移植性,在一開始,我們就應(yīng)該養(yǎng)成良好的習(xí)慣,用變量的別名進(jìn)行定義。
如在8位單片機(jī)的平臺(tái)下,有如下一個(gè)變量定義
    uint16    g_nTimeCounter  =  0 ;
        如果移植32單片機(jī)的平臺(tái)下,想要其的范圍依舊為16位。
    可以直接修改uint16 的定義,即
    typedef  unsigned  short  int    uint16 ;  
        這樣就可以了,而不需要到源文件處處尋找并修改。

將常用的數(shù)據(jù)類型全部采用此種方法定義,形成一個(gè)頭文件,便于我們以后編程直接調(diào)用。
文件名 MacroAndConst.h
其內(nèi)容如下:
#ifndef   _MACRO_AND_CONST_H_
#define   _MACRO_AND_CONST_H_

typedef    unsigned int    uint16;  
typedef    unsigned int   UINT;  
typedef    unsigned int   uint;  
typedef    unsigned int   UINT16;  
typedef    unsigned int   WORD;  
typedef    unsigned int   word;
typedef      int        int16;  
typedef      int        INT16;  
typedef    unsigned long  uint32;  

typedef    unsigned long     UINT32;  
typedef    unsigned long    DWORD;  
typedef    unsigned long    dword;  
typedef    long            int32;  
typedef    long            INT32;  
typedef    signed  char     int8;
typedef    signed  char     INT8;  
typedef    unsigned char      byte;  
typedef    unsigned char     BYTE;  
typedef    unsigned char     uchar;
typedef    unsigned char     UINT8;  
typedef    unsigned char    uint8;
typedef    unsigned char    BOOL;  

#endif

至此,似乎我們對(duì)于源文件和頭文件的分工以及模塊化編程有那么一點(diǎn)概念了。那么讓我們趁熱打鐵,將上一章的我們編寫的LED閃爍函數(shù)進(jìn)行模塊劃分并重新組織進(jìn)行編譯。

在上一章中我們主要完成的功能是P0口所驅(qū)動(dòng)的LED以1Hz的頻率閃爍。其中用到了定時(shí)器,以及LED驅(qū)動(dòng)模塊。因而我們可以簡(jiǎn)單的將整個(gè)工程分成三個(gè)模塊,定時(shí)器模塊,LED模塊,以及主函數(shù)
對(duì)應(yīng)的文件關(guān)系如下

main.c   
Timer.c  --Timer.h
Led.c      --Led.h
在開始重新編寫我們的程序之前,先給大家講一下如何在KEIL中建立工程模板吧,這個(gè)模板是我一直沿用至今。希望能夠給大家一點(diǎn)啟發(fā)。
下面的內(nèi)容就主要以圖片為主了。同時(shí)輔以少量文字說明。
我們以芯片AT89S52為例。





























































分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏13 分享淘帖 頂1 踩

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

無效樓層,該帖已經(jīng)被刪除
無效樓層,該帖已經(jīng)被刪除
無效樓層,該帖已經(jīng)被刪除
5#
ID:51183 發(fā)表于 2013-6-22 19:23 | 只看該作者
很好
回復(fù)

使用道具 舉報(bào)

6#
無效樓層,該帖已經(jīng)被刪除
7#
無效樓層,該帖已經(jīng)被刪除
8#
ID:52102 發(fā)表于 2013-7-23 22:47 | 只看該作者
很好的資料
回復(fù)

使用道具 舉報(bào)

9#
ID:52066 發(fā)表于 2013-7-24 08:56 | 只看該作者
very good{:soso_e179:}
回復(fù)

使用道具 舉報(bào)

10#
無效樓層,該帖已經(jīng)被刪除
11#
ID:56946 發(fā)表于 2013-11-16 16:04 | 只看該作者
回復(fù)

使用道具 舉報(bào)

12#
ID:50943 發(fā)表于 2013-12-12 17:41 | 只看該作者
好好好
回復(fù)

使用道具 舉報(bào)

13#
ID:57999 發(fā)表于 2013-12-18 22:05 | 只看該作者
好好學(xué)習(xí)
回復(fù)

使用道具 舉報(bào)

14#
ID:35471 發(fā)表于 2014-1-21 23:04 | 只看該作者
寫的的確不錯(cuò)
回復(fù)

使用道具 舉報(bào)

15#
ID:58905 發(fā)表于 2014-1-22 09:23 | 只看該作者
樓主你對(duì)我們初學(xué)者的貢獻(xiàn)真是太大了,學(xué)習(xí)了!
回復(fù)

使用道具 舉報(bào)

16#
無效樓層,該帖已經(jīng)被刪除
17#
ID:59700 發(fā)表于 2014-3-15 13:13 | 只看該作者

這么強(qiáng)大的流水燈 真想做一個(gè)
回復(fù)

使用道具 舉報(bào)

18#
無效樓層,該帖已經(jīng)被刪除
19#
ID:63772 發(fā)表于 2016-6-6 21:59 | 只看該作者
謝謝樓主分享!非常好的資料
回復(fù)

使用道具 舉報(bào)

20#
ID:278558 發(fā)表于 2018-1-22 13:42 | 只看該作者
佩服啊
回復(fù)

使用道具 舉報(bào)

21#
ID:268118 發(fā)表于 2018-1-22 20:12 | 只看該作者
感謝樓主的分享!深度好文。
回復(fù)

使用道具 舉報(bào)

22#
ID:111376 發(fā)表于 2018-1-31 12:14 | 只看該作者

感謝樓主的分享!點(diǎn)32個(gè)贊!
回復(fù)

使用道具 舉報(bào)

23#
ID:577469 發(fā)表于 2019-7-7 10:07 | 只看該作者
做個(gè)記號(hào),方便下次看
回復(fù)

使用道具 舉報(bào)

24#
ID:419368 發(fā)表于 2019-7-7 16:46 | 只看該作者
深度好文。
回復(fù)

使用道具 舉報(bào)

25#
ID:579329 發(fā)表于 2019-7-7 20:08 | 只看該作者
記號(hào),記號(hào),記號(hào)
回復(fù)

使用道具 舉報(bào)

26#
ID:232547 發(fā)表于 2019-10-11 10:38 | 只看該作者
模塊化的編程思想,講的不錯(cuò)。
回復(fù)

使用道具 舉報(bào)

27#
ID:622523 發(fā)表于 2019-10-11 21:21 | 只看該作者
謝謝樓主分享!
回復(fù)

使用道具 舉報(bào)

28#
無效樓層,該帖已經(jīng)被刪除
29#
無效樓層,該帖已經(jīng)被刪除
30#
無效樓層,該帖已經(jīng)被刪除

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表