鍍金池/ 教程/ 數(shù)據(jù)庫/ 11.5 UART 串口通信的基本應(yīng)用
8.3 C 語言函數(shù)的形參和實(shí)參
12.2 C 語言指針變量的聲明
12.5 ?C 語言字符數(shù)組和字符指針
7.3 單片機(jī) LED 點(diǎn)陣的介紹
11.5 UART 串口通信的基本應(yīng)用
9.9 單片機(jī)蜂鳴器控制程序和驅(qū)動電路
10. 單片機(jī)實(shí)例練習(xí)與經(jīng)驗(yàn)積累
10.3 單片機(jī)交通燈控制程序和設(shè)計原理
9.8 實(shí)用的 28BYJ-48 步進(jìn)電機(jī)控制程序
8.2 C 語言函數(shù)的調(diào)用
12.4 C 語言指向數(shù)組元素的指針
7.1 C 語言變量的作用域
11.2 RS232 通信接口
12.7 1602 液晶的讀寫時序介紹
7.2 C 語言變量的存儲類別
8. C 語言函數(shù)進(jìn)階與單片機(jī)按鍵
10.4 51單片機(jī) RAM 區(qū)域的劃分
12.1 C 語言變量的地址
11. UART 串口通信
7. 變量進(jìn)階與點(diǎn)陣 LED
8.4 單片機(jī)按鍵介紹
9.3 電機(jī)的分類
9.1 單片機(jī) IO 口的結(jié)構(gòu)
單片機(jī)通信實(shí)例與 ASCII 碼
8.1 單片機(jī)最小系統(tǒng)解析(電源、晶振和復(fù)位電路)
9.2 單片機(jī)上下拉電阻
11.4 單片機(jī) IO 口模擬 UART 串口通信
9.5 讓 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)起來
9.7 28BYJ-48 步進(jìn)電機(jī)控制程序基礎(chǔ)
12.8 1602 液晶指令介紹
12.3 C 語言指針的簡單示例
8.7 單片機(jī)矩陣按鍵的掃描
7.4 單片機(jī) LED 點(diǎn)陣的圖形顯示
8.6 單片機(jī)按鍵消抖程序
10.2 單片機(jī)中 PWM 的原理與控制程序
7.6 單片機(jī) LED 點(diǎn)陣的橫向移動(動態(tài)顯示)
11.3 USB 轉(zhuǎn)串口通信
12.9 1602 液晶簡單顯示程序
9.4 28BYJ-48 步進(jìn)電機(jī)原理
8.5 ?單片機(jī)獨(dú)立按鍵掃描程序
12. C 語言指針基礎(chǔ)與1602液晶的初步認(rèn)識
9. 單片機(jī)中的步進(jìn)電機(jī)與蜂鳴器
10.1 單片機(jī)數(shù)字秒表程序
7.5 單片機(jī) LED 點(diǎn)陣的縱向移動(動態(tài)顯示)
8.8 單片機(jī)簡易加法計算器程序
11.1 單片機(jī)串行通信介紹
10.5 單片機(jī)長短按鍵的應(yīng)用
12.6 1602 液晶介紹(電路和引腳圖)
9.6 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)動精度與深入分析

11.5 UART 串口通信的基本應(yīng)用

通信的三種基本類型

常用的通信從傳輸方向上可以分為單工通信、半雙工通信、全雙工通信三類。

單工通信就是指只允許一方向另外一方傳送信息,而另一方不能回傳信息。比如電視遙控器、收音機(jī)廣播等,都是單工通信技術(shù)。

半雙工通信是指數(shù)據(jù)可以在雙方之間相互傳播,但是同一時刻只能其中一方發(fā)給另外一方,比如我們的對講機(jī)就是典型的半雙工。

全雙工通信就發(fā)送數(shù)據(jù)的同時也能夠接收數(shù)據(jù),兩者同步進(jìn)行,就如同我們的電話一樣,我們說話的同時也可以聽到對方的聲音。

UART 模塊介紹

