嵌入式系統學習筆記

上回聽線上 OS 課時沒一邊聽一邊寫筆記,前兩天想動筆寫一下時,忽然不知該從哪裡開始。

故這次決定每上完一個章節就內化一下寫成筆記。

 

什麼是嵌入式系統? 

被內建在特定裝置中、負責控制或處理專門功能的電腦系統。通常不是用來跑通用應用程式,而是針對單一或少數功能設計。

把計算、軟體能力放到一個產品上,會購買這個產品的原因通常是因為產品的功能,而非系統。 

整個系統著重於兩件事:讓我知道世界 & 讓世界知道我。

 

嵌入式系統的開發

和純軟體開發流程不太一樣的部分:

  1. 要閱讀說明手冊,了解板子上針腳的功能

  2. 用模擬器模擬軟體運行(寫程式用的電腦與最後運行程式的裝置可能使用不同的指令集)

  3. 將程式燒進開發版裡驗證與調整

  4. 軟體開發完成後請硬體參考電路圖做精簡與修改

linker & locator

linker:把多個程式與 library 合併成一個目標檔,在嵌入式系統上可能會因為 MCU 沒有虛擬記憶體,需要配合規劃。

locator:把執行檔寫到目標機器(開發板)上。

 

程式撰寫層面的不同:

  1. 不會有 GUI 介面

  2. 通常是在跑一個無窮迴圈

  3. 完全吃掉 CPU 的運算效能

  4. IO 通常會透過周邊裝置(peripherals)專用的特殊 register ,並以 bits 或 bytes 控制(需要使用 bitwise 的方法控制)

  5. 使用較多的 bit 控制 (請參見  C 語言筆記:Bit 操作C 語言筆記:保留關鍵字 中的 union) 



Timer and Clock

在嵌入式系統設計中,時間控制是關鍵的一環,許多應用上都需要精準的時脈 (clock) 與定時器 (timer)。

Clock

時脈,一個週期性震盪的訊號,通常是方波。它的作用就是像節拍器,決定了系統中所有電路運作的節奏。

clock 來源: 

石英震盪器
Crystal Oscillator, XTAL

RC震盪器
Resistor-Capacitor Oscillator

外部時鐘

外掛在晶片腳位

內建在晶片上

在另一個晶片上/
由上位電路提供

啟動需要幾 ms 才能穩定

啟動快

 

穩定、誤差小

誤差大,受溫度電壓影響

 

成本高

成本低

 

 

不適合高精度通訊/長時間計時
快速啟動或低功耗模式

