嵌入式處理器(如 ARM7/9)採用流水線架構。流水線滿載時每週期完成一條指令。跳轉指令(分支、函數呼叫、循環)會清空流水線,需數週期重新填充。
對策:盡量使用條件執行指令(ARM 的 cond 後綴)替代分支跳轉,保持流水線流暢。
LPC2200 系列(約 60MHz)參考延遲 10ms 代碼:
for (i = 0; i < 200; ++i)
for (j = 0; j < 200; ++j); // 空循環
注意:編譯器優化等級(-O1/-O2)可能優化掉空循環,實務應用必須用硬體定時器或內建延遲函數,不可依賴軟體循環。
網路協議、串口傳輸常需將 16 位值拆高低位元組:
uint16_t crc = 0xABCD;
uint8_t high = (crc & 0xFF00) >> 8; // 0xAB
uint8_t low = crc & 0x00FF; // 0xCD
位運算 &、|、>>、<< 是嵌入式開發的基本功。
int a[] = {1,2,3,4,5}; // 長度自動推導為 5
// a[5] 訪問越界 → 未定義行為(隨機值)
不指定長度便利但危險:嚴禁訪問初始化元素以外的索引。
ARM ADS 編譯器預設無半主機模式支援 printf。解決方案:
fputc 向 UART 發送字元__sys_write 系統調用fputc| 操作 | 代碼 | 說明 |
|---|---|---|
| 置位 | reg |= (1 << n); | 將第 n 位設 1,其它不變 |
| 清零 | reg &= ~(1 << n); | 將第 n 位清 0 |
| 翻轉 | reg ^= (1 << n); | 第 n 位取反 |
| 讀取 | (reg >> n) & 1 | 獲取第 n 位值 |
| 批次置位 | PINSEL0 |= 0x00000005; | 同時置位 0 和 2 |
本質:函數名 = 函數代碼首地址。呼叫函數 = CALL address + 參數傳遞 + 返回地址入棧。核心是將地址載入 PC 暫存器。
// 定義:返回 int、接受無參數的函數指針
int (*p)(); // 括號不可省:p 先與 * 結合成指針,再與 () 結合成函數
// 對比:返回 int* 的函數
int *func(); // func 是函數名,返回 int*
進階:可「呼叫不存在的函數實體」(如跳轉到 Bootloader 地址、中斷向量表動態修改),極具靈活性也極度危險。
int (*p)[4]; // p 是指向「含 4 個 int 的陣列」的指標(行指標)
int *p[4]; // p 是「含 4 個 int* 的陣列」(指標陣列)
記憶口訣:從右往左找第一個括號。括號裡是參數 → 函數;括號裡是數字 → 陣列指標;無括號 → 指標陣列。
union Data {
int i;
char c;
double d;
}; // 大小 = max(sizeof(int), sizeof(char), sizeof(double)) = 8 字節(對齊後)
8 位 MCU 發送 16 位資料:
union {
uint16_t word;
uint8_t byte[2];
} send_buff;
send_buff.byte[0] = low_byte;
send_buff.byte[1] = high_byte;
UART_Send(send_buff.word); // 一次發送 16 位
a += 3 * 5; // 等價 a = a + (3 * 5),而非 (a + 3) * 5
+= 等複合賦值優先級低於算術運算子,右側完整表達式先求值。
嵌入式 C 開發是與硬體共舞:理解流水線、精通位運算、敬畏指標、善用共用體、謹慎對齊、驯服編譯器。這 12 條精華,是從「能跑」到「穩、快、省」的進階階梯。