IO 口模擬串口通信,讓大家了解了串口通信的本質(zhì),但是我們的單片機(jī)程序卻需要不停的檢測掃描單片機(jī) IO 口收到的數(shù)據(jù),大量占用了單片機(jī)的運(yùn)行時間。這時候就會有聰明人想了,其實(shí)我們并不是很關(guān)心通信的過程,我們只需要一個通信的結(jié)果,最終得到接收到的數(shù)據(jù)就行了。這樣我們可以在單片機(jī)內(nèi)部做一個硬件模塊,讓它自動接收數(shù)據(jù),接收完了,通知我們一下就可以了,我們的51單片機(jī)內(nèi)部就存在這樣一個 UART 模塊,要正確使用它,當(dāng)然還得先把對應(yīng)的特殊功能寄存器配置好。

51單片機(jī)的 UART 串口的結(jié)構(gòu)由串行口控制寄存器 SCON、發(fā)送和接收電路三部分構(gòu)成,先來了解一下串口控制寄存器 SCON。如表11-1表11-2所示。

表11-1 SCON——串行控制寄存器的位分配(地址 0x98、可位尋址)

7 6 5 4 3 2 1 0
符號 SM0 SM1 SM2 REN TB8 RB8 TI RI
復(fù)位值 0 0 0 0 0 0 0 0

表11-2 SCON——串行控制寄存器的位描述

http://wiki.jikexueyuan.com/project/mcu-tutorial-two/images/40.png" alt="" />

前邊學(xué)了那么多寄存器的配置,相信 SCON 這個地方,對于大多數(shù)同學(xué)來說已經(jīng)不是難點(diǎn)了,應(yīng)該能看懂并且可以自己配置了。對于串口的四種模式,模式1是最常用的,就是我們前邊提到的1位起始位,8位數(shù)據(jù)位和1位停止位。下面我們就詳細(xì)介紹模式1的工作細(xì)節(jié)和使用方法,至于其它3種模式與此也是大同小異,真正遇到需要使用的時候大家再去查閱相關(guān)資料就行了。

在我們使用 IO 口模擬串口通信的時候,串口的波特率是使用定時器 T0 的中斷體現(xiàn)出來的。在硬件串口模塊中,有一個專門的波特率發(fā)生器用來控制發(fā)送和接收數(shù)據(jù)的速度。對于STC89C52 單片機(jī)來講,這個波特率發(fā)生器只能由定時器 T1 或定時器 T2 產(chǎn)生,而不能由定時器 T0 產(chǎn)生,這和我們模擬的通信是完全不同的概念。

如果用定時器2,需要配置額外的寄存器,默認(rèn)是使用定時器1的,我們本章內(nèi)容主要就使用定時器 T1 作為波特率發(fā)生器來講解,方式1下的波特率發(fā)生器必須使用定時器 T1 的模式2,也就是自動重裝載模式,定時器的重載值計算公式為:

    TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率

和波特率有關(guān)的還有一個寄存器,是一個電源管理寄存器 PCON,他的最高位可以把波特率提高一倍,也就是如果寫 PCON |= 0x80 以后,計算公式就成了:

    TH1 = TL1 = 256 - 晶振值/12 /16 /波特率

公式中數(shù)字的含義這里解釋一下,256是8位定時器的溢出值,也就是 TL1 的溢出值,晶振值在我們的開發(fā)板上就是11059200,12是說1個機(jī)器周期等于12個時鐘周期,值得關(guān)注的是這個16,我們來重點(diǎn)說明。在 IO 口模擬串口通信接收數(shù)據(jù)的時候,采集的是這一位數(shù)據(jù)的中間位置,而實(shí)際上串口模塊比我們模擬的要復(fù)雜和精確一些。他采取的方式是把一位信號采集16次,其中第7、8、9次取出來,這三次中其中兩次如果是高電平,那么就認(rèn)定這一位數(shù)據(jù)是1,如果兩次是低電平,那么就認(rèn)定這一位是0,這樣一旦受到意外干擾讀錯一次數(shù)據(jù),也依然可以保證最終數(shù)據(jù)的正確性。

了解了串口采集模式,在這里要給大家留一個思考題?!熬д裰?12/2/16/波特率”這個地方計算的時候,出現(xiàn)不能除盡,或者出現(xiàn)小數(shù)怎么辦,允許出現(xiàn)多大的偏差?把這部分理解了,也就理解了我們的晶振為何使用 11.0592 M 了。

串口通信的發(fā)送和接收電路在物理上有2個名字相同的 SBUF 寄存器,它們的地址也都是 0x99,但是一個用來做發(fā)送緩沖,一個用來做接收緩沖。意思就是說,有2個房間,兩個房間的門牌號是一樣的,其中一個只出人不進(jìn)人,另外一個只進(jìn)人不出人,這樣的話,我們就可以實(shí)現(xiàn) UART 的全雙工通信,相互之間不會產(chǎn)生干擾。但是在邏輯上呢,我們每次只操作 SBUF,單片機(jī)會自動根據(jù)對它執(zhí)行的是“讀”還是“寫”操作來選擇是接收 SBUF 還是發(fā)送 SBUF,后邊通過程序,我們就會徹底了解這個問題。

UART 串口程序

一般情況下,我們編寫串口通信程序的基本步驟如下所示:

  1. 配置串口為模式1。
  2. 配置定時器 T1 為模式2,即自動重裝模式。
  3. 根據(jù)波特率計算 TH1 和 TL1 的初值,如果有需要可以使用 PCON 進(jìn)行波特率加倍。
  4. 打開定時器控制寄存器 TR1,讓定時器跑起來。

這里還要特別注意一下,就是在使用 T1 做波特率發(fā)生器的時候,千萬不要再使能 T1 的中斷了。

我們先來看一下由 IO 口模擬串口通信直接改為使用硬件 UART 模塊時的程序代碼,看看程序是不是簡單了很多,因?yàn)榇蟛糠值墓ぷ饔布K都替我們做了。程序功能和 IO 口模擬的是完全一樣的。

#include <reg52.h>
void ConfigUART(unsigned int baud);
void main(){
    ConfigUART(9600); //配置波特率為 9600
    while (1){
        while (!RI); //等待接收完成
        RI = 0; //清零接收中斷標(biāo)志位
        SBUF = SBUF + 1; //接收到的數(shù)據(jù)+1 后,發(fā)送回去
        while (!TI); //等待發(fā)送完成
        TI = 0; //清零發(fā)送中斷標(biāo)志位
    }
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud){
    SCON = 0x50; //配置串口為模式 1
    TMOD &= 0x0F; //清零 T1 的控制位
    TMOD |= 0x20; //配置 T1 為模式2
    TH1 = 256 - (11059200/12/32)/baud; //計算 T1 重載值
    TL1 = TH1; //初值等于重載值
    ET1 = 0; //禁止 T1 中斷
    TR1 = 1; //啟動 T1
}

當(dāng)然了,這個程序還是用在主循環(huán)里等待接收中斷標(biāo)志位和發(fā)送中斷標(biāo)志位的方法來編寫的,而實(shí)際工程開發(fā)中,當(dāng)然就不能這么干了,我們也只是為了用直觀的對比來告訴同學(xué)們硬件模塊可以大大簡化程序代碼,那么實(shí)際使用串口的時候就用到串口中斷了,來看一下用中斷實(shí)現(xiàn)的程序。請注意一點(diǎn),因?yàn)榻邮蘸桶l(fā)送觸發(fā)的是同一個串口中斷,所以在串口中斷函數(shù)中就必須先判斷是哪種中斷,然后再作出相應(yīng)的處理。

#include <reg52.h>
void ConfigUART(unsigned int baud);
void main(){
    EA = 1; //使能總中斷
    ConfigUART(9600); //配置波特率為 9600
    while (1);
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud){
    SCON = 0x50; //配置串口為模式1
    TMOD &= 0x0F; //清零 T1 的控制位
    TMOD |= 0x20; //配置 T1 為模式2
    TH1 = 256 - (11059200/12/32)/baud; //計算 T1 重載值
    TL1 = TH1; //初值等于重載值
    ET1 = 0; //禁止 T1 中斷
    ES = 1; //使能串口中斷
    TR1 = 1; //啟動 T1
}
/* UART 中斷服務(wù)函數(shù) */
void InterruptUART() interrupt 4{
    if (RI){ //接收到字節(jié)
        RI = 0; //手動清零接收中斷標(biāo)志位
        SBUF = SBUF + 1; //接收的數(shù)據(jù)+1 后發(fā)回,左邊是發(fā)送 SBUF,右邊是接收 SBUF
    }
    if (TI){ //字節(jié)發(fā)送完畢
        TI = 0; //手動清零發(fā)送中斷標(biāo)志位
    }
}

大家可以試驗(yàn)一下,看看是不是和前邊用 IO 口模擬通信實(shí)現(xiàn)的效果一致,而主循環(huán)卻完全空出來了,我們就可以隨意添加其它功能代碼進(jìn)去。