裸機系統上的類比除錯

作者 : Damian Bonicatto和Phoenix Bonicatto

從韌體串流資料並將其顯示在示波器上,有助於提供一種加快訊號處理韌體除錯的強大工具;選擇帶有DAC週邊裝置的MCU可能會很有幫助…

我承認「類比除錯」(analog debugging)這個標題有點神秘。閱讀本文後,嵌入式韌體開發人員可能會遭受到認知失調的困擾,但相信我,這以後會說得通的。本標題的弦外之音在於處理微控制器(MCU)中進行處理訊號的任務。

許多涉及較小MCU的任務都與處理來自感測器(如麥克風、水中收音器、壓力感測器等)的原始訊號有關。其中一些訊號需要清理或以其他方式處理。該處理可能會使用多種數位訊號處理(DSP)韌體技術,例如FIR和IIR濾波器、混頻器、FFT等。

隨著訊號串流傳輸通過MCU,我們希望透過除錯驗證的資料可能會很廣泛。例如,訊號通過濾波器後會看到什麼,或者當訊號通過相關函數器時,其輸出是什麼。這就是類比除錯的用武之地,我們可以用它來即時觀察訊號。較小的MCU可能缺少較大處理器所具有的一些強大除錯工具,例如BDM、JTAG和SWD。

較小的MCU也可以不使用作業系統而作為裸機運行,但這樣的話,作業系統中任何可用的除錯工具都無法使用。這種工具的缺乏和即時訊號處理的複雜性,會使除錯程式碼出現問題。但是,除錯需要深入瞭解MCU內部的資料在發生什麼,並且在處理串流類比訊號時,我們可能希望查看其於類比域中的實際情況。

通常,在除錯韌體時,工程師會使用MCU上的序列埠(如果存在)列印出正在執行的程式碼的變數值或指示符。這裡有很多問題:

  • 首先,在小型MCU中,可能沒有足夠的空間用於列印常式,因為記憶體可能稀缺。
  • 其次,速度可能是個問題。在DSP類型的處理中,通常是一個接一個地對輸入訊號進行即時處理,無法暫停下來處理相當長的列印調用。
  • 第三,列印常式通常會使用中斷,這可能會導致即時系統出現問題。
  • 最後,以序列埠列印輸出資料時,無法為我們正處理的資料提供直接類比視圖。

例如,假設使用類比數位轉換器(ADC)從感測器接收訊號,可以在感測器的輸出上掛一個示波器而在類比視圖中查看訊號和雜訊,但是,如果透過序列埠查看相同的訊號,則在MCU讀取該ADC並發送出此序列埠後,所看到的就是一堆數字。現在可以將這些數字放入試算表、繪製圖表,或者設置另一台帶有ADC和顯示器的裝置再次查看該資料,但這似乎會有點慢和費事,而且肯定不是即時的。

現在,如果沒有可用的序列埠或者其不適合除錯,工程師可以用一個LED連接到MCU,然後根據被偵錯工具中的各種條件控制其亮滅。將示波器連接到該LED或可用的I/O線,從而能夠查看其狀態,或透過翻轉韌體中的LED或I/O線來測量狀態變化之間的時序。這非常有效,但不符合我們想要獲得訊號的類比視圖想法,因為它會受到濾波器、相關器、切片器和混頻器等各級處理。

如果有某個地方能夠連接示波器探棒,並且可以在此韌體中快速列印輸出已處理樣本,那就會很好。那麼,我們可以使用什麼呢?第一個想法是將DAC連接到MCU,或者更好的是,如果MCU本身帶有DAC周邊裝置,那就直接使用它。為了嘗試這種技術,採用ADI的8位元DAC AD7801連接到我正制作中的Arduino Nano設計中。Nano的核心是Microchip的ATmega328,其上並不帶有DAC。AD7801使用8根資料線的平行輸入,並透過另一根線同步;其寫入速度非常快而且非常簡單。(請注意,可以使用此設置查看8位元資料,但對於其他DAC也可以使用10位元、12位元或其他大小,或者可以對其進行縮放來適應8位元DAC。)將這8根資料線連接到Arduino上的埠D並將WR線連接到Arduino的D13,如1所示。

圖1:將ADI的8位元DAC AD7801連接到Arduino Nano設計。

現在,為了將資料發送到DAC,只需要3行Arduino IDE C程式碼即可:

PORTD = data; // 將資料位元組放到D0D7
PORTB=PORTB & B11011111; // D13拉低而使數據鎖存到AD7801
PORTB = PORTB | B00100000; // 拉高D13