需要準確實時(RTC/
網路同步的應用

在系統設計中,往往會同時存在多種 clock,並依需求切換。

Clock 與現實時間的關係

多少 clock 會對應一秒完全是由 CPU 的頻率來計算,算式如下:

例如:f = 16 MHz → T = 1 / 16,000,000 ≈ 62.5 ns。

Timer

Timer 是一個依據輸入時脈 (clock) 計數的硬體計數器,每個 clock cycle 會使計數器 +1。

當計數器達到設定值 (Compare Value) 或 Overflow 時,就會觸發中斷。在一些程式運作中會經常去歸零 Timer,如果達到會產生 Overflow,通常也表示該程式死掉了。

使用 Timer 可以固定時間間隔觸發 ISR (Interrupt Service Routine)、 準確實現 delay 或週期性任務、系統監控、捕捉 (Input Capture) 測量訊號週期與頻率。 

Timer 的工作模式:

  • Stop mode:Timer 停止,不進行計數。用於不需要計時、要省電時。

  • Up mode:從 0 往上計數到設定值,然後清零再重來。 常用於週期性事件,例如 PWM 週期控制 或 固定時間中斷。

  • Continuous mode:從 0 計數到最大值,然後溢位回到 0,繼續計數。適合用來做 自由運行計時器(free-running timer),可以測量事件的時間差。

  • Up/Down mode:Timer 從 0 計數到特定值,然後反向往下數回到 0,再往上數,形成「三角波」。常用於對稱 PWM 產生,例如馬達控制或需要降低諧波失真的應用。

Delay

以前寫程式會用 delay、sleep 或是空跑迴圈來做出延遲的效果,但這方法有明顯的缺點: 

  • CPU 無法做其他工作,浪費效能。
  • 時間不精準:不同編譯器最佳化、溫度、電壓都會影響實際 delay 長度。

因此,在嵌入式系統中,我們需要獨立於 CPU 的 Timer 來處理精準時間。


Interrupt

直譯為「中斷」,在嵌入式系統中經常需要處理按鍵輸入、timer overflow 等事件,如果只是依賴 CPU 主動、週期性地去檢查裝置或暫存器,判斷是否有事件發生 (此行為又稱 polling),會很浪費 CPU 效能。

interrupt 有以下特性:

  • 會隨時發生

  • 不依照主程式控制流程

  • 不同 interrupt 來源可設定優先級,確保重要事件不會延遲太久

  • Maskable:可以透過軟體或硬體設定來忽略 interrupt;當然也有不可被忽略的 interrupt (Non-Maskable Interrupt 簡稱 NMI),像是硬體故障

Interrupt 運作流程

  1. 事件觸發 interrupt (ex: 按鍵輸入、timer overflow)

  2. 控制器判斷 interrupt 是否有效,並發送信號給 CPU

  3. CPU 暫停目前程式,保存必要 register

  4. CPU 根據 Interrupt Vector 跳轉至對應的 ISR
    (ISR 細節請參見:嵌入式系統筆記:ISR)

  5. ISR 執行完畢後,CPU 透過 RETI (Return from Interrupt)等指令恢復原本程式流程

Interrupt VS Polling

 

Interrupt

Polling

CPU 效率

高,只在事件發生時執行

低,需要持續巡迴檢查

即時性

佳,可立即反應

較差,可能延遲

程式結構

較複雜,需要考慮同步

簡單,容易實作

適用場合

事件不固定、需即時響應

定時監控、事件固定週期


Analog-to-Digital Converter (ADC)

 (圖片 reference)

我覺得上面這張圖說明了一切。簡單來說,就是將類比訊號(Analog)轉換成數位訊號(Digital)。

題外話,DCA 指的是數位訊號轉為類比訊號。

雖然概念很簡單,開發時要確認一下板子是幾 bits 的 ADC、有幾個通道(類比訊號輸入來源)、輸入電壓的範圍。

為什麼需要 ADC? 

因為在電路中僅能處理「0」與「1」的訊號,但現實世界中的資訊(例如溫度、光線、聲音、電壓等)大多以類比訊號表現。 為了讓 CPU 可以處理這些資訊,所以需要透過 ADC 做轉換。

ADC 的基本原理

Sampling Rate (取樣率)

指每秒轉換類比訊號的次數。

例如 1 kSPS (kilo Samples Per Second) = 每秒 1000 筆資料。

如果取樣率過低(=兩次取樣的間隔時間太長),可能會導致我們忽略掉兩次取樣中間的變化(又稱混疊效應,Aliasing)。

Encoding bits 

每次取樣會用多少個 bit 紀錄。越多 bits 越不容易失真。

借圖說明不同數量的 encoding bits 記錄下的訊號差異:

高精度示波器揭密微小訊號ADC位元數/本底雜訊為關鍵| 新通訊 

 (圖片 reference

8-bit ADC 指的是可以記錄 2 的 8 次方 = 256 個數值 (0 ~ 255)。

舉個例子,若有個溫度計可測量 0~100 度,則 8-bit ADC 中每個 bit 能記錄的最小單位為:

(100-0) / 255  ≈ 0.392 度。

ADC 的工作流程

  1. 選擇要取樣的腳位

  2. 鎖定與取樣
    電路取樣時,不論要取的是溫度、音量還是其他,都是取樣電壓,所以在取樣當下要鎖定電壓

  3. 根據 encoding bits 轉換成 digital 的值

  4. 儲存結果 

ADC 運行模式

ADC 通常會搭配 Timer、Interrupt、DMA(直接把結果搬到記憶體) 使用。

  • 單次轉換 (Single Conversion):只取一次樣

  • 連續轉換 (Continuous Conversion): 一直取樣到程式終止中止

  • 掃描模式 (Scan Mode) :依序掃描多個 ADC 通道

  • 觸發轉換 (Triggered Conversion): 透過 Timer 或外部事件觸發 ADC


Serial Communication

中文直翻叫「序列通訊」 ,是用「一條資料線」一次只傳送一個 bit 的傳輸方式。相較於一次傳很多 bits 的平行傳輸,較省線、能減少硬體連線複雜度、長距離也較為穩定。

為了確保資料能被正確解析,和網路一樣有傳輸協定(Protocol)的存在。

題外話,常見的 USB 也是 Serial Communication。

Serial Communication 類型

Synchronous Communication (同步傳輸) 

除了傳 data 的線外,會有一根負責傳 clock 的線。

傳送 clock 的裝置通常被稱為 master,其他的稱為 slave,看到 clock 訊號時,表示有 data 要收送。

Asynchronous Communication (非同步傳輸) 

沒有 clock 線,兩方的 clock 可能會不同步,通常傳送方會在傳送資料前後自帶同步信息(含開始信號、停止信號)。

一般來說,每次傳遞的訊息不會太多,因為時間一長,兩邊的 clock 可能又會不同步(ex: 一個是石英震盪器,一個是 RC 振盪器,久了 RC 振盪器會因為電壓溫度等因素,頻率有所變動)。

除了同步信息外,也會透過設定相同的 Baud Rate 來同步( Baud Rate:通訊速率,表示一秒鐘能傳送/接收多少符號,若 Baud Rate = 4600,表示一秒能傳接 4600 個符號) 。

Serial Communication 架構

這邊沒辦法把所有 Interface 都拉出來說,只筆記部分。

RS-232

是很老的 Serial Communication 架構,在個人電腦上幾乎沒有這個硬體 (會用軟體模擬),只有某些領域有使用。

UART (Universal Asynchronous Receiver/Transmitter) 

  • 中文直翻叫「通用非同步收發傳輸器」,用於非同步傳輸 
  • 在嵌入式系統中很常用,會用一個 timer 結合軟體達成
  • 有三根線:receiver(接收) / transmitter(發送) / ground(接地),如果要同時收發,就會用到 ground,一般來說只會用前兩條傳輸資料
  • 傳輸結構
    •  Start Bit (ST)
    •  Stop Bit (ST)
  • 會使用 Start Bit (ST) 和 Stop Bit (ST) 來界定封包
  • 接收端也會根據 ST 來對齊取樣時間 
  • 容易受雜訊影響(=不能傳太遠),速度上比 SPI、I²C 慢 

SPI (Serial Peripheral Interface)

  • 中文直翻叫「串行外設介面」
  • 屬於 master-slave 架構,用於同步傳輸
  • 使用四條訊號線
    • SCLK (Serial Clock):clock 訊號
    • MISO (Master In Slave Out):slave 傳給 master 的傳輸線
    • MOSI (Master Out Slave In):master 傳給 slave 的傳輸線
    • CS (Chip Select) :讓 master 知道要和哪個 slave 通訊的信號線,低電位時表示該 slave 與 master 通訊
  • 速度較快,常用於陀螺儀、通訊模組、LCD 控制等 

I²C(Inter-Integrated Circuit)

  • 是I²C Bus簡稱,中文直翻叫「積體匯流排電路」,正確讀法是「I-squared-C」
  • 屬於 master-slave 架構,用於同步傳輸
  • 使用兩條線
    • SDA (Serial Data):傳 data
    • SCL (Serial Clock):clock 訊號
  • 應用在「構造簡單、可犧牲速度來減少成本」的項目,如: 低速的 ADC / DAC、硬體監視


Low-Power Optimization

在嵌入式系統上,電池的壽命、單次充飽可以用多久,對於產品來說往往有決定性的競爭力。

低功耗的優點包含:延長電池壽命、降低散熱需求、提升系統穩定性 (較不易過熱)。

所以,低消耗優化是一件很重要的事。

下面說明不同層級來進行低功耗優化: 

Device / Transistor level (裝置與電晶體)

這一層屬於硬體製程與半導體技術的優化。

  • 開發低功耗的元件

  • 降低供應電壓

  • 調整電晶體的閾值電壓,減少切換時的消耗

Circuit level (電路)

這邊是屬於晶片設計與硬體架構的優化。

  • Clock gating:關閉不需要的時鐘訊號

  • Frequency reduction:降低工作頻率

  • Circuit turned off:裝置閒置時,關閉電路模組

  • Asynchronous circuits:在需要時才驅動 

  • Multi-VDD Domains:不同功能模式用不同的供應電壓

System level

  • Compiler optimization for energy:編譯器優化,使用低功耗的指令、產生更省電的程式碼,例如把乘法改成加法+位元移動 (請參見  C 語言筆記:Bit 操作)

  • Idle base:系統閒置一段時間後進入睡眠模式

  • Power-aware memory management:盡量 local 存取,避免一直去記憶體中拉資料(記憶體可先休眠) 

  • Power-aware buffer cache:將讀寫的資料集中到快取中,累積滿了再一次讀寫 

  • Cooperative I/O:讓 I/O 批次運行 -> 閒置時間連續 -> 閒置時跳低功耗模式

  • Dynamic Power Management (DPM):系統依負載動態調整功率,例如 linux 中的 cpuidle、cpufreq framework 

  • Dynamic Voltage and Frequency Scaling (DVFS):動態調整電壓與頻率 

Application level

  • 將資料批次處理

  • 演算法優化

  • 避免不必要的 polling 改用 interrupt  

  • 避免頻繁傳輸,使用壓縮、封包合併 

 

Program Organization

Design Pattern

中文直翻大概是「設計模式」,是對軟體中反覆出現的問題給的解決方案,有點像框架或樣板,只需要填入一些內容即可。

  • Data stream pattern
    在信號處理時,經常會需要用過去的資料一起計算,這時會使用 circular buffer

  • State machine pattern
    以有限狀態機 (Finite State Machine,FSM) 方式組織程式,描述事件或輸入在不同狀態下的切換。因為以前學過 FSM,所以這邊不詳細解釋,下面放個維基百科的範例圖

 

Software Architecure

在嵌入式系統中,需要有一個基本架構來跑程式 (雖然很像 OS,但用「如何設計整個系統的程式組織方式」來描述比較貼切)。

架構的選擇會根據需求 (ex:即時性、功耗、效能) 和成本來決定。 

  • round-robin
    有點類似上面提過的 polling,由 switch + 無限迴圈組成,系統會依序去執行任務,然後不斷重複這個過程(迴圈)
    實作起來簡單,但無法應對即時性需求,也容易因為前面的任務延遲,影響到後面的任務
  • interrupt-driven
    以 interrupt 的方式來處理事件,CPU 大部分時間閒置
    可以大幅降低功耗,但 interrupt 過多會讓系統很複雜,一直被 interrupt 可能無法執行完一個任務,或是發生優先權倒置
  • task queue
    這個理論上後面會有更詳細的筆記,所以這邊只做簡略的介紹
    會把各種事件 (包含 interrupt) 以 task 的形式,放入 queue 中,再由簡單的迴圈/排程依序處理,可以想成作業系統克成中簡單的「Ready Queue + Event Queue」
    這個方式能處理不同事件的非同步性,但需要額外的排程邏輯

  • RTOS (Real-Time Operating System) 
    這個理論上後面會有更詳細的筆記,這邊一樣只做簡略的介紹
    中文直翻叫「即時作業系統」,和電腦使用的 OS 相比,著重於即時性,確保任務在時間內完成有完整的多工支援、即時性與模組化程式設計,但也比較複雜


RTOS and MQX

先說 RTOS。

如上面所說,RTOS 全稱是 Real-Time Operating System,中文譯為「即時作業系統」,能保證在限定的時間內做出反應,當單純的 round-robin 方式無法滿足需要的任務管理需求 (例如需要同時處理多項任務) 時,我們就需要導入 RTOS 來協助我們。

基本上,RTOS 就是為嵌入式系統 (嵌入式系統必須在時間限制內完成工作) 而生的 OS。

RTOS 的核心特性

  • Allows multi-tasking
    能同時管理多個任務

  • Scheduling of the tasks with priorities
    提供排程演算法,根據優先權決定任務的執行順序,例如 Preemptive Scheduling 

  • Synchronization of the resource access
    有鎖 (Mutex)、信號量 (Semaphore) 等機制,避免多任務同時存取共享資源導致錯誤

  • Inter-task communication
    有訊息佇列、事件旗標 (Event Flags)、管道 (Pipes) 等方式,讓不同任務安全地交換資料

  • Time predictable
    時間可預測,保證系統在可控的時間範圍內完成工作

  • Interrupt handling
    中斷處理 

RTOS 和一般的 OS 有什麼不同?

 

一般電腦上的 OS

RTOS

設計目標

追求使用者體驗、多工效率、資源共享

保證任務在嚴格時間限制內完成(確定性)

任務排程

強調公平(time-sharing

優先權排程 (Priority-based Scheduling)

即時性

無法保證,僅「盡量快」

可保證任務在截止時間(deadline)內完成

資源

強調大量資源共享(虛擬記憶體、檔案系統、多使用者)

強調高效率與低延遲,資源管理精簡

複雜度

系統龐大,功能齊全,支援 GUI、網路、多媒體

系統精簡,僅包含即時調度、驅動程式、同步機制

可靠性

通常需依靠外部機制確保可靠性(如 watchdog、容錯系統)

高可靠性設計,常內建錯誤偵測與快速回復機制

延遲(Latency

不可預測,可能受背景程序影響

可預測,通常為微秒級或毫秒級

硬體保護

(system call)

不一定

另外,因為資源較為有限,RTOS 在初始化上會比一般 OS 多很多。 

那什麼是 MQX?

MQX 是一個由 Freescale(後來被 NXP 收購)所開發的商用 RTOS,特點是 multi-thread 與優先權設計,同時也提供:

  • Task scheduling
  • Task management
  • Interrupt handing
  • Task synchronization:鎖 (Mutex)、信號量 (Semaphore)、events、messages
  • memory management
  • IO subsystems
  • Kernel logging  

此外,MQX 分為核心功能與擴充 library,可以根據產品需求靈活搭配。

MQX RTOS - Embedded Access 

除此之外,MQX 有完善的文件、範例程式,以及 IDE / 開發環境支援,較容易上手。 

如果想要深入了解的話,可以看看 MQX 的 User Guide。 

Task Synchronization

中文直接翻譯叫「任務同步」,但我覺得這樣翻譯並不精確。Task Synchronization 是指我們有很多 task 同時進行,這些 task 可能共用了一些 resource,我們不能讓不同的 task 同時使用同一個 resource。(在作業系統會對應到的章節大概是:Process Synchronization、Deadlock、Processes & Threads 等)

在 RTOS 中,會用下面三種作法來達成:

  • Mutual Exclusion
    中文翻起來叫「互斥執行」,簡單來說就是要確保同一時間只有一個任務在執行,避免同時存取共享資源。 通常會使用 Mutex、Semaphore 等機制來達成。 

  • Control Flow
    控制「誰先跑、誰後跑」,確保任務之間的執行順序。通常會使用 Events、Semaphore 等機制來達成。

  • Data Flow 
    任務之間透過「資料傳遞」來達成同步關係,消除資源的爭用問題。例如:Task B 需要使用 Task A 產生的資料。通常會使用 Message Queue 等機制來達成。

同步機制如下:

Mutex

針對可共用的 resource 加互斥鎖 (在程式中會用 lock、unclock 實踐)。

  • Waiting Protocols

    • Queuing
      當 task 想要取得資源,但資源已經被其他 task 持有時,它會進入等待狀態,FIFO

    • Priority queuing
      和 queuing 類似,但當資源釋放時,最高優先權的 task 會先得到資源
      此方式是為了防止優先權倒置

    • Spin only
      當資源被其他 task 持有時,會持續 spinning(忙等) 到資源被釋放
      適合短暫鎖定,避免 context switch overhead

    • Limited spin
      忙等的次數或時間有限制
      常見的方式會是每忙等一段時間,提升一些優先權,這樣資源釋放時更可能優先執行;或是忙等到上限後先將 task 進入 sleep/block

  • Scheduling protocol
    為了避免出現優先權倒置,RTOS 提供了兩種處理方式

    • Priority Inheritance
      中文直翻叫「優先權繼承」
      狀況是這樣:低優先權任務 task A 先拿到資源,高優先權任務 task B 想要拿同樣的資源,但被 task A 擋住,而 task A 還需要其他資源才可執行卻因為優先權低搶不到,這樣就發生了優先權倒置
      解法就是暫時把 task A 的優先權提升到和 task B 相同,讓 task A 盡早完成(完成時優先權會降回去),把資源釋放給 task B

    • Priority Protection
      中文直翻叫「優先權保護」
      這個方式是讓每個 mutex 都有優先權,如果一個 task 想要鎖定某個優先權較高的 mutex,會先將該 task 的優先權提升至與該 mutex 同級
      除了優先權倒置外,也可避免 deadlock

Event

我上的線上課程提到是 MQX 的機制,但查了一下也不只有 MQX 才有。

結構上就是一排陣列,當 task 完成後,就在指定位置插上 flag,部分 task 需要等 flag 出現才能夠執行。舉例:task B 需要 flag 30 設為 true 時才會執行,而 flag 30 會在 task A 執行完時設為 true,這樣 task B 就必須等待 task A 執行完才能執行。

所需 flag 之間的關係可以是 AND (ex: 需要等 flag 1、flag 3 同時為 true 才執行) 或 OR (ex: 等 flag 1、flag 3 其中一個為 true 即可執行)。 

Semaphore

直翻叫「信號量」(維基百科上又叫號誌或旗號),它的用法廣泛但速度比較慢,可以拿來取代 Mutex 和 Event。

Semaphore 包含:

  • counter:Mutual Exclusion 一次只能允許一個 task 進入一段 code,Semaphore 則同時可以有數個(看 counter 上限是多少)

  • queue:等待中 task 的 queue 

常見種類有二:

  • Binary Semaphore:避免同一時間多個任務同時存取共享資源

    • 和 mutex 類似,但缺乏優先權機制

    • counter 只會是 0 或 1

  • Counting Semaphore:多個 task 共享有限數量的資源

    •  每個任務想用資源時必須「P 操作」(等待,ex: xSemaphoreTake),用完後「V 操作」(釋放,ex: xSemaphoreGive),counter 增加

    • counter 可以大於 1 

Message Queue 

可以想像成 task 之間有一個「郵箱」或「排隊櫃台」,task 可以往裡面放資料或取資料。 

運作方式: 

  • Producer 將訊息寫入 Queue

    • 如果 Queue 滿了, Producer 會被 block,或返回

  • Consumer 從 Queue 取出訊息

    •  如果 Queue 是空的,會等到有新訊息進來

  • Wakeup:如果 Queue 有訊息但沒有 Consumer(或 Consumer 被 block),會叫醒 Consumer

經常會用在 Sensor、ISR 與任務的橋樑等部分。

 

其他 MQX 的補充:

Timer 是一個 64 bits 的計數器,有兩個初始 Timer:從 1970.01.01 00:00:00 開始計數的 Timer,從系統開始時計數的 Timer。如果許久不開系統,該如何得到正確的 Timer 數值呢?通常是靠其他設備的時間,如 GPS、網路。 

ISR 不是 task,而是一個小而快的程式。每個 Interrupt 發生後,系統會先進入 kernel ISR,由它來處理保存/切換上下文,確保系統一致性,處理完成後恢復最高優先權任務。MQX 在開始執行時,會先初始化 ISR table。 


I/O Device Driver

應用程式無法直接操作硬體暫存器或控制訊號,而必須透過 Device Driver 提供的介面,才能安全且一致地存取設備。

 

App → Driver → Hardware:應用程式發出指令,經由驅動程式轉換後操作硬體。

Hardware → Driver → App:硬體狀態或回應,再透過驅動程式回傳給應用程式。 

開發 device driver 時,通常會有下列功能:

  • io_device_open (必要)
  • io_device_close (必要)
  • io_device_read
  • io_device_write
  • io_device_ioctl:用於該裝置的其他控制  

 

Embedded C Programming

這章主要講述在嵌入式系統上的 programing 技巧。

會用到以前寫過的一些文章,我列在後面:C 語言筆記:Bit 操作C 語言筆記:保留關鍵字

Embedded VS Desktop

  • 成本:這些成本會轉換為資源限制,例如:小小的記憶體、只會算整數不會算浮點數的 CPU (比較便宜)、省電 (盡量不跑空迴圈) 等
  • 在限定時間內回應
  • 和硬體有極高的關聯性 

因為較 low level 所以通常會用 C 來開發,有時候甚至會用組合語言控制 (當然也可以先寫 C 再轉換成組合語言,不過現行的 compiler 已經很成熟了,跟手寫的組合語言不會差到很多)。如果使用高階語言的話,開發速度會比較快(執行速度不一定),也會強迫宣告變數型態,避免變數誤用。 

 為了寫出很有效率的 code,我們需要知道下面的知識:

  • architecture characteristics 
  • debugging tool
  • 硬體支援什麼樣的資料型態 (包含幾個 bits)
  • 有什麼 library 

計算速度

以下都是越上面越快。我們要盡量避免使用很慢的 type 或指令,像盡量用 int、少用數學函數。

  • 變數型態
    補充一點,用硬體來算 float 雖然快,但那顆 IC 貴
    • int
    • 用硬體算 float
    • 用軟體算 float
  • 計算方式
    • + / -
    • x (可以用很多加法來取代)
    • ÷ (有些可以用很多減法來取代)
    • sqrt、sin、log …… 
  • 判斷式
    轉為組合語言時,很多的 if else 會需要不斷比較
    • switch
    • 很多的 if else 

為了節省時間,我們會用一些 Bit 操作 (像是 X2 或 ÷2),也會將乘除用加減來加快速度。而數學式的部分可以預先寫好 table 做 mapping。

其他方法 

  • lazy logical operator:在判斷運算式時,程式只會計算必須要算的部分,一旦結果已經確定,就不會繼續執行剩下的運算。如 if (a != 0 && (10 / a) > 2) { ... },若 a 為 1,則在 a != 0 時就會做出判斷

  • 有迴圈+陣列時,可使用指標取代如 for (int i=0; i<10; i++) arr[i]=1;  這種 code,好處是不需要多使用變數空間、轉成組合語言也比較快,但缺點是程式碼比較難看懂

  • Macro:比起 function 不用跳來跳去,可以直接展開,但用太多檔案會變很大,也不容易看懂 

  • Static 宣告變數:stack 空間通常有限,若把 task 內需要長期存在的 buffer 宣告成 static,將變數放在靜態區域,可以減少 stack 的使用、甚至避免 stack overflow

  • golbal:通常用於 ISR、硬體暫存器中,雖然可以讓共享變數變容易,但同時也會一直占用記憶體空間

  • Layout of Storage:記憶體會以 32、64、128 bits 為單位讀取資料,若一個 int (32 bit) 存在記憶體第一排的尾與記憶體第二排的頭,那在讀取時會需要讀取兩次

  • malloc & free:小的嵌入式系統幾乎不會使用,因為每個空間都已經定義好要做什麼了

  • Memory-Mapped I/O
    先解釋一下這是什麼。簡單來說就是,把 I/O 裝置的暫存器地址映射到 CPU 的記憶體位址空間,程式不需要額外的特殊指令來存取 I/O,只要像存取記憶體一樣用指標讀寫,就能直接控制硬體
    優點:存取統一、快速 (存取 I/O 就像操作 RAM 一樣快,不需要額外的指令)
    缺點:占用記憶體空間、需要 volatile、存取速度受限於匯流排


小結:優化很多時候是在「取捨」上,空間、時間、好維護、開發快,不太可能同時擁有。除了上面提到的方法外,想讓優化程式可以從編譯完的組合語言下手 (但也很容易改爆)。






留言