規(guī)范化和模塊化編程
0 引言
通過(guò)一年多的編程經(jīng)歷,經(jīng)常會(huì)為雜亂無(wú)章的程序弄的暈頭轉(zhuǎn)向,影響編程質(zhì)量和進(jìn)度。同時(shí)也為了程序的可移植性和可讀性,規(guī)范化和模塊化編程應(yīng)該在開(kāi)始編寫(xiě)的第一個(gè)程序時(shí)就要有規(guī)范化和模塊化編程的思想,并在實(shí)踐中運(yùn)用,養(yǎng)成規(guī)范化和模塊化編程的好習(xí)慣。
1 規(guī)范化編程
談到規(guī)范性編程這里我們是在符合c語(yǔ)言基本運(yùn)用原理的基礎(chǔ)上加以說(shuō)明,以下我們主要講以下幾個(gè)方面:
1.1 定義一個(gè)自己config.h文件
首先我把我使用的config文件列出:
typedef signed char S8;
typedef signed int S16;
typedef signed long S32;
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
typedef volatile signed char vS8;
typedef volatile signed int vS16;
typedef volatile signed long vS32;
typedef volatile unsigned char vu8;
typedef volatile unsigned int vu16;
typedef volatile unsigned long vu32;
typedef const u8 FLASH;
typedef enum{FALSE=0,TRUE=!FALSE} BOOL;
為什么要定義一個(gè)自己的這樣一個(gè)文件,主要有兩個(gè)原因:
1節(jié)約編程時(shí)間
2更高的可移植性
同樣也是為本工程形成一種規(guī)范,這是一種局部規(guī)范,讀者可以定義一個(gè)適合自己的config文件。
1.2 變量名的選取
首先要知道變量名的組成成分:字母,下劃線(xiàn),數(shù)字;而且要注意的是數(shù)字不能作為開(kāi)頭,并且字母區(qū)分大小寫(xiě),下劃線(xiàn)主要的功能用于分隔兩個(gè)有意義的單詞或者是區(qū)別形參和實(shí)參等用途。
其次就是怎么正確選擇的問(wèn)題了,在開(kāi)始編程時(shí)大家都可能喜歡用a,b,c等簡(jiǎn)單字母作為變量名,這樣只是單純的定義了一個(gè)變量,讀者并不能從中獲取很多信息量,比如這個(gè)變量的用途等。所以為了能表達(dá)的更準(zhǔn)確并能獲得更多的信息量,應(yīng)該選取有意義的英文單詞或者中文拼音,可以用下劃線(xiàn)作為單詞之間,也可使用首字母大寫(xiě)區(qū)分,具體可根據(jù)個(gè)人編程習(xí)慣。
例:取一個(gè)關(guān)于定時(shí)器定時(shí)計(jì)數(shù)的變量,可以有以下幾種模式(僅供參考):
1U16 TimerCounter;
2U16 timer_counter;
這樣選取的變量名不僅達(dá)到了有意義的要求,而且更美觀。從接觸C到開(kāi)始編程就要養(yǎng)成一個(gè)良好的習(xí)慣,選取變量名是往往程序首先要做的事,所以變量名的選取也是規(guī)范化編程的第一步,很關(guān)鍵。
1.3 與硬件資源相關(guān)用define去定義
在說(shuō)明這個(gè)問(wèn)題之前,我們先看個(gè)例子:
#include reg51.h>
#include "config.h"
sbit led = P0^0;
void fun1(void);
void delay(void);
void main(void)
{
while(1)
{
delay();
fun1();
delay();
}
}
void fun1(void)
{
U8 i;
U8 temp = 0xfe;
led = 0;
for(i=0;i8;i++)
{
P1 = temp;
temp = temp 1;
delay();
}
led = 1;
}
void delay(void)
{
U8 i,j;
for(i=0;i200;i++)
{
for(j=0;i200;j++);
}
}
為了能形成對(duì)比,我們?cè)倏催\(yùn)用規(guī)范化編程原理的程序:
#include reg51.h>
#include "config.h"
sbit led = P0^0;
#define Led_On led = 0
#define Led_Off led = 1
#define LedCyclePort P1
void Soft_DealyTimer(void);
void LedCycleProc(void);
void main(void)
{
while(1)
{
Soft_DealyTimer();
LedCycleProc();
Soft_DealyTimer();
}
}
void LedCycleProc(void)
{
U8 i;
U8 temp = 0xfe;
Led_On;
for(i=0;i8;i++)
{
LedCyclePort = temp;
temp = temp 1;
Soft_DealyTimer();
}
Led_Off;
}
void Soft_DealyTimer(void)
{
U8 i,j;
for(i=0;i200;i++)
{
for(j=0;i200;j++);
}
}
通過(guò)以上兩個(gè)程序我們可以看出來(lái)具體區(qū)別是什么,程序中沒(méi)有了類(lèi)似于P1這種標(biāo)識(shí),而是巧妙的利用define定義P1,以及函數(shù)名的修改,都是為了體現(xiàn)有意義和可移植性的要求。以上只是一個(gè)很簡(jiǎn)單有關(guān)于define這個(gè)關(guān)鍵字的用法,巧妙運(yùn)用能使程序的可讀性和可移植性大大增強(qiáng),也是規(guī)范性編程不可或缺的關(guān)鍵因素。
1.4 合理選取變量的數(shù)據(jù)類(lèi)型,防止掉入C陷進(jìn)
在說(shuō)明之前先看一個(gè)簡(jiǎn)單的例子:
#include reg51.h>
#include "config.h"
sbit led = P0^0;
#define Led_On led = 0
#define Led_Off led = 1
void Soft_DealyTimer(void);
void main(void)
{
while(1)
{
Led_On;
Soft_DealyTimer();
Led_Off;
Soft_DealyTimer();
}
}
void Soft_DealyTimer(void)
{
U8 i,j;
for(i=0;i=256;i++)
{
for(j=0;i=200;j++);
}
}
初看覺(jué)得沒(méi)什么問(wèn)題,可是當(dāng)你下載到MCU運(yùn)行時(shí),你會(huì)發(fā)現(xiàn)燈永遠(yuǎn)是亮的,不會(huì)熄滅,為什么呢?我們來(lái)分析一下,燈亮說(shuō)明至少運(yùn)行到了while(1)中的Led_On語(yǔ)句,說(shuō)明應(yīng)該問(wèn)題就出在軟件延時(shí)函數(shù),細(xì)看我們發(fā)現(xiàn)i的取值大了,因?yàn)閁8 i的范圍是0~255,雖然我們知道unsigned char 是無(wú)符號(hào)8位,28值是256,但是要注意的是單片機(jī)初始值都是從0開(kāi)始的,所以要注意這些細(xì)節(jié)問(wèn)題。
有些人看了上面的例子會(huì)想,我都用long型或者int型就不是沒(méi)有問(wèn)題了嗎?但是你這樣的話(huà)就增大了MCU內(nèi)存的開(kāi)銷(xiāo),不利于程序快速運(yùn)行,所以合理選擇變量數(shù)據(jù)類(lèi)型也是很重要的。
1.5 在結(jié)構(gòu)體中按變量從小到大排列
先看個(gè)例子:
Struct
{
Int s; //占用第0和第1個(gè)字節(jié)
Char c1; //占用第2個(gè)字節(jié),由于對(duì)其原因,第3個(gè)字節(jié)為空
Long l; //占用第4,5,6,7個(gè)共四個(gè)字節(jié)
Char c2; //占用第8個(gè)字節(jié),第9個(gè)字節(jié)為空
}s;
由此可以看出浪費(fèi)了2個(gè)字節(jié)空間,所以我們應(yīng)該調(diào)整變量順序,如下:
Struct
{
Int s; //占用第0和第1個(gè)字節(jié)
Char c1; //占用第2個(gè)字節(jié)
Char c2; //占用第3個(gè)字節(jié)
Long l; //占用第4,5,6,7個(gè)共四個(gè)字節(jié)
}s;
為什么會(huì)有上述情況出現(xiàn),原因是結(jié)構(gòu)體變量是字對(duì)齊,但是在有些單片機(jī)中可以軟件設(shè)置為字節(jié)對(duì)齊,這樣也可以解決上述問(wèn)題,但是按順序存放明顯是規(guī)范性編程中的一員,一個(gè)好習(xí)慣不會(huì)因?yàn)槭韬鲈斐蓛?nèi)存開(kāi)銷(xiāo)增大。
2 模塊化編程
為什么要模塊化編程,主要原因當(dāng)然也是可讀性和可移植性。
模塊化編程思路:
1分析系統(tǒng)項(xiàng)目功能模塊,一般的系統(tǒng)可能有以下幾個(gè)模塊:最小系統(tǒng)模塊(能讓MCU工作的編程模塊),鍵盤(pán)和顯示模塊(一般會(huì)用譯碼鎖存器件,如智能調(diào)節(jié)儀所使用的是CH452),AD模塊(采集傳感器信號(hào)),繼電器模塊(控制一些器件工作,相當(dāng)于開(kāi)關(guān)),通訊模塊(UART)等。
2將每個(gè)模塊分別用.c和.h建立模塊編程,.h文件用來(lái)存放模塊相關(guān)資源定義,以及函數(shù)聲明等功能,.c文件用于存放該模塊功能程序代碼。
3用main.c將各個(gè)模塊串結(jié)成一個(gè)完整的系統(tǒng),在main函數(shù)中代碼要簡(jiǎn)潔,最好只有兩三個(gè)函數(shù),比如:
Void main(void)
{
System_Init();
While(1)
{
If(Key_Value)
Key_Handle();
else
System_Handle();
}
}
以上分析了模塊化編程的基本思路,然后我們?cè)賮?lái)具體看個(gè)例子,以通訊模塊為例:
先看.h文件:
#ifndef __uart_H
#define __uart_H
#include "config.h"
#define Buf_Max_Len 32
#define UART0_TX_ENABLE
#define UART0_TX_DISABLE
#define UART0_RX_ENABLE
#define UART0_RX_DISAbLE
typedef enum
{
Select_Uart0 = 0,
Select_Uart1
}UART_SelectTypeDef;
typedef enum
{
B9600_Freuency,
B2400_Freuency
}UART_CommMode;
typedef volatile struct
{
VU8 ReadIndex;
VU8 SendIndex;
VU8 CharCount;
VU8 Buffer[Buf_Max_Len];
}UART_TypeDef;
#endif
還有.c文件:
#include reg51.h>
#include "uart.h"
void UART_Init(UART_TypeDef *self,UART_SelectTypeDef in_sel,UART_CommMode in_mode)
{
switch(in_mode)//波特率設(shè)置
{
case B9600_Freuency:
// 相應(yīng)設(shè)置代碼
break;
case B2400_Freuency:
// 相應(yīng)設(shè)置代碼
break;
default:
break;
}
switch(in_sel)//串口選擇0或1
{
case Select_Uart0:
break;
case Select_Uart1:
break;
default:break;
}
}
void Buffer_Init(UART_TypeDef *self)
{
self->ReadIndex = self->SendIndex = self->CharCount = 0;
}
void UART_SendType(UART_TypeDef *self,U8 in_char)
{
if(self->CharCount Buf_Max_Len)
{
self->Buffer[self->SendIndex] = in_char;
self->SendIndex++;
self->CharCount++;
}
}
void UART_GetType(UART_TypeDef *self)
{
U8 ctmp = 0;
if(self->CharCount)
{
ctmp = self->Buffer[self->ReadIndex];
self->ReadIndex--;
self->CharCount--;
}
}
void UART_SendChar(UART_TypeDef *self,U8 in_char)
{
UART0_TX_ENABLE;
SBUF = UART_SendType(self->Buffer,in_char);
UART0_TX_DISABLE;
}
void UART_GetChar(UART_TypeDef *self,U8 in_char)
{
U8 ctmp;
UART0_RX_ENABLE;
UART_SendType(self->Buffer,SBUF);
UART0_RX_DISABLE;
}
以上的例子只是簡(jiǎn)單的說(shuō)明了模塊化編程原理及一般流程,可能我們已經(jīng)注意到形參使用的是指針結(jié)構(gòu)體,如此可以節(jié)約系統(tǒng)時(shí)間并減少系統(tǒng)內(nèi)存開(kāi)銷(xiāo)。
3 總結(jié)
編程習(xí)慣很重要,由于面對(duì)大型的工程和團(tuán)隊(duì)合作,養(yǎng)成一個(gè)規(guī)范化編程和模塊化編程的好習(xí)慣相當(dāng)重要,也可以說(shuō)是直接影響團(tuán)隊(duì)的工程進(jìn)程和新代碼成員的跟進(jìn)進(jìn)度,所以在開(kāi)始學(xué)習(xí)編寫(xiě)程序代碼前必須養(yǎng)成一個(gè)良好的編程習(xí)慣,規(guī)范化和模塊化編程是其精髓。
評(píng)論