串口通信概述
串口通信指串口按位(bit)發送和接收字節。盡管比按字節(byte)的并行通信慢,但是串口可以在使用一根線發送數據的同時用另一根線接收數據。
常用三種串口通信協議
1、RS-232
RS-232(ANSI/EIA-232標準)是IBM-PC及其兼容機上的串行連接標準。可用于許多用途,比如連接鼠標、打印機或者Modem,同時也可以接工業儀器儀表。用于驅動和連線的改進,實際應用中RS-232的傳輸長度或者速度常常超過標準的值。RS-232只限于PC串口和設備間點對點的通信。RS-232串口通信最遠距離是50英尺。
從計算機連出的線的截面。
RS-232針腳的功能:
數據:
TXD(pin 3):串口數據輸出(Transmit Data)
RXD(pin 2):串口數據輸入(Receive Data)
握手:
RTS(pin 7):發送數據請求(Request to Send)
CTS(pin 8):清除發送(Clear to Send)
DSR(pin 6):數據發送就緒(Data Send Ready)
DCD(pin 1):數據載波檢測(Data Carrier Detect)
DTR(pin 4):數據終端就緒(Data Terminal Ready)
地線:
GND(pin 5):地線
其它
RI(pin 9):鈴聲指示
2、RS-422
RS-422(EIA RS-422-AStandard)是Apple的Macintosh計算機的串口連接標準。RS-422使用差分信號,RS-232使用非平衡參考地的信號。差分傳輸使用兩根線發送和接收信號,對比RS-232,它能更好的抗噪聲和有更遠的傳輸距離。在工業環境中更好的抗噪性和更遠的傳輸距離是一個很大的優點。
3、RS-485
RS-485(EIA-485標準)是RS-422的改進,因為它增加了設備的個數,從10個增加到32個,同時定義了在最大設備個數情況下的電氣特性,以保證足夠的信號電壓。有了多個設備的能力,你可以使用一個單個RS-485口建立設備網絡。出色抗噪和多設備能力,在工業應用中建立連向PC機的分布式設備網絡、其他數據收集控制器、HMI或者其他操作時,串行連接會選擇RS-485。RS-485是RS-422的超集,因此所有的RS-422設備可以被RS-485控制。RS-485可以用超過4000英尺的線進行串行通行。
串口的基本結構
SBUF:51單片機中的特殊寄存器,串行數據緩沖器(一個接收一個發送),兩個其實是共用的一個地址99H,但是兩個在物理上面是分開的。
當發送使用時,就采用SBUF=XXX; (XXX為需要傳送的數據)
當接收使用時,采用XXX=SBUF;
記得因為是串行的所以傳輸都是一位一位進行的。
T1溢出率:T1計時器的溢出頻率(就是計時器每次低位計滿向高位進位時間的倒數)
用處:用于計算波特率(每秒傳輸二進制代碼的位數)
串口通信方式
并行
適合短距離通信,并行通信控制簡單、相對傳輸速度快(8位一起傳輸)。
串行
只能一位一位的傳送。
同步(了解)
建立發送方時鐘對接收方時鐘的直接控制,使雙方達到完全同步。此時,傳輸數據的位之間的距離均為“位間隔”的整數倍,同時傳送的字符間不留間隙。
發送方對接收方的同步可以通過外同步和自同步
異步(常)
以字符(構成的幀)為單位進行傳輸。數據位從低到高傳送。
格式:
這里的空閑時間是任意的。
單片機C語言之串口通信協議
現在我們要做一個實驗,將一個字節從51單片機發送到電腦串口調試助手上。這個實驗的目的是為了掌握串口通信協議的收發過程。
虛擬串口
實驗一、虛擬串口實驗
一般單片機都有專門的串口引腳,51里面分別是P3.0和P3.1,這些引腳擁有串口的硬件電路,因此使用它們并不需要設置信號的發送停止。為了掌握協議,我們使用其他的引腳來模擬串口,所以也叫虛擬串口。這里我們選用P1.0,然而注意到我們51單片機要發送數據給電腦,必須經過一個串口轉USB設備(即TTL電平轉換為RS232電平),而限于我們的開發板只有P3.0與P3.1連接到了串口轉USB設備,所以我們可以將P1.0短接到P3.1 。 下圖是這個串口轉USB的原理圖。
代碼如下:
#include “reg51.h”
/*
將P1.0虛擬成串口發送腳TX
以9600bit/s的比特率向外發送數據
因為波特率是 9600bit/s
所以me發送一位的時間是 t=1000000us/9600=104us
*/
sbit TX=P3^1; //P1^0 output TTL signal, need to transferred to rs232 signal, can be connected to P3^1
#define u16 unsigned int //宏定義
#define u8 unsigned char
u8 sbuf;
bit ti=0;
void delay(u16 x)
{
while(x--);
}
void Timer0_Init()
{
TMOD |= 0x01;
TH0=65440/256;
TH0=65440%256;
TR0=0;
}
void Isr_Init()
{
EA=1;
ET0=1;
}
void Send_Byte(u8 dat)
{
sbuf=dat;//通過引入全局變量sbuf,可以保存形參dat
TX=0; //A 起始位
TR0=1;
while(ti==0); //等待發送完成
ti=0; //清除發送完成標志
}
void TF0_isr() interrupt 1 //每104us進入一次中斷
{
static u8 i; //記錄進入中斷的次數
TH0=65440/256;
TL0=65440%256;
i++;
if(i》=1 && i《=8)
{
if((sbuf&(1《《(i-1)))==0) // (sbuf&(1《《(i-1)))表示取出i-1位
{
TX=0;
}
else
{
TX=1;
}
}
if(i==9) //停止位
{
TX=1;
}
if(i==10)
{
TR0=0;
i=0;
ti=1; //發送完成
}
}
void main()
{
TX=1; //使TX處于空閑狀態
Timer0_Init();
Isr_Init();
while(1)
{
Send_Byte(65); //0x41
delay(60000);
}
}
實驗引入了定時器0來控制發送線上的各個位的保持時間。首先main函數進入,TX置1則使發送線處于空閑,這時候發送方和接受方都處于空閑。接下來初始化定時器0,TR0置0表示還不要啟動定時器0。接著中斷系統初始化,此時中斷系統已經開啟。進入while循環,先進Send_Byte()函數,將65傳給形參dat,dat再將65賦值給sbuf,到這里準備工作就做好了。接著TX置0,這個是起始位,要保持這個起始位104us。于是就啟動定時器TR0置1,計時器開始計數。當第一次溢出的時候,也就是過了104us,進入中斷,同時接收方也偵測到了這個突然被拉低的信號,于是迅速啟動自己的定時器。進入中斷子函數后,先是重裝定時器初值,然后i加1,也就是當i=1時,就應該發送數據的最低位了,總共有8位數據,所以使用條件語句if(i》=1 && i《=8)來判斷是否發送完數據位。然后再通過if(i==9) 來發送停止位,最后當i=10時,也就是發送完了,這時候要關閉定時器(那么程序也就),同時i置0,ti置1(才能跳出while(ti==0)循環),最后將ti置0,保證下次要發送字節時讓程序停留在while(ti==0)。
片上串口
以上說的是虛擬串口,上文中談到與串口相關的引腳P3.0與P3.1,事實上51單片機自帶片上串口,那這個串口又該怎么使用呢?
片上串口支持同步模式與異步模式。簡單來說同步模式就是指有時鐘線,而異步模式無時鐘線。這里的時鐘線是指在同步通信時,用一根線專門傳輸時鐘信號,這個信號用來與要發送的每一位保持同步,這樣就避免了例如異步通信中因為采用定時器而引入的時間誤差。
片上串口還支持8位模式和9位模式。如下圖所示
其中D0-D7是一個字節的8個位。9位模式只是多了一個位TB8,這個TB8的作用是奇偶校驗或多機通信。奇偶校驗原理這不加分析。多機通信時比如主機只發送數據給網絡中的一臺地址為0x02的設備,這時候先讓TB8為1,前面的D0-D7則為地址即0x02,之后再讓TB8為0,前面的D0-D7則為數據了。
上面設置了片上串口的模式,另外還要設置串口的波特率。
片上串口的波特率等于定時器1工作在方式2時溢出率的32分頻。如果要定時器1工作在方式2,那么TMOD=0x20。另外要保證為32分頻,我們還必須設置計數器初值。設晶振為11.0592Mhz,則定時器的計數脈沖為F=f/12,則定時器每計一個脈沖的時間為T=12/f。又令計數器的起點為x,則溢出一次要計的脈沖數為(256-x)。所以在計數起點為x時,溢出一次的時間為t=12/f*(256-x)。則對應的溢出率為1/t=f/(12*(256-x))。對應的波特率就為b=f/(384*(256-x))。
x=256-f/(384*b)
其中f為晶振頻率,b為希望的波特率,x為定時器的計數起點TH1的值。
例如當晶振為11.0592M,希望波特率為9600bit/s,則TH1=253。題外話,我們同樣可以演算出在其他常用波特率情況下,TH1始終為一個整數。這里也就解釋了為什么51里面選用了11.0592M的晶振而不是12M,這樣就保證了串口的時序更加準確,雖然犧牲了定時器的準確度。
實驗二,片外串口發送一個字節。
好了現在開始我們的實驗之旅。直接看代碼吧。
#include “reg51.h”
#define u16 unsigned int
#define u8 unsigned char
void delay(u16 x)
{
while(x--);
}
void Uart_Init() //串口初始化
{
SCON=0x50; //8位異步模式
TMOD|=0x20; //定時器1工作方式2
TH1=253;//9600bit/s
TR1=1;
}
void Send_Byte(u8 dat)
{
SBUF=dat; //啟動發送,只需要把發送內容給SBUF這個寄存器
while(TI==0); //等待發送完成,因為TI為1時表示在發送停止位
TI=0;
}
void main()
{
Uart_Init();
while(1)
{
Send_Byte(‘m’);
delay(60000);
}
}
實驗二較之實驗一,代碼減少了很多,而且不用考慮繁瑣的位發送時序。只需要明白各個寄存器SCON,TMOD,TCON,SBUF的用法。TI是SCON中的第一位,為發送中斷請求標志位。在本方式中,在停止位開始發送時由內部硬件置位,響應中斷后TI必須又軟件清零。
實驗三、片上串口發送一個字符串
上面介紹了如何發送一個字節,那如何發送一個字符串甚至文本呢?這里我們首先介紹下字符串的概念。
字符串:從存儲器的某個地址開始,連續存放多個字符的ASCII碼,并且在最后一個字符的后面存放一個0,這段連續的內存空間就叫字符串,最后的0叫字符串的結束符。注意這里的0和加單引號的0不是一個概念,加單引號的0是指0的ASCII碼。
數組與字符串的關系:字符串是數組的一種特殊情況,數組在特定條件下可當做字符串用。C語言用雙引號描述一個字符串,如“abcd”。
下面我們通過一個實驗來展示如何發送字符串。我們實驗的目標是打印字符串“Hello World ! 第一!”到打印機。直接上代碼。
#include “reg51.h”
#define u16 unsigned int
#define u8 unsigned char
void delay(u16 x)
{
while(x--);
}
void Uart_Init() //串口初始化
{
SCON=0x50; //8位異步模式
TMOD|=0x20; //定時器1工作方式2
TH1=253;//9600bit/s
TR1=1;
}
void Send_Byte(u8 dat) //串口發送一個字節
{
SBUF=dat; //啟動發送,只需要把發送內容給SBUF這個寄存器
while(TI==0); //等待發送完成,因為TI為1時表示在發送停止位
TI=0;
}
void Send_String(u8 *str) //發送一個字符串 *str為字符串第一個字符的地址
{
abc: //標號
if(*str != 0)
{
Send_Byte(*str);
str++;
goto abc;
}
}
void main()
{
Uart_Init();
while(1)
{
Send_String(“Hello World! 第一!”);
Send_Byte(10);
delay(60000);
delay(60000);
}
}
實驗效果
評論
查看更多