串口通信的原理
串口通信(SerialCommunicaTIons)的概念非常簡單,串口按位(bit)發送和接收字節。盡管比按字節(byte)的并行通信慢,但是串口可以在使用一根線發送數據的同時用另一根線接收數據。它很簡單并且能夠實現遠距離通信。比如IEEE488定義并行通行狀態時,規定設備線總長不得超過20米,并且任意兩個設備間的長度不得超過2米;而對于串口而言,長度可達1200米。典型地,串口用于ASCII碼字符的傳輸。通信使用3根線完成,分別是地線、發送、接收。由于串口通信是異步的,端口能夠在一根線上發送數據同時在另一根線上接收數據。其他線用于握手,但不是必須的。串口通信最重要的參數是波特率、數據位、停止位和奇偶校驗。對于兩個進行通信的端口,這些參數必須匹配。
a,波特率:這是一個衡量符號傳輸速率的參數。指的是信號被調制以后在單位時間內的變化,即單位時間內載波參數變化的次數,如每秒鐘傳送240個字符,而每個字符格式包含10位(1個起始位,1個停止位,8個數據位),這時的波特率為240Bd,比特率為10位*240個/秒=2400bps。一般調制速率大于波特率,比如曼徹斯特編碼)。通常電話線的波特率為14400,28800和36600。波特率可以遠遠大于這些值,但是波特率和距離成反比。高波特率常常用于放置的很近的儀器間的通信,典型的例子就是GPIB設備的通信。
b,數據位:這是衡量通信中實際數據位的參數。當計算機發送一個信息包,實際的數據往往不會是8位的,標準的值是6、7和8位。如何設置取決于你想傳送的信息。比如,標準的ASCII碼是0~127(7位)。擴展的ASCII碼是0~255(8位)。如果數據使用簡單的文本(標準ASCII碼),那么每個數據包使用7位數據。每個包是指一個字節,包括開始/停止位,數據位和奇偶校驗位。由于實際數據位取決于通信協議的選取,術語“包”指任何通信的情況。
c,停止位:用于表示單個包的最后一位。典型的值為1,1.5和2位。由于數據是在傳輸線上定時的,并且每一個設備有其自己的時鐘,很可能在通信中兩臺設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,并且提供計算機校正時鐘同步的機會。適用于停止位的位數越多,不同時鐘同步的容忍程度越大,但是數據傳輸率同時也越慢。
d,奇偶校驗位:在串口通信中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。對于偶和奇校驗的情況,串口會設置校驗位(數據位后面的一位),用一個值確保傳輸的數據有偶個或者奇個邏輯高位。例如,如果數據是011,那么對于偶校驗,校驗位為0,保證邏輯高的位數是偶數個。如果是奇校驗,校驗位為1,這樣就有3個邏輯高位。高位和低位不真正的檢查數據,簡單置位邏輯高或者邏輯低校驗。這樣使得接收設備能夠知道一個位的狀態,有機會判斷是否有噪聲干擾了通信或者是否傳輸和接收數據是否不同步。
RS232概述
在我們電腦上,一般都會有一個9針的串行接口,這個串行接口叫做RS232接口,它和UART通信有關聯,但是由于現在筆記本電腦不帶9針串口,所以和單片機通信越來越趨于使用USB虛擬串口。
九針串口分工頭和母頭
公頭上5下4,上5從左到右為1.2.3.4.5;下4從左到右為6.7.8.9;
母頭上5下4,上5從左到右為5.4.3.2.1;下4從左到右為9.8.7.6;
RS232接口一共有9個引腳,分別定義是:1、載波檢測DCD;2、接收數據RXD;3、發送數據TXD;4、數據終端準備好DTR;5、信號地線SG;6、數據準備好DSR;7、請求發送RTS;8、清除發送CTS;9、振鈴提示RI。我們要讓這個串口和我們單片機進行通信,我們只需要關心其中的2腳RXD、3腳TXD和5腳GND即可
雖然這三個引腳的名字和我們單片機上的串口名字一樣,但是卻不能直接和單片機對連通信,這是為什么呢?隨著我們了解的內容越來越多,我們得慢慢知道,不是所有的電路都是5V代表高電平而0V代表低電平的。對于RS232標準來說,它是個反邏輯,也叫做負邏輯。為何叫負邏輯?它的TXD和RXD的電壓,-3V~-15V電壓代表是1,+3~+15V電壓代表是0。低電平代表的是1,而高電平代表的是0,所以稱之為負邏輯。因此電腦的9針RS232串口是不能和單片機直接連接的,需要用一個電平轉換芯片MAX232來完成
這個芯片就可以實現把標準RS232串口電平轉換成我們單片機能夠識別和承受的UART0V/5V電平。從這里大家似乎慢慢有點明白了,其實RS232串口和UART串口,它們的協議類型是一樣的,只是電平標準不同而已,而MAX232這個芯片起到的就是中間人的作用,它把UART電平轉換成RS232電平,也把RS232電平轉換成UART電平,從而實現標準RS232接口和單片機UART之間的通信連接。
USB轉串口通信
隨著技術的發展,工業上還有RS232串口通信的大量使用,但是商業技術的應用上,已經慢慢的使用USB轉UART技術取代了RS232串口,絕大多數筆記本電腦已經沒有串口這個東西了,那我們要實現單片機和電腦之間的通信該怎么辦呢?
們只需要在電路上添加一個USB轉串口芯片,就可以成功實現USB通信協議和標準UART串行通信協議的轉換,在我們的開發板上,我們使用的是CH340T這個芯片
我們需要用跳線帽把中間和下邊的針短接在一起。右側的CH340T這個電路很簡單,把電源、晶振接好后,6腳和7腳的DP和DM分別接USB口的2個數據引腳上去,3腳和4腳通過跳線接到了我們單片機的TXD和RXD上去。
CH340T的電路里3腳位置加了個4148的二極管,是一個小技巧。因為STC89C52這個單片機下載程序時需要冷啟動,就是先點下載后上電,上電瞬間單片機會先檢測需要不需要下載程序。雖然單片機的VCC是由開關來控制,但是由于CH340T的3腳是輸出引腳,如果沒有此二極管,開關后級單片機在斷電的情況下,CH340T的3腳和單片機的P3.0(即RXD)引腳連在一起,有電流會通過這個引腳流入后級電路并且給后級的電容充電,造成后級有一定幅度的電壓,這個電壓值雖然只有兩三伏左右,但是可能會影響到正常的冷啟動。加了二極管后,一方面不影響通信,另外一個方面還可以消除這種不良影響。這個地方可以暫時作為了解,大家如果自己做這類電路,可以參考一下。
IO口模擬UART串口通信
UART串口波特率,常用的值是300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200等速率。IO口模擬UART串行通信程序是一個簡單的演示程序,我們使用串口調試助手下發一個數據,數據加1后,再自動返回。
串口調試助手,這里我們直接使用STC-ISP軟件自帶的串口調試助手,先把串口調試助手的使用給大家說一下,如圖11-6所示。第一步要選擇串口助手菜單,第二步選擇十六進制顯示,第三步選擇十六進制發送,第四步選擇COM口,這個COM口要和自己電腦設備管理器里的那個COM口一致,波特率按我們程序設定好的選擇,我們程序中讓一個數據位持續時間是1/9600秒,那這個地方選擇波特率就是選9600,校驗位選N,數據位8,停止位1。
串口調試助手的實質就是利用電腦上的UART通信接口,發送數據給我們的單片機,也可以把我們的單片機發送的數據接收到這個調試助手界面上。
因為初次接觸通信方面的技術,所以我把后面的IO模擬串口通信程序進行一下解釋,大家可以邊看我的解釋邊看程序,把底層原理先徹底弄懂。
變量定義部分就不用說了,直接看main主函數。首先是對通信的波特率的設定,在這里我們配置的波特率是9600,那么串口調試助手也得是9600。配置波特率的時候,我們用的是定時器T0的模式2。模式2中,不再是TH0代表高8位,TL0代表低8位了,而只有TL0在進行計數,當TL0溢出后,不僅僅會讓TF0變1,而且還會將TH0中的內容重新自動裝到TL0中。這樣有一個好處,就是我們可以把想要的定時器初值提前存在TH0中,當TL0溢出后,TH0自動把初值就重新送入TL0了,全自動的,不需要程序中再給TL0重新賦值了,配置方式很簡單,大家可以自己看下程序并且計算一下初值。
波特率設置好以后,打開中斷,然后等待接收串口調試助手下發的數據。接收數據的時候,首先要進行低電平檢測while(PIN_RXD),若沒有低電平則說明沒有數據,一旦檢測到低電平,就進入啟動接收函數StartRXD()。接收函數最開始啟動半個波特率周期,初學可能這里不是很明白。大家回頭看一下我們的圖11-2里邊的串口數據示意圖,如果在數據位電平變化的時候去讀取,因為時序上的誤差以及信號穩定性的問題很容易讀錯數據,所以我們希望在信號最穩定的時候去讀數據。除了信號變化的那個沿的位置外,其它位置都很穩定,那么我們現在就約定在信號中間位置去讀取電平狀態,這樣能夠保證我們讀的一定是正確的。
一旦讀到了起始信號,我們就把當前狀態設定成接收狀態,并且打開定時器中斷,第一次是半個周期進入中斷后,對起始位進行二次判斷一下,確認一下起始位是低電平,而不是一個干擾信號。以后每經過1/9600秒進入一次中斷,并且把這個引腳的狀態讀到RxdBuf里邊。等待接收完畢之后,我們再把這個RxdBuf加1,再通過TXD引腳發送出去,同樣需要先發一位起始位,然后發8個數據位,再發結束位,發送完畢后,程序運行到while(PIN_RXD),等待第二輪信號接收的開始。
uart模塊介紹
IO口模擬串口通信,讓大家了解了串口通信的本質,但是我們的單片機程序卻需要不停的檢測掃描單片機IO口收到的數據,大量占用了單片機的運行時間。這時候就會有聰明人想了,其實我們并不是很關心通信的過程,我們只需要一個通信的結果,最終得到接收到的數據就行了。這樣我們可以在單片機內部做一個硬件模塊,讓它自動接收數據,接收完了,通知我們一下就可以了,我們的51單片機內部就存在這樣一個UART模塊,要正確使用它,當然還得先把對應的特殊功能寄存器配置好。
51單片機的UART串口的結構由串行口控制寄存器SCON、發送和接收電路三部分構成,先來了解一下串口控制寄存器SCON。
SCON串行控制器的位分配(地址:0x98)
位:符號:復位值: 0:RI:0;1:TI:0;2:RB8:0;3:TB8:0;4:REN:0;5:SM2:0;6:SM1:0;7:SM0:0;
0位RI:接收中斷標志位,當接收電路接收到停止位的中間位置時,RI由硬件置1,必須通過軟件清零
1位TI:發送中斷標志位,當發送電路發送到停止位的中間位置時,TI由硬件置1,必須通過軟件清零。
2位RB8:模式2和3中接收到的第9位數據(很少用),模式1用來接收停止位。
3位TB8:模式2和3中要發送的第9位數據(很少用)。
4位REN:使能串行接收。由軟件置位使能接收,軟件清零則禁止接收。
5位SM2:多機通信控制位(極少用),模式1直接清零。
6位SM1和7位SM0:
這兩位共同決定了串口通信的模式0~模式3共4種模式。我們最常用的就是模式1,也就是SM0=0,SM1=1,下邊我們重點就講模式1,其它模式從略。
對于串口的四種模式,模式1是最常用的,就是我們前邊提到的1位起始位,8位數據位和1位停止位。下面我們就詳細介紹模式1的工作細節和使用方法,至于其它3種模式與此也是大同小異,真正遇到需要使用的時候大家再去查閱相關資料就行了。
在我們使用IO口模擬串口通信的時候,串口的波特率是使用定時器T0的中斷體現出來的。在硬件串口模塊中,有一個專門的波特率發生器用來控制發送和接收數據的速度。對于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的溢出值,晶振值在我們的開發板上就是11059200,12是說1個機器周期等于12個時鐘周期,值得關注的是這個16,我們來重點說明。在IO口模擬串口通信接收數據的時候,采集的是這一位數據的中間位置,而實際上串口模塊比我們模擬的要復雜和精確一些。他采取的方式是把一位信號采集16次,其中第7、8、9次取出來,這三次中其中兩次如果是高電平,那么就認定這一位數據是1,如果兩次是低電平,那么就認定這一位是0,這樣一旦受到意外干擾讀錯一次數據,也依然可以保證最終數據的正確性。
串口通信的發送和接收電路在物理上有2個名字相同的SBUF寄存器,它們的地址也都是0x99,但是一個用來做發送緩沖,一個用來做接收緩沖。意思就是說,有2個房間,兩個房間的門牌號是一樣的,其中一個只出人不進人,另外一個只進人不出人,這樣的話,我們就可以實現UART的全雙工通信,相互之間不會產生干擾。但是在邏輯上呢,我們每次只操作SBUF,單片機會自動根據對它執行的是“讀”還是“寫”操作來選擇是接收SBUF還是發送SBUF,后邊通過程序,我們就會徹底了解這個問題。
UART串口程序:
一般情況下,我們編寫串口通信程序的基本步驟如下所示:
1、配置串口為模式1。
2、配置定時器T1為模式2,即自動重裝模式。
3、根據波特率計算TH1和TL1的初值,如果有需要可以使用PCON進行波特率加倍。
4、打開定時器控制寄存器TR1,讓定時器跑起來。
這里還要特別注意一下,就是在使用T1做波特率發生器的時候,千萬不要再使能T1的中斷了。
我們先來看一下由IO口模擬串口通信直接改為使用硬件UART模塊時的程序代碼,看看程序是不是簡單了很多,因為大部分的工作硬件模塊都替我們做了。程序功能和IO口模擬的是完全一樣的。
通信實例與ASCLL碼
先拋開我們使用的漢字不談,那么我們常用的字符就包含了0~9的數字、A~Z/a~z的字母、還有各種標點符號等。那么在單片機系統里面我們怎么來表示它們呢?ASCII碼(AmericanStandardCodeforInformationInterchange,即美國信息互換標準代碼)可以完成這個使命:我們知道,在單片機中一個字節的數據可以有0~255共256個值,我們取其中的0~127共128個值賦予了它另外一層涵義
我們用字符格式發送一個小寫的a,返回一個十六進制的0x61,數碼管上顯示的也是61,ASCII碼表里字符a對應十進制是97,等于十六進制的0x61;我們再用字符格式發送一個數字1,返回一個十六進制的0x31,數碼管上顯示的也是31,ASCII表里字符1對應的十進制是49,等于十六進制的0x31。這下大家就該清楚了:所謂的十六進制發送和十六進制接收,都是按字節數據的真實值進行的;而字符格式發送和字符格式接收,是按ASCII碼表中字符形式進行的,但它實際上最終傳輸的還是一個字節數據。這個表格,當然不需要大家去記住,理解它,用的時候過來查就行了。
51單片機串口通信實例(字符串接收和發送)
#include《reg52.h》
//------------------串口通信協議-----------------//
/*
客戶端數據包格式解釋(長度恒為15):
例如:A01_fmq_01Off___#
A--------數據包的開始標記(可以為A到Z,意味著數據包可以有26種)
01-----設備代號
fmq_01Off___--------指令(長度恒為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部
#---------數據包的結束標記
服務器端數據包格式解釋(長度恒為15):
例如:A02_SenT010250#
A--------數據包的開始標記(可以為A到Z,意味著數據包可以有26種)
02-----設備代號
SenT010250--------指令(長度恒為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部
#---------數據包的結束標記
*/
char buf_string[16]; //定義數據包長度為15個字符
#define deviceID_1Bit ‘0’ //用于串口通信時,定義本地設備ID的第1位
#define deviceID_2Bit ‘2’ //用于串口通信時,定義本地設備ID的第2位
#define datapackage_headflag ‘A’ //用于串口通信時,定義數據包頭部的驗證標記
char DataPackage_DS18B20[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,‘_’,‘S’,‘e’,‘n’,‘T’,‘X’,‘X’,‘X’,‘X’,‘X’,‘X’,‘#’};
char HeartBeat[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,‘_’,‘B’,‘e’,‘a’,‘t’,‘X’,‘X’,‘X’,‘X’,‘X’,‘X’,‘#’};
//----------------------------------------------//
/*******************************
串口通信
MCU:89C52RC 11.0592MHz
//11.0592MHz 0xd0 1200bps
//12MHz 0xcc 1200bps
//11.0592MHz 0xfa 9600bps
//0xf4 11.0592MHz 0xf3 12MHz 4800bps
//均在SMOD=1的情況下(波特率倍增模式)
*******************************/
//串口發送函數
void PutString(unsigned char *TXStr)
{
ES=0;
while(*TXStr!=0)
{
SBUF=*TXStr;
while(TI==0);
TI=0;
TXStr++;
}
ES=1;
}
//串口接收函數
bit ReceiveString()
{
char * RecStr=buf_string;
char num=0;
unsigned char count=0;
loop:
*RecStr=SBUF;
count=0;
RI=0;
if(num《14) //數據包長度為15個字符,嘗試連續接收15個字符
{
num++;
RecStr++;
while(!RI)
{
count++;
if(count》130)return 0; //接收數據等待延遲,等待時間太久會導致CPU運算閑置,太短會出現“數據包被分割”,默認count=130
}
goto loop;
}
return 1;
}
//定時器1用作波特率發生器
void Init_USART()
{
SCON=0x50; //串口方式1,使能接收
TMOD|=0x20; //定時器1工作方式2(8位自動重裝初值)
TMOD&=~0x10;
TH1=0xfa; //9600bps
TL1=0xfa;
PCON|=0x80; //SMOD=1
TR1=1;
TI=0;
RI=0;
//PS=1; //提高串口中斷優先級
ES=1; //開啟串口中斷使能
}
//比較指令頭部
bit CompareCMD_head(char CMD_head[])
{
unsigned char CharNum;
for(CharNum=0;CharNum《4;CharNum++) //指令長度為10個字符
{
if(!(buf_string[CharNum+4]==CMD_head[CharNum]))
{
return 0; //指令頭部匹配失敗
}
}
return 1; //指令頭部匹配成功
}
//比較指令尾部(start:從哪里開始比較,quality:比較多少個字符,CMD_tail[]:要比較的字符串)
bit CompareCMD_tail(unsigned char start,unsigned char quality,char CMD_tail[])
{
unsigned char CharNum;
for(CharNum=0;CharNum《quality;CharNum++)
{
if(!(buf_string[start+CharNum]==CMD_tail[CharNum]))
{
return 0;
}
}
return 1;
}
bit Deal_UART_RecData() //處理串口接收數據包函數(成功處理數據包則返回1,否則返回0)
{
//PutString(buf_string);
if(buf_string[0]==datapackage_headflag&&buf_string[14]==‘#’) //進行數據包頭尾標記驗證
{
switch(buf_string[1]) //識別發送者設備ID的第1位數字
{
case ‘0’:
switch(buf_string[2]) //識別發送者設備ID的第2位數字
{
case ‘3’:
if(CompareCMD_head(“Ligt”)) //判斷指令頭部是否為“Ligt”
{
//下面是指令尾部分析
switch(buf_string[8])
{
case ‘0’:
switch(buf_string[9])
{
case ‘0’:
return 0;
case ‘1’:
if(CompareCMD_tail(10,3,“Off”)) //A03_Ligt01Off_#
{
//要執行的代碼
return 1;
}
if(CompareCMD_tail(10,3,“On_”))
{
return 1;
}
return 0;
default:
return 0;
}
case ‘1’:
default:
return 0;
}
}
if(CompareCMD_head(“SenT”))
{
}
if(CompareCMD_head(“jdq_”))
{
}
if(CompareCMD_head(“Try!”))
{
}
return 0;
default:
return 0;
}
default:
return 0;
}
}
return 0;
}
/************************
中斷函數
************************/
//串口中斷服務函數-----------
void USART() interrupt 4 //標志位TI和RI需要手動復位,TI和RI置位共用一個中斷入口
{
if(ReceiveString())
{
//數據包長度正確則執行以下代碼
Deal_UART_RecData();
}
else
{
//數據包長度錯誤則執行以下代碼
//LED1=~LED1;
}
RI=0; //接收并處理一次數據后把接收中斷標志清除一下,拒絕響應在中斷接收忙的時候發來的請求
}
/***************************
主函數
***************************/
void main()
{
EA=1;
Init_USART();
while(1)
{
//PutString(buf_string);//空格20H,回車0DH
}
}
-
單片機
+關注
關注
6035文章
44554瀏覽量
634633 -
uart
+關注
關注
22文章
1235瀏覽量
101354 -
串口通信
+關注
關注
34文章
1624瀏覽量
55507
原文標題:深入理解51單片機串口通信及通信實例
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論