0 引言
通過一年多的編程經歷,經常會為雜亂無章的程序弄的暈頭轉向,影響編程質量和進度。同時也為了程序的可移植性和可讀性,規范化和模塊化編程應該在開始編寫的第一個程序時就要有規范化和模塊化編程的思想,并在實踐中運用,養成規范化和模塊化編程的好習慣。
1 規范化編程
談到規范性編程這里我們是在符合c語言基本運用原理的基礎上加以說明,以下我們主要講以下幾個方面:
1.1 定義一個自己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;
為什么要定義一個自己的這樣一個文件,主要有兩個原因:
1節約編程時間
2更高的可移植性
同樣也是為本工程形成一種規范,這是一種局部規范,讀者可以定義一個適合自己的config文件。
1.2 變量名的選取
首先要知道變量名的組成成分:字母,下劃線,數字;而且要注意的是數字不能作為開頭,并且字母區分大小寫,下劃線主要的功能用于分隔兩個有意義的單詞或者是區別形參和實參等用途。
其次就是怎么正確選擇的問題了,在開始編程時大家都可能喜歡用a,b,c等簡單字母作為變量名,這樣只是單純的定義了一個變量,讀者并不能從中獲取很多信息量,比如這個變量的用途等。所以為了能表達的更準確并能獲得更多的信息量,應該選取有意義的英文單詞或者中文拼音,可以用下劃線作為單詞之間,也可使用首字母大寫區分,具體可根據個人編程習慣。
例:取一個關于定時器定時計數的變量,可以有以下幾種模式(僅供參考):
1U16 TimerCounter;
2U16 timer_counter;
這樣選取的變量名不僅達到了有意義的要求,而且更美觀。從接觸C到開始編程就要養成一個良好的習慣,選取變量名是往往程序首先要做的事,所以變量名的選取也是規范化編程的第一步,很關鍵。
1.3 與硬件資源相關用define去定義
在說明這個問題之前,我們先看個例子:
#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;i<8;i++) { P1 = temp; temp = temp << 1; delay(); } led = 1; } void delay(void) { U8 i,j; for(i=0;i<200;i++) { for(j=0;i<200;j++); } } 為了能形成對比,我們再看運用規范化編程原理的程序: #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;i<8;i++) { LedCyclePort = temp; temp = temp << 1; Soft_DealyTimer(); } Led_Off; } void Soft_DealyTimer(void) { U8 i,j; for(i=0;i<200;i++) { for(j=0;i<200;j++); } }
通過以上兩個程序我們可以看出來具體區別是什么,程序中沒有了類似于P1這種標識,而是巧妙的利用define定義P1,以及函數名的修改,都是為了體現有意義和可移植性的要求。以上只是一個很簡單有關于define這個關鍵字的用法,巧妙運用能使程序的可讀性和可移植性大大增強,也是規范性編程不可或缺的關鍵因素。
1.4 合理選取變量的數據類型,防止掉入C陷進
在說明之前先看一個簡單的例子:
#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++);
}
}
初看覺得沒什么問題,可是當你下載到MCU運行時,你會發現燈永遠是亮的,不會熄滅,為什么呢?我們來分析一下,燈亮說明至少運行到了while(1)中的Led_On語句,說明應該問題就出在軟件延時函數,細看我們發現i的取值大了,因為U8 i的范圍是0~255,雖然我們知道unsigned char 是無符號8位,28值是256,但是要注意的是單片機初始值都是從0開始的,所以要注意這些細節問題。
有些人看了上面的例子會想,我都用long型或者int型就不是沒有問題了嗎?但是你這樣的話就增大了MCU內存的開銷,不利于程序快速運行,所以合理選擇變量數據類型也是很重要的。
1.5 在結構體中按變量從小到大排列
先看個例子:
Struct
{
Int s; //占用第0和第1個字節
Char c1; //占用第2個字節,由于對其原因,第3個字節為空
Long l; //占用第4,5,6,7個共四個字節
Char c2; //占用第8個字節,第9個字節為空
}s;
由此可以看出浪費了2個字節空間,所以我們應該調整變量順序,如下:
Struct
{
Int s; //占用第0和第1個字節
Char c1; //占用第2個字節
Char c2; //占用第3個字節
Long l; //占用第4,5,6,7個共四個字節
}s;
為什么會有上述情況出現,原因是結構體變量是字對齊,但是在有些單片機中可以軟件設置為字節對齊,這樣也可以解決上述問題,但是按順序存放明顯是規范性編程中的一員,一個好習慣不會因為疏忽造成內存開銷增大。
2 模塊化編程
為什么要模塊化編程,主要原因當然也是可讀性和可移植性。
模塊化編程思路:
1分析系統項目功能模塊,一般的系統可能有以下幾個模塊:最小系統模塊(能讓MCU工作的編程模塊),鍵盤和顯示模塊(一般會用譯碼鎖存器件,如智能調節儀所使用的是CH452),AD模塊(采集傳感器信號),繼電器模塊(控制一些器件工作,相當于開關),通訊模塊(UART)等。
2將每個模塊分別用.c和.h建立模塊編程,.h文件用來存放模塊相關資源定義,以及函數聲明等功能,.c文件用于存放該模塊功能程序代碼。
3用main.c將各個模塊串結成一個完整的系統,在main函數中代碼要簡潔,最好只有兩三個函數,比如:
Void main(void)
{
System_Init();
While(1)
{
If(Key_Value)
Key_Handle();
else
System_Handle();
}
}
以上分析了模塊化編程的基本思路,然后我們再來具體看個例子,以通訊模塊為例:
先看.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)//波特率設置 { case B9600_Freuency: // 相應設置代碼 break; case B2400_Freuency: // 相應設置代碼 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; }
以上的例子只是簡單的說明了模塊化編程原理及一般流程,可能我們已經注意到形參使用的是指針結構體,如此可以節約系統時間并減少系統內存開銷。
3 總結
編程習慣很重要,由于面對大型的工程和團隊合作,養成一個規范化編程和模塊化編程的好習慣相當重要,也可以說是直接影響團隊的工程進程和新代碼成員的跟進進度,所以在開始學習編寫程序代碼前必須養成一個良好的編程習慣,規范化和模塊化編程是其精髓。
以上所有程序是我零時編寫,可能有一些欠妥之處,請指正。謝謝大家。