




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、嵌入式系統(tǒng)程序可移植性設(shè)計及性能優(yōu)化【摘要】在嵌入式系統(tǒng)的程序設(shè)計中,由于軟硬件平臺的多變性,對程序的可移植性、可擴充性、可裁減性及可維護性等有更嚴格的要求。本文從宏定義設(shè)計、數(shù)據(jù)結(jié)構(gòu)設(shè)計及函數(shù)設(shè)計等方面,簡單介紹了可移植性的設(shè)計問題。在嵌入式應(yīng)用中非常注重代碼的時空效率,即產(chǎn)生的代碼運行時間和占用的存儲空間盡可能少。程序設(shè)計一章介紹了如何提高程序的運行效率的相關(guān)技巧?!娟P(guān)鍵詞】嵌入式,可移植性,可維護,可裁減,宏定義設(shè)計,數(shù)據(jù)結(jié)構(gòu)設(shè)計,時空效率目錄1宏定義設(shè)計- 1 -1.1為何要采用宏定義?- 1 -1.2宏定義的基本規(guī)則- 1 -1.3依賴關(guān)系定義宏改善移植性- 1 -1.4通過偏移量和
2、掩碼進行位操作- 2 -2數(shù)據(jù)結(jié)構(gòu)設(shè)計- 4 -2.1結(jié)構(gòu)體中成員對齊規(guī)則- 4 -2.1.1自然對界- 4 -2.1.2指定對界- 4 -2.2合理設(shè)計成員順序- 5 -2.2.1減少結(jié)構(gòu)體存儲空間- 5 -2.2.2填充部分域,避免字節(jié)對齊問題- 6 -2.2.3字節(jié)對齊問題實例- 7 -2.3采用位域構(gòu)造結(jié)構(gòu)體- 8 -2.3.1位域設(shè)計傳輸協(xié)議- 8 -2.3.2位域的可移植性問題- 9 -2.3.3位域設(shè)計硬件配置字- 10 -2.4通過union和struct傳遞不同格式報文- 11 -2.5將相關(guān)功能變量封裝為結(jié)構(gòu)體- 13 -3函數(shù)設(shè)計- 15 -3.1避免過多函數(shù)參數(shù),提高調(diào)
3、用性能- 15 -3.2合理設(shè)計模塊,減小耦合度- 16 -3.3用宏函數(shù)提高時間效率- 18 -3.3.1宏參數(shù)的基本規(guī)則- 18 -3.3.2宏語句的基本規(guī)則- 18 -3.3.3宏的副作用- 20 -3.4Const修飾輸入指針參數(shù)- 21 -4程序設(shè)計- 22 -4.1循環(huán)轉(zhuǎn)置- 22 -4.2減小運算強度- 23 -4.2.1位操作實現(xiàn)求余運算- 23 -4.2.2用移位實現(xiàn)乘除法運算- 23 -4.2.3將循環(huán)體內(nèi)的乘法運算改成循環(huán)自加運算- 23 -4.3減少不變計算- 24 -4.3.1循環(huán)內(nèi)部避免恒定式- 24 -4.3.2避免結(jié)構(gòu)體深度訪問- 25 -4.4減少存儲訪問指令
4、周期和個數(shù)- 26 -4.5查表- 28 -4.6使用自加、自減指令- 28 -4.7根據(jù)頻率進行case 排序- 29 -4.8函數(shù)指針表替代switchcase- 30 -嵌入式系統(tǒng)程序可移植性設(shè)計及性能優(yōu)化之一宏定義設(shè)計【摘要】本節(jié)介紹了嵌入式系統(tǒng)程序設(shè)計中采用宏定義進行常量定義的必要性。說明了宏常量定義的基本規(guī)則以及如何采用依賴關(guān)系定義宏常量來保證其可移植性和裁減性。最后介紹了如何利用宏定義實現(xiàn)掩碼偏移量等來高效的進行位操作?!娟P(guān)鍵詞】嵌入式,可移植性,宏定義,依賴關(guān)系,掩碼,偏移量,位操作1宏定義設(shè)計- 1 -1.1為何要采用宏定義?- 1 -1.2宏定義的基本規(guī)則- 1 -1.3依
5、賴關(guān)系定義宏改善移植性- 1 -1.4通過偏移量和掩碼進行位操作- 2 -1 宏定義設(shè)計1.1 為何要采用宏定義?在程序設(shè)計過程中,對于經(jīng)常使用的一些常量,如果將它直接寫到程序中去,一旦常量的數(shù)值發(fā)生變化,就必須逐個找出程序中所有的常量,并逐一進行修改,這樣必然會降低程序的可維護性。因此,應(yīng)盡量通過預(yù)處理命令方式將常量定義為特定的字符,這樣常量就有了統(tǒng)一的表現(xiàn)形式,不會出現(xiàn)輸入錯誤導(dǎo)致的不一致性。另外宏常量意義明確,大大改善了代碼的可讀性。只讀的變量也可以實現(xiàn)上述宏常量所帶來的可移植性、可靠性及可讀性等特點,但其要占據(jù)存儲空間,需要訪問內(nèi)存,相比宏常量的立即數(shù)尋址而言效率要低。在C中提倡用co
6、nst只讀變量來定義常量,是因為這樣可以提供更嚴格的類型安全檢查。但由于C中const只讀變量不能用于某些場合,因此在嵌入式C中仍多數(shù)采用宏來定義常量。1.2 宏定義的基本規(guī)則下面以一個實例來說明宏定義的基本規(guī)則,如用預(yù)處理指令#define 聲明一個常量,用以表明1年中有多少秒,不考慮潤年#define C_SECONDS_PER_YEAR (60 * 60 * 24 * 365)ULa) 命名風(fēng)格,為了與普通變量區(qū)分開來,宏定義通常全部大寫,多個單元之間用下劃線隔開;b) 整個表達式應(yīng)括起來,若有參數(shù)則應(yīng)將每個參數(shù)都括起來,防止替換擴展后可能帶來的異常問題;c) 常量表達式先合并后再替換。
7、預(yù)處理器將為你計算常量表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有運行性能代價的。d) 為常量添加類型信息。宏的不足之一在于缺乏類型安全檢查,人為的提供類型信息可以有效檢查出此類問題。UL告訴編譯器這個常量是無符號長整型數(shù),因此將其賦值給u16型變量會出現(xiàn)告警。 1.3 依賴關(guān)系定義宏改善移植性嵌入式系統(tǒng)程序的最大特點是硬件平臺的多變性,因此需要根據(jù)具體的應(yīng)用情況更改大量配置,而這些配置基本都是由宏定義來實現(xiàn)的,放在特定的頭文件中,與其他的代碼隔離,在一定程度上改善了代碼的可移植性。但有些時候,多個宏定義有嚴重的依賴關(guān)系,增減某個宏會引起其他定義的更
8、改,如何定義這些宏對嵌入式程序的可移植性有很大影響。A常量分別定義#define C_DD_MODULE_ID_AOM (0x) /* AOM模塊ID */#define C_DD_MODULE_ID_RRCM (0x) /* RRCM模塊ID */#define C_DD_MODULE_ID_RLC (0x) /* RLC模塊ID */#define C_DD_MODULE_ID_TRM (0x) /* TRM模塊ID */#define C_DD_MODULE_ID_MCP_MIN (C_DD_MODULE_ID_AOM) #define C_DD_MODULE_ID_MCP_MAX (C
9、_DD_MODULE_ID_TRM)B依賴定義#define C_DD_MODULE_ID_MCP_MIN (0x) /* MCP最小模塊ID */#defineC_DD_MODULE_ID_AOM (C_DD_MODULE_ID_MCP_MIN) /* AOM模塊ID */#define C_DD_MODULE_ID_RRCM (C_DD_MODULE_ID_AOM + 1) /* RRCM模塊ID */#define C_DD_MODULE_ID_RLC (C_DD_MODULE_ID_RRCM + 1) /* RLC模塊ID */#define C_DD_MODULE_ID_TRM (C
10、_DD_MODULE_ID_RLC + 1) /* TRM模塊ID */#define C_DD_MODULE_ID_MCP_MAX (C_DD_MODULE_ID_TRM) /* MCP最大模塊ID */“A常量分別定義”方式,因為各個宏定義值必須連續(xù),若更改或者刪除C_DD_MODULE_ID_AOM,其他的定義基本都受到影響,將嚴重影響到代碼的可擴充性和可裁減性?!癇依賴定義”方式,其原則是:a) base用常量定義;b) 第一個定義為base;c) 其他的在上一個基礎(chǔ)上加1; d) max項即為最后一項。這樣整體改動起來只需要更改base;在中間刪除或添加部分項時只需要更改一個上下銜接
11、處即可;添加則比較簡單,只需要在原有最后項后添加即可。這種方式若改動部分定義對其他定義的影響最小,大大改善了代碼的可移植性。1.4 通過偏移量和掩碼進行位操作嵌入式系統(tǒng)經(jīng)常需要和硬件打交道,而配置硬件寄存器則是系統(tǒng)初始化階段的重要任務(wù),如何清晰明了的進行配置決定了代碼的可讀性。盡管可以使用位域,但位域是不可移植的,因此利用宏定義來解決可移植性問題。對于每一個待操作相應(yīng)位來說,應(yīng)具備以下幾個標(biāo)識:待操作的位名稱B_NAME;操作位所占據(jù)的位寬B_WIDTH_NAME操作位第一位的偏移量B_SHIFT_NAME操作位的掩碼B_MASK_NAME以PRI為例進行說明:#define PRI DD_C
12、64_EDMA_OPT_PRI#define C_WIDTH _DD_C64_EDMA_OPT_PRI (3)#define C_SHIFT_DD_C64_EDMA_OPT_PRI (29)可以手動定義對應(yīng)位的掩碼,如下:#define C_MASK_DD_C64_EDMA_OPT_PRI (0xe)但更好的方式是利用位寬和偏移量來自動構(gòu)成掩碼#define BIT_MASK(_name) (1U C_WIDTH _#_name)-1)( C_SHIFT_#_name)#define C_MASK_DD_C64_EDMA_OPT_PRI BIT_MASK(PRI)BIT_MASK(PRI)經(jīng)過
13、宏替換后即為:(1U( C_WIDTH _DD_C64_EDMA_OPT_PRI)-1) /( C_SHIFT_DD_C64_EDMA_OPT_PRI)對于每個位的取值也應(yīng)該用宏定義來表示,這樣清晰明確#define C_DD_C64_EDMA_OPT_URGENT_PRI (0x0)#define C_DD_C64_EDMA_OPT_HIGH_PRI (0x1)#define C_DD_C64_EDMA_OPT_MEDIUM_PRI (0x2)#define C_DD_C64_EDMA_OPT_LOW_PRI (0x3)具備了掩碼和偏移量即可對各個位進行操作了#define SET_BITS
14、(_reg,_name_val)/(_reg)=(_ reg)&(BIT_MASK(_name) /| (_val)( C_SHIFT_#_ name)&(BIT_MASK(_name)通過如下方式調(diào)用設(shè)置優(yōu)先級SET_BITS(u32Opt, PRI, C_DD_C64_EDMA_OPT_HIGH_PRI);即實現(xiàn)了將u32Opt的PRI等位設(shè)置為C_DD_C64_EDMA_OPT_HIGH_PRISET_BITS宏可應(yīng)用于待操作的位為多位的情況,當(dāng)待操作的位僅為一位時,可用更簡單的操作方式#define SET_BIT(_reg,_name) (_reg) |= (BIT_MASK(_na
15、me)#define CLR_BIT(_reg,_name) (_reg) &= (BIT_MASK(_name)以TCINT為例:#define TCINT DD_C64_EDMA_OPT_TCINT#define C_WIDTH _DD_C64_EDMA_OPT_TCINT (1)#define C_SHIFT_DD_C64_EDMA_OPT_PRI (20)SET_BIT(u32Opt, TCINT)CLR_BIT(u32Opt, TCINT) 嵌入式系統(tǒng)程序可移植性設(shè)計及性能優(yōu)化之二數(shù)據(jù)結(jié)構(gòu)設(shè)計【摘要】本章介紹了結(jié)構(gòu)體中成員的對齊規(guī)則,及在此規(guī)則上如何調(diào)整成員順序或填充部分字段保證其所
16、占內(nèi)存大小不會因為編譯器的不同導(dǎo)致差異。然后介紹了如何利用位域設(shè)計網(wǎng)絡(luò)通信協(xié)議及由此帶來的大小端系統(tǒng)的可移植性問題;同時介紹了用位域在特定平臺上配置硬件寄存器的技巧。最后介紹了如何利用union在不同系統(tǒng)間傳輸變長數(shù)據(jù)包及如何進行數(shù)據(jù)封裝并提供相關(guān)操作接口的相關(guān)技巧?!娟P(guān)鍵詞】嵌入式,可移植性,數(shù)據(jù)結(jié)構(gòu),結(jié)構(gòu)體對齊規(guī)則,非對齊訪問,位域,傳輸協(xié)議,大小端,硬件配置字,數(shù)據(jù)封裝,初始化操作接口2數(shù)據(jù)結(jié)構(gòu)設(shè)計- 4 -2.1結(jié)構(gòu)體中成員對齊規(guī)則- 4 -2.1.1自然對界- 4 -2.1.2指定對界- 4 -2.2合理設(shè)計成員順序- 5 -2.2.1減少結(jié)構(gòu)體存儲空間- 5 -2.2.2填充部分域
17、,避免字節(jié)對齊問題- 6 -2.2.3字節(jié)對齊問題實例- 7 -2.3采用位域構(gòu)造結(jié)構(gòu)體- 8 -2.3.1位域設(shè)計傳輸協(xié)議- 8 -2.3.2位域的可移植性問題- 9 -2.3.3位域設(shè)計硬件配置字- 10 -2.4通過union和struct傳遞不同格式報文- 11 -2.5將相關(guān)功能變量封裝為結(jié)構(gòu)體- 13 -1 數(shù)據(jù)結(jié)構(gòu)設(shè)計程序設(shè)計是算法和數(shù)據(jù)結(jié)構(gòu)的集合,因此數(shù)據(jù)結(jié)構(gòu)是程序設(shè)計的基礎(chǔ),就象建造豪華的公寓也必須從一磚一瓦開始。大型的C/C+程序,勢必要涉及一些進行數(shù)據(jù)組合的結(jié)構(gòu)體,這些結(jié)構(gòu)體可以將原本意義屬于一個整體的數(shù)據(jù)組合在一起。嵌入式系統(tǒng)軟硬件平臺具備多變性,不同處理器對于數(shù)據(jù)對齊
18、訪問的要求不同,另外不同的編譯器可以設(shè)置不同的數(shù)據(jù)對齊規(guī)則,這些都將導(dǎo)致結(jié)構(gòu)體在不同軟硬件平臺下的可移植性問題。在網(wǎng)絡(luò)協(xié)議、通信控制等嵌入式系統(tǒng)的C/C+編程中,經(jīng)常要傳送的不是簡單的字節(jié)流(u8型數(shù)組),而是多種數(shù)據(jù)組合起來的一個整體,其表現(xiàn)形式是一個結(jié)構(gòu)體。經(jīng)驗不足的開發(fā)人員往往將所有需要傳送的內(nèi)容依順序保存在u8型數(shù)組中,通過指針偏移的方法傳送網(wǎng)絡(luò)報文等信息。這樣做編程復(fù)雜,易出錯,按順序存儲的數(shù)組不便于增添其他成分,因此一旦控制方式及通信協(xié)議有所變化,程序就要進行非常細致的修改,移植性差;而結(jié)構(gòu)體的成員增減時不影響原有單元的操作,因為編譯器會自動計算各個成員的偏移量。因此從某種程度上來
19、說,會不會用struct及怎樣用struct對程序的可移植性和可讀性有較大影響。1.1 結(jié)構(gòu)體中成員對齊規(guī)則1.1.1 自然對界對于結(jié)構(gòu)體,編譯器會自動進行成員變量的對齊,以提高運算效率。缺省情況下,編譯器為結(jié)構(gòu)體的每個成員按其自然對界(natural alignment)條件分配空間。各個成員按照它們被聲明的順序在內(nèi)存中順序存儲,第一個成員的地址和整個結(jié)構(gòu)的地址相同。自然對界即默認對齊方式,是指按結(jié)構(gòu)體的成員中size最大的成員對齊。例如:struct naturalalignu8 u8a;u16 u16b;u8 u8c;在上述結(jié)構(gòu)體中,size最大的是u16,其長度為2字節(jié),因而結(jié)構(gòu)體中的
20、u8成員a、c都以2為單位對齊,sizeof(naturalalign)的結(jié)果等于6。1.1.2 指定對界一般地,可以通過下面的方法來改變?nèi)笔〉膶鐥l件:a) 使用偽指令#pragma pack (n),編譯器將按照n個字節(jié)對齊;b) 使用偽指令#pragma pack (),取消自定義字節(jié)對齊方式。所有處于“#pragma pack (n)”和“#pragma pack ()”之間的結(jié)構(gòu)體將按照指定對界對齊。當(dāng)pragma pack (n)中指定的n大于結(jié)構(gòu)體中最大成員的size,則其不起作用,結(jié)構(gòu)體仍然按照size最大的成員進行對界。例如:#pragma pack (n)struct pa
21、cku8 u8a;u32 u32b;u8 u8c;#pragma pack ()當(dāng)n為4、8、16時,其對齊方式均一樣,sizeof(naturalalign)的結(jié)果都等于12。而當(dāng)n為2時,其發(fā)揮了作用,使得sizeof(naturalalign)的結(jié)果為8。另外,通過_attribute(packed (n)也可以單個結(jié)構(gòu)體的成員對齊在n字節(jié)邊界,這樣即使平臺改變了,編譯器不同了,也將采用統(tǒng)一的對界方式,這種方式對于不同體系的處理器之間的數(shù)據(jù)交互很重要,移植性好。1.2 合理設(shè)計成員順序1.2.1 減少結(jié)構(gòu)體存儲空間在默認的自然對界情況下,若最大數(shù)據(jù)類型為u32,則u32四字節(jié)對齊,u16
22、二字節(jié)對齊,整個結(jié)構(gòu)體大小為sizeof(u32)的倍數(shù),該結(jié)構(gòu)體定義的變量首地址自動對齊在sizeof(u32)邊界上。故:struct naturalalignAu16 u16a;u32 u32b;u8 u8c;struct naturalalignBu16 u16a;u8 u8c;u32 u32b;struct naturalalignCu8 u8c;u16 u16a;u32 u32b;sizeof(naturalalignA) = 12sizeof(naturalalignB) = 8sizeof(naturalalignC) = 8struct naturalalignDu8 u8a
23、;u16 u16b;u8 u8c;struct naturalalignEu8 u8a;u8 u8c;u16 u16b;sizeof(naturalalignD) = 6sizeof(naturalalignE) = 4struct naturalalignFu8 u8a;u32 u32b;u8 u8c;struct naturalalignGu8 u8a;u8 u8c;u32 u32b;sizeof(naturalalignF) =12sizeof(naturalalignG) = 8從上面可以看出,從存儲空間的角度看,naturalalignA、naturalalignD、naturala
24、lignF都是不合理的設(shè)計。1.2.2 填充部分域,避免字節(jié)對齊問題struct naturalalignDu8 u8a;u16 u16b;u8 u8c;struct naturalalignHu8 u8a;u8 padding;u16 u16b;u8 u8c;u8 padding;struct naturalalignIu8 u8a;u8 u8c;u16 u16b;在自然對界情況下,sizeof(naturalalignD) = sizeof(naturalalignH) = 6此時并不會因為填充部分域后導(dǎo)致結(jié)構(gòu)體變大,只是避免編譯器自動填充而已。但對于指定對界時可能會使結(jié)構(gòu)體變大。natu
25、ralalignI調(diào)整了成員的順序,減少存儲空間的同時保證了u16 u16b的對齊,無需設(shè)計填充域,同時編譯器也不會自動填充struct naturalalignJu16 u16a;u8 u8c;u8 padding;u32 u32b;struct naturalalignKu8 u8c;u8 padding;u16 u16a;u32 u32b;struct naturalalignLu8 u8c;u16 u16a;u8 padding;u32 u32b;naturalalignJ中填充后保證了u32 u32b的對齊,naturalalignK填充后保證了u16 u16a的對齊也保證了u32
26、u32b的對齊,要注意的是naturalalignL表面上填充域后u32 u32b對齊了,但由于u16 u16a未處于二字節(jié)對齊邊界上,實際上編譯器在u8 u8c后自動填充了一個域保證u16 u16a的對齊,在u8 padding后自動填充了三個u8保證u32 u32b的對齊。naturalalignJ和naturalalignK都是合理的填充方式。#pragma pack (2)struct packA u16 u16a;u32 u32b;u8 u8c;#pragma pack ()#pragma pack (2)struct packBu16 u16a;u8 u8c;u32 u32b;#p
27、ragma pack ()#pragma pack (1)struct packCu8 u8a;u16 u16b;u8 u8c;#pragma pack ()在指定對界情況下,大于pack字節(jié)的將按照設(shè)定值進行對齊sizeof(packA) = 8sizeof(packB) = 8sizeof(packC) = 4由于packA類型變量對齊在sizeof(u32)邊界上,2字節(jié)對齊時u32 u32b的地址沒有對齊在四字節(jié)邊界上,此時的影響為:不能進行非對齊訪問的處理器:u32 u32temp packA.b將導(dǎo)致處理器內(nèi)存訪問異常;可非對齊訪問的處理器:u32 u32temp packA.b對
28、于b的訪問實際上是分兩個u16來分別訪問然后合成一個u32后賦值給u32temp的,比對齊的u32變量訪問效率要低。u32 *u32temp &packA.b,因為u32temp為u32型指針變量,其值必須為4的倍數(shù),而(&packA.b)并不是4的倍數(shù),顯然此處的賦值是不合理的。指針類型強制轉(zhuǎn)換時也可能存在這種異常問題。為了避免指定對界不統(tǒng)一帶來的內(nèi)存訪問異常問題,在數(shù)據(jù)結(jié)構(gòu)設(shè)計時總的原則是:不考慮編譯器自動填充的情況下,通過適當(dāng)填充使u16對齊在二字節(jié)上,u32對齊在四字節(jié)上,此時無論編譯器何種對界,結(jié)構(gòu)體的大小總是固定的,且不會存在內(nèi)存訪問問題。struct naturalalignEu
29、8 u8a;u8 u8c;u16 u16b;struct naturalalignKu8 u8c;u8 u8padding;u16 u16a;u32 u32b;struct naturalalignHu8 u8a;u8 u8padding;u16 u16b;u8 u8c;u8 u8padding;1.2.3 字節(jié)對齊問題實例下面以一個實例來說明對齊方式不同導(dǎo)致的不同處理器間的數(shù)據(jù)交互問題。三種平臺:a) ARM Linux,嵌入式平臺,未定義數(shù)據(jù)結(jié)構(gòu),轉(zhuǎn)發(fā)字節(jié)流,采用偏移量;b) Windows Mobile PDA,嵌入式平臺,將數(shù)據(jù)組合為了結(jié)構(gòu)體,按照字節(jié)對齊;c) Windows XP
30、PC,自然對界。相應(yīng)的數(shù)據(jù)結(jié)構(gòu)定義如下:typedef struct _DBGEstEntry u16 id; u8 hopcount; u8 sendEst; DBGEstEntry;/add by oy 06.8.4typedef struct _DebugPacket u8 estEntries; DBGEstEntry estList; DebugPacket;ARM和PDA交互時因為都是按照字節(jié)對齊的,在將字節(jié)流強制轉(zhuǎn)換為結(jié)構(gòu)體時,對數(shù)據(jù)的解析方式一致,沒有任何問題。后將代碼移植到PC平臺,同樣的程序結(jié)果卻不一樣。究其原因就是因為不同平臺采用了不一致的對齊方式。PC平臺下VC默認采用
31、8字節(jié)自然對界,由于DBGEstEntry最大類型為uu3216_t,因此DebugPacket的成員estEntries后默認補齊了一個u8,相當(dāng)于此時不同平臺采用了不一樣的結(jié)構(gòu)來解析數(shù)據(jù),就出現(xiàn)異常了。添加一個填充域,指針強制轉(zhuǎn)換時,地址前移一個以取消填充域的影響。typedef struct _DebugPacket u8 reserved; u8 estEntries; DBGEstEntry estList; DebugPacket;或者針對此數(shù)據(jù)結(jié)構(gòu)采用指定的對齊方式,和ARM和PDA平臺一致。typedef struct _DebugPacket u8 reserved; u8
32、estEntries; DBGEstEntry estList; _attribute(packed (1) DebugPacket;因此對于嵌入式平臺上的數(shù)據(jù)結(jié)構(gòu)設(shè)計,一定要合理調(diào)整順序及填充部分域避免平臺和編譯器的不同導(dǎo)致的字節(jié)對齊問題。1.3 采用位域構(gòu)造結(jié)構(gòu)體1.3.1 位域設(shè)計傳輸協(xié)議在大多數(shù)情況下,我們一般這樣定義結(jié)構(gòu)體:/* 與MCP驅(qū)動通信交互的數(shù)據(jù)幀頭部標(biāo)識*/typedef struct tag_STRU_DD_FRMHDR u8 u8FrmFlag; /* 幀頭標(biāo)識 */u8 u8SrcModuleId; /* 源模塊ID */u8 u8DstModuleId;/* 目的
33、模塊ID */u8 u8padding;u16 u16Length; /* 數(shù)據(jù)長度 */u16 u16padding STRU_DD_FRMHDR;對于一般的應(yīng)用,這已經(jīng)能很充分地實現(xiàn)數(shù)據(jù)的“封裝”。但是在實際工程中,由于傳輸鏈路中每次傳輸?shù)臄?shù)據(jù)有限,頭部越長,導(dǎo)致有效數(shù)據(jù)越少,如CAN總線通信中,數(shù)據(jù)總長8個字節(jié),有效的減少頭部信息就很重要。由于數(shù)據(jù)長度u16Length不便縮短,而u8SrcModuleId和u8DstModuleId分別用四位就可表示,因此可以用位域來表示此結(jié)構(gòu)體。位域就是一個基本類型變量中的不同的位表示不同的含義。譬如一個硬件寄存器,假設(shè)為16 bit ,而每個bit
34、 都可以表達不同的含義。這個時候我們用什么數(shù)據(jù)結(jié)構(gòu)來表達這個寄存器呢?答案還是結(jié)構(gòu)體!這時要用到結(jié)構(gòu)體的高級特性,就是在基本成員變量的后面添加“: 數(shù)據(jù)位數(shù)”組成新的結(jié)構(gòu)體,如下:typedef struct tag_STRU_DD_FRMHDRu32 bit8FrmFlag :8; /* 幀頭標(biāo)識 */u32 bit4SrcModuleId:4; /* 源模塊ID */u32 bit4DstModuleId :4;/* 目的模塊ID */u32 bit16Length:16; /* 數(shù)據(jù)長度 */ STRU_DD_FRMHDR;上述結(jié)構(gòu)體中的四個成員加起來只占用了一個unsigned u32
35、 的空間?;境蓡T變量被拆分后,訪問的方法仍然和訪問沒有拆分的情況是一樣的。1.3.2 位域的可移植性問題在拆分基本成員變量的情況下,我們要特別注意數(shù)據(jù)的存放順序,這還與CPU 是Big endian 還是Little endian 來決定。Little endian 和Big endian 是CPU 存放數(shù)據(jù)的兩種不同順序。對于整型、長整型等數(shù)據(jù)類型,Big endian 認為第一個字節(jié)是最高位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的高位字節(jié)到低位字節(jié));而Little endian 則相反,它認為第一個字節(jié)是最低位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的低位字節(jié)到高位字節(jié))。如我們定義
36、IP 包頭結(jié)構(gòu)體為:struct iphdr #if defined(_LITTLE_ENDIAN_BITFIELD) u8 ihl:4, version:4; #elif defined (_BIG_ENDIAN_BITFIELD) u8 version:4, ihl:4; #else #error Please fix #endif u8 tos; u16 tot_len; u16 id; u16 frag_off; u8 ttl; u8 protocol; u16 check; _u32 saddr; _u32 daddr; ; 在Little endian 模式下,iphdr 中定義:
37、u8 ihl:4, version:4; 因為位域是從低位字節(jié)開始計算的,其存放方式為:第1 字節(jié)低4 位 ihl ,第1 字節(jié)高4 位 version (IP 的版本號)若在Big endian 模式下還這樣定義,則存放方式為:第1 字節(jié)低4 位 version (IP 的版本號),第1 字節(jié)高4 位 ihl 這與實際的IP 協(xié)議是不匹配的,所以在Linux 內(nèi)核源代碼中,IP 包頭結(jié)構(gòu)體的定義利用了宏來區(qū)分兩種不同的情況。#if defined(_LITTLE_ENDIAN_BITFIELD) #elif defined (_BIG_ENDIAN_BITFIELD) #endif 對于咱們
38、的應(yīng)用情況,此STRU_DD_FRMHDR是PowerPC和DSP交互時使用的,目前二者都為big endian模式,因此二者采用位域?qū)?shù)據(jù)解析時是一致的,但若一方為小端,則數(shù)據(jù)解析將出錯。由此我們總結(jié)位域的使用要點:a) C/C+ 語言的結(jié)構(gòu)體支持對其中的基本成員變量按位拆分,其使用方法和結(jié)構(gòu)體一致,便于編程;b) 但要特別注意拆分后的數(shù)據(jù)的存放順序,為了支持代碼的跨平臺移植,應(yīng)用宏定義來區(qū)分大小端的不同數(shù)據(jù)結(jié)構(gòu)。1.3.3 位域設(shè)計硬件配置字在嵌入式系統(tǒng)中經(jīng)常要和底層硬件打交道,此時需要配置各種硬件選項,而這些選項通常都是由一個寄存器的不同位表示的,進行配置時經(jīng)常要用到與或、移位等操作,編
39、程不便,因此可以利用位域的形式來訪問不同域,其和結(jié)構(gòu)體的訪問形式一樣,非常方便。/* struct defined for the OPTION word for EDMA - big endian */typedef struct tag_STRU_DD_C64_EDMA_OPT u32 u32bit3Pri : 3; /* Priority */ u32 u32bit2Esize : 2; /* Element size */ u32 u32bit1TwoDs : 1; /* Source dimension */ u32 u32bit2Sum : 2; /* Source address
40、 update mode */ u32 u32bit1TwoDd : 1; /* Destination dimension */ u32 u32bit2Dum : 2; /* Destination address update mode */ u32 u32bit1Tcu32 : 1; /* Transfer complete u32errupt */ u32 u32bit4Tcc : 4; /* Transfer complete code */ u32 u32bit1Rsvd1 : 1; /* Resverd */ u32 u32bit2Tccm : 2; /* Transfer co
41、mplete code */ u32 u32bit1Atcu32 : 1; /* Alternate transfer complete u32r */ u32 u32bit1Rsvd2 : 1; /* Resverd */ u32 u32bit6Atcc : 6; /* Alternate transfer complete code */ u32 u32bit1Rsvd3 : 1; /* Resverd */ u32 u32bit1Pdts : 1; /* PDT mode for source */ u32 u32bit1Pdtd : 1; /* PDT mode for Destina
42、tion */ u32 u32bit1Link : 1; /* Link */ u32 u32bit1Fs : 1; /* Frame synchronization */ STRU_DD_C64_EDMA_OPT;硬件寄存器通常上電后各位為0,大多數(shù)選項都是默認的,只需配置部分非0的選項。采用位域時只會更改相關(guān)的配置項,不會影響其他位。而沒有位域時需要與或等操作避免影響其他位。對于硬件配置字通常定義相關(guān)常量來表示不同的配置,如下:/* Define constants to construct EDMA option word */* Priority, 3bit */#define C_D
43、D_C64_EDMA_OPT_URGENT_PRI (0x0)#define C_DD_C64_EDMA_OPT_HIGH_PRI (0x1)#define C_DD_C64_EDMA_OPT_MEDIUM_PRI (0x2)#define C_DD_C64_EDMA_OPT_LOW_PRI (0x3)/* Element size, 2bit */#define C_DD_C64_EDMA_OPT_ESIZE_32 (0x0)#define C_DD_C64_EDMA_OPT_ESIZE_16 (0x1)#define C_DD_C64_EDMA_OPT_ESIZE_8 (0x2)1.4 通
44、過union和struct傳遞不同格式報文網(wǎng)絡(luò)通信中進行數(shù)據(jù)包交互時通常以固定數(shù)據(jù)區(qū)傳遞,但每次傳遞的消息類型可能不同,因此固定數(shù)據(jù)區(qū)的有效數(shù)據(jù)長度可能不一樣,此時需要字段標(biāo)識消息類型和消息長度,以便雙方對數(shù)據(jù)進行解析。每種消息都有共有的消息頭開始,然后是特定的消息信息,這樣消息的結(jié)構(gòu)將變得非常清晰,統(tǒng)一的格式如下:typedef struct tag_STRU_MAT_XXX STRU_DD_MSG_HDR struMsgHdr; STRU_DD_XXX struXXX; STRU_MAT_XXX;消息頭如下:typedef struct tag_STRU_DD_MSG_HDR u8 u8O
45、pCode; u8 u8Len; u8 au8MsgHdrDataC_DD_COMM_MSG_HEADER_DATA_BYTE_LEN; STRU_DD_MSG_HDR;通過消息頭中的u8OpCode操作碼來確定消息類型,也就確定了消息的具體內(nèi)容;若對于某種消息,其有效數(shù)據(jù)長度是可變的,則u8Len將標(biāo)識消息數(shù)據(jù)域的長度當(dāng)消息內(nèi)容固定時,最好以結(jié)構(gòu)體封裝消息內(nèi)容。typedef struct tag_STRU_MAT_SET_BPP_TOD_SFN_TSN_REQ STRU_DD_MSG_HDR struMsgHdr; u32 u32TodPart1; u32 u32TodPart2; u16
46、 u16SfnPeriod; u16 u16TsnPeriod; u16 u16Sfn; u16 u16Tsn; STRU_MAT_SET_BPP_TOD_SFN_TSN_REQ;上述方式消息內(nèi)容零散,同時也不便于將其另行傳遞給其他模塊。封裝后的結(jié)構(gòu)如下:typedef struct tag_STRU _BPP_TOD_SFN_TSN_PARAM u32 u32TodPart1; u32 u32TodPart2; u16 u16SfnPeriod; u16 u16TsnPeriod; u16 u16Sfn; u16 u16Tsn; STRU _BPP_TOD_SFN_TSN_PARAM;typ
47、edef struct tag_STRU_MAT_SET_BPP_TOD_SFN_TSN_PARAM _REQ STRU_DD_MSG_HDR struMsgHdr; STRU _BPP_TOD_SFN_TSN_ PARAM struTimeParam; STRU_MAT_SET_BPP_TOD_SFN_TSN_PARAM _REQ;不同消息STRU_MAT_XXX長度不同,但又需要統(tǒng)一的數(shù)據(jù)區(qū)來存儲,因此需要定義一個數(shù)據(jù)結(jié)構(gòu)來統(tǒng)一各種消息,關(guān)鍵在于統(tǒng)一的數(shù)據(jù)區(qū)長度如何確定,其既保證可以容納所有消息類型同時又不浪費存儲空間?一種方式算出各種消息占用的最大存儲空間,對于具體的消息數(shù)據(jù)采用純數(shù)組存
48、儲,其根據(jù)頭部的u8OpCode確定消息類型,然后再強制轉(zhuǎn)換消息數(shù)據(jù)進行解析。typedef struct tag_STRU_DD_COMM_MSG STRU_DD_MSG_HDR struMsgHdr; u32 au32CommData C_DD_COMM_MSG_MAX_DATA_WORD_LEN; STRU_DD_COMM_MSG;另外一種方式是通過union在同一片內(nèi)存空間存儲不同格式信息,由header來分辨消息類型和長度typedef struct tag_STRU_DD_MAT_COMM_MSG STRU_DD_MSG_HDR struMsgHdr; union STRU_ BP
49、P_CUR_LOAD struCurLoad STRU _BPP_TOD_SFN_TSN_ PARAM struTimeParam; STRU_DD_BPP_LOAD_PARAM struLoadParam; unionMsgData; STRU_DD_MAT_COMM_MSG;各種具體的消息共用一塊固定長度內(nèi)存,取決于最大的消息大小不用根據(jù)各種消息類型算C_DD_COMM_MSG_MAX_DATA_WORD_LEN的值,消息類型的改變不影響tag_STRU_DD_MAT_COMM_MSG,可以自適應(yīng)消息類型的變化;但添加新消息類型時需要更改STRU_DD_MAT_COMM_MSG。在傳輸鏈路
50、上進行數(shù)據(jù)傳輸時并不是傳遞STRU_DD_MAT_COMM_MSG,而是傳遞具體的消息頭和消息內(nèi)容,因為有效信息可能并沒有這么多數(shù)據(jù),這樣可以節(jié)省傳輸時間。此時可用sizeof(STRU_MAT_XXX)或者sizeof(STRU_DD_MSG_HDR) + sizeof(STRU_DD_XXX)1.5 將相關(guān)功能變量封裝為結(jié)構(gòu)體何謂相關(guān)?是指多個不同意義的變量任何時候都是統(tǒng)一定義、統(tǒng)一初始化、統(tǒng)一傳遞給其他模塊,就好像難兄難弟一樣任何時候都在一塊,此時就應(yīng)該將這些變量組織為結(jié)構(gòu)體,優(yōu)勢在于:a) 便于管理、定義、聲明,避免零散的變量;b) 意義明確,結(jié)構(gòu)清晰;c) 函數(shù)調(diào)用時避免傳遞過多參數(shù),提高調(diào)用性能,參數(shù)少不易出錯。不足在于對于結(jié)構(gòu)體的訪問效率不如單獨的變量,但此性能影響很??;為了代碼更好的可讀性、可移植可維護性性和可靠性,此處結(jié)構(gòu)體的形式更合適。以
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 防癌護理科普
- 三下鄉(xiāng)暑期實踐活動個人總結(jié)模版
- 護士行業(yè)實習(xí)心得體會模版
- 新疆吐魯番市2024-2025學(xué)年高一下學(xué)期期中考試 生物 含解析
- 學(xué)生旅游活動方案
- 18 文言文二則《囊螢夜讀》課件
- 2025屆山東省招遠市八下數(shù)學(xué)期末質(zhì)量跟蹤監(jiān)視模擬試題含解析
- 調(diào)經(jīng)止痛護理方案
- 仁愛英語七年級上知識點短語總結(jié)模版
- 特應(yīng)性角結(jié)膜炎的臨床護理
- 民法典與工程索賠課件
- 公共管理學(xué)黎民
- 電梯使用單位安全管理專題培訓(xùn)
- 2025屆福建省廈門市音樂學(xué)校生物七下期末學(xué)業(yè)質(zhì)量監(jiān)測試題含解析
- 托育培訓(xùn)課程課件
- 中國卒中學(xué)會急性缺血性卒中再灌注治療指南(2024)解讀
- 浙江開放大學(xué)2025年《社會保障學(xué)》形考任務(wù)2答案
- 守護生態(tài)平衡 共享多彩世界 課件 -2025年高中生物多樣性日主題教育
- GA/T 2161-2024法庭科學(xué)非法集資類案件資金數(shù)據(jù)分析規(guī)程
- 【+初中語文++】++第11課《山地回憶》課件++統(tǒng)編版語文七年級下冊
- 2025-2030中國黃金珠寶首飾行業(yè)市場深度發(fā)展趨勢與前景展望戰(zhàn)略研究報告
評論
0/150
提交評論