while(!TI) 完整解析深入理解 8051 單片機串口通訊中 TI (Transmit Interrupt) 旗標的運作機制,以及 while(!TI) 輪詢等待發送完成的程式設計模式。
| 屬性 | 說明 |
|---|---|
| 全名 | Transmit Interrupt Flag (發送中斷旗標) |
| 所屬暫存器 | SCON (串口控制暫存器) 的第 1 位 (bit 1) |
| 硬體自動置位 | 當串口完成一個位元組發送後,硬體自動將 TI 設為 1 |
| 軟體必須清除 | CPU 必須在程式中手動將 TI 清 0,準備下一次發送 |
| 初始值 | 複位後為 0 |
while(!TI) 逐字解析TI = 0 → !TI = 1 (真) → while 迴圈持續執行 (等待)
TI = 1 → !TI = 0 (假) → while 迴圈結束,繼續往下執行
SBUF = data; (啟動硬體發送)while(!TI); (阻塞直到發送完成)TI = 0; (手動清除,準備下次發送)void UART_SendByte(unsigned char dat) {
SBUF = dat; // 將資料寫入發送緩衝器,啟動發送
while(!TI); // 等待發送完成 (TI 變為 1)
TI = 0; // 必須手動清除 TI 旗標
}
void UART_SendString(char *str) {
while(*str) {
UART_SendByte(*str++);
}
}
時間軸 →
CPU: SBUF=資料A ────────────────────── SBUF=資料B
│ ↑ │
│ 等待 │
硬體: 發送資料A ──────→ TI=1 (發送完成)
↑
此時 TI=0,while(!TI) 成立,CPU 卡在迴圈
硬體: 發送資料B ──────→ TI=1
CPU: TI=0 (清除旗標) → 繼續執行
#define UART_TIMEOUT 10000
bit UART_SendByte_Timeout(unsigned char dat) {
unsigned int timeout = 0;
SBUF = dat;
while(!TI) {
if(++timeout > UART_TIMEOUT) {
return 0; // 發送超時,返回失敗
}
}
TI = 0;
return 1; // 發送成功
}
// 全域緩衝區
unsigned char tx_buffer[TX_BUFFER_SIZE];
unsigned char tx_head = 0, tx_tail = 0, tx_count = 0;
// 發送函式 (非阻塞)
void UART_SendByte_ISR(unsigned char dat) {
if(tx_count < TX_BUFFER_SIZE) {
tx_buffer[tx_head++] = dat;
tx_head %= TX_BUFFER_SIZE;
tx_count++;
ES = 1; // 啟用串口中斷
}
}
// 串口中斷服務程式
void UART_ISR() interrupt 4 {
if(TI) {
TI = 0;
if(tx_count > 0) {
SBUF = tx_buffer[tx_tail++];
tx_tail %= TX_BUFFER_SIZE;
tx_count--;
} else {
ES = 0; // 緩衝區空,關閉發送中斷
}
}
if(RI) {
RI = 0;
// 接收處理...
}
}
| 特性 | 輪詢 while(!TI) | 中斷驅動 |
|---|---|---|
| 程式複雜度 | 簡單 | 較複雜 |
| CPU 佔用 | 阻塞等待,佔用 CPU | 非阻塞,CPU 可執行其他任務 |
| 即時性 | 發送期間無法回應其他事件 | 可即時回應中斷/其他任務 |
| 適用場景 | 簡單應用、資料量少、無即時需求 | 複雜系統、即時控制、大量資料傳輸 |
| 程式碼大小 | 小 | 較大 |
| 錯誤現象 | 可能原因 | 解決方案 |
|---|---|---|
| 程式卡在 while(!TI) 無法繼續 | 1. 波特率設定錯誤 2. 晶振頻率不符 3. 串口模式設定錯誤 4. 硬體連線問題 | 檢查 TH1/TL1 設定、確認晶振、檢查 SM0/SM1、用示波器測 TX 腳位 |
| 資料發送錯誤/亂碼 | 1. 未清除 TI 旗標 2. 波特率誤差過大 3. 位元格式不符 (資料位、停止位、校驗位) | 確保 TI=0、校準波特率、檢查 SM0/SM1/SM2 設定 |
| 連續發送資料遺失 | 未等待前一筆發送完成即寫入 SBUF | 務必使用 while(!TI) 等待 |
| 旗標 | 全名 | 觸發條件 | 應用 |
|---|---|---|---|
| TI | Transmit Interrupt | 發送完成 | while(!TI) 等待發送 |
| RI | Receive Interrupt | 接收完成 | while(!RI) 等待接收 / 中斷接收 |
unsigned char UART_ReceiveByte() {
while(!RI); // 等待接收完成
RI = 0; // 清除接收旗標
return SBUF; // 讀取接收資料
}
TI 是串口發送完成的硬體旗標while(!TI) 實作輪詢等待,阻塞 CPU 直到發送完成