在16MHz Arduino上,此程式碼需要大約5個週期或大約312ns,DAC的建立時間為1.2μs。所以,我們可以看到這種資料顯示的方法比較快地完成,不需要中斷,也不需要太多的程式碼。將此程式碼插入韌體的適當位置,就能查看重要資料。將3行程式碼放入到巨集或函數中,可能會更簡潔。如果為此創建函數,則應使用“always_inline”編譯指示對其進行編譯,以便確保其快速運行。

現在連接了DAC,下面來看一些除錯示例,如2

圖2:感測器輸入訊號的示波器快照。

這是感測器輸入訊號的示波器快照(為了清楚起見,此處刪除了格線)。底部跡線(粉紅色/紫色)是原始訊號,因為它正在進入ATmega328上的ADC接腳。可以在這條線上看到明顯的雜訊。上面的跡線(黃色)是經過MCU韌體中的一些濾波和其他處理後的相同訊號。我們已將DAC寫入除錯程式碼插入到此流程中,因此DAC中的採樣時序與ADC相同。如果需要,還可以對MCU中的訊號進行抽取。暫時忽略訊號中的「突波」(spike),可以看到處理過程已經消除了大部份雜訊。我們現在有了一個可以評估的乾淨訊號。應該注意的是,DAC輸出是一個連續的訊號串流,而不僅僅是擷取一些短暫的記憶體緩衝。

但這些「突波」是什麼?它們是我有意放入程式碼中的一些除錯功能,以便查看處理過程如何進行。我們所看到的訊號實際上是被訊號介質破壞了的專有數位訊號。程式碼任務透過以下方式讀取數位資料封包:

  • 發現前導「封包開始」符號序列
  • 追蹤採樣時間,以便可在適當的時間對樣本進行切片
  • 繼續收集樣本,直到資料封包結束

圖3:添加注釋的已處理訊號視圖。

3顯示添加了注釋的已處理訊號視圖。在程式碼中所做的是將訊號從最小值50放大到最大值200。這樣就可以在256個可用值中留出一些空間,從而在訊號的上方和下方添加「突波」。我們首先看到的是標有「檢測到前導碼」的「突波」。這是在程式碼驗證已找到前導碼(B00000011)時創建的,它可以使用以下Arduino IDE程式碼輕鬆產生:

PORTD = 255; // 255放到D0D7
PORTB = PORTB & B11011111; // D13拉低以將數據鎖存到AD7801
PORTB = PORTB | B00100000; //拉高D13

這會在示波器跡線上創建一個312ns寬的標記,其振幅等於DAC的最大電壓。訊號跡線內往上和往下的「突波」,是指示程式碼確定符號邊界位置的標記。這對於在正確的時間對符號進行切片非常重要,並且在出現長時間運行的0或1時變得至關重要。這是因為沒有發現從0到1或從1到0的轉換。

在示波器上查看這些「突波」非常有用,因為它可以讓我們驗證實際時序並確認沒有遺漏。這些符號邊界「突波」是使用以下Arduino IDE程式碼(插入到符號時序程式碼的適當位置)向DAC發送127而創建的:

PORTD = 127; // 127放到D0D7
PORTB = PORTB & B11011111; // D13拉低以將數據鎖存到AD7801
PORTB = PORTB | B00100000; // 拉高D13

透過使用以下程式碼(插入到監視從0到1或從1到0的符號轉換的程式碼)向DAC發送0,將符號轉換標記為「突波」:

PORTD = 0; // 0放到D0D7
PORTB = PORTB & B11011111; // D13拉低以將數據鎖存到AD7801
PORTB = PORTB | B00100000; // 拉高D13

因此,使用DAC可以查看覆蓋到實際已處理跡線上的除錯資訊,清楚顯示除錯程式碼的各個部份。這比使用LED、I/O線和示波器強大許多倍。由於包含時序資訊,這也可能比序列埠發送資料更有用。

眼尖的人可能已經注意到,在3的右側邊緣,探棒衰減不是1或10倍,而是53.5倍。這是可以在許多較新示波器上完成的技巧,有時稱為自訂衰減設置。將其設置為53.5的原因是如此可以使用示波器游標直接讀取DAC的8位元輸入值。也就是說,如果將游標向上滑動到前導檢測「突波」的頂部,則示波器游標讀數為255,或者,如果將游標移動到符號邊界「突波」的末尾,則其讀數為127。使用8位元DAC時,此設置的公式為255/MaxVolts。MaxVolts是輸入最大二進位輸入時DAC的輸出電壓,本例中為255。因此,對於5V導軌,自訂設置為51.0(我的導軌只有4.77V,所以我的數字是53.5)。使用10:1探頭時,可能需要將此數位乘以10,然後再將其輸入示波器。

