這與我們在源文件中定義函數時有點類似。不同的是,在其前面添加了extern 修飾符表明其是一個外部函數,可以被外部其它模塊進行調用。
#ifndef _LCD_H_
#define _LCD_H_
#endif
這個幾條條件編譯和宏定義是為了防止重復包含。假如有兩個不同源文件需要調用LcdPutChar(char cNewValue)這個函數,他們分別都通過#include “Lcd.h”把這個頭文件包含了進去。在第一個源文件進行編譯時候,由于沒有定義過 _LCD_H_ 因此 #ifndef _LCD_H_ 條件成立,于是定義_LCD_H_ 并將下面的聲明包含進去。在第二個文件編譯時候,由于第一個文件包含時候,已經將_LCD_H_定義過了。因此#ifndef _LCD_H_ 不成立,整個頭文件內容就沒有被包含。假設沒有這樣的條件編譯語句,那么兩個文件都包含了extern LcdPutChar(char cNewValue) ; 就會引起重復包含的錯誤。
不得不說的typedef
很多朋友似乎了習慣程序中利用如下語句來對數據類型進行定義
#define uint unsigned int
#define uchar unsigned char
然后在定義變量的時候 直接這樣使用
uint g_nTimeCounter = 0 ;
不可否認,這樣確實很方便,而且對于移植起來也有一定的方便性。但是考慮下面這種情況你還會 這么認為嗎?
#define PINT unsigned int * //定義unsigned int 指針類型
PINT g_npTimeCounter, g_npTimeState ;
那么你到底是定義了兩個unsigned int 型的指針變量,還是一個指針變量,一個整形變量呢?而你的初衷又是什么呢,想定義兩個unsigned int 型的指針變量嗎?如果是這樣,那么估計過不久就會到處抓狂找錯誤了。
慶幸的是C語言已經為我們考慮到了這一點。typedef 正是為此而生。為了給變量起一個別名我們可以用如下的語句
typedef unsigned int uint16 ; //給指向無符號整形變量起一個別名 uint16
typedef unsigned int * puint16 ; //給指向無符號整形變量指針起一個別名 puint16
在我們定義變量時候便可以這樣定義了:
uint16 g_nTimeCounter = 0 ; //定義一個無符號的整形變量
puint16 g_npTimeCounter ; //定義一個無符號的整形變量的指針
在我們使用51單片機的C語言編程的時候,整形變量的范圍是16位,而在基于32的微處理下的整形變量是32位。倘若我們在8位單片機下編寫的一些代碼想要移植到32位的處理器上,那么很可能我們就需要在源文件中到處修改變量的類型定義。這是一件龐大的工作,為了考慮程序的可移植性,在一開始,我們就應該養成良好的習慣,用變量的別名進行定義。
如在8位單片機的平臺下,有如下一個變量定義
uint16 g_nTimeCounter = 0 ;
如果移植32單片機的平臺下,想要其的范圍依舊為16位。
可以直接修改uint16 的定義,即
typedef unsigned short int uint16 ;
這樣就可以了,而不需要到源文件處處尋找并修改。
將常用的數據類型全部采用此種方法定義,形成一個頭文件,便于我們以后編程直接調用。
文件名 MacroAndConst.h
其內容如下:
#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
至此,似乎我們對于源文件和頭文件的分工以及模塊化編程有那么一點概念了。那么讓我們趁熱打鐵,將上一章的我們編寫的LED閃爍函數進行模塊劃分并重新組織進行編譯。
在上一章中我們主要完成的功能是P0口所驅動的LED以1Hz的頻率閃爍。其中用到了定時器,以及LED驅動模塊。因而我們可以簡單的將整個工程分成三個模塊,定時器模塊,LED模塊,以及主函數
對應的文件關系如下
main.c
Timer.c --?Timer.h
Led.c --?Led.h
在開始重新編寫我們的程序之前,先給大家講一下如何在KEIL中建立工程模板吧,這個模板是我一直沿用至今。希望能夠給大家一點啟發。
下面的內容就主要以圖片為主了。同時輔以少量文字說明。
我們以芯片AT89S52為例。