參考資料:https://www.bilibili.com/video/BV1Mb411e7re?p=19
一、串列埠相關資訊
1. 串列埠簡介
串列埠是一種應用十分廣泛的通訊介面,串列埠成本低、容易使用、通訊線路簡單,可實現兩個裝置的互相通訊。
微控制器的串列埠可以使微控制器與微控制器、微控制器與電腦、微控制器與各式各樣的模組互相通訊,極大的擴充套件了微控制器的應用範圍,增強了微控制器系統的硬體實力。
51微控制器內部自帶UART (Universal Asynchronous Receiver Transmitter,通用非同步收發器),可實現微控制器的串列埠通訊。
2. 串列埠線路的連線
- 簡單雙向串列埠通訊有兩根通訊線(傳送端
TXD
和接收端RXD
) TXD
與RXD
要交叉連線- 當只需單向的資料傳輸時,可以直接一根通訊線
- 當電平標準不一致時,需要加電平轉換晶片
示意圖:

3. 串列埠電平標準
電平標準是資料1和資料0的表達方式,是傳輸線纜中人為規定的電壓與資料的對應關係,串列埠常用的電平標準有如下三種:
- TTL電平:
+5V
表示1
,0V
表示0
(微控制器使用的是這個) - RS232電平: -3--15V表示1,+3~+15V表示0
- RS485電平:兩線壓差+2+6V表示1,-2-6V表示0 (差分訊號)
4. 常見通訊介面比較

相關術語:
- 全雙工:通訊雙方可以在同一時刻互相傳輸資料
- 半雙工:通訊雙方可以互相傳輸資料,但必須分時複用一根資料線
- 單工:通訊只能有一方傳送到另一方,不能反向傳輸
- 非同步:通訊雙方各自約定通訊速率
- 同步:通訊雙方靠一根時鐘線來約定通訊速率
- 匯流排:連線各個裝置的資料傳輸線路(類似於一條馬路,把路邊各住戶連線起來,使住戶可以相互交流)
二、51微控制器的UART
1. STC89C52的UART資源
STC89C52
有1個UART
STC89C52
的UART有四種工作模式:
- 模式0 :同步移位暫存器
- 模式1 : 8位UART,波特率可變(常用)
- 模式2 : 9位UART,波特率固定
- 模式3 : 9位UART,波特率可變
2. 串列埠引數
波特率:串列埠通訊的速率(傳送和接收各資料位的間隔時間)
檢驗位:用於資料驗證
停止位:用於資料幀間隔
示意圖如下:

3. 串列埠模式圖

4. 中斷通路的配置

從大的方向來說就是當微控制器使用串列埠來傳送資料或接受資料,都會先把資料寫入到快取SBUF
中,然後會提出中斷申請,然後再經過中斷通路執行對應的中斷程式。
5. 串列埠相關暫存器

三、相關暫存器的配置
SCON暫存器

1. SM0,SM1

我們主要通過配置這兩位來設定UART
的工作模式,如上所說,串列埠有4種工作模式,而我們常用的為第二種,故需要設定SM0=0
,SM1=1
。
2. REN

3. TI和RI


簡單地說就是當微控制器使用串列埠傳送或接收資料後,TI
或RI
位就會置一,告訴我們資料已被髮送或接收了,然後我們需要在程式碼層對其重新置零。
程式碼配置
SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
所以配置的程式碼為:
SCON = 0x50; // 0101 0000
PCON暫存器


由於我們不需要檢測幀錯誤,故我們這裡只需要配置SMOD
,我們不需要波特率加倍,故這裡置零,所以總的配置為:
PCON = 0x7F;
至於為什麼其他的位全部為1我也不太清楚,我是根據STC-ISP軟體生成的程式碼配置的
使用STC-ISP軟體自動生成配置程式碼
其實後面還有幾項重要的配置,可是b站視訊中的老師講的很不清楚,直接就使用軟體生成程式碼了,我也只能照做了。。。
開啟STC-ISP軟體並開啟到【波特率計算器】的tab:

然後按照上圖這樣配置需要的引數,然後我們就獲得了串列埠配置的所有程式碼。
然後我們即可將這段程式碼複製到專案中,注意還需要將與AUXR
相關的兩個語句刪除掉,上一部分已經說明過我們當前版本的微控制器是不需要配置這個的。
總的程式碼如下:
void UartInit(void) // 4800bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; // 8位資料,可變波特率
TMOD &= 0x0F; //清除定時器1模式位
TMOD |= 0x20; //設定定時器1為8位自動重灌方式
TL1 = 0xFA; //設定定時初值
TH1 = 0xFA; //設定定時器重灌值
ET1 = 0; //禁止定時器1中斷
TR1 = 1; //啟動定時器1
}
四、通過程式碼使用串列埠
1. 通過串列埠傳送資料
編寫函式:
void UartSendByte(unsigned char Data) {
SBUF = Data;
while (TI == 0);
TI = 0;
}
即我們將一個位元組的資料寫入到SBUF
暫存器中,然後微控制器即會自動將這個位元組的資料傳送出去,傳送後TI
位會被置一。此時代表資訊傳送完成,我們需要手動將TI
位置零。
然後我們可以編寫一個每隔一段時間傳送一個遞增的數字的程式:
unsigned char dataToSend = 0;
int main() {
UartInit();
while (1) {
UartSendByte(dataToSend);
dataToSend++;
defaultDeley();
}
}
tips:
接收資料可以使用STC-ISP軟體自帶的串列埠助手:
![]()
此時我們需要注意將波特率設定為和我們前面配置的一致,這樣我們才能確保接收到正確的資料。
執行結果:

2. 通過串列埠接收資料
我們使用串列埠接收資料並進行處理需要藉助中斷系統,當微控制器使用串列埠傳送或接收資料時都會發出中斷請求,此時我們需要先配置中斷通路(二. 4),即:
ES = 1;
EA = 1;
在Uart_Init()
函式中新增這兩句即可。
總的配置語句為:
void Uart_Init(void) // 4800bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; // 8位資料,可變波特率
TMOD &= 0x0F; //清除定時器1模式位
TMOD |= 0x20; //設定定時器1為8位自動重灌方式
TL1 = 0xFA; //設定定時初值
TH1 = 0xFA; //設定定時器重灌值
ET1 = 0; //禁止定時器1中斷
TR1 = 1; //啟動定時器1
// 配置中斷通路
ES = 1;
EA = 1;
}
然後我們在檢視串列埠中斷訊號對應的中斷號:

由圖可知對應的中斷號為4
,因此我們可以像定時器那樣編寫中斷程式(函式):
void UART_Routine() interrupt 4 {
...
}
因為接受資料和傳送資料都會觸發中斷程式,我們這裡只使用中斷程式來接受資料,故我們可以先使用一個if
語句進行過濾:
void UART_Routine() interrupt 4 {
if(RI == 1) {
...
}
}
當RI == 1
,即遇到接受資訊而觸發的中斷時,我們將接受到的資訊進行處理,我們可以用接收到的位元組資料來控制LED燈的亮滅,則我們可以如下編寫:
void UART_Routine() interrupt 4 {
if(RI == 1) {
// 將接收到的資料取反後賦值到P2,則bit為1時亮燈
P2 = ~SBUF;
// 然後將接收到的資料傳送出去
Uart_SendByte(SBUF);
// RI需要手動置零
RI = 0;
}
}
不要忘記
RI
位需要手動置零
主函式:
int main() {
Uart_Init();
while (1) {}
}
然後我們即可使用【串列埠助手】傳送資訊到微控制器上了。

五、將串列埠相關功能封裝成模組
// Uart.c
#include "Uart.h"
#include<Atmel/REGX52.H>
void Uart_Init(void) // 4800bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; // 8位資料,可變波特率
TMOD &= 0x0F; //清除定時器1模式位
TMOD |= 0x20; //設定定時器1為8位自動重灌方式
TL1 = 0xFA; //設定定時初值
TH1 = 0xFA; //設定定時器重灌值
ET1 = 0; //禁止定時器1中斷
TR1 = 1; //啟動定時器1
ES = 1;
EA = 1;
}
void Uart_SendByte(unsigned char Data) {
SBUF = Data;
while (TI == 0);
TI = 0;
}
/*
接收串列埠資料中斷函式模板
void UART_Routine() interrupt 4 {
if(RI == 1) {
RI = 0;
}
}
*/
標頭檔案寫上函式宣告即可。