嵌入式多節點(diǎn)的無(wú)線(xiàn)批量程序更新系統設計
一 總體設計和平臺簡(jiǎn)介
項目旨在實(shí)現多ARM節點(diǎn)通過(guò)無(wú)線(xiàn)通信完成對批量節點(diǎn)的程序燒錄,如圖2.1所示。系統分為上位機、發(fā)射接收模塊和待燒錄節點(diǎn)三個(gè)部分,上位機通過(guò)ID號選擇待燒錄節點(diǎn)并通過(guò)無(wú)線(xiàn)模塊向下廣播燒錄數據,各被選擇節點(diǎn)通過(guò)無(wú)線(xiàn)模塊接收燒錄數據檢查無(wú)誤后存儲。上位機軟件設定待燒錄節點(diǎn)的ID、燒錄文件目錄、發(fā)送數據包大小、發(fā)送速率等參數后將數據打包傳送到基站,基站通過(guò)無(wú)線(xiàn)發(fā)射模塊廣播數據。
圖2.1 多節點(diǎn)無(wú)線(xiàn)批量燒錄示意
整體思想是利用已有的代碼和目標代碼進(jìn)行比較。將兩者的差異通過(guò)無(wú)線(xiàn)網(wǎng)絡(luò )(802.15.4)廣播出去。在每個(gè)接受節點(diǎn)根據收到的差異文件(也就是補丁文件patch),對原有代碼進(jìn)行修改(patching的過(guò)程)以達到更新程序的目的。
總體上來(lái)說(shuō)本項目有兩大難點(diǎn),涉及到巧妙的算法設計。
1、如何用盡可能少的字節數,來(lái)表示不同代碼之間的差異?
2、如何確??煽啃詡鬏??
關(guān)于問(wèn)題1,我們知道要待傳輸的字節數越少,對通信的要求越低。更新程序的效率也會(huì )更高。而且少的字節數也意味著(zhù)丟更少的包。關(guān)于問(wèn)題2,由于我們是要對代碼進(jìn)行修正,所以一個(gè)字節的錯誤可能就會(huì )造成整個(gè)程序的崩潰。這對嵌入式程序,特別是運行在成千上萬(wàn)個(gè)節點(diǎn)上的程序是不可接受的,必須保證100%的正確接受。除此兩大難點(diǎn)(也是關(guān)鍵點(diǎn))之外,還有一些別的附加要求。如果滿(mǎn)足了能夠提高系統的持久性。分別是:
1、使用盡可能小的RAM。因為嵌入式芯片的RAM普遍珍貴。
2、消耗盡可能少的能量。
3、更新速度盡可能的快。
項目使用的硬件平臺是我們自制的智能小車(chē)eMouse 。平臺采用 TI公司32位Stellaris LM3S1968微處理器,工作頻率為50MHz,內含256 KB單周期Flash和64 KB單周期SRAM,flash支持可由用戶(hù)管理的塊保護和數據編程;板上Zigbee模塊通過(guò)串口與CPU通信,模塊僅提供MAC層服務(wù),CPU與模塊間以MAC幀的形式通過(guò)串口傳遞數據。eMouse外觀(guān)如圖2.2所示。
圖2.2 硬件平臺eMouse
項目開(kāi)發(fā)系統環(huán)境為Ubuntu9.04,程序編譯和下載工具分別為開(kāi)源的sourcery G++和Openocd,用戶(hù)界面采用java語(yǔ)言編寫(xiě)。
以下章節將對系統設計作詳盡的論述。
二 程序更新設計與實(shí)現
一些傳統的更新方法注重代碼本身的特點(diǎn)。比如以函數為基本的更新單位。在每個(gè)節點(diǎn)上運行一個(gè)動(dòng)態(tài)鏈接器,將新的函數重新鏈接到原程序。其實(shí)代碼本身可以將其視為一串二進(jìn)制的文本文件。代碼的更新即是從一串舊的文本更新為一串新的文本。
為此我們定義了一系列基本的更新操作命令,以及兩個(gè)輔助的索引指針:in_index以及out_index。in_index代表輸入文件當前的索引值,而out_index代表輸出文件當前的索引值。
基本的命令如下:
Copy:將in_index所指的字節復制到out_index處,并且in_index和out_index分別加1。
Replace A:將當前out_index所指的字節用A來(lái)替換,并且in_index和out_index分別加1。
Delete:in_index加1,out_index不變。實(shí)際為刪除in_index所指的字節
Insert A:在當前out_index處插入A,in_index不變,out_index加1。
Kill:表示刪除in_index后所有的原程序代碼。
其中包含了如下的子問(wèn)題:
2.1 命令的表示
通過(guò)上面所說(shuō)的基本命令的組合,我們可以表示出從一個(gè)舊文件到一個(gè)新文件的過(guò)程。隨之帶來(lái)了一個(gè)問(wèn)題。這些命令應該如何表示才能盡可能的降低補丁文件(命令的組合)的大???
有幾個(gè)需要注意的地方:
如果有連續的Copy命令,我們可以將其合并成一條命令,只要在Copy后加上表示長(cháng)度的Length參數即可。
同理,如果有連續的Delete命令,也可以將其合并成一條命令,只要在Delete后加上表示長(cháng)度的Length參數即可。
如果可以利用Replace命令,就不要用Delete和Insert命令的組合。這其實(shí)是另一重要的子問(wèn)題:如何根據這些命令產(chǎn)生盡可能少補丁文件?
有五條基本命令,這樣為了區別這五條命令,至少需要3個(gè)比特。
由于大多數情況下,更新的大多數是程序的參數。也就是說(shuō)Copy命令的數目遠遠大于其他命令。我們定義這5條命令如下表所示:
表3.1
命令 | 操作碼(最左端開(kāi)始) | 操作數的長(cháng)度(緊跟操作碼) | 總長(cháng)度(字節) |
Copy | 1 | 15 | 2 |
Delete | 000 | 13 | 2 |
Replace | 001XXXXX | 8 | 2 |
Insert | 010XXXXX | 8 | 2 |
Kill | 011XXXXX | 8 | 2 |
經(jīng)過(guò)大量實(shí)驗,我們發(fā)現連續出現Copy的情況最多,因此Copy命令操作碼只有1位,即只要是最左端比特為1,則此命令為Copy命令。這樣Copy的操作數為15個(gè)比特,一次能表示復制32768個(gè)字節。同理,Delete的格式同Copy時(shí)相同的,只不過(guò)其操作碼較長(cháng),操作數只有13位,最多能代表刪除8192個(gè)字節。實(shí)際上這也完全夠用了。
Replace和Insert操作碼的有效位為最左端三位,緊跟著(zhù)5個(gè)比特是保留位,當前還沒(méi)有用到。操作數的長(cháng)度為一個(gè)字節,表示當前要替換的或者要插入的新值。
Kill命令操作碼為左端3個(gè)比特,剩下的15個(gè)比特都是保留位。操作數的長(cháng)度為一個(gè)字節,表示刪除的起始索引。
綜上可以看出,指令的格式都是定長(cháng)的——2個(gè)字節。定長(cháng)的代價(jià)是會(huì )浪費一定的比特。造成實(shí)際生成的補丁文件略大(由于Insert,Replace和Kill的保留位)。但正如MIPS處理器,定長(cháng)的規定使得整個(gè)指令集簡(jiǎn)潔有序。雖然產(chǎn)生的指令條數要比X86系列的CISC機要多,但簡(jiǎn)潔的特性總是讓人喜歡的。
2.2 命令的產(chǎn)生
這是最有挑戰性的問(wèn)題,如何根據前面定義的基本命令,產(chǎn)生盡可能小的操作指令集(補丁文件)?仔細觀(guān)察發(fā)現,其實(shí)此問(wèn)題包含了一個(gè)最優(yōu)子結構,也就是說(shuō),我們可以用動(dòng)態(tài)規劃的算法來(lái)解決這個(gè)問(wèn)題,保證產(chǎn)生的補丁文件是最小的。
假設原程序的長(cháng)度為m個(gè)字節,目標程序的長(cháng)度為n個(gè)字節。定義= x[1..i],Yj = y[1..j],其中x[1..i]表示源程序的第一個(gè)到第i個(gè)字節,y[1..j]表示目標程序的第一個(gè)到第j字節。用c[i,j]表示從Xi 到Yj所用的最小的代價(jià)。由于所有的命令長(cháng)度均相同,故每條命令代價(jià)都為1,c[i,j]也就是代表從Xi 到Yj 所需的最小的命令數,求得最小的命令數,別且記錄下其操作,我們就能得到最小的補丁文件。這樣我們有以下幾種情況:
如果最后的操作為Copy,則一定有x[i] = y[j]。原問(wèn)題包含將Xi-1 轉化到Yj-1的子問(wèn)題。c[i,j] = c[i-1,j-1]+1
如果最后的操作為Replace,則一定有x[i] != y[j]。原問(wèn)題包含將Xi-1 轉化到Yj-1的子問(wèn)題。c[i,j] = c[i-1,j-1]+1
如果最后的操作為 Delete,則沒(méi)有什么必須滿(mǎn)足的條件。原問(wèn)題包含將Xi-1 轉化到Yj的子問(wèn)題。c[i,j] = c[i-1,j]+1
如果最后的操作為 Insert,也沒(méi)有什么必須滿(mǎn)足的條件。原問(wèn)題包含將Xi 轉化到Yj-1的子問(wèn)題。c[i,j] = c[i,j-1]+1
如果最后的操作為Kill。由于Kill表示刪除源程序所有剩余的字節。Kill只能出現在最后一個(gè)操作上。即完成Kill后就已經(jīng)使得Xm 轉化為了Yn。
c[m,n] = min(c[i,n]) + 1, 0= i= m
這樣所有的情況都已經(jīng)包含在內。對于每一個(gè)i,j我們可以求得最c[i,j]
公式從上到下依次代表了Copy,Replace,Delete,Insert和Kill這五種情況。
整體的偽代碼如代碼3.1所示:注意,我們不僅求得每一個(gè)c[i,j]而且記錄下了與其相應的操作.op[i,j]這個(gè)數組中的每個(gè)元素為一個(gè)結構體,包含操作數以及操作碼。
代碼3.1得到c[i,j]以及op[i,j]
這樣,我們得到了c[m,n]以及操作表。下面就是要求得操作序列。根據之前生成的操作表,采用一個(gè)遞歸的方法得出最小代價(jià)的操作序列。偽代碼如代碼3.2所示:
代碼3.2生成最小代價(jià)的操作序列
這樣,我們得到在定長(cháng)命令下,最小的補丁文件。以上都是在PC機上進(jìn)行的。即界面中的生成補丁按鈕。
圖3.3 界面-生成補丁功能
2.3在LM3S1968上的實(shí)現
在PC機上的部分比較容易實(shí)現(生成patch文件)。但在LM3S1968這個(gè)嵌入式芯片上進(jìn)行代碼的替換就不是很簡(jiǎn)單了。首先我們要確定各個(gè)文件的位置。這里為了簡(jiǎn)單起見(jiàn),將flash的0x0000到0x3000處,設為更新服務(wù)程序區,初始化必要的硬件(通信、flash等),等待基站發(fā)送的命令來(lái)更新程序或者直接將控制轉移給應用程序程序,本部分的程序在啟動(dòng)后首先運行。如果檢測0x4000處為合法的應用程序,則將控制權轉交給它,每個(gè)應用程序在接受到了“等待接受”命令后,又將控制權轉移給更新服務(wù)程序,等待從基站發(fā)來(lái)的其他命令。需要注意的是在將控制權轉移到應用程序時(shí),中斷向量表的位置,棧指針,是兩個(gè)要小心設置的量。否則會(huì )造成整個(gè)系統的崩潰。而且本部分只能用匯編語(yǔ)言寫(xiě),具體可以參見(jiàn)bl_start_gcc.S。0x3000到0x7000處為應用程序區,存放待運行的程序。0x7000以后存放這從主機發(fā)來(lái)的Patch文件。
整體的流程為:
圖3.4更新流程圖
三 可靠數據分發(fā)協(xié)議的設計與實(shí)現
3.1 Deluge協(xié)議簡(jiǎn)介
Deluge協(xié)議是一個(gè)優(yōu)秀的可靠性數據分發(fā)協(xié)議,由加利福尼亞大學(xué)伯克利分校的David Culler等人在2004年提出的,首先在TinyOS1.1.8操作系統上實(shí)現。協(xié)議的設計初衷是用來(lái)進(jìn)行較大規模的數據分發(fā),比如大塊數據傳輸和遠程系統升級等。
在Deluge協(xié)議中,采用了協(xié)商式交互策略(ADV-REQ-DATA)來(lái)實(shí)現受控泛洪。而整個(gè)網(wǎng)絡(luò )由狀態(tài)機來(lái)控制數據的分發(fā),網(wǎng)絡(luò )中每個(gè)節點(diǎn)都處在MAINTAIN、RX和TX三種狀態(tài)的其中一種,并且遵循該種狀態(tài)下的一系列動(dòng)作規則。在Deluge協(xié)議中,把將要分發(fā)的目標文件(Sobj)劃分為固定大小的程序包(Spkt),由N個(gè)程序包(Spkt)組成一個(gè)程序頁(yè)(Spage)。Deluge協(xié)議對整個(gè)目標文件數據的劃分如圖4.1所示?;谶@種數據結構,Deluge協(xié)議支持空間多路技術(shù)以提高數據傳輸的速度,在協(xié)議中的具體實(shí)現是流水線(xiàn)傳輸(Pipelining)。
圖4.1 Deluge協(xié)議中分發(fā)數據的結構
Deluge協(xié)議引入了復雜的控制信息,而目前很多無(wú)線(xiàn)傳感器網(wǎng)絡(luò )應用中的節點(diǎn)都不能支持像TinyOS這樣的操作系統,因此實(shí)現起來(lái)難度較高;同時(shí),許多數據分發(fā)的應用場(chǎng)景提供Deluge協(xié)議中的一些高級功能并不能明顯提升網(wǎng)絡(luò )性能,比如網(wǎng)絡(luò )節點(diǎn)較少則不需要流水線(xiàn)數據分發(fā),數據塊較少則不需要分頁(yè)機制等?;谝陨显?,本設計在提出若干常見(jiàn)應用場(chǎng)景假設的基礎上對Deluge協(xié)議做了簡(jiǎn)化和補充。
3.2 可靠數據分發(fā)協(xié)議的設計
在闡述具體的設計思路之前,先提出以下應用場(chǎng)景的假設。
假設一:網(wǎng)絡(luò )節點(diǎn)不支持高級的操作系統??梢岳斫鉃楸仨毧紤]節點(diǎn)處理和通信能力有限,而且通信協(xié)議要從底層(如MAC層)實(shí)現。
假設二:大部分待燒錄節點(diǎn)分布在數據基站的通訊范圍之內??梢岳斫鉃橥ㄐ艆f(xié)議不需要實(shí)現復雜的多跳通信和流水線(xiàn),可以充分利用數據基站第一次數據廣播,這一點(diǎn)下文會(huì )詳細闡述。
基于以上兩點(diǎn)假設,可靠性數據分發(fā)協(xié)議的具體設計如下。
考慮到不同平臺的無(wú)線(xiàn)收發(fā)模塊提供的服務(wù)接口和通信質(zhì)量的差異以及程序更新對網(wǎng)絡(luò )可靠性的要求,通信協(xié)議選擇在網(wǎng)絡(luò )層實(shí)現可靠數據分發(fā)的機制,協(xié)議只需要硬件平臺在MAC層提供收發(fā)數據幀的應用接口即可。協(xié)議中,數據分發(fā)分為兩個(gè)階段:第一輪發(fā)送階段和節點(diǎn)間交流階段。圖4.2為兩個(gè)階段通信方式示意圖。
圖4.2 數據分發(fā)兩階段通信方式示意圖
(實(shí)線(xiàn)代表發(fā)送完整數據文件,虛線(xiàn)表示發(fā)送數據頁(yè))
1、第一輪發(fā)送階段。
數據基站(如PC)在接收節點(diǎn)準備好后不間斷廣播數據幀,直至數據發(fā)送結束;接收節點(diǎn)盡力接收數據,并記錄自己已有數據幀的id信息,期間不向源節點(diǎn)發(fā)送反饋信息。
在原始的Deluge協(xié)議中沒(méi)有這一階段,因為Deluge協(xié)議中可能無(wú)線(xiàn)傳感器網(wǎng)絡(luò )龐大,分布范圍也較廣,所以數據分發(fā)一旦啟動(dòng),所有接收到數據的節點(diǎn)都參與到數據發(fā)送中來(lái);而本設計中,網(wǎng)絡(luò )充分利用了假設二中的節點(diǎn)分布條件,通常情況下,在第一輪發(fā)送結束后,相當大比例的節點(diǎn)就已經(jīng)接收到了大部分的數據,而這個(gè)過(guò)程中因為只有數據基站在發(fā)送廣播,網(wǎng)絡(luò )中數據傳輸的效率是最高的。當然,這種節點(diǎn)分布條件不滿(mǎn)足的情況也不會(huì )明顯降低數據分發(fā)效率。
節點(diǎn)間交流階段。
交流階段參考了trickle算法的“polite gossip”策略,所有節點(diǎn)(包括數據基站)都參與到交流中去。每個(gè)節點(diǎn)的交流的目的都是相同的,即將自己擁有的數據包發(fā)送給需要的節點(diǎn)和請求并接收自己需要的數據包。
第2階段是保證可靠性的關(guān)鍵,協(xié)議中讓源節點(diǎn)也參與到交流中來(lái),這是為了防止網(wǎng)絡(luò )狀況極差以至在第一輪發(fā)送結束之后所有節點(diǎn)接收數據的總和都不構成完整數據文件的極端情況。這一步中,節點(diǎn)長(cháng)時(shí)間處于“維護”狀態(tài)標志數據分發(fā)結束。
節點(diǎn)首先廣播廣告,每一個(gè)廣告包含一個(gè)摘要(φ),摘要(φ)由兩部分組成:(1)本節點(diǎn)的IP標識v。(2)本節點(diǎn)的最大可用頁(yè)號p,即φ(v,p)??捎庙?yè)號p的定義:頁(yè)p所包含的包被節點(diǎn)全部接收,稱(chēng)頁(yè)p完成。頁(yè)p被完成并且它之前的所有的頁(yè)(0,p)也被節點(diǎn)全部接收,稱(chēng)頁(yè)p可用。節點(diǎn)通過(guò)廣告來(lái)了解對方擁有的數據信息,繼而向比自己數據更完備的節點(diǎn)發(fā)送數據頁(yè)請求。協(xié)議中將時(shí)間分成時(shí)間片(round),在每一個(gè)時(shí)間片中,節點(diǎn)來(lái)決定是否廣播一個(gè)廣告。假設時(shí)間片的長(cháng)度由Tm,i來(lái)表示,它的上下界由Tl和Th來(lái)表示,則有取TlTm,iTh。在每一個(gè)時(shí)間片i中,節點(diǎn)維護—個(gè)隨機值ri,ri的值在Tm,i/2和Tm,i之間,ri值的范圍選取是為了解決短監聽(tīng)問(wèn)題(short—listen problem)。
交流階段中,節點(diǎn)擁有“維護”、“請求”和“發(fā)送”中的人一個(gè)狀態(tài)。節點(diǎn)在“維護”狀態(tài)廣播廣告并聽(tīng)取其他節點(diǎn)的廣播;在請求階段向其他節點(diǎn)發(fā)送數據頁(yè)請求,并接收對方發(fā)來(lái)的數據;在發(fā)送狀態(tài)廣播被請求的數據頁(yè)。圖4.3為狀態(tài)轉換示意圖。主要的交流規則如下。
(1)“維護”狀態(tài)規則
M1: 假設時(shí)間片i的開(kāi)始時(shí)間為ti,節點(diǎn)在ti+ri的時(shí)間段內,若接收不到廣告φ=φ,則廣播廣告φ;若收到與φ不一致的廣告(包括φ=φ、廣告幀和數據幀等),則調整時(shí)間片為T(mén)l,并立即重新開(kāi)始時(shí)間片;若接收到廣告φ=φ,則調整時(shí)間片為min(2*Tm,i ,Th )。
M2: 節點(diǎn)在收到廣告φ(v,p)中p大于自身的最大可用頁(yè)p時(shí),轉向“請求”狀態(tài),向節點(diǎn)v發(fā)送數據頁(yè)請求;節點(diǎn)收到請求幀,則轉向“發(fā)送”狀態(tài),廣播被請求數據頁(yè)。
規則1能控制冗余廣告的發(fā)送,節約網(wǎng)絡(luò )資源,并且根據網(wǎng)絡(luò )狀況動(dòng)態(tài)調整時(shí)間片長(cháng)度,從而是網(wǎng)絡(luò )資源得到有效的利用。
規則2實(shí)現從“維護”狀態(tài)到“請求”和“發(fā)送”狀態(tài)的轉換。
(2)“請求”狀態(tài)規則:
M3:若節點(diǎn)在向源節點(diǎn)發(fā)出數據頁(yè)請求后節點(diǎn)在時(shí)間t(t為自定義時(shí)間長(cháng)度,是經(jīng)驗值,根據網(wǎng)絡(luò )狀況而定)內沒(méi)有收到數據,則再次發(fā)送請求,若累計請求次數大于k(k為自定義次數),則認為請求失敗,返回“維護”狀態(tài);若節點(diǎn)接收到數據頁(yè),則在接收結束后返回“維護”狀態(tài)。
規則3中考慮到網(wǎng)絡(luò )的質(zhì)量因素,定義了等待時(shí)間t和最大請求次數k。
(3)“發(fā)送”狀態(tài)規則:
M4:節點(diǎn)進(jìn)入“發(fā)送”狀態(tài)立即廣播被請求的數據頁(yè),廣播結束后返回“維護”狀態(tài)。
規則4中要注意的是,節點(diǎn)以廣播的方式發(fā)送數據,這意味著(zhù)處于“請求”狀態(tài)的節點(diǎn)可以接收任何節點(diǎn)(不一定是它請求的指定節點(diǎn))發(fā)送的符合其需要的數據包,這也是協(xié)議中避免網(wǎng)絡(luò )冗余的一個(gè)體現。
圖4.3 可靠數據分發(fā)交流階段節點(diǎn)狀態(tài)轉換示意圖
以上是本設計中可靠數據分發(fā)協(xié)議的全部?jì)热?,本文在下一節中將詳細論述協(xié)議的軟件設計實(shí)現。
3.3 可靠數據分發(fā)協(xié)議的軟件設計實(shí)現
協(xié)議的軟件設計在網(wǎng)絡(luò )層實(shí)現,涉及到MAC層接口的調用。本節先簡(jiǎn)單介紹本設計實(shí)驗平臺上網(wǎng)絡(luò )模塊提供的MAC層應用接口,然后詳細論述軟件的設計和實(shí)現。
3.3.1 MAC層接口簡(jiǎn)介
首先做兩點(diǎn)說(shuō)明。
第一,設計中使用的MAC層接口不提供絕對可靠的網(wǎng)絡(luò )通信。一方面是因為設計使用實(shí)驗室自制的硬件平臺主要用于做群體實(shí)驗,而群體實(shí)驗不需要可靠的網(wǎng)絡(luò )通信,所以平臺的通信模塊也沒(méi)有能實(shí)現可靠通信的機制;另一方面要求MAC層提供可靠通信也不是必要的。
第二,網(wǎng)絡(luò )層只使用了MAC層提供的數據幀發(fā)送和數據幀接收兩個(gè)接口,網(wǎng)絡(luò )層的幀結構包含在MAC數據幀的數據域中。
從第一點(diǎn)可以看到,協(xié)議在網(wǎng)絡(luò )層實(shí)現可靠數據傳輸的機制,降低了對MAC層通信質(zhì)量的要求,而第二點(diǎn)說(shuō)明協(xié)議僅僅需要MAC層提供兩個(gè)最基本的應用接口。本設計中的可靠數據分發(fā)協(xié)議對底層通信的要求很低,具有較好的魯棒性和可移植性。
本設計實(shí)驗平臺上提供的MAC層數據幀發(fā)送命令結構如圖4.4所示,其中區域3為數據域,包含網(wǎng)絡(luò )層的幀結構,另外節點(diǎn)在MAC層以廣播的方式通信,所以命令中不包含源節點(diǎn)和目的節點(diǎn)的地址信息。MAC層接收到數據幀后,將數據域分離出來(lái)存儲到接收緩存區;發(fā)送數據時(shí),將發(fā)送緩存區中的數據加上MAC層數據幀的頭部和尾部并發(fā)送出去,網(wǎng)絡(luò )層只關(guān)心發(fā)送和接收緩沖區中的數據。這里規定以下章節中提到的各種幀結構均指網(wǎng)絡(luò )層幀結構。
圖4.4 MAC層接口數據幀發(fā)送命令
3.3.2 可靠數據分發(fā)協(xié)議的數據結構設計
網(wǎng)絡(luò )層數據要經(jīng)過(guò)緩存,解析再到存儲或者執行三步操作,并且不同種類(lèi)的幀要區別處理,因此一個(gè)好的數據結構設計方案對簡(jiǎn)化數據處理操作和提高數據處理效率是非常有必要的。圖4.5為網(wǎng)絡(luò )層數據流圖,數據幀的流向為:
從MAC層讀入后放入原始數據緩沖區;
經(jīng)解析后得到幀結構;
將幀結構作相關(guān)處理后僅提取頁(yè)號(p)、幀號(id)和數據(data)放到寫(xiě)flash緩沖區;
寫(xiě)flash。
注意以上是數據幀的流向,除數據幀以外的其他類(lèi)型幀(如請求幀,結束幀等)只執行第(1)、(2)步操作。下面著(zhù)重論述圖中每個(gè)階段涉及到的數據結構。
圖4.5 網(wǎng)絡(luò )層數據流圖
緩沖區Deluge_buf
Deluge_buf是一個(gè)環(huán)形緩沖區,用于緩存原始的網(wǎng)絡(luò )層數據。緩沖區實(shí)際上是由一個(gè)環(huán)形鏈表、兩個(gè)指針和一個(gè)整數組成。鏈表的每個(gè)節點(diǎn)用于存儲實(shí)際數據,節點(diǎn)數目根據需要而定;一個(gè)tail指針和一個(gè)head指針,分別指鏈表的讀出點(diǎn)和寫(xiě)入點(diǎn),執行一次讀出或寫(xiě)入操作后,tail或head指針都向前移動(dòng)一次,整數的作用是統計當前鏈表上可用節點(diǎn)的數目。Deluge_buf結構體定義如下:
struct Deluge_buf {
struct data_entry queue_data[QUEUE_LENGTH]; // The data of current queue
uint8 recv_num;
uint8 queue_head;
uint8 queue_tail;
};
值得注意的是結構體data_entry中Payload項的組成在不同類(lèi)型的幀中是不同的,比如數據幀中Payload包括頁(yè)號p、幀號id和數據data以及數據長(cháng)度data_len,而廣告幀中只包含p和id,因此解析方法要根據type值來(lái)區分。
幀結構DelugeData
如圖五所示,DelugeData定義了幀類(lèi)型(type)等六個(gè)數據項,設計中根據不同的幀類(lèi)型規定了各個(gè)數據項的含義,具體定義如表4.1所示,“—”表示該數據項在幀中沒(méi)有定義。
表4.1 DelugeData中數據項含義的定義
數據項 幀類(lèi)型 | type | v | p | id | data | data_len |
數據幀 | DATA | 版本號 | 頁(yè)號 | 幀號 | 數據 | 數據長(cháng)度 |
結束幀 | END | 版本號 | 頁(yè)號 | 幀號 | — | — |
廣告幀 | ADV | 版本號 | 頁(yè)號 | 源節點(diǎn)標識 | — | — |
請求幀 | REQ | 版本號 | 頁(yè)號 | 目標節點(diǎn)標識 | — | — |
命令幀 | CMD | 命令參數 | — | — | — | — |
3、緩沖區Flash_buf
因為寫(xiě)flash操作比網(wǎng)絡(luò )傳輸慢得多,為了避免寫(xiě)flash拖慢整個(gè)數據分發(fā)速度,建立緩沖區Flash_buf用于緩存準備好的數據。Flash_buf也是一個(gè)環(huán)形緩沖區,原理和Deluge_buf相同。緩沖區的節點(diǎn)包含p、id、data三個(gè)數據項和指針域next,其中data是要寫(xiě)入flash的數據,p和id用于計算待寫(xiě)入的flash地址。
3.3.3 可靠數據分發(fā)協(xié)議的軟件架構設計
可靠數據分發(fā)協(xié)議的軟件構架設計包括發(fā)送端和接收端兩塊內容。發(fā)送端軟件運行在數據基站上,分為兩個(gè)階段,第一階段通知節點(diǎn)連續地發(fā)送整個(gè)文件,第二階段運行狀態(tài)機參與到節點(diǎn)的交流中去;接收端軟件運行在待燒錄節點(diǎn)上,第一個(gè)階段盡可能多的接收基站發(fā)送來(lái)的數據,第二階參與節點(diǎn)間討論。因為發(fā)送端第一階段軟件比較簡(jiǎn)單,第二階段和接收端相同,所以這里只重點(diǎn)介紹接收端的軟件構架設計。
第一階段:
程序完成初始化后進(jìn)入準備接收狀態(tài),當數據幀到來(lái)時(shí)將數據提取出來(lái)寫(xiě)到flash相應的地址(地址由頁(yè)號p和幀號id計算得到),并將該幀標記為“完成幀”;若接收到結束幀,則記錄結束幀的頁(yè)號pmax和幀號idmax并進(jìn)入第二階段;若接收到其他類(lèi)型幀則直接進(jìn)入第二階段。第一階段的軟件流程圖如圖4.6所示。
圖4.6 接收端軟件第一階段流程圖
第二階段:
完成第一輪接收后,程序運行ADV-REQ-DATA狀態(tài)機,和其他節點(diǎn)交流,完善或幫助其他節點(diǎn)完善數據文件。狀態(tài)機分為MAINTAIN(維護)、RX(請求)和TX(發(fā)送)三個(gè)狀態(tài),程序首先進(jìn)入MAINTAIN狀態(tài)。MAINTAIN狀態(tài)下,程序監聽(tīng)廣告幀和請求幀并在適當時(shí)機發(fā)送廣告,根據協(xié)議規定,程序可能跳轉到RX狀態(tài)或TX狀態(tài)進(jìn)行數據幀請求和發(fā)送操作,操作完成后返回MAINTAIN狀態(tài)。程序中定義一個(gè)最長(cháng)時(shí)間tmax,如果MAINTAIN狀態(tài)持續時(shí)間超過(guò)tmax,則認為整個(gè)數據分發(fā)過(guò)程結束,程序檢查自己接收到的數據是否完備后退出。第二階段的軟件流程圖如圖4.7所示。
圖4.7 接收端軟件第二階段流程圖
四 系統測試
本測試將用三個(gè)程序作為用例,以測試系統的可用性。三個(gè)程序分別為:
Led.bin實(shí)現簡(jiǎn)單的跑馬燈;
GoAhead.bin 三輛小車(chē)將一直向前方走,即使碰到障礙物也不停止;
RandomWalk.bin 三輛小車(chē)將進(jìn)行隨機行走,并且碰到障礙物后會(huì )進(jìn)行壁障,轉彎。
首先我們將批量更新跑馬燈的程序,然后我們來(lái)看GoAhead.bin,如圖5.1所示。完整的程序鏡像大小為3340Bytes
圖5.1 GoAhead.bin的大小
當前在節點(diǎn)上已經(jīng)運行了Led.bin,我們將使用Led.bin和GoAhead.bin進(jìn)行比較,生成patch.bin文件,即補丁文件。
圖5.2生成的patch.bin文件
我們看到,生成的patch.bin文件僅僅是原程序GoAhead.bin的1/3大??!圖5.3是patch.bin代表的命令(截取一部分)。
圖5.3 patch.bin代表的命令
下面從GoAhead.bin 生成 RandomWalk.bin,RandomWalk.bin的大小如圖5.4所示:
圖5.4 Randomalk.bin
圖5.5從生成的patch.bin文件的大小可以看到,其為RandomWalk的大約1/3。但有個(gè)值得注意的地方是,RandomWalk.bin比GoAhead.bin大了1000多個(gè)字節。添加的著(zhù)1000多個(gè)字節是占patch.bin的主要內容??梢?jiàn)發(fā)送patch.bin比較適合于修改部分變量或者函數的時(shí)候。如果是單純的增加功能,比較適合于發(fā)送完整的鏡像文件。
圖5.5 patch.bin文件
五 總結
測試結果表明,本設計實(shí)現了可靠性無(wú)線(xiàn)批量嵌入式節點(diǎn)程序更新,燒錄出錯率低;更新效率高;不依賴(lài)操作系統,具有很好的可移植性,項目總體上實(shí)現了設計的目標。另一方面由于時(shí)間限制,系統仍然存在一些不足。以下是設計中幾點(diǎn)需要優(yōu)化的地方和相應的改進(jìn)意見(jiàn)。
系統在Linux環(huán)境下進(jìn)行了開(kāi)發(fā)和應用,沒(méi)有開(kāi)發(fā)Windows版本。項目組準備在下一階段把系統移植到Windows平臺上。
尚未實(shí)現程序的動(dòng)態(tài)更新,即每次更新前都要將正在運行的程序關(guān)掉,強制節點(diǎn)進(jìn)入準備狀態(tài)??梢苑峙湟粋€(gè)專(zhuān)用線(xiàn)程用于程序更新,同時(shí)為了避免更新對正在運行的程序造成影響,需要在更新過(guò)程中引入動(dòng)態(tài)鏈接技術(shù)
評論