前言
為了能夠使得產(chǎn)品得到更好的開(kāi)發(fā)速度與以后更好的迭代和移植,框架分層是很有必要的。但如對(duì)于中小型項(xiàng)目嚴(yán)格遵循這些原則,勢(shì)必會(huì)消耗過(guò)多精力去思考怎么設(shè)計(jì)系統(tǒng),這是一個(gè)抉擇的過(guò)程。
一、框架分層是什么?
在嵌入式架構(gòu)中:一般分為硬件架構(gòu)與軟件架構(gòu)。這里是嵌入式軟件設(shè)計(jì),也是大多數(shù)人接觸的設(shè)計(jì)。
所謂的分層,也可以理解為模塊化的設(shè)計(jì),但是框架分層的設(shè)計(jì)一般會(huì)遵循以下幾點(diǎn)原則
每個(gè)模塊提供的接口要統(tǒng)一,只能增加,不能改。在設(shè)計(jì)的時(shí)候得考慮好兼容性,使用起來(lái)麻煩不麻煩等等。
同一級(jí)模塊與模塊之間相互獨(dú)立,互不影響,不能相互調(diào)用,只能調(diào)用它下一層的接口。
不同模塊構(gòu)成不同的層,層與層之間不能跨級(jí)調(diào)用。
模塊中又可以繼續(xù)分層,可以增減分層,這個(gè)需要根據(jù)自己的項(xiàng)目需求來(lái)進(jìn)行設(shè)置。
一般可以分為:硬件驅(qū)動(dòng)層–>功能模塊層–>應(yīng)用接口層–>業(yè)務(wù)邏輯層–>應(yīng)用層
讓我們看看這個(gè)經(jīng)典的圖,簡(jiǎn)單了解一下框架分層。
從圖中不難觀察出,設(shè)計(jì)都是遵循設(shè)計(jì)的原則的,層與層之間不能相互調(diào)用。
二、框架分層的優(yōu)劣勢(shì)
1.優(yōu)勢(shì)
單一職責(zé):每一層只負(fù)責(zé)一個(gè)職責(zé),職責(zé)邊界清晰,不會(huì)造成跨級(jí)調(diào)用,在大型項(xiàng)目中,每個(gè)人負(fù)責(zé)的部分不一樣,加快整個(gè)項(xiàng)目的開(kāi)發(fā)進(jìn)度。
高內(nèi)聚:分層是把相同的職責(zé)放在同一個(gè)層中,所有業(yè)務(wù)邏輯內(nèi)聚在領(lǐng)域?qū)印T跍y(cè)試的時(shí)候,只需要測(cè)試該領(lǐng)域的層即可,一般不需要考慮其他層的問(wèn)題。
低耦合:依賴關(guān)系非常簡(jiǎn)單,上層只能依賴于下層,沒(méi)有循環(huán)依賴。
易維護(hù):面對(duì)變更容易修改。在平臺(tái)更改后,如果只是改了驅(qū)動(dòng),其他層都不需要?jiǎng)樱恍枰羊?qū)動(dòng)層給更改,其他層的功能不需要更改。
易復(fù)用:如果功能模塊變動(dòng)了,只需升級(jí)相應(yīng)的功能模塊,其他的模塊不受影響,應(yīng)用層也不受影響。
如果想要更好地利用這些優(yōu)勢(shì),那得嚴(yán)格遵循設(shè)計(jì)的原則。
2.劣勢(shì)
開(kāi)發(fā)成本高:因?yàn)槎鄬臃謩e承擔(dān)各自的職責(zé),增加功能需要在多個(gè)層增加代碼,這樣難免會(huì)增加開(kāi)發(fā)成本。但是合理的抽象,根據(jù)自己的項(xiàng)目設(shè)置合理的層級(jí)是能降低開(kāi)發(fā)成本的。
性能略低:業(yè)務(wù)流需要經(jīng)過(guò)多層代碼的處理,性能會(huì)有所消耗。
可擴(kuò)展性低:因?yàn)樯舷聦又g存在耦合度,有些功能變化可能涉及到多層的修改。
有優(yōu)勢(shì)也有劣勢(shì),需要根據(jù)自己的項(xiàng)目需要,進(jìn)行部分的取舍,如果是中小型項(xiàng)目,可以不需要分層(如果不考慮到以后會(huì)迭代的話),或者部分分層就夠了,既能利用框架分層的部分優(yōu)勢(shì),也能降低開(kāi)發(fā)成本。
三、一個(gè)簡(jiǎn)單的例子
由于主要討論的是軟件框架的分層設(shè)計(jì),這里使用STM32cubemx來(lái)進(jìn)行硬件的初始化,盡可能少考慮到硬件驅(qū)動(dòng)的部分。
以一個(gè)智能小燈的作為例子:
功能
按鍵控制小燈的亮度,等級(jí)為:0,1,2,3
串口可以觀察當(dāng)前小燈亮度等級(jí)
OLED也可以觀察當(dāng)前小燈亮度等級(jí)
下面就是這個(gè)例子的一個(gè)簡(jiǎn)單的圖示。
這和例子比較簡(jiǎn)單,業(yè)務(wù)邏輯層完全可以去除,直接從應(yīng)用層調(diào)用功能模塊層,加快開(kāi)發(fā)進(jìn)度。
最后附上一點(diǎn)點(diǎn)代碼,就是關(guān)于LED如何進(jìn)行在不同層進(jìn)行封裝
硬件層
首先看HAL庫(kù)生成提供的代碼,這個(gè)就是LED硬件層,也就是GPIO層,cubemx已經(jīng)生成了,在stm32f4xx_hal_gpio.c(我用的是F4),以及有相應(yīng)的GPIO的驅(qū)動(dòng)了,這里不需要我們進(jìn)行處理。
硬件層驅(qū)動(dòng)層
看LED部分的驅(qū)動(dòng),也就是下面的這兩個(gè)函數(shù)
voidMX_TIM1_Init(void); voidHAL_TIM_MspPostInit(TIM_HandleTypeDef*timHandle); 12 /*TIM1initfunction*/ voidMX_TIM1_Init(void) { /*USERCODEBEGINTIM1_Init0*/ /*USERCODEENDTIM1_Init0*/ TIM_ClockConfigTypeDefsClockSourceConfig={0}; TIM_MasterConfigTypeDefsMasterConfig={0}; TIM_OC_InitTypeDefsConfigOC={0}; TIM_BreakDeadTimeConfigTypeDefsBreakDeadTimeConfig={0}; /*USERCODEBEGINTIM1_Init1*/ /*USERCODEENDTIM1_Init1*/ htim1.Instance=TIM1; htim1.Init.Prescaler=168-1; htim1.Init.CounterMode=TIM_COUNTERMODE_UP; htim1.Init.Period=10000; htim1.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter=0; htim1.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE; if(HAL_TIM_Base_Init(&htim1)!=HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource=TIM_CLOCKSOURCE_INTERNAL; if(HAL_TIM_ConfigClockSource(&htim1,&sClockSourceConfig)!=HAL_OK) { Error_Handler(); } if(HAL_TIM_PWM_Init(&htim1)!=HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger=TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE; if(HAL_TIMEx_MasterConfigSynchronization(&htim1,&sMasterConfig)!=HAL_OK) { Error_Handler(); } sConfigOC.OCMode=TIM_OCMODE_PWM1; sConfigOC.Pulse=0; sConfigOC.OCPolarity=TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity=TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode=TIM_OCFAST_DISABLE; sConfigOC.OCIdleState=TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState=TIM_OCNIDLESTATE_RESET; if(HAL_TIM_PWM_ConfigChannel(&htim1,&sConfigOC,TIM_CHANNEL_2)!=HAL_OK) { Error_Handler(); } sBreakDeadTimeConfig.OffStateRunMode=TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode=TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel=TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime=0; sBreakDeadTimeConfig.BreakState=TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity=TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.AutomaticOutput=TIM_AUTOMATICOUTPUT_DISABLE; if(HAL_TIMEx_ConfigBreakDeadTime(&htim1,&sBreakDeadTimeConfig)!=HAL_OK) { Error_Handler(); } /*USERCODEBEGINTIM1_Init2*/ /*USERCODEENDTIM1_Init2*/ HAL_TIM_MspPostInit(&htim1); } voidHAL_TIM_MspPostInit(TIM_HandleTypeDef*timHandle) { GPIO_InitTypeDefGPIO_InitStruct={0}; if(timHandle->Instance==TIM1) { /*USERCODEBEGINTIM1_MspPostInit0*/ /*USERCODEENDTIM1_MspPostInit0*/ __HAL_RCC_GPIOE_CLK_ENABLE(); /**TIM1GPIOConfiguration PE11------>TIM1_CH2 */ GPIO_InitStruct.Pin=GPIO_PIN_11; GPIO_InitStruct.Mode=GPIO_MODE_AF_PP; GPIO_InitStruct.Pull=GPIO_NOPULL; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate=GPIO_AF1_TIM1; HAL_GPIO_Init(GPIOE,&GPIO_InitStruct); /*USERCODEBEGINTIM1_MspPostInit1*/ /*USERCODEENDTIM1_MspPostInit1*/ } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
對(duì)其進(jìn)行封裝,就是我們想要的Led小燈的驅(qū)動(dòng)了,到時(shí)候如果需要,改驅(qū)動(dòng)直接改底層就行了。
voidLed_init() { MX_TIM1_Init(); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);//啟動(dòng)PWM } 12345
功能模塊層
根據(jù)上面的需求要求劃分為四個(gè)不同等級(jí),同時(shí)也需要對(duì)LED驅(qū)動(dòng)進(jìn)行進(jìn)一步封裝,以便滿足層與層之間不能跨級(jí)調(diào)用的原則(到這里是不是發(fā)現(xiàn)很麻煩!小項(xiàng)目就不要用啦!)
//ARR計(jì)數(shù)器設(shè)置值為0~10000 #defineLED_GRADE_00 #defineLED_GRADE_13000 #defineLED_GRADE_26000 #defineLED_GRADE_310000 //設(shè)置LED亮度功能 voidLed_Set_brightness(intGrade) { if(Grade==LED_GRADE_0) { __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Grade); HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_2);//關(guān)閉PWM輸出 } else { HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2,Grade); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Grade); } } //啟動(dòng)LED功能 voidLed_Start() { Led_init(); } 12345678910111213141516171819202122232425
業(yè)務(wù)邏輯層
這里僅僅以啟動(dòng)層為例:
voidStart_app() { Led_Start(); } 1234
應(yīng)用層
基本流程是:?jiǎn)?dòng)業(yè)務(wù)邏輯->讀取業(yè)務(wù)邏輯->處理業(yè)務(wù)邏輯->顯示業(yè)務(wù)邏輯。
四、總結(jié)
到這里,一個(gè)簡(jiǎn)單的例子也解釋完畢了,通過(guò)LED這個(gè)簡(jiǎn)單的例子,已經(jīng)大概了解到這個(gè)設(shè)計(jì)的復(fù)雜了,如果是大型項(xiàng)目,運(yùn)用起來(lái)會(huì)很爽,小型的話完全沒(méi)必要這樣分層,太麻煩了,嚴(yán)重減慢開(kāi)發(fā)效率,時(shí)間都用在思考如何進(jìn)行分層才能符合框架分層的原則。
審核編輯:湯梓紅
-
led
+關(guān)注
關(guān)注
242文章
23741瀏覽量
671338 -
嵌入式
+關(guān)注
關(guān)注
5142文章
19561瀏覽量
315381 -
OLED
+關(guān)注
關(guān)注
119文章
6273瀏覽量
227263 -
嵌入式軟件
+關(guān)注
關(guān)注
4文章
245瀏覽量
27198
原文標(biāo)題:舉例說(shuō)明嵌入式軟件如何做分層框架設(shè)計(jì)
文章出處:【微信號(hào):工程師進(jìn)階筆記,微信公眾號(hào):工程師進(jìn)階筆記】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
嵌入式里通用微秒計(jì)時(shí)函數(shù)框架設(shè)計(jì)與實(shí)現(xiàn)
嵌入式軟件分層框架的優(yōu)劣
嵌入式軟件的基本構(gòu)成以及功能
嵌入式軟件設(shè)計(jì)上的程序模塊分為哪幾類(lèi)
探討一下嵌入式軟件分層設(shè)計(jì)
嵌入式軟件建立統(tǒng)一框架方法的研究
嵌入式應(yīng)用框架EAF詳解
關(guān)于嵌入式應(yīng)用框架(EAF)的分析
嵌入式系統(tǒng)底層軟件可移值性設(shè)計(jì)及開(kāi)發(fā)流程分析

嵌入式框架-分層

嵌入式系統(tǒng)框架----軟件篇

嵌入式開(kāi)發(fā)|嵌入式軟件框架《二》前后臺(tái)任務(wù)框架-cola os系統(tǒng)

痞子衡嵌入式:嵌入式里通用微秒(microseconds)計(jì)時(shí)函數(shù)框架設(shè)計(jì)與實(shí)現(xiàn)

嵌入式軟件分層隔離的典范是什么?

評(píng)論