久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2078|回復: 0
收起左側

C語言模塊化編程

[復制鏈接]
ID:244597 發表于 2017-10-31 22:59 | 顯示全部樓層 |閱讀模式
單片機 C 語言模塊化編程
下面讓我們揭開模塊化神秘面紗,一窺其真面目。
C 語言源文件 *.c
提到 C 語言源文件,大家都不會陌生。因為我們平常寫的程序代碼幾乎都在這個 XX.C 文件里面。編譯
器也是以此文件來進行編譯并生成相應的目標文件。作為模塊化編程的組成基礎,我們所要實現的所有功能的源
代碼均在這個文件里。理想的模塊化應該可以看成是一個黑盒子。即我們只關心模塊提供的功能,而不管模塊內
部的實現細節。好比我們買了一部手機,我們只需要會用手機提供的功能即可,不需要知曉它是如何把短信發出
去的,如何響應我們按鍵的輸入,這些過程對我們用戶而言,就是是一個黑盒子。
在大規模程序開發中,一個程序由很多個模塊組成,很可能,這些模塊的編寫任務被分配到不同的人。而你在編
寫這個模塊的時候很可能就需要利用到別人寫好的模塊的借口,這個時候我們關心的是,它的模塊實現了什么樣
的接口,我該如何去調用,至于模塊內部是如何組織的,對于我而言,無需過多關注。而追求接口的單一性,把
不需要的細節盡可能對外部屏蔽起來,正是我們所需要注意的地方。
C 語言頭文件 *.h
談及到模塊化編程,必然會涉及到多文件編譯,也就是工程編譯。在這樣的一個系統中,往往會有多個
C 文件,而且每個 C 文件的作用不盡相同。在我們的 C 文件中,由于需要對外提供接口,因此必須有一些函數或
者是變量提供給外部其它文件進行調用。
假設我們有一個 LCD.C 文件,其提供最基本的 LCD 的驅動函數
LcdPutChar(char cNewValue) ; //在當前位置輸出一個字符
而在我們的另外一個文件中需要調用此函數,那么我們該如何做呢?
頭文件的作用正是在此。可以稱其為一份接口描述文件。其文件內部不應該包含任何實質性的函數代碼。我
們可以把這個頭文件理解成為一份說明書,說明的內容就是我們的模塊對外提供的接口函數或者是接口變量。同
時該文件也包含了一些很重要的宏定義以及一些結構體的信息,離開了這些信息,很可能就無法正常使用接口函
數或者是接口變量。但是總的原則是:不該讓外界知道的信息就不應該出現在頭文件里,而外界調用模塊內接口
函數或者是接口變量所必須的信息就一定要出現在頭文件里,否則,外界就無法正確的調用我們提供的接口功能。
因而為了讓外部函數或者文件調用我們提供的接口功能,就必須包含我們提供的這個接口描述文件----即頭文件。
同時,我們自身模塊也需要包含這份模塊頭文件(因為其包含了模塊源文件中所需要的宏定義或者是結構體),好
比我們平常所用的文件都是一式三份一樣,模塊本身也需要包含這個頭文件。
下面我們來定義這個頭文件,一般來說,頭文件的名字應該與源文件的名字保持一致,這樣我們便可以清晰的知
道哪個頭文件是哪個源文件的描述。
于是便得到了 LCD.C 的頭文件 LCD.h 其內容如下。
#ifndef _LCD_H_
#define _LCD_H_
extern LcdPutChar(char cNewValue) ;
#endif
這與我們在源文件中定義函數時有點類似。不同的是,在其前面添加了 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為例。
OK ,到此一個簡單的工程模板就建立起來了,以后我們再新建源文件和頭文件的時候,就可以直接保存到 src
文件目錄下面了。
下面我們開始編寫各個模塊文件。
首先編寫 Timer.c 這個文件主要內容就是定時器初始化,以及定時器中斷服務函數。其內容如下。
#include <reg52.h>
bit g_bSystemTime1Ms = 0 ; // 1MS 系統時標
void Timer0Init(void)
{
TMOD &= 0xf0 ;
TMOD |= 0x01 ; //定時器0工作方式1
TH0 = 0xfc ; //定時器初始值
TL0 = 0x66 ;
TR0 = 1 ;
ET0 = 1 ;
}
void Time0Isr(void) interrupt 1
{
TH0 = 0xfc ; //定時器重新賦初值
TL0 = 0x66 ;
g_bSystemTime1Ms = 1 ; //1MS 時標標志位置位
}
由于在 Led.c 文件中需要調用我們的 g_bSystemTime1Ms 變量。同時主函數需要調用 Timer0Init()初始化函數,
所以應該對這個變量和函數在頭文件里作外部聲明。以方便其它函數調用。
Timer.h 內容如下。
#ifndef _TIMER_H_
#define _TIMER_H_
extern void Timer0Init(void) ;
extern bit g_bSystemTime1Ms ;
#endif
完成了定時器模塊后,我們開始編寫 LED 驅動模塊。
Led.c 內容如下:
#include <reg52.h>
#include "MacroAndConst.h"
#include "Led.h"
#include "Timer.h"
static uint16 g_u16LedTimeCount = 0 ; //LED 計數器
static uint8 g_u8LedState = 0 ; //LED 狀態標志, 0表示亮,1表示熄滅
#define LED P0 //定義 LED 接口
#define LED_ON() LED = 0x00 ; //所有 LED 亮
#define LED_OFF() LED = 0xff ; //所有 LED 熄滅
void LedProcess(void)
{
if(0 == g_u8LedState) //如果 LED 的狀態為亮,則點亮 LED
{
LED_ON() ;
}
else //否則熄滅 LED
{
LED_OFF() ;
}
}
void LedStateChange(void)
{
if(g_bSystemTime1Ms) //系統1MS 時標到
{
g_bSystemTime1Ms = 0 ;
g_u16LedTimeCount++ ; //LED 計數器加一
if(g_u16LedTimeCount >= 500) //計數達到500,即500MS 到了,改變 LED 的狀態。
{
g_u16LedTimeCount = 0 ;
g_u8LedState = ! g_u8LedState ;
}
}
}
這個模塊對外的借口只有兩個函數,因此在相應的 Led.h 中需要作相應的聲明。
Led.h 內容:
#ifndef _LED_H_
#define _LED_H_
extern void LedProcess(void) ;
extern void LedStateChange(void) ;
#endif
這兩個模塊完成后,我們將其 C 文件添加到工程中。然后開始編寫主函數里的代碼。
如下所示:
#include <reg52.h>
#include "MacroAndConst.h"
#include "Timer.h"
#include "Led.h"
sbit LED_SEG = P1^4; //數碼管段選
sbit LED_DIG = P1^5; //數碼管位選
sbit LED_CS11 = P1^6; //led 控制位
void main(void)
{
LED_CS11 = 1 ; //74HC595輸出允許
LED_SEG = 0 ; //數碼管段選和位選禁止(因為它們和 LED 共用 P0口)
LED_DIG = 0 ;
Timer0Init() ;
EA = 1 ;
while(1)
{
LedProcess() ;
LedStateChange() ;
}
}
整個工程截圖如下:
至此,第三章到此結束。
一起來總結一下我們需要注意的地方吧
1. C 語言源文件(*.c)的作用是什么
2. C 語言頭文件(*.h)的作用是什么
3. typedef 的作用
4. 工程模板如何組織
5. 如何創建一個多模塊(多文件)的工程
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 99视频网| av高清| 亚洲精品天堂 | 欧美午夜精品久久久久免费视 | 天天操网 | 成人久久18免费网站麻豆 | 亚洲精品一区二三区不卡 | 91精品国产91久久久久久最新 | 精品久久电影 | 国产精品久久久久久久久图文区 | 日本久久久影视 | 999久久久久久久久6666 | 国产免费视频在线 | 男女午夜激情视频 | 国产ts人妖系列高潮 | 国产日韩免费观看 | 国产高清视频在线观看 | 日韩在线不卡 | 91久久久精品国产一区二区蜜臀 | 久久久成人网 | 最新日韩av | 午夜男人的天堂 | 亚洲协和影视 | 色悠悠久 | 91色视频在线观看 | 成人免费三级电影 | 国产一区二区免费 | 精品一区二区三区在线观看国产 | 精品无码久久久久国产 | 观看av | 国产乱码精品一区二区三区中文 | 资源首页二三区 | 欧美亚洲国产精品 | 亚洲激情一级片 | 噜久寡妇噜噜久久寡妇 | 日韩精品 电影一区 亚洲 | a视频在线 | 欧美国产精品一区二区 | 国产在线对白 | 红桃视频一区二区三区免费 | 日韩一级在线 |