嵌入式工程師超長經(jīng)驗(yàn)分享:從單片機(jī)coder到嵌入式programer的簡(jiǎn)單歷程
其實(shí)UML就是一個(gè)工具,提供了用例圖、順序圖、活動(dòng)圖、類圖、狀態(tài)機(jī)圖、部署圖、包圖等工具,輔助工程師完成:分析->設(shè)計(jì)->實(shí)施->測(cè)試的整個(gè)過程。每個(gè)過程都有細(xì)分,例如分析階段:首先是需求分析,然后有系統(tǒng)分析,再次有對(duì)象結(jié)構(gòu)分析等。需求分析階段會(huì)使用到用例圖、順序圖、狀態(tài)機(jī)圖、順序圖等,需求分析階段的最重要的要素是軟件的外部功能視圖和使用場(chǎng)景等。其中前者使用用例圖表述,它也提供了溝通用戶和開發(fā)者的橋梁;后者用順序圖或狀態(tài)機(jī)圖等表述,提供了系統(tǒng)每個(gè)功能實(shí)現(xiàn)的可能路徑。其他過程和需求分析階段類似,這里篇幅所限就不再一一提及。UML就是這樣同我們的設(shè)計(jì)過程關(guān)聯(lián)起來的。
將面向?qū)ο蟮姆椒ㄓ糜贛CU是有必要的,也是可能的,當(dāng)然也是很有效的。這樣的努力最起碼可以拉近mcu開發(fā)者同其他領(lǐng)域的C開發(fā)者之間的距離,彌補(bǔ)那道似乎難以逾越的鴻溝,比如看到linux內(nèi)核代碼時(shí),你會(huì)發(fā)現(xiàn)原來如此親切。當(dāng)然,隨著對(duì)面向?qū)ο蠓椒ǖ纳钊肜斫?,你?huì)發(fā)現(xiàn)C++也不再那么讓你不知道如何使用,或者把C++用得像面向過程的語言一樣。當(dāng)然本人C++菜鳥,還望高手指教。
然而面向?qū)ο蟮姆椒ㄒ卜且货矶?,一朝搞定,它是一個(gè)循序漸進(jìn)的過程,特別是應(yīng)用與mcu這樣的平臺(tái),好多東西是靠摸索出來的。如何開始,先從何處下手是個(gè)問題。
21ic同仁liufb提議:“正如《重構(gòu)與模式》所說:如果想成為一名更優(yōu)秀的軟件設(shè)計(jì)師,了解優(yōu)秀軟件設(shè)計(jì)的演變過程比學(xué)習(xí)優(yōu)秀設(shè)計(jì)本身更有價(jià)值,因?yàn)樵O(shè)計(jì)的演變過程中蘊(yùn)藏著大智慧?!?br style="word-break: break-all;color: rgb(68, 68, 68);font-family: Tahoma, "Microsoft Yahei", Simsun;text-align: start;background-color: rgb(222, 240, 251);">
我決定發(fā)掘一下我近十年以來的階段性C代碼,試圖去發(fā)現(xiàn)一點(diǎn)什么,這個(gè)我之前還從未嘗試過,能找到的一起的代碼也寥寥無幾。不過我覺得值得一試,那就從此開始吧。
bit FUN,Shift_on,Add_on,Sub_on,fun_flag;
bit dspflagz1,dspflagz2,dspflagz3;
unsigned char z1,z2,td3,working,DSP_m,DSP_n;
unsigned char l1,l2,r,m;
bitflagv,flagy,flags,flag0,flagx,beepflag1,beepflag2,flagt,flagw;
bit working_on,function_on, AINTSYR_on,AINTSYW_on,BINTSYR_on,BINTSYW_on ;
bitprogram_on,program_woking,up_flag,down_flag,up_on,down_on;
unsigned char AINTSY_state, BINTSY_state, function_state;
unsigned char tx1,tx2,tx3,tw,TX,t;
unsigned char display_state ,x1,x2,w1,w2;
unsigned char program_state,program_working;
unsigned char clk_number;
unsigned char code DS[]={0,33,63,86,100,86,63,33};
unsigned chards_curtime[6]={0x05,0x03,0x02,0x01,0x07,0x06};
unsigned char clk_data[6]={06,1,1,0,0,1};
unsigned char set_time[6];
sbit switch_work= 0xB0;
sbit switch_function=0xB1;
sbit switch_program=0xB2;
sbit switch_up=0x90;
sbit switch_down=0x91;
sbit switch_AINTSYR=0x92;
sbit switch_AINTSYW=0x93;
sbit switch_BINTSYR=0x94;
sbit switch_BINTSYW=0x95;
sbit RS=0xA2;
sbit RW=0xA1;
sbit E=0xA0;
sbit CS2=0xA3;
sbit CS1=0xA4;
sbit DACS1=0xA7;
sbit DACS2=0xA6;
sbit DACS3=0xA5;
sbit ds_sclk=0xB3 ; /*初始化變量*/
sbit ds_io=0xB4;
sbit ds_rst=0xB5;
//////////////////////////////////////////////////////////////////////////////////////////////////////
void text(void)
{
bit Flag_add; /*加一標(biāo)志*/
bit Flag_sub; /*減一標(biāo)志*/
unsigned char max_value; /*各時(shí)間單位的最大值*/
unsigned char min_value; /*各時(shí)間單位的最小值*/
/*if(FUN==1)
{ */ /*定義標(biāo)志位*/
if(switch_work==0) /*移位鍵*/
{
if(Shift_on==0)
{
Shift_on=1;
buzzer();
clk_number++;
if(clk_number>6)clk_number=1;
}
}
else
Shift_on=0;
if(switch_up==0) /*加一鍵*/
{
if(Add_on==0)
{
Add_on=1;
buzzer();
Flag_add=1;
}
}
else
Add_on=0;
if(switch_down==0) /*減一鍵*/
{
if(Sub_on==0)
{
Sub_on=1;
buzzer();
Flag_sub=1;
}
}
else
Sub_on=0;
switch(clk_number)
{
case 1: max_value=99;min_value=0;break;
case 2: max_value=12;min_value=1;break;
case 3: if(clk_data[1]==1||
clk_data[1]==3||
clk_data[1]==5||
clk_data[1]==7||
clk_data[1]==8||
clk_data[1]==10||
clk_data[1]==12)
max_value=31; /*1,3,5,7,8,10,12*/
else if(
clk_data[1]==4||
clk_data[1]==6||
clk_data[1]==9||
clk_data[1]==11)
max_value=30; /*4,6,9,11*/
else if((clk_data[0]%4==0)||clk_data[0]==0)
max_value=29; /*閏年*/
else
max_value=28;
min_value=1;
break;
case 4: max_value=23;min_value=0;break;
case 5: max_value=59;min_value=0;break;
case 6: max_value=7;min_value=1;break;
}
if(Flag_add==1)
{
clk_data[clk_number-1]++;
Flag_add=0;
if(clk_data[clk_number-1]>max_value)
clk_data[clk_number-1]=min_value;
}
else if(Flag_sub==1)
{
clk_data[clk_number-1]--;
Flag_sub=0;
if(clk_data[clk_number-1]
-1 ]==0xff)clk_data[clk_number-1]=max_value;
}
if(switch_function==0)
{
if(function_on==0)
{
function_on=1;
FUN=0;
buzzer();
function_state=1;
fun0_flag=1;
set_time[0]=(clk_data[4]/10)*0x10+(clk_data[4]%10);
set_time[1]=(clk_data[3]/10)*0x10+(clk_data[3]%10);
set_time[2]=(clk_data[2]/10)*0x10+(clk_data[2]%10);
set_time[3]=(clk_data[1]/10)*0x10+(clk_data[1]%10);
set_time[4]=(clk_data[5]/10)*0x10+(clk_data[5]%10);
set_time[5]=(clk_data[0]/10)*0x10+(clk_data[0]%10);
}
}
else
{
function_on=0;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
下邊是我同事的一個(gè)函數(shù),女工程師,女中豪杰,哈哈
//////////////////////////////////////////////////////////////////////////////////////////////////////
void check_switch()
{
if(FUN==0)
{
if(switch_work==0)
{
if(working_on==0)
{
working_on=1;
buzzer();
if(working==1)
{
working=0;
flag0=0;
}
else
{
working=1;
flag0=1;
}
}
}
else
{
working_on=0;
}
if(switch_function==0)
{
if(function_on==0)
{
function_on=1;
buzzer();
program_state=1;
if(function_state==1)
{
function_state=2;
}
else
{
if(function_state==2)
{
function_state=3;
}
else
{
if(function_state==3)
{
function_state=4;
FUN=1;
fun1_flag=1;
working=0;
flagx=1;
}
else
{
function_state=1;
}
}
}
}
}
else
{
function_on=0;
}
if(switch_program==0)
{
if(program_on==0)
{
program_on=1;
buzzer();
program_working=1;
flagv=1;
flagy=1;
flagt=1;
flagw=1;
}
}
else
{
program_on=0;
}
if(switch_up==0)
{
if(up_flag==0)
{
up_flag=1;
up_on=1;
buzzer();
}
}
else
{
up_flag=0;
}
if(switch_down==0)
{
if(down_flag==0)
{
down_flag=1;
down_on=1;
buzzer();
}
}
else
{
down_flag=0;
}
if(switch_AINTSYR==0)
{
if(AINTSYR_on==0)
{
AINTSYR_on=1;
buzzer();
if(AINTSY_state<=63)
{
AINTSY_state=AINTSY_state+1;
}
}
}
else
{
AINTSYR_on=0;
}
if(switch_AINTSYW==0)
{
if(AINTSYW_on==0)
{
AINTSYW_on=1;
buzzer();
if(AINTSY_state>=1)
{
AINTSY_state=AINTSY_state-1;
}
}
}
else
{
AINTSYW_on=0;
}
if(switch_BINTSYR==0)
{
if(BINTSYR_on==0)
{
BINTSYR_on=1;
buzzer();
if(BINTSY_state<=63)
{
BINTSY_state=BINTSY_state+1;
}
}
}
else
{
BINTSYR_on=0;
}
if(switch_BINTSYW==0)
{
if(BINTSYW_on==0)
{
BINTSYW_on=1;
buzzer();
if(BINTSY_state>=1)
{
BINTSY_state=BINTSY_state-1;
}
}
}
else
{
BINTSYW_on=0;
}
}
else
{
text();
}
}
void main()
{
init_working();/*初始化程序*/
start_t0:check_switch();
prepare_work();
if(fun0_flag==1){
ds_settime(set_time);
}
display();
goto start_t0;
}
Wegner將早期高級(jí)語言做了分類:
以上內(nèi)容摘自Grady Booch《面向?qū)ο螅悍治雠c設(shè)計(jì)》
1. 所有子程序共用全局?jǐn)?shù)據(jù)。
2. 共用數(shù)據(jù)帶來大量交叉耦合。
3. 大量的標(biāo)志位或者數(shù)據(jù)定義很難讀懂它到底代表什么意思。
早期語言寫程序的特點(diǎn):
既然你發(fā)現(xiàn)了它的缺陷,既然你已經(jīng)知道它的‘錯(cuò)’在哪里,那問題可以得到解決了吧!問題怎么解決,如何改進(jìn)?別著急!接下來我們將會(huì)看語言的下一次革新,還會(huì)附上下一階段的代碼做為實(shí)例。我簡(jiǎn)直就是一部活生生的發(fā)展史了。
軟件固有特性:復(fù)雜性等問題
【軟件固有的復(fù)雜性】
* 問題域的復(fù)雜性 :
? ?? ?? ?—— 非功能性需求(可用性,健壯性,成本,性能等)的加入。
? ?? ?? ?—— 開發(fā)人員同用戶之間溝通的困難。
? ?? ?? ?—— 設(shè)計(jì)過程中的需求變更。
* 管理開發(fā)過程的困難性
* 軟件中隨處可見的靈活性
—— 這直接導(dǎo)致了軟件開發(fā)領(lǐng)域不能像其他領(lǐng)域,可以對(duì)每個(gè)構(gòu)件給出一個(gè)行業(yè)標(biāo)準(zhǔn),比如機(jī)械制造行業(yè),軟件行業(yè)卻很少能如此。這導(dǎo)致開發(fā)者需要打造大部分模塊。
* 描述離散系統(tǒng)行為的問題
—— 無法用連續(xù)系統(tǒng)建模的方式,本身也不受物理規(guī)律支配。
【復(fù)雜系統(tǒng)的5個(gè)屬性】
* 層次性:可以逐級(jí)劃分子系統(tǒng)。
* 相對(duì)本原:不同觀察者對(duì)同系統(tǒng)組件的本原性認(rèn)識(shí)不同。
* 分離關(guān)注:根據(jù)“組件”內(nèi)部之間關(guān)聯(lián)程度高于“組件”間外部關(guān)聯(lián)程度來劃分組件。
* 共同模式:如細(xì)胞,管脈系統(tǒng)在動(dòng)物和植物中都有。
* 穩(wěn)定的中間形式:復(fù)雜系統(tǒng)是演化而來的,曾經(jīng)的‘復(fù)雜系統(tǒng)’或其他‘復(fù)雜系統(tǒng)’變成基本組成。
【復(fù)雜系統(tǒng)的規(guī)范形式】
* 復(fù)雜系統(tǒng)的兩種構(gòu)成層次:1. 組成部分(Part of) 2. 是一種(Is a)
在開始分析07年的代碼之前,先科普一下:
【普及基礎(chǔ)知識(shí)】
第二代和第三代前期程序設(shè)計(jì)語言的程序結(jié)構(gòu):
這是07年中的一個(gè)研發(fā)項(xiàng)目,是一個(gè)電力抄表終端,下面代碼是他的液晶顯示屏(128x64)的菜單部分。
代碼有點(diǎn)長,先看頭文件:
menu.h
//字模數(shù)組
extern root_canshu[],root_shuju[],root_zhuangtai[],exit[],zhongduancanshu[],fukongcanshu[],celiangdian0[],
celiangdian0canshu[],celiangdian1canshu[],
celiangdian1[],celiangdian2[],celiangdian3[],zhongduanzhuangtai[],zhuzhantongxin[],fuhekongzhi[]
,tongxincanshu[],gaojingcanshu[],duankoucanshu[],biaoxieyi[],jibencanshu[],gongkongcanshu[]
,diankongcanshu[],dianzijishu[],biaojitongxin[],gongkongfangan[],diankongfangan[],diannengliang[],
xuliang[],shunshiliang[],huanyingshiyong[],celiangdian0shuju[],celiangdian1shuju[],celiangdian2shuju[],celiangdian3shuju[];
//--------------------------------------------------------------------------
//-------------------------------------------
// 子界面編號(hào)數(shù)組
//-------------------------------------------
unsigned char Son0[1] ={1}; //對(duì)應(yīng)界面0
unsigned char Son1[4] ={2,3,4,0};
unsigned char Son2[5] ={5,6,7,8,1};
unsigned char Son3[5] ={9,10,11,12,1};
unsigned char Son4[5] ={55,56,57,58,1};
unsigned char Son5[5] ={31,32,33,34,2};
unsigned char Son6[5] ={35,36,37,38,2};
unsigned char Son7[3] ={39,40,2};
unsigned char Son8[3] ={41,42,2};
unsigned char Son9[4] ={43,44,45,3};
unsigned char Son10[4] ={46,47,48,3};
unsigned char Son11[4] ={49,50,51,3};
unsigned char Son12[4] ={52,53,54,3};
//*****************
//以下是增加的葉子
//*****************
//-----------------------------------------------------------
//各個(gè)界面顯示內(nèi)容指針數(shù)組
//-----------------------------------------------------------
unsigned char *menu_char0[1]={huanyingshiyong};
unsigned char *menu_char1[4]={root_canshu,root_shuju,root_zhuangtai,exit}; //各元素枝指向?qū)?yīng)行顯示內(nèi)容的字模數(shù)組
unsigned char *menu_char2[5]={zhongduancanshu,fukongcanshu,celiangdian0canshu,celiangdian1canshu,exit};
unsigned char *menu_char3[5]={celiangdian0shuju,celiangdian1shuju,celiangdian2shuju,celiangdian3shuju,exit};
unsigned char *menu_char4[5]={zhongduanzhuangtai,zhuzhantongxin,biaojitongxin,fuhekongzhi,exit};
unsigned char *menu_char5[5]={tongxincanshu,gaojingcanshu,duankoucanshu,biaoxieyi,exit};
unsigned char *menu_char6[5]={jibencanshu,gongkongcanshu,diankongcanshu,gongkongfangan,exit};
unsigned char *menu_char7[3]={jibencanshu,gaojingcanshu,exit};
unsigned char *menu_char8[3]={jibencanshu,gaojingcanshu,exit};
unsigned char *menu_char9[4]={diannengliang,xuliang,shunshiliang,exit};
unsigned char *menu_char10[4]={diannengliang,xuliang,shunshiliang,exit};
unsigned char *menu_char11[4]={diannengliang,xuliang,shunshiliang,exit};
unsigned char *menu_char12[4]={diannengliang,xuliang,shunshiliang,exit};
//-----------------------------------------------------------
// 各行文字?jǐn)?shù)量
//-----------------------------------------------------------
unsigned char char_num0[1] = {4};
unsigned char char_num1[4] = {2,2,2,2};
unsigned char char_num2[5] = {4,4,6,6,2};
unsigned char char_num3[5] = {6,6,6,6,2};
unsigned char char_num4[5] = {4,4,4,4,2};
unsigned char char_num5[5] = {4,4,4,3,2};
unsigned char char_num6[5] = {4,4,4,4,2};
unsigned char char_num7[3] = {4,4,2};
unsigned char char_num8[3] = {4,4,2};
unsigned char char_num9[4] = {3,2,3,2};
unsigned char char_num10[4] = {3,2,3,2};
unsigned char char_num11[4] = {3,2,3,2};
unsigned char char_num12[4] = {3,2,3,2};
//-----------------------------------------------------------
struct INTERFACE
{
//當(dāng)前行號(hào)對(duì)應(yīng)子菜單編號(hào),和反顯行,對(duì)于不需要反顯示的無意義
//行號(hào)在每次進(jìn)入新的界面時(shí)清零
unsigned char MAX_ROW_NUM; //該界面的最大行數(shù)
unsigned char **MENU_char; //指向一指針數(shù)組,該數(shù)組元素為指向各行顯示內(nèi)容的指針
unsigned char *Son_num; //指向當(dāng)前界面子界面編號(hào)數(shù)組的指針
unsigned char *Row_num; //指向當(dāng)前界面各行文字?jǐn)?shù)量數(shù)組的指針
};
struct INTERFACE Windows[13] =
{
{1,menu_char0,Son0,char_num0},
{4,menu_char1,Son1,char_num1},
{5,menu_char2,Son2,char_num2},
{5,menu_char3,Son3,char_num3},
{5,menu_char4,Son4,char_num4},
{5,menu_char5,Son5,char_num5},
{5,menu_char6,Son6,char_num6},
{3,menu_char7,Son7,char_num7},
{3,menu_char8,Son8,char_num8},
{4,menu_char9,Son9,char_num9},
{4,menu_char10,Son10,char_num10},
{4,menu_char11,Son11,char_num11},
{4,menu_char12,Son12,char_num12}
};
//葉子節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)
struct LEAF
{
unsigned char Father_num; //該葉子要返回的父親界面號(hào)
void (*Display_leaf)(); //指向該葉子的顯示函數(shù)的指針
};
struct LEAF leaf[28] =
{
{5,display_tongxincanshu}, {5,display_gaojingcanshu},
{5,display_duankoucanshu}, {5,display_biaoxieyi},
{6,display_jibencanshu}, {6,display_gongkongcanshu},
{6,display_diankongcanshu}, {6,display_gongkongfangan},
{7,display_cljibencanshu}, {7,display_clgaojingcanshu},
{8,display_cljibencanshu}, {8,display_clgaojingcanshu},
{9,display_diannengliang}, {9,display_xuliang},
{9,display_shunshiliang}, {10,display_diannengliang},
{10,display_xuliang}, {10,display_shunshiliang},
{11,display_diannengliang}, {11,display_xuliang},
{11,display_shunshiliang}, {12,display_diannengliang},
{12,display_xuliang}, {12,display_shunshiliang},
{4,display_zhongduanzhuangtai}, {4,display_zhuzhantongxinzhuangtai},
{4,display_biaojitongxinzhuangtai},{4,display_fuhekongzhizhuangtai}
};
//-----------------------------------------函數(shù)--------------------------------------------------
void Display_char(unsigned char,unsigned char ,unsigned char *,unsigned char);
void Display_row(unsigned char,unsigned char ,unsigned char ,unsigned char *,unsigned char);
void Display_window(unsigned char,unsigned char);
//
//-----------------------------------------函數(shù)--------------------------------------------------
void Display_char(unsigned char,unsigned char ,unsigned char *,unsigned char);
void Display_row(unsigned char,unsigned char ,unsigned char ,unsigned char *,unsigned char);
void Display_window(unsigned char,unsigned char);
//-------------------------------------------------------------------------
//功能: 顯示16x16漢字
//參數(shù): begneX : 行地址 beginRow:列地址 n: 漢字?jǐn)?shù)量
// s: 顯示內(nèi)容 標(biāo)志寄存器: 是否反顯
//調(diào)用: display_Lf() display_Rf()
//zkq 2007.06.04
//-------------------------------------------------------------------------
void display16x16RL(unsigned char beginX,unsigned char beginRow,unsigned char n,unsigned char *s,unsigned char 標(biāo)志寄存器)
{
unsigned char i;
if(標(biāo)志寄存器)
{
LCD_Write_ComR(0xb8|beginX);
LCD_Write_ComR(0x40|0);
for(i=0;i<64;i++)
{
LCD_Write_DatR(0xff);
}
LCD_Write_ComL(0xb8|beginX);
LCD_Write_ComL(0x40|0);
for(i=0;i<64;i++)
{
LCD_Write_DatL(0xff);
}
LCD_Write_ComR(0xb8|beginX + 1);
LCD_Write_ComR(0x40|0);
for(i=0;i<64;i++)
{
LCD_Write_DatR(0xff);
}
LCD_Write_ComL(0xb8|beginX + 1);
LCD_Write_ComL(0x40|0);
for(i=0;i<64;i++)
{
LCD_Write_DatL(0xff);
}
}
for(i=0;i
{
displayrow(i,beginX,beginRow+i,s,標(biāo)志寄存器);
}
}
//--------------------------------------------------------------------------------------
void Display_window(unsigned char win_num,unsigned char sel_row)
{
unsigned char j,f;
if(Windows[win_num].MAX_ROW_NUM < 4)
f = Windows[win_num].MAX_ROW_NUM - 1;
else f = 3;
if(sel_row<4)
{
for(j=0;j<=f;j++)
{
if(j==sel_row)
display16x16RL(2*j,1,*(Windows[win_num].Row_num + j),*(Windows[win_num].MENU_char + j),1);
else
display16x16RL(2*j,1,*(Windows[win_num].Row_num + j),*(Windows[win_num].MENU_char + j),0);
}
}
else if(sel_row<8)
{
for(j=0;j<=Windows[win_num].MAX_ROW_NUM - 5;j++)
{
if(j==sel_row%4)
display16x16RL(2*j,1,*(Windows[win_num].Row_num + j + 4),*(Windows[win_num].MENU_char + j + 4),1);
else
display16x16RL(2*j,1,*(Windows[win_num].Row_num + j + 4),*(Windows[win_num].MENU_char + j + 4),0);
}
}
}
對(duì)于接受過自頂而下的結(jié)構(gòu)化程序設(shè)計(jì)的人來說,首先想到的是用算法分解將復(fù)雜系統(tǒng)劃分成簡(jiǎn)單的部分。其中每一個(gè)模塊是某個(gè)總體過程(some overall process)的一個(gè)主要步驟(a major step)。下面例子是Booch給出的一個(gè)程序的部分設(shè)計(jì),完成更新一個(gè)主控文件的內(nèi)容。它完全就是對(duì)一個(gè)流程的逐級(jí)細(xì)化。
這樣劃分的模塊雖然可以獨(dú)立編譯,但是完全沒有封裝的概念,它只關(guān)注流程,只是功能的劃分。接下來我的實(shí)例中你會(huì)體會(huì)到這一點(diǎn),當(dāng)然前輩的功力還是很深厚的,程序一直很穩(wěn)定,但是閱讀起來很費(fèi)勁,擴(kuò)展性不好,當(dāng)然像TCP部分也做了一定的封裝。整體上主要還是按照功能而不是實(shí)體做為模塊劃分的標(biāo)準(zhǔn)。即使如此,我從中還是收益匪淺的。
接下我會(huì)把代碼的各個(gè)功能模塊做些介紹,期間插入我自己的理解,最后根據(jù)分析結(jié)果做些總結(jié)。后邊的代碼沒有那么簡(jiǎn)單了,加上多年沒有看過,以后就不再設(shè)什么前提。溫故而知新,很多新發(fā)現(xiàn)也是出乎我意料的,因?yàn)榻裉斓难酃獠煌郧?。大家有什么意見和觀點(diǎn)盡管提,感興趣的話一起開始發(fā)現(xiàn)之旅吧。
首先從內(nèi)容上對(duì)這個(gè)程序做個(gè)大概的瀏覽:
所有代碼:
C文件:
makefile:
int main(void)
{
.......
init_system(); //系統(tǒng)初始化
InitPassiveSock(); //監(jiān)聽套接字初始化
pthread_create(&ReceID, NULL, (void *)&RecePross, NULL); //創(chuàng)建接收處理線程
while(1){
QuerySocketsMsg(); //處理socket
.......
}
.......
}
void init_system(void)
{
init_para(); // 1
init_serial(); // 2
init_ethernet(); // 3
......
}
開始之前,先介紹一下這個(gè)程序最主要的幾個(gè)數(shù)據(jù)結(jié)構(gòu):
/* 套接字結(jié)構(gòu) */
typedef struct Sockets {
fd_set readfds, writefds, exceptfds; // 1. 要被檢測(cè)的可讀、可寫、例外的套接字集合
int PmuSock; // 2. PMU規(guī)范套接字
......
struct Sockets_Comm *Comm; // 3. 為設(shè)置中每一個(gè)通訊通道建立網(wǎng)絡(luò)接口
} Sockets;
2. 用來監(jiān)聽(listen())主站鏈接的監(jiān)聽socket。
3. 是個(gè)結(jié)構(gòu)體數(shù)組:數(shù)組長度等于通信通道數(shù)量。結(jié)構(gòu)體定義如下:
/* COMM網(wǎng)絡(luò)結(jié)構(gòu) */
typedef struct Sockets_Comm{
int Protocol; // 1. 該鏈接使用的規(guī)約類型
int MainSock; // 2. 主套接字(即上面提到的監(jiān)聽套接字accept到的鏈接套接字)
pthread_t MainThreadID; // 4. 主線程ID,該線程用于處理該鏈接通信。
pthread_cond_t MainCond;
pthread_mutex_t MainMutex;
............
}Sockets_Comm;
? ? ? ? 2. (如上注釋)
? ? ? ? 3. (如上注釋)
在面向?qū)ο缶幊填I(lǐng)域中,開閉原則規(guī)定“軟件中的對(duì)象(類,模塊,函數(shù)等等)應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是封閉的”[1],這意味著一個(gè)實(shí)體是允許在不改變它的源代碼的前提下變更它的行為。該特性在產(chǎn)品化的環(huán)境中是特別有價(jià)值的,在這種環(huán)境中,改變?cè)创a需要代碼審查,單元測(cè)試以及諸如此類的用以確保產(chǎn)品使用質(zhì)量的過程。遵循這種原則的代碼在擴(kuò)展時(shí)并不發(fā)生改變,因此無需上述的過程。
開閉原則的命名被應(yīng)用在兩種方式上。這兩種方式都使用了繼承來解決明顯的困境,但是它們的目的,技術(shù)以及結(jié)果是不同的。
勃蘭特·梅耶一般被認(rèn)為是最早提出開閉原則這一術(shù)語的人,[來源請(qǐng)求]在他1988年發(fā)行的《面向?qū)ο筌浖?gòu)造》中給出。這一想法認(rèn)為一旦完成,一個(gè)類的實(shí)現(xiàn)只應(yīng)該因錯(cuò)誤而修改,新的或者改變的特性應(yīng)該通過新建不同的類實(shí)現(xiàn)。新建的類可以通過繼承的方式來重用原類的代碼。衍生的子類可以或不可以擁有和原類相同的接口。
梅耶的定義提倡實(shí)現(xiàn)繼承。具體實(shí)現(xiàn)可以通過繼承方式來重用,但是接口規(guī)格不必如此。已存在的實(shí)現(xiàn)對(duì)于修改是封閉的,但是新的實(shí)現(xiàn)不必實(shí)現(xiàn)原有的接口。
多態(tài)開閉原則
在20世紀(jì)90年代,開閉原則被廣泛的重新定義由于抽象化接口的使用,在這中間實(shí)現(xiàn)可以被改變,多種實(shí)現(xiàn)可以被創(chuàng)建,并且多態(tài)化的替換不同的實(shí)現(xiàn)。
相比梅耶的使用方式,多態(tài)開閉原則的定義倡導(dǎo)對(duì)抽象基類的繼承。接口規(guī)約可以通過繼承來重用,但是實(shí)現(xiàn)不必重用。已存在的接口對(duì)于修改是封閉的,并且新的實(shí)現(xiàn)必須,至少,實(shí)現(xiàn)那個(gè)接口。
羅伯特·C·馬丁1996年發(fā)表的文章《開閉原則》[2]是使用這種方法的啟發(fā)式著作。在2001年,Craig Larman把開閉原則關(guān)聯(lián)到了Alistair Cockburn的名為受護(hù)的變量的模式以及David Parnas關(guān)于信息隱藏的討論。[3]
以上內(nèi)容來源于《維基百科》,總得來說,開閉原則不僅針對(duì)面向?qū)ο笤O(shè)計(jì),即使面向過程設(shè)計(jì)的軟件,如果盡可能做到這一點(diǎn)也是很有必要的。雖然面向過程的先天性決定了,這是困難的。然而有些規(guī)模龐大的軟件:如linux內(nèi)核,雖然它本身具備面向?qū)ο笏枷?,終究不是一個(gè)完全面向?qū)ο蟮拇蠊こ?,但是它的開閉原則做得很到位,這個(gè)了解的人一定深有體會(huì)。
曾經(jīng)看到過Linus在一個(gè)帖子里痛批了面向?qū)ο笳Z言。他認(rèn)為面向?qū)ο笳Z言以對(duì)象為核心,加一些相關(guān)聯(lián)的方法,簡(jiǎn)直是囈語。重要的東西應(yīng)該是數(shù)據(jù)結(jié)構(gòu),對(duì)象本身有啥重要?真正有意思的,是在不同類型的不同對(duì)象交互而且有鎖規(guī)則的時(shí)候。但是,即使是這時(shí)候,封裝什么“對(duì)象接口”也絕對(duì)錯(cuò)誤,因?yàn)椴辉偈菃我粚?duì)象的問題了。他的結(jié)論是,面向?qū)ο蠼鉀Q的都是一些小問題。
確實(shí)有很多“大人物”一直強(qiáng)調(diào):“最重要的是數(shù)據(jù)結(jié)構(gòu)”。
關(guān)于這個(gè)問題,我最近幾天在考慮。一個(gè)基本的現(xiàn)象是:目前幾乎所有的操作系統(tǒng)都不是以對(duì)象為核心的,就拿大家熟悉的ucos來說,它最核心的是:
中斷的管理 ------
內(nèi)存的管理 ------ 內(nèi)存控制塊
任務(wù)間的通信和同步等 ------ 消息隊(duì)列,互斥量,郵箱等
無不體現(xiàn)了數(shù)據(jù)結(jié)構(gòu)的重要,這里面幾乎看不到對(duì)象的存在。
到底對(duì)象的適用范圍在哪里,我們?cè)撛趺醋??其?shí)這是一個(gè)很有挑戰(zhàn)性的問題,目前還沒有看到過專門討論這個(gè)問題的論著。首先我們大致可以看到:所有“平臺(tái)部分”代碼(像操作系統(tǒng))最好不要以對(duì)象為中心,在沒有使用操作系統(tǒng)的應(yīng)用里,對(duì)CPU的管理:中斷的管理,并發(fā)的實(shí)現(xiàn),消息和同步機(jī)制等也屬于這個(gè)范疇,這部分的特點(diǎn)是:
2. 雖然CPU平臺(tái)有差異,但是體系結(jié)構(gòu)相同或相似,處理方法大部分相同(否則就不存在跨平臺(tái)的OS)。
3. 這部分是同CPU打交道的,和人類的思維差別很大。
2. 怎么劃分兩部分的界線。
3. 怎么才能最有效的結(jié)合兩部分,有沒有一套完整統(tǒng)一的方法。
4. 兩部分各自的實(shí)現(xiàn)問題。
其中第4個(gè)問題是我們千百本書里面介紹有的。要這樣,最近一直在想這個(gè)問題:
Soul:我在寫書討論“面向?qū)ο蟮木窒扌浴?/span>
我 :En.這個(gè)倒與我的意見一致。哈哈哈。
我 :“絕對(duì)可以用面向過程的方法來實(shí)現(xiàn)任意復(fù)雜的系統(tǒng)。要知道,航天飛機(jī)也是在面向過程的時(shí)代上的天。但是,為了使一切變得不是那么復(fù)雜,還是出現(xiàn)了‘面向?qū)ο蟪绦蛟O(shè)計(jì)’的方法。”
我 :——哈,我那本書里,在“面向?qū)ο蟆币徊糠智暗囊闹?。就是這樣寫的。
Soul:現(xiàn)在的程序是按照馮。諾伊曼的第一種方案做的,本來就是順序的,而不是同步的。CPU怎么說都是一條指令一條指令執(zhí)行的。
我 :面向過程是對(duì)“流程”、“結(jié)構(gòu)”和“編程方法”的高度概括。而面向?qū)ο蟊旧碇唤鉀Q了“結(jié)構(gòu)”和“編程方法”的問題,而并沒有對(duì)“流程”加以改造。
Soul:確實(shí)如此。確實(shí)如此。
我 :對(duì)流程進(jìn)一步概括的,是“事件驅(qū)動(dòng)”程序模型。而這個(gè)模型不是OO提出的,而是Windows的消息系統(tǒng)內(nèi)置的。所以,現(xiàn)在很多人迷惑于“對(duì)象”和“事件”,試圖通過OO來解決一切的想法原本就是很可笑的。
Soul:我先停下來,和你討論這個(gè)問題,順便補(bǔ)充到書里去。
我 :如果要了解事件驅(qū)動(dòng)的本質(zhì),就應(yīng)該追溯到Windows內(nèi)核。這樣就涉及到線程、進(jìn)程和窗體消息系統(tǒng)這些與OO無關(guān)的內(nèi)容。所以,整個(gè)RAD的編程模型是OO與OS一起構(gòu)建的?,F(xiàn)在很多的開發(fā)人員只知其OO的外表,而看不到OS的內(nèi)核,所以也就總是難以提高。
Soul:OO里面我覺得事件的概念是很牽強(qiáng)的,因?yàn)檎嬲膶?duì)象之間是相互作用,就好像作用力和反作用力,不會(huì)有個(gè)“順序”的延時(shí)。
我 :應(yīng)該留意到,整個(gè)的“事件”模型都是以“記錄”和“消息”的方式來傳遞的。也就是說,事件模型停留在“面向過程”編程時(shí)代使用的“數(shù)據(jù)結(jié)構(gòu)”的層面上。因此,也就不難明白,使用/不使用OO都能寫Windows程序。
我 :因?yàn)榱鞒踢€是在“面向過程”時(shí)代。
Soul:所以所謂的面向?qū)ο蟮氖录€是“順序”的。所以我們經(jīng)常要考慮一個(gè)事件發(fā)生后對(duì)其他過程的影響,所以面向?qū)ο蟋F(xiàn)在而言是牽強(qiáng)的。
我 :如果你深入OS來看SEH,來看Messages,就知道這些東西原本就不是為了“面向?qū)ο蟆倍鴾?zhǔn)備的。面向?qū)ο蠓庋b了這些,卻無法改造它們的流程和內(nèi)核。因?yàn)镺O的抽象層面并不是這個(gè)。
我 :事件的連續(xù)性并不是某種編程方法或者程序邏輯結(jié)構(gòu)所決定的。正如你前面所說的,那是CPU決定的事。
Soul:比如條件選擇,其實(shí)也可以用一種對(duì)象來實(shí)現(xiàn),而事實(shí)沒有。這個(gè)是因?yàn)閏pu的特性和面向?qū)ο筇闊?/span>
我 :可能,將CPU做成面向?qū)ο蟮目赡苓€是比較難于想象和理解。所以MS才啟動(dòng).NET Framework。我不認(rèn)為.NET在面向?qū)ο蠓椒ㄉ嫌惺裁闯?,也不認(rèn)為它的FCL庫會(huì)有什么奇特的地方?!怂鼈冏銐螨嫶蟆5俏艺J(rèn)為,如果有一天OS也是用.NET Framework來編寫的,OS一級(jí)的消息系統(tǒng)、異常機(jī)制、線程機(jī)制等等都是.NET的,都是面向?qū)ο蟮?。那么,在這個(gè)基礎(chǔ)上,將“事件驅(qū)動(dòng)”并入OO層面的模型,才有可能。
Soul:所以我發(fā)覺面向?qū)ο蟮乃季S第一不可能徹底,第二只能用在總體分析層上。在很多時(shí)候,實(shí)質(zhì)上我們只是把一個(gè)順序的流程折疊成對(duì)象。
我 :倒也不是不可能徹底。有絕對(duì)OO的模型,這樣的模型我見過。哈哈~~但說實(shí)在的,我覺得小應(yīng)用用“絕對(duì)OO”的方式來編寫,有失“應(yīng)用”的本意。我們做東西只是要“用”,而不是研究它用的是什么模型。所以,“Hello World”也用OO方式實(shí)現(xiàn),原本就只是出現(xiàn)在教科書中的Sample罷了。哈哈。
Soul:還有不可能用徹底的面向?qū)ο蠓椒▉肀磉_(dá)世界。?因?yàn)檫@個(gè)世界不是面向?qū)ο蟮摹?是關(guān)系網(wǎng)絡(luò)圖,面向?qū)ο笾皇菢洌荒芷娴谋磉_(dá)世界。所以很多時(shí)候面向?qū)ο笕ソ鉀Q問題會(huì)非常痛苦。所以編程退到數(shù)據(jù)結(jié)構(gòu)更合理,哈哈。
我 :如果內(nèi)存是“層狀存取”的,那么我們的“數(shù)據(jù)結(jié)構(gòu)”就可以基于多層來形成“多層數(shù)據(jù)結(jié)構(gòu)”體系。如果內(nèi)存是“樹狀存取”的,那么我們當(dāng)然可以用“樹”的方式來存取。——可惜我們只有順序存取的內(nèi)存。
我 :程序=數(shù)據(jù)+算法 ——這個(gè)是面向過程時(shí)代的事。?程序=數(shù)據(jù)+算法+方法 ——在OO時(shí)代,我們看到了事件驅(qū)動(dòng)和模型驅(qū)動(dòng),所以出現(xiàn)了“方法”問題。
Soul:我的經(jīng)驗(yàn)是:總體結(jié)構(gòu)->面向?qū)ο螅P(guān)系->數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)->算法
Soul:看來我們對(duì)面向?qū)ο蟮恼J(rèn)識(shí)還是比較一致的。
? ? 1. 編程技巧:可移植性問題、模塊化,C語言問題等等。
? ? 2. 功能實(shí)現(xiàn):菜單的實(shí)現(xiàn)問題、GUI
? ? 3. 工具的使用:keil,gcc,uml等。
? ? 4. 外設(shè)驅(qū)動(dòng):顯示屏,can,輸入設(shè)備等。
? ? 5. 功能模塊:加密,校驗(yàn),濾波等等。
? ? 6. 處理器相關(guān):中斷處理技巧,寄存器操作,定時(shí)器等等。
? ?? ?
? ?? ?
STC16是開發(fā)者的噩夢(mèng)?
少寫點(diǎn)if-else吧,它的效率有多低你知道嗎?
早期MCU芯片是怎么加密的?
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!