實用的嵌入式編碼技巧 第二部分

2021-10-06 15:15:11 字數 3209 閱讀 9003

除本章中討論的問題外,還有其他更妙的問題 第1部分來自硬體和軟體的互動。這些可能不符合折返的經典定義,但是會帶來類似的風險,並且需要類似的解決方案。

我們在嵌入式硬體和軟體之間的模糊介面上工作,由於我們的**和裝置之間的相互作用,這還會產生其他問題。有些導致不穩定的故障,而且幾乎無法診斷,導致我們的客戶大為惱火。

所有最嚴重的錯誤是很少出現的錯誤,無法複製。但是,可靠的系統不能容忍任何型別的缺陷,尤其是通過我們測試的隨機缺陷,也許被「啊,這只是小故障」行為所忽略。

每當嵌入式硬體和軟體非同步互動時,潛在的惡魔就會潛伏。也就是說,當某些物理裝置以自己的速率執行時,將以以不同速度執行的韌體取樣。

我瀏覽了一些開源**,並遇到了非同步互動的典型示例。oar corporation提供的rtems實時作業系統是經過精心編寫的,結構合理的產品,具有許多簡潔的功能。

但是,至少對於68302發行版而言,計時器處理例程存在某種缺陷,這種缺陷很少會發生,但可能會造成災難性的後果。這只是我經常看到的埋在專有韌體中的問題的乙個非常公開的例子。

該**簡單明瞭,看起來與其他計時器處理程式非常相似。

inttimer_hi;

中斷計時器()

長read_timer(無效)

16位硬體計時器溢位時,將呼叫中斷服務程式。isr為硬體提供服務,遞增名為timer_hi的全域性變數,然後返回。

因此,timer_hi會將硬體計數的次數保持為65536。函式read_timer返回當前的「時間」(isr和硬體計時器所跟蹤的經過時間,以微秒為單位)。它也令人愉快地沒有併發症。

像大多數此類例程一樣,它讀取硬體定時器暫存器的當前內容,將timer_hi左移16位,並新增從定時器讀取的值。也就是說,當前時間是計時器的當前值和溢位次數的串聯。

假設硬體滾動了5次,建立了五個中斷。timer_hi等於5。當我們呼叫read_timer時,內部暫存器可能是0x1000。該例程返回值0x51000。簡單,看似沒有問題。

比賽條件

但是,讓我們仔細考慮一下。確實有兩件事同時發生。與rtos分配cpu資源的多工環境不同,因此所有任務似乎在同時執行,因此不是同時存在,這意味著「顯然是在同一時間」。

否,在這種情況下,只要呼叫read_timer中的**便會執行,並且時鐘計數計時器以其自己的速率執行。這兩個區域是同步的。

硬體設計的基本規則是,每當非同步事件突然同步時,就會驚慌失措。例如,當兩個不同的處理器共享乙個記憶體陣列時,需要大量的卷積邏輯,以確保任何時候都只能訪問乙個。如果cpu使用不同的時鐘,則問題將更加棘手,因為設計人員可能會發現這兩個請求獨佔記憶體訪問的時間彼此之間相差不超過十億分之一秒。

這就是所謂的「種族」的條件,是許多grayhairs和戲劇性failures.one源read_time r』srace條件可能包括:

它讀取硬體,並獲得0xffff的值。

在有機會從變數timer_hi檢索大部分時間之前,硬體再次遞增至0x0000。

溢位觸發中斷。isr執行timer_hi現在為0x0001,而不是0,因為它之前只有10 ns。

isr返回了我們無所畏懼的read_timer例程,沒有中斷發生的想法,將新的0x0001與先前讀取的計時器值0xffff巧妙地連線起來,並返回了0x1ffff(乙個非常不正確的值)。

或者,假設在禁用中斷的時間(例如,是否有其他isr需要時間)期間呼叫read_timer。編寫封裝的**和驅動程式的少數危險之一是,您完全確定例程被呼叫時系統所處的狀態。在這種情況下:

read_timer啟動。計時器為0xffff,無溢位。

在發生其他事情之前,它計數為0x0000。中斷關閉時,掛起的中斷會延遲。

read_timer 返回值0x0000,而不是正確的0x10000或合理的0xffff。