這非常方便,因為可以直接讀取DAC所設置的數字,或者換句話說,內部變數在調用DAC時所具有的值。我們來考慮一下這點吧!本質上,以這種方式能「即時」讀取變數……這幾乎與列印語句一樣好,但速度更快且不會產生打擾。請注意,示波器垂直刻度的雜訊和解析度會使精度降低,因此可能只能得到實際值的±1或±2個計數,但仍然相當不錯。

除了資料串流訊號外,使用這種技術,8位元DAC還可以同時表示8個二進位標誌的狀態,或程式中8位元變數的當前值。換句話說,使用8位元DAC所能提供的資訊,是監控單個I/O線能提供資訊的8倍。

那麼如果沒有DAC可供使用怎麼辦?此時可以採用MCU的脈衝寬度調變(PWM)周邊裝置執行類似操作。許多小型MCU都有PWM,而當它們有PWM時,通常都有多個,一般是六個。PWM和DAC之間的區別之一是,PWM輸出需要使用低通濾波器進行濾波,以便將輸出轉換為一水平電壓。因此,當將訊號樣本發送到PWM時,這一水平電壓會重新創建可在示波器上顯示的訊號,就像使用DAC所做的那樣。這種濾波可以透過簡單的RC濾波器來完成。

不過這裡有一些警告;低通濾波器意味著只能顯示具有低頻成份的訊號,即反應較慢。因此,應該將PWM的頻率初始化為可用的最高頻率。在16MHz ATmega328上,PWM可以設置為大約31kHz的最大頻率,因此低通訊號應設計為大約3~4kHz的頻率成份。

使用PWM的Arduino IDE程式碼,在初始化後甚至比DAC程式碼更加簡單。將8位元值寫入PWM的程式碼很簡單:

analogWrite(PinNumber, data)

其中,“data”是一個8位元採樣值,“PinNumber”是PWM輸出的接腳號碼。

儘管PWM可能不那麼準確或無法顯示更高頻率的訊號,但它有一個重要功能。由於一些MCU具有多達6個PWM,這意味著有多達6個輸出可用於即時傳輸資料。我們可能有一個四跡示波器,可同時顯示四個變數,這樣就留下了兩個備用PWM輸出。此外,經由PWM或DAC兩個輸出還可以提供I和Q兩項資料,從而滿足通常的DSP訊號處理所需(並可以讓我們探索負頻率)。請注意,就像DAC程式碼一樣,PWM程式碼也不需要中斷。

另一個可用於DAC或PWM所傳遞訊號的強大工具是頻譜。4中的示波器螢幕截圖顯示了一個示例。

圖4:對DAC或PWM所傳遞訊號使用頻譜的示例。

上面的跡線顯示在MCU中所產生的波形。該訊號實際上是對兩個頻率(f1=165Hz和f2=135Hz)逐一採樣混合或相乘,然後在產生時將其發送到DAC。在頻率混合中,所得到的頻率是頻率之和與頻率之差。原始產生的頻率受到混頻操作抑制,這在示波器跡線下半部份的FFT中可以清楚地看到。大多數示波器——甚至是業餘水準的示波器——都可提供FFT作為一種數學運算功能。

如果系統中沒有DAC或PWM,仍然可以使用一些東西來獲取運行中韌體訊號的資訊。例如,可以編寫程式碼來對PWM訊號進行bit-bang,儘管這可能僅對低頻訊號或緩慢變化的變數有用。

希望我將類比除錯的想法講清楚了。從韌體資料串流資料並將其顯示在示波器上這一主要概念,可以為我們提供一種強大的工具,進而加快訊號處理韌體除錯。如果可行,選擇帶有DAC周邊裝置的MCU或在我們的第一個原型PCB中加入DAC可能會很有用。我們總是可以等到以後再進行刪除或在BOM中將其製作為NO-POP。

(參考原文:Analog debugging on bare-metal systems,by Damian Bonicatto & Phoenix Bonicatto)

本文同步刊登於EDN Taiwan 202212月號雜誌

活動簡介

從無線連接、更快的處理和運算、網路安全機制、更複雜的虛擬實境(VR)到人工智慧(AI)等技術,都將在未來的每一個嵌入式系統中發揮更關鍵功能。「嵌入式系統設計研討會」將全面涵蓋在電子產業最受熱議的「智慧」、「互連」、「安全」與「運算」等系統之相關硬體和軟體設計。

會中將邀請來自嵌入式設計相關領域的研究人員、代表廠商以及專家,透過專題演講、產品展示與互動交流,從元件、模組到系統,從概念設計到開發工具,深入介紹嵌入式系統設計領域的最新趨勢、創新和關注重點,並深入分享關於嵌入式系統設計的經驗、成果以及遇到的實際挑戰及其解決方案。

贊助廠商

加入LINE@,最新消息一手掌握!

發表評論