10.3 UART串口通信的基本應用
10.3.1 通信的三種基本類型常用的通信從傳輸方向上可以分為單工通信、半雙工通信、全雙工通信三類。 單工通信就是指只允許一方向另外一方傳送信息,而另一方不能回傳信息。比如電視遙控器、收音機廣播等,都是單工通信技術。 半雙工通信是指數據可以在雙方之間相互傳播,但是同一時刻只能其中一方發給另外一方,比如對講機就是典型的半雙工。 全雙工通信就發送數據的同時也能夠接收數據,兩者同步進行,就如同電話一樣,說話的同時也可以聽到對方的聲音。 10.3.2 UART模塊介紹使用定時器模塊定時某一特定時間時,利用定時器中斷系統,可以讓單片機只有在定時器中斷發生時去執行相應動作。同樣,串口的中斷系統,可以讓串口通信模塊自動接收完數據后,通知單片機去執行相應的動作。當然,這一切都要基于配置好對應的特殊功能寄存器的前提。 51單片機的UART串口的結構由串行口控制寄存器SCON、發送電路和接收電路三部分構成,串口控制寄存器SCON如表10-1表10-2所示。 表10-1 SCON——串行控制寄存器的位分配(地址0x98、可位尋址)
表10-1.jpg (36.34 KB, 下載次數: 0)
下載附件
2026-4-21 14:01 上傳
表10-2 SCON——串行控制寄存器的位描述
表10-2.jpg (230.25 KB, 下載次數: 0)
下載附件
2026-4-21 14:01 上傳
對于串口的四種模式,模式1是最常用的,即前邊提到的1位起始位,8位數據位和1位停止位。下面主要介紹模式1的工作細節和使用方法,至于其它3種模式與此也是大同小異,真正遇到需要使用的時候再去查閱相關資料。 UART串口模塊有一個專門的波特率發生器用來控制發送和接收數據的速度。對于STC89C52單片機來講,這個波特率發生器只能由定時器T1或定時器T2控制產生,而不能由定時器T0產生。 如果用定時器2,需要配置額外的寄存器,默認是使用定時器1的。本章內容使用定時器T1作為波特率發生器,方式1下的波特率發生器必須使用定時器T1的模式2,也就是自動重裝載模式,定時器的重載值計算公式為: TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率 和波特率有關的還有一個寄存器,是一個電源管理寄存器PCON,他的最高位可以把波特率提高一倍,也就是如果寫PCON |= 0x80以后,計算公式就成了: TH1 = TL1 = 256 - 晶振值/12 /16 /波特率 公式中數字的含義這里解釋一下,256是8位定時器的溢出值,也就是TL1的溢出值,晶振值在Kingst51開發板上是11059200,12表達的是1個機器周期等于12個時鐘周期,值得關注的是這個16,采取并確認信號是0還是1的方式是把一位信號采集16次,其中第7、8、9次取出來,這三次中其中兩次如果是高電平,那么就認定這一位數據是1,如果兩次是低電平,那么就認定這一位是0,這樣一旦受到意外干擾讀錯一次數據,也依然可以保證最終數據的正確性。 了解了串口采集模式,在這里留一個思考題。“晶振值/12/2/16/波特率”計算的時候,出現不能除盡,或者出現小數怎么辦,允許出現多大的偏差?弄清楚這個問題,也就理解了晶振為何使用11.0592M了。 串口通信的發送和接收電路在物理上有2個名字相同的SBUF寄存器,它們的地址也都是0x99,但是一個用來做發送緩沖,一個用來做接收緩沖。就是說,有2個房間,兩個房間的門牌號是一樣的,其中一個只出人不進人,另外一個只進人不出人,這樣就可以實現UART的全雙工通信,相互之間不會產生干擾。在邏輯上每次操作SBUF,單片機會自動根據對它執行的是“讀”操作還是“寫”操作來選擇是接收SBUF還是發送SBUF。 10.3.3UART串口通信配置流程通常情況下編寫串口通信程序的基本步驟如下所示: 1、配置串口為模式1。 2、配置定時器T1為模式2,即自動重裝模式。 3、根據波特率計算TH1和TL1的初值,如果有需要可以使用PCON進行波特率加倍。 4、打開定時器控制寄存器TR1,讓定時器跑起來。 這里還要特別注意一下,就是在使用T1做波特率發生器的時候已經被使用,千萬不要再使能T1的中斷了。 10.4 通信實例與ASCII碼串口通信一項重要功能就是實現單片機和電腦之間的信息交互,既可以用電腦控制單片機,也可以把單片機的一些信息狀況發給電腦上的軟件。下面做一個簡單的例程,實現單片機串口調試助手發送的一個字節的數據,在Kingst51開發板上的數碼管上顯示出來,并且單片機再將接收到的數據通過串口發送給電腦。 - #include <reg52.h>
- sbit ADDR3 = P1^3;
- sbit ENLED = P1^4;
- unsigned char code LedChar[] = { //數碼管顯示字符轉換表
- 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
- 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
- };
- unsigned char LedBuff[7] = { //數碼管+獨立LED顯示緩沖區
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
- unsigned char T0RH = 0; //T0重載值的高字節
- unsigned char T0RL = 0; //T0重載值的低字節
- unsigned char RxdByte = 0; //串口接收到的字節
- void ConfigTimer0(unsigned int ms);
- void ConfigUART(unsigned int baud);
- void main()
- {
- EA = 1; //使能總中斷
- ENLED = 0; //選擇數碼管和獨立LED
- ADDR3 = 1;
- ConfigTimer0(1); //配置T0定時1ms
- ConfigUART(9600); //配置波特率為9600
-
- while (1)
- { //將接收字節在數碼管上以十六進制形式顯示出來
- LedBuff[0] = LedChar[RxdByte & 0x0F];
- LedBuff[1] = LedChar[RxdByte >> 4];
- }
- }
- /* 配置并啟動T0,ms-T0定時時間 */
- void ConfigTimer0(unsigned int ms)
- {
- unsigned long tmp; //臨時變量
-
- tmp = 11059200 / 12; //定時器計數頻率
- tmp = (tmp * ms) / 1000; //計算所需的計數值
- tmp = 65536 - tmp; //計算定時器重載值
- tmp = tmp + 13; //補償中斷響應延時造成的誤差
- T0RH = (unsigned char)(tmp>>8); //定時器重載值拆分為高低字節
- T0RL = (unsigned char)tmp;
- TMOD &= 0xF0; //清零T0的控制位
- TMOD |= 0x01; //配置T0為模式1
- TH0 = T0RH; //加載T0重載值
- TL0 = T0RL;
- ET0 = 1; //使能T0中斷
- TR0 = 1; //啟動T0
- }
- /* 串口配置函數,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
- }
- /* LED動態掃描刷新函數,需在定時中斷中調用 */
- void LedScan()
- {
- static unsigned char i = 0; //動態掃描索引
-
- P0 = 0xFF; //關閉所有段選位,顯示消隱
- P1 = (P1 & 0xF8) | i; //位選索引值賦值到P1口低3位
- P0 = LedBuff[ i]; //緩沖區中索引位置的數據送到P0口
- if (i < 6) //索引遞增循環,遍歷整個緩沖區
- i++;
- else
- i = 0;
- }
- /* T0中斷服務函數,完成LED掃描 */
- void InterruptTimer0() interrupt 1
- {
- TH0 = T0RH; //重新加載重載值
- TL0 = T0RL;
- LedScan(); //LED掃描顯示
- }
- /* UART中斷服務函數 */
- void InterruptUART() interrupt 4
- {
- if (RI) //接收到字節
- {
- RI = 0; //手動清零接收中斷標志位
- RxdByte = SBUF; //接收到的數據保存到接收字節變量中
- SBUF = RxdByte; //接收到的數據又直接發回,叫作-"echo",
- //用以提示用戶輸入的信息是否已正確接收
- }
- if (TI) //字節發送完畢
- {
- TI = 0; //手動清零發送中斷標志位
- }
- }
復制代碼 打開STC下載軟件右側菜單欄的串口助手,在“接收緩沖區”和“發送緩沖區”各自有兩個選項--文本格式和HEX格式,這是什么意思呢?
拋開漢字不談,常用的字符包含了0~9的數字、A~Z/a~z的字母、還有各種標點符號等。那么在單片機系統里面怎么來表示它們呢?ASCII碼(American Standard Code for Information Interchange,即美國信息互換標準代碼)可以完成這個使命:在單片機中一個字節的數據可以有0~255共256個值,取其中的0~127共128個值賦予了它另外一層涵義,即讓它們分別來代表一個常用字符,其具體的對應關系如表10-3所示。
表10-3 ASCII碼字符表
表10-3.jpg (259.91 KB, 下載次數: 0)
下載附件
2026-4-21 14:02 上傳
這樣就在常用字符和字節數據之間建立了一一對應的關系,現在一個字節就既可以代表一個整數又可以代表一個字符了,至于什么時候代表整數,什么時候代表字符主要看編程者的實際意圖。ASCII碼在單片機系統中應用非常廣泛。首先將發送緩沖區和接收緩沖區的“文本模式”取消勾選,在“HEX模式”勾選上。選擇對應的串口號,波特率9600,校驗位無校驗,停止位1位,然后發送緩沖區的白框內,隨意輸入一個數字比如“31”,點擊發送數據,就可以將0x31發送出去,并且能看到接收緩沖區顯示也接收到了一個“31”,將操作步驟按照數字編排到串口助手截圖中,如圖10-4所示。
圖10-4.png (18.89 KB, 下載次數: 0)
下載附件
2026-4-21 14:02 上傳
將這個程序的串口助手發送模式保持“HEX模式”不變,接收模式改成“文本模式”,會觀察到接收到一個數字1,也就是0x31對應的ASCII值,從表10-3可以查詢到。如果將接收緩沖區保持“HEX模式”,而發送模式改為“文本模式”,在接收緩沖區會看到發送了0x33和0x31,也就是發送的時候31當做兩個字符發送出去的,接收到的0x33和0x31分別是3和1的ASCII值。
|