因此,看起來如此簡單的演算法存在相當細微的問題,因此需要一種更複雜的方法。rtems rtos,至少在其68 k分布中,可能會產生偶發但嚴重的錯誤。

當然,被誤讀的可能性很小。實際上,隨著我們稱為read_timer的頻率降低,出現錯誤的機率直線下降。種族狀況多久出現一次?每週一次?

許多嵌入式系統執行了好幾年卻沒有重新啟動。可靠的產品不得包含易碎的**。作為健壯的系統設計者,我們面臨的挑戰是識別此類問題並建立每次都能正常工作的替代解決方案。

有哪些選項可用?

幸運的是,確實存在許多解決方案。最簡單的方法是在嘗試讀取計時器之前先停止它。這是乙個簡單且***的解決方案,不會有溢位的可能使上半部分和下半部分資料不同步。我們會浪費時間。由於硬體通常會對處理器的時鐘進行計數,或者將時鐘除以乙個小數,因此在執行讀取操作的少數指令期間,它可能會丟失很多滴答聲。

如果中斷在禁用計數後導致上下文切換,則問題將更加嚴重。在此期間關閉中斷將消除不必要的任務,但會增加系統延遲和複雜性。

我只是討厭禁用中斷,系統延遲增加,有時除錯工具有點時髦。如果我看到很多禁用中斷指令,那麼在閱讀**時會出現乙個紅旗。儘管不一定很糟糕,但這通常表明該**被強行提交(通過英勇的除錯工作,而不是精心設計),或者環境有些困難和奇怪。

另一種解決方案是先讀取timer_hi變數,然後讀取hardwaretimer,然後重新讀取timer_hi。如果兩個變數值都不相同,則會發生中斷。迭代直到兩個變數讀取相等。好處是:正確的資料,中斷持續存在,並且系統不會丟失計數。

缺點:在負載重的多工環境中,例程可能在獲取兩次相同的讀取之前可能迴圈很長時間。函式的執行時間是不確定的。我們已經從乙個非常簡單的計時器讀取器變成了可以執行幾毫秒而不是幾微秒的更複雜的**。

另乙個選擇可能是簡單地禁用讀取周圍的中斷。在我們已經閱讀完isr之後,它會阻止isr獲得控制權和changetimer_hi,但會帶來另乙個問題。

我們輸入read_timer並立即關閉中斷。假設硬體計時器在我們臭名昭著的0xffff處,並且timer_hi為零。現在,在**有機會做其他事情之前,就會發生溢位。隨著上下文切換的關閉,我們錯過了過渡。

**從定時器暫存器和fromtimer_hi讀取零,返回零,而不是正確的0x10000,甚至返回0x0ffff區域。儘管我反對這種做法,但是禁用中斷可能確實是一件好事。

有了它們,我們的閱讀程式總是有可能在很長一段時間內被較高優先順序的任務和其他isr暫停。也許足夠長的時間讓計時器輪流滾動幾次,所以讓我們嘗試修復**。考慮以下:

longread_timer(void)

pop_interrupt_state;

return(((ulong)high)<< 16 +(ulong)low);

}我們對rtems**進行了三處更改。首先,中斷已關閉,如所述。其次,您會注意到沒有顯式的中斷重新啟用。出現了兩個新的偽c語句,它們推動並彈出中斷狀態。請片刻,這是管理系統中斷狀態的一種更複雜的方法。

Web API 第二部分

web api 第二部分 元素偏移量 offset element.offsettop element.offsetleft element.offsetwidth 可以得到元素的大小 寬度和高度 是包含padding border width element.offsetheight elemen...

redux 第二部分

redux 的使用方法,為什麼使用 action.js 檔案,進行優化 將其分開,然後我們通過工廠函式的每次返回不同的物件,由於引數是固定的,每次返回的都是事件型別和事件資料,所以我們可以使用乙個函式,通過其返回值來返回乙個物件,讓後傳遞給 action 我們的 reducer 函式有兩個引數,引數...

實驗二 第二部分

第二部分 ftp協議分析 1.兩個同學一組,a和b。2.在a主機上架設ftp伺服器 3.在b主機上執行wireshark,並登陸a主機的ftp伺服器,上傳一張,得到抓取的資料報,4.進行追蹤tcp流,顯示為原始資料,分析得到a主機登陸ftp的過程及使用者名稱密碼,還原上傳的,第二部分 ftp協議分析...