在 Keil C51 單片機開發中,<intrins.h> 是連接 C 語言與 8051 硬件指令集的橋樑。它提供的內部函數直接映射到 CPU 指令,實現了高效的位操作、精確延時、原子位測試等功能,讓 C 代碼具備接近匯編的性能與控制力。本文系統梳理該頭文件的全部函數、指令對應、使用示例與工程實踐技巧。
| 內部函數 | 功能描述 | 8051 指令 | 返回類型 | 參數 |
|---|---|---|---|---|
_crol_ | 字符循環左移 | RL A | unsigned char | (val, n) |
_cror_ | 字符循環右移 | RR A | unsigned char | (val, n) |
_irol_ | 整數循環左移 (16-bit) | RL A (雙字節) | unsigned int | (val, n) |
_iror_ | 整數循環右移 (16-bit) | RR A (雙字節) | unsigned int | (val, n) |
_lrol_ | 長整數循環左移 (32-bit) | RL A (四字節) | unsigned long | (val, n) |
_lror_ | 長整數循環右移 (32-bit) | RR A (四字節) | unsigned long | (val, n) |
_nop_ | 空操作 (1 機器週期) | NOP | void | 無 |
_testbit_ | 測試位並清零 (原子操作) | JBC bit, rel | bit | (bit_addr) |
unsigned char _crol_(unsigned char val, unsigned char n);
unsigned char _cror_(unsigned char val, unsigned char n);
unsigned int _irol_(unsigned int val, unsigned char n);
unsigned int _iror_(unsigned int val, unsigned char n);
unsigned long _lrol_(unsigned long val, unsigned char n);
unsigned long _lror_(unsigned long val, unsigned char n);
將輸入值 val 的二進制位循環移動 n 位。左移時高位循環至低位,右移時低位循環至高位。編譯器生成連續的 RL A 或 RR A 指令,無函數調用開銷,執行週期 = n 個機器週期。
#include <intrins.h>
#include <reg51.h>
void uart_send_bit_reverse(unsigned char dat) {
// 利用循環右移實現 LSB first 發送
for (int i = 0; i < 8; ++i) {
SBUF = _cror_(dat, 1); // 每次右移1位,最低位移至最高位
while (!TI); TI = 0;
}
}
unsigned int crc16_update(unsigned int crc, unsigned char data) {
crc ^= data;
for (int i = 0; i < 8; ++i) {
if (crc & 0x0001) crc = _iror_(crc, 1) ^ 0xA001;
else crc = _iror_(crc, 1);
}
return crc;
}
void _nop_(void);
NOP 指令,無 CALL/RET 開銷。// 1. I/O 口翻轉間隔滿足建立時間
P1_0 = 1;
_nop_(); _nop_(); // 2μs 延時
P1_0 = 0;
// 2. 精確微秒延時函數 (12MHz)
void delay_us(unsigned char us) {
while (us--) {
_nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_(); // 8 NOP ≈ 1μs (需實測校準)
}
}
// 3. 防止編譯器重排導致時序錯誤
EA = 0; // 關總中斷
_nop_(); // 確保後續指令不被移至 EA=0 之前
// 關鍵非原子操作...
EA = 1; // 開總中斷
bit _testbit_(bit x);
生成 JBC bit_addr, $+3 指令:
x 的值。整過程不可中斷,屬硬件原子操作。
sbit 定義的位、SFR 位 (如 P1_0、TF0)、內部 RAM 位變量 (bit 類型)。if (_testbit_(FLAG) && ...) 會報錯。sbit INT_FLAG = P3^2; // 外部中斷0輸入腳兼作標誌位
void EX0_ISR() interrupt 0 {
// 中斷服務程序中置位(假設外部電路拉低觸發)
// 此處硬件自動置位,軟件無需操作
}
void main() {
IT0 = 1; // 下降沿觸發
EX0 = 1; // 開啟外部中斷0
EA = 1; // 開總中斷
while (1) {
if (_testbit_(INT_FLAG)) { // 原子讀取並清零
// 處理中斷事件
process_event();
}
// 主循環其他任務...
}
}
C51\INC\INTRINS.H,專案包含路徑正確即可直接 #include <intrins.h>。_crol_ 傳 int 會隱式截斷高字節導致錯誤。n >= 8 (char) 或 n >= 16 (int) 時行為未定義,需自行 n %= 8 處理。_nop_(),建議延時函數單獨文件降低優化或用 volatile 修飾循環變量。intrins.h 內容不同,_nop_ 通用,位操作需改用 CMSIS __RBIT、__REV 等內部函數。intrins.h 是 C51 開發者的「瑞士軍刀」,將 8051 獨特的位處理指令集封裝為標準 C 函數。熟練掌握循環移位實現高效加密/校驗、_nop_ 構建精確定時、_testbit_ 實現無鎖同步,是從「會用 C51」進階到「精通 8051 體系架構」的關鍵。現代 32 位 MCU 開發雖以 HAL/CMSIS 為主,但理解底層指令映射機制,對嵌入式軟件工程師的底層思維養成依然極具價值。