10.1 串口通信基礎(chǔ)知識
前面講解的都是單片機(jī)自身的一些功能,并未涉及單片機(jī)與其它單片機(jī)或者計算機(jī)之間通信。那么單片機(jī)與其它設(shè)備之間又是怎么通信的呢?通常來講有兩種通信方式,即并行通信與串行通信。
兩個單片機(jī)之間的并口通信,以 8 位為例,直接將單片機(jī) 1 的 8 位 I/O 口 P0 與單片機(jī) 2 的 P1 相連接,在同步時鐘信號的作用進(jìn)行數(shù)據(jù)的傳輸。以單片機(jī) 1 向單片機(jī) 2 傳輸數(shù)據(jù) 0x7B 為例,單片機(jī) 1 只需將數(shù)據(jù) 0x7B 輸出到 P0 口上,單片機(jī) 2 在同步時鐘信號的步調(diào)下將 0x7B 直接讀取。此時同步時鐘信號的周期長短將決定數(shù)據(jù)傳輸?shù)乃俣龋芷谠蕉蹋俣仍娇?。并口通信的最大?yōu)點為速度快,一個時鐘周期內(nèi)可以傳播一個字節(jié),甚至多個字節(jié),缺點為需要占用大量的 I/O 口資源,對于大多數(shù)資源緊張的應(yīng)用場合來說,這種方法是不可取的,而串行通信有效的解決了資源問題。
STC89C52 系列單片機(jī)配置了串行方式通信接口,對應(yīng)單片機(jī)的管腳 P3.0/RxD,串行數(shù)據(jù)接收引腳、P3.1/TxD,串行數(shù)據(jù)發(fā)送引腳。如上圖所示,單片機(jī) 1 的串行發(fā)送引腳 TxD 與單片機(jī) 2 的串行數(shù)據(jù)接收引腳 RxD 相連接,形成了一條單片機(jī) 1 發(fā)送、單片機(jī) 2 接收的數(shù)據(jù)通信鏈路。單片機(jī) 2 的 TxD 與單片機(jī) 1 的 RxD 連接,形成了一條單片機(jī) 2 發(fā)送、單片機(jī) 1 接收數(shù)據(jù)的通信鏈路。單片機(jī) 1 的 GND 與單片機(jī) 2 的 GND 相連接確保了兩個單片機(jī)在同一電源基準(zhǔn)下工作。接下來講解單片機(jī)串行通信的工作原理。
同樣以單片機(jī) 1 向單片機(jī) 2 發(fā)送數(shù)據(jù) 0x7B 為例,二進(jìn)制表示為 01111011,單片機(jī)依次 01111011 的最低位‘1’到最高位‘0’依次發(fā)送出去。發(fā)送 1 的時候為將 TxD 拉高’一段時間’,發(fā)送 0 的時候為將 TxD 拉低‘一段時間’,即發(fā)送數(shù)據(jù)的時候后,每發(fā)送一位都持續(xù)‘一段時間’,發(fā)送 0x7B 的從低位到高位的順序為:1->1->0->1->1->1->1->0。“一段時間”的長短決定了單片機(jī)的串口傳輸?shù)乃俣?,時間越短,傳輸速度越快。那么在同樣位傳輸速率的情況下,并行通信速度為串行通信的 8 倍,顯然串行通信以犧牲速度的方式換取了更多的資源,這就是時間換資源的概念。
10.2 波特率概念
上面講解的“一段時間”實際上為單片機(jī)串口傳輸 1 位數(shù)據(jù)所耗費(fèi)的時間,在應(yīng)用中我們常常把 1 秒傳輸多少位來衡量單片機(jī)串口傳輸速率,這就是波特率的概念。例如通信雙方約定波特率為:9600bps,即每秒傳輸 9600 位(bps:bit per second)。波特率在單片串口通信中為非常重要的參數(shù)指標(biāo),通信雙方只有在約定共同的波特率下才能保證數(shù)據(jù)的正確傳輸。
在單片機(jī)串口通信中,并沒有同步時鐘信號來統(tǒng)一數(shù)據(jù)發(fā)送和接收,通過雙方約定的波特率來保證數(shù)據(jù)的通信。以單片機(jī) 1 向單片機(jī) 2 發(fā)送數(shù)據(jù)為例,假設(shè)雙方設(shè)定的波特率為 9600bps,那么單片機(jī) 2 是怎么知道單片機(jī) 1 什么時候給它發(fā)送數(shù)據(jù),又是什么時候結(jié)束的呢?
這里我們引入兩個重要的概念,起始位和停止位。單片機(jī)在沒有進(jìn)行串口通信的情況下,數(shù)據(jù)發(fā)送引腳 TxD 為高電平,當(dāng)單片機(jī) 1 需要發(fā)送數(shù)據(jù)時,首先通過 TXD 發(fā)送一位“0”,即把單片機(jī) TxD 從高電平拉低,當(dāng)單片機(jī) 2 檢測到數(shù)據(jù)接收引腳 RxD 的低電平后便開始接收數(shù)據(jù)了。那么這里單片機(jī) 1 發(fā)送的第一位“0”稱為起止位。緊接著單片機(jī) 1 將 8 位數(shù)據(jù)依次發(fā)送出去,當(dāng) 8 位數(shù)據(jù)發(fā)送完后,單片機(jī)發(fā)送一位“1”,單片機(jī) 2 接收完 8 位數(shù)據(jù)后,又接收到一位“1”,便知道停止發(fā)送數(shù)據(jù)了,至此便完成了一次數(shù)據(jù)的傳輸。這里單片機(jī) 1 發(fā)送的最后一位“1”稱之為停止位。根據(jù)上面的步驟,完成一次數(shù)據(jù)的傳輸,包括起始位、8 位數(shù)據(jù)、停止位,總共 10 位數(shù)據(jù),如下圖所示。這里的 10 位數(shù)據(jù)稱之為一幀數(shù)據(jù)。
10.3 單片機(jī)串口通信
下面講解一下單片機(jī)串口的工作原理。STC89C52系列單片機(jī)串口內(nèi)部模塊有兩個獨立的串行口緩沖寄存器(SBUF),兩個寄存器均為8位,一個為發(fā)送SBUF,只能往里寫數(shù)據(jù)、另一個為接收SBUF、只能讀取數(shù)據(jù)。當(dāng)單片機(jī)要通過串口往外發(fā)送數(shù)據(jù)時,只需要將待發(fā)送的數(shù)據(jù)寫入發(fā)送SBUF中,通過TxD引腳將數(shù)據(jù)發(fā)送出去。當(dāng)一幀數(shù)據(jù)發(fā)送完成后,內(nèi)部硬件自動置位TI,即TI=1,請求中斷處理。前面講過串口中斷是單片機(jī)中斷源的一種,產(chǎn)生中斷后,程序進(jìn)入串口中斷函數(shù)。同樣,當(dāng)單片機(jī)接收到一幀數(shù)據(jù)后,內(nèi)部硬件自動置位RI,即RI=1,請求串口中斷處理,進(jìn)入串口中斷函數(shù)。讀取接收SBUF可獲得通過串口接收到的數(shù)據(jù)。
在單片機(jī)內(nèi)部編程中兩個寄存器共用 SBUF 這個名字,那么怎么區(qū)分是對發(fā)送還是接收 SBUF 進(jìn)行操作呢?由于這兩個寄存器一個只能讀、另一個只能寫。當(dāng)程序中往 SBUF 中寫入數(shù)據(jù)時,則代表操作發(fā)送 SBUF,當(dāng)程序中讀取 SBUF 的數(shù)據(jù)時,則代表操作接收 SBUF。
通過上面的介紹,無論是接收到一幀數(shù)據(jù),還是發(fā)送完一幀數(shù)據(jù),程序都會產(chǎn)生串口中斷,那么在串口中斷程序中是怎么區(qū)分來的是接收中斷還是發(fā)送中斷呢?在單片機(jī)串口中斷程序開始的地方通過查詢中斷標(biāo)志位 TI、RI 哪個為 1 來判斷串口中斷的類型。
10.4 串口通信控制寄存器
和單片機(jī)的其它功能的實現(xiàn)一樣,通過配置相應(yīng)的特殊功能寄存器來實現(xiàn)串口通信的功能。與 STC89C52 系列單片機(jī)串口通信相關(guān)的寄存器如下表所示:
熟練掌握上述特殊功能寄存器的應(yīng)用便能掌握 STC89C52 系列單片機(jī)的串口功能應(yīng)用。
10.4.1 串行口控制寄存器 SCON 和 PCON
STC89C52 系列單片機(jī)共設(shè)有兩個控制寄存器:串行口控制寄存器 SCON 和波特率選擇特殊功能寄存器 PCON。SCON 用于設(shè)置串口的工作方式和某些控制功能,寄存器格式如下表所示:
其中,SM0、SM1 共同組合確定串口的工作方式:
如上所示,單片機(jī)共有 4 種工作方式,各工作方式的主要區(qū)別為數(shù)據(jù)的位數(shù)以及波特率的不同。方式 0、1 均為 8 位數(shù)據(jù)模式,方式 2,3 為 9 位數(shù)據(jù)模式。方式 0,2 為固定波特率模式,方式 1,3 為波特率可變模式。根據(jù)應(yīng)用場合的不同可以選擇相應(yīng)的工作模式。方式 1,3 最為常用。
SM2:為方式 2 或方式 3 多機(jī)通信控制位。
REN:允許/禁止串行接收控制位。有軟件置位,當(dāng) REN=1 時,允許接收 RxD 引腳數(shù)據(jù),當(dāng) REN=0 時,禁止接收串行數(shù)據(jù)。
TB8:在方式 2 或方式 3,它為要發(fā)送的第 9 位數(shù)據(jù),按需要進(jìn)行置位或清零。例如可作為數(shù)據(jù)的校驗位。
RB8:在方式 2 或方式 3,是接收到的第 9 位數(shù)據(jù)。
TI:發(fā)送中斷請求標(biāo)志位。在發(fā)送完數(shù)據(jù)后,由內(nèi)部硬件自動置位,即 TI=1,向主機(jī)請求中斷,響應(yīng)中斷后必須用軟件復(fù)位,即 TI=0。
RI:接收中斷請求標(biāo)志位。在發(fā)送完數(shù)據(jù)后,由內(nèi)部硬件自動置位,即 RI=1,向主機(jī)請求中斷,響應(yīng)中斷后必須用軟件復(fù)位,即 RI=0。
在中斷請求 TI 或 RI 響應(yīng)完串口中斷函數(shù)后,必須由軟件進(jìn)行復(fù)位,否則將程序?qū)⒍啻芜M(jìn)入中斷函數(shù)。
PCON 為串口波特率選擇特殊功能寄存器,格式如下:
SMOD:波特率選擇位。當(dāng)軟件置位 SMOD,即 SMOD=1,則使串口工作方式 1,2,3 的波特率加倍,當(dāng)軟件清零 SMOD,即 SMOD=0,波特率不加倍。
中斷允許寄存器 IE 的 ES 位為串行口中斷允許位,ES=1,允許串行口中斷,ES=0,屏蔽串行口中斷。
根據(jù)上面介紹,我們大致可分析出單片機(jī)在進(jìn)行通信之前要對上述寄存器進(jìn)行設(shè)置。通過設(shè)置寄存器 SCON 選擇串口工作方式,允許串口接收數(shù)據(jù)。設(shè)置 PCON 寄存器是否波特率加倍,設(shè)置中斷控制寄存器 IE 允許串口中斷。
10.4.2 波特率的計算
由表 10-4 所示,STC89C52 系列單片機(jī)工作在方式 1,3 時,波特率不僅與 SMOD 值有關(guān),還與定時器 1 的溢出率有關(guān)。
方式 1,3 波特率 =2SMOD/32x(定時器 1 的溢出率)
- 當(dāng)單片機(jī)工作在 12T 模式下,定時器 1 的溢出率 =SYSclk/12/(256-TH1)
- 當(dāng)單片機(jī)共工作在 6T 模式下,定時器 1 的溢出率 =SYSclk/6/(256-TH1)
其中,SYSclk 為系統(tǒng)時鐘頻率,在 RY-51 單片機(jī)系統(tǒng)時鐘為晶振時鐘頻率 11.0592MHz。
其中,TH1 為定時器工作在模式 2 的初始值,當(dāng)定時器工作在模式 2 時為 8 位自動重裝模式,當(dāng)定時器產(chǎn)生溢出時,系統(tǒng)會自動把 TL1 的值自動重載到 TH1 中開始重新定時,因此,在單片機(jī)編程過程中只需在初始化時將初始值同時賦值給 TH1,TL1 保證波特率正常的產(chǎn)生。
由上面公式計算可得,當(dāng)單片機(jī)工作在 12T 模式下,定時器 1 初始值:
TH1 =TL1=256- SYSclk x2SMOD/32 /12/波特率
當(dāng)單片機(jī)工作在 6T 模式下,定時器 1 初始值:
TH1 =TL1=256- SYSclk x2SMOD/32 /6/波特率
10.4.3 串口轉(zhuǎn) USB
單片機(jī)與單片之間通可以采用串口直接相連的方式進(jìn)行,而計算機(jī)一般并沒有配置與單片機(jī)接口一致的的串行通信接口,但一般計算機(jī)都配備了 USB 接口。在我們前面經(jīng)常使用到的通過 USB 將程序下載到單片機(jī),實際上是在計算機(jī) USB 與單片機(jī)之間增加了一個串口轉(zhuǎn) USB 模塊。在 RY-51 單片機(jī)開發(fā)板是配置了串口轉(zhuǎn) USB 芯片 CH340G,硬件原理圖如下所示:
如圖所示,USB+、USB-分別連接到計算機(jī) USB 接口的數(shù)據(jù)正負(fù)端,USB_RX、USB_TX 分別連接到單片機(jī)的 P3.0(RxD)、P3.1(TxD)引腳。在計算機(jī)安裝好串口轉(zhuǎn) USB 驅(qū)動后,便可實現(xiàn)計算機(jī)與單片機(jī)之間的通信。我們經(jīng)常使用串口調(diào)試助手來實現(xiàn)單片機(jī)與計算機(jī)專家的通信,串口調(diào)試助手軟件主界面如下圖所示:
如上圖所示,串口調(diào)試助手界面主要包含三大塊,首先為通信方式選擇區(qū),根據(jù)通信雙方的約定選擇合適的參數(shù),數(shù)據(jù)發(fā)送區(qū),將數(shù)據(jù)輸入到這個區(qū)域,點擊發(fā)送可將數(shù)據(jù)發(fā)送到單片機(jī),接收數(shù)據(jù)顯示區(qū),顧名思義計算機(jī)接收到單片機(jī)發(fā)來的數(shù)據(jù)將顯示到該區(qū)域。
10.5 串口功能應(yīng)用
前面介紹了單片機(jī)串口工作原理,這節(jié)我們結(jié)合上面內(nèi)容舉例說明串口的實際應(yīng)用。程序?qū)崿F(xiàn)的功能為:首先由計算機(jī)通過串口調(diào)試助手往單片機(jī)發(fā)送一個數(shù)據(jù),當(dāng)單片機(jī)接收到數(shù)據(jù)后翻轉(zhuǎn) led0 小燈,并將接收到的數(shù)據(jù)加 1 發(fā)送給計算機(jī),發(fā)送完數(shù)據(jù)后翻轉(zhuǎn) led1。串口應(yīng)用程序編寫順序如下:
- 配置串口工作方式 1,并允許接收串口數(shù)據(jù)
- 初始化 SMOD
- 配置定時器位工作模式 2,8 位自動重裝模式
- 初始化 TH1,TL1
- 啟動定時器,并禁止定時器中斷
- 允許串口中斷,允許總中斷
- 編寫串口中斷程序
串口應(yīng)用程序代碼如下:
/*----------------------------------------------------
** 串口應(yīng)用程序
** 串口工作方式1,定時器1工作模式2
** 將接收到的數(shù)據(jù)加1發(fā)送回去,收到數(shù)據(jù)時翻轉(zhuǎn)led0
** 發(fā)送完數(shù)據(jù)時翻轉(zhuǎn)led1
**
----------------------------------------------------*/
#include< reg52.h >
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200 //單片機(jī)晶振頻率
#define BAUD 9600 //波特率設(shè)置為9600
sbit led0 = P1^0; //led0位聲明
sbit led1 = P1^1; //led1位聲明
void main()
{
SCON = 0x50;//串口配置為工作方式1
PCON = 0; //波特率不加倍
TMOD = 0x20;//設(shè)置定時器1為8位自動重裝模式
TH1=TL1= 256- FOSC/32/12/BAUD;//定時器1賦初始值
ET1 = 0;//禁止定時器1中斷
TR1 = 1;//啟動定時器1
ES = 1;//允許串口中斷
EA = 1;//開總中斷
while(1);
}
//串口中斷函數(shù)
void Uart_r(void) interrupt 4
{
if(RI)
{
RI = 0; //清零接收中斷
SBUF = SBUF+1; //讀取接收到的數(shù)據(jù)加1并發(fā)送出去
led0 = ~led0; //翻轉(zhuǎn)led0
}
if(TI)
{
TI = 0; //清零發(fā)送中斷
led1 = ~led1; //翻轉(zhuǎn)led1
}
}
將上述代碼編譯后,下載到開發(fā)板進(jìn)行測試,打開串口調(diào)試助手軟件,設(shè)置后下圖中紅框中參數(shù),點擊打開串口,如果打開串口成功,軟件左上角紅色指示燈燈會點亮。
在數(shù)據(jù)發(fā)送區(qū)輸入 16 進(jìn)制數(shù)“EF”,點擊“手動發(fā)送”按鈕將數(shù)據(jù)“EF”發(fā)送給單片機(jī),此時觀察到串口調(diào)試助手?jǐn)?shù)據(jù)接受區(qū)接收到 “F0”,接收到的值正好為發(fā)送的數(shù)值加 1,與我們預(yù)期的結(jié)果相同。同時觀察 RY-51 開發(fā) led0,led1 小燈也發(fā)生了翻轉(zhuǎn)。
上面的程序只是發(fā)送單個字節(jié)的程序,下面舉例說明字符串的發(fā)送方法。功能為當(dāng) RY-51 開發(fā)板上電后向計算機(jī)發(fā)送指定的字符串。為了程序的可讀性,我們將定時器的初始化放到初始化函數(shù) Uart_int()。代碼如下所示:
/*----------------------------------------------------
** 串口應(yīng)用程序
** 串口工作方式1,定時器1工作模式2
** 將接收到的數(shù)據(jù)加1發(fā)送回去,收到數(shù)據(jù)時翻轉(zhuǎn)led0
** 發(fā)送完數(shù)據(jù)時翻轉(zhuǎn)led1
** 上電后向單片機(jī)發(fā)送指定字符串
**
----------------------------------------------------*/
#include< reg52.h >
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200 //單片機(jī)晶振頻率
#define BAUD 9600 //波特率設(shè)置為9600
sbit led0 = P1^0; //led0位聲明
sbit led1 = P1^1; //led1位聲明
sbit dir485 = P3^7;
uchar busy = 0;//發(fā)送完一幀數(shù)據(jù)標(biāo)志位
void Uart_init(void);//串口初始化函數(shù)聲明
void Send_String(char *s);//字符串發(fā)送函聲明
void main()
{
Uart_init(); // 串口初始化
EA = 1; //開總中斷
Send_String("RY-51 STC89C52 Uart test!n"); //發(fā)送字符串
while(1);
}
//串口中斷函數(shù)
void Uart_r(void) interrupt 4
{
if(RI)
{
RI = 0; //清零接收中斷
dir485=1;
SBUF = SBUF+1; //讀取接收到的數(shù)據(jù)加1并發(fā)送出去
// led0 = ~led0; //翻轉(zhuǎn)led0
}
if(TI)
{ dir485=0;
TI = 0; //清零發(fā)送中斷
led1 = ~led1; //翻轉(zhuǎn)led1
busy = 0; //發(fā)送完數(shù)據(jù),清除發(fā)送完標(biāo)志位
}
}
//串口初始化函數(shù)
void Uart_init(void)
{
SCON = 0x50;//串口配置為工作方式1
PCON = 0; //波特率不加倍
TMOD = 0x20;//設(shè)置定時器1為8位自動重裝模式
TH1=TL1= 256- FOSC/32/12/BAUD;//定時器1賦初始值
ET1 = 0;//禁止定時器1中斷
TR1 = 1;//啟動定時器1
ES = 1;//允許串口中斷
}
//字符串發(fā)送函
void Send_String(char *s)
{
while(*s) //檢查字符串是否到了結(jié)尾
{
while(busy); //等待上一幀數(shù)據(jù)發(fā)送完畢
busy = 1;
dir485=1;
SBUF = *s++; //發(fā)送當(dāng)前字符,并將字符指針移到下一個字符
}
dir485=0;
}
將程序編譯后,下載到 RY-51 開發(fā)板,設(shè)置后串口調(diào)試助手,并不選中“十六進(jìn)制顯示”,給開發(fā)板重新加電,收到的指定字符串如下圖所示:
10.6 本章小結(jié)
本章介紹了51單片機(jī)串口通信的基本原理。
-
單片機(jī)
+關(guān)注
關(guān)注
6023文章
44376瀏覽量
628400 -
定時器
+關(guān)注
關(guān)注
23文章
3218瀏覽量
113682 -
串行通信
+關(guān)注
關(guān)注
4文章
555瀏覽量
35165 -
串口通信
+關(guān)注
關(guān)注
34文章
1601瀏覽量
55235 -
并行通信
+關(guān)注
關(guān)注
1文章
31瀏覽量
10772
發(fā)布評論請先 登錄
相關(guān)推薦
評論