咸陽網(wǎng)站建設(shè)學(xué)校代發(fā)軟文
學(xué)習(xí)使用的開發(fā)板:STC89C52RC/LE52RC
編程軟件:Keil5
燒錄軟件:stc-isp
開發(fā)板實圖:
文章目錄
- 串口
- 硬件電路
- UART
- 串口相關(guān)寄存器
- 編碼
- 單片機通過串口發(fā)送數(shù)據(jù)
- 電腦通過串口發(fā)送數(shù)據(jù)控制LED燈
串口
- 串口是一種應(yīng)用十分廣泛的通訊接口,串口成本低、容易使用、通信線路簡單,可實現(xiàn)兩個設(shè)備的互相通信
- 單片機的串口可以使單片機與單片機、單片機與電腦、單片機與各式各樣的模塊互相通信,極大的擴展了單片機的應(yīng)用范圍,增強了單片機系統(tǒng)的硬件實力
51單片機內(nèi)部自帶UART
(Universal Asynchronous Receiver Transmitter,通用異步收發(fā)器
),可實現(xiàn)單片機的串口通信
硬件電路
簡單雙向串口通信有兩根通信線(發(fā)送端TXD
(Transmit Exchange Data)和接收端RXD
(Receive Exchange Date))
TXD和RXD要交叉連接
,設(shè)備1的TXD連接設(shè)備2的RXD,設(shè)備1的RXD連接設(shè)備2的TXD
當(dāng)只要單向傳輸數(shù)據(jù)時,可以只有一根通信線
GND是一定要連接的,若兩個設(shè)備都可以各自供電,則不需要連接VCC
當(dāng)電平標(biāo)準(zhǔn)
不一致時,還需要加電平轉(zhuǎn)換芯片
電平標(biāo)準(zhǔn)
電平標(biāo)準(zhǔn)是數(shù)據(jù)1和數(shù)據(jù)0的表達方式,是傳輸線纜中人為規(guī)定的電壓與數(shù)據(jù)的對應(yīng)關(guān)系,串口常用的電平標(biāo)準(zhǔn)有如下三種:
- TTL電平:+5V表示1,0V表示0,一般用于單片機
- RS232電平:-3 ~ -15V 表示1,+3 ~ +15V 表示0,一般用于電腦
- RS485電平:兩線壓差 +2 ~ +6V 表示1,-2 ~ -6V 表示0(差分信號),一般用于CAN總線
前兩個通信距離都較近,一般只有十幾米,距離過遠傳輸?shù)臄?shù)據(jù)就很容易出錯;而RS485
通信距離遠,信號可靠性高,傳輸距離可達1KM以上
相關(guān)術(shù)語
- 全雙工:通信雙方可以在同一時刻互相傳輸數(shù)據(jù)
- 半雙工:通信雙方可以互相傳輸數(shù)據(jù),但同時間只能有一段發(fā)送,另一端接收,必須分時復(fù)用一根數(shù)據(jù)線
- 單工:通信雙方只能一方發(fā)送到另一方,不能反向傳輸。比如遙控器控制電視,只能遙控器向電視發(fā)送數(shù)據(jù)
- 異步:通信雙方各自約定通信速率
- 同步:通信雙方靠一根時鐘線來約定通信速率
數(shù)據(jù)傳輸是依靠高低電平的,也就是電平協(xié)議。比如如下兩個數(shù)據(jù)
看電平都是從高電平變?yōu)榈碗娖?#xff0c;但是如果通信速率不一樣,則獲取的數(shù)據(jù)不一樣
通信速率快,10可能會被解析為1100;通信速率慢,1100也可能會被解析為10。
所以約定好通信速率很重要
- 總線:連接各個設(shè)備的數(shù)據(jù)傳輸線路(類似一條馬路,把路邊的住戶連接起來,使住戶可以相互交流)
常見通信接口比較
- UART:為本節(jié)學(xué)習(xí)的串口通信接口
- I2C:板子上的
C24C02
使用該串口 - SPI:板子上的
DS1302
使用非標(biāo)準(zhǔn)SPI - 1-Wire:板子上的
DS18B20
使用該串口
常見的還有CAN總線
和USB
,CAN總線常用于汽車領(lǐng)域
UART
51單片機的UART
STC89C52有1個UART,RXD和TXD分別和P3.0和P3.1這兩個I/O口共用同一個引腳
STC89C52的UART有四種工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可變(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可變
串口參數(shù)及時序圖
波特率
:串口通信的速率(發(fā)送和接收各數(shù)據(jù)位的間隔時間)- 校驗位:用于數(shù)據(jù)驗證,一定程序可以知道數(shù)據(jù)是否錯誤,也需要雙方提前協(xié)商統(tǒng)一校驗。常用的校驗如01校驗,奇偶校驗。
9位UART就是多了一位校驗位
- 停止位:用于數(shù)據(jù)幀間隔,發(fā)送多個數(shù)據(jù),如何間隔兩個數(shù)據(jù),就使用停止位
波特率就是上述異步雙方要約定好的如何對數(shù)據(jù)進行采樣
串口收發(fā)數(shù)據(jù),都是從低位開始
串口模式圖
簡單的串口模式圖
UART是集成在單片機內(nèi)部的,通過TXD引腳發(fā)送數(shù)據(jù),RXD引腳接收數(shù)據(jù)
UART可以分為三個部分,中間為定時器T1
——控制波特率;左側(cè)綠框的SBUF
用于收發(fā)數(shù)據(jù);右側(cè)為中斷系統(tǒng)
SBUF:串口數(shù)據(jù)緩存寄存器,物理上是兩個獨立的寄存器,但占用相同的地址。寫操作時,寫入的是發(fā)送寄存器,讀操作時,讀出的是接收寄存器
完整的串口和中斷系統(tǒng)模式圖如下:
下面通過介紹相關(guān)寄存器來講述串口通信的原理
串口相關(guān)寄存器
串行口控制寄存器SCON和PCON
STC89C52系列單片機的串行口設(shè)有兩個控制寄存器:串行控制寄存器SCON和波特率選擇特殊功能寄存器PCON
PCON
電源控制寄存器,可能是電源控制還有剩下比特位,所以波特率選擇和幀錯誤控制位也集成其中,減少資源消耗
格式如下:
我們只要關(guān)注SMOD
和 SMOD0
即可
- SMOD:
波特率選擇位
。當(dāng)軟件置SMOD = 1時,使串行通信方式1、2、3的波特率加倍;SMOD = 0,則不加倍。復(fù)位時SMOD = 0 - SMOD0:
幀錯誤檢測有效控制位
。當(dāng)SMOD = 1,SCON寄存器中的SM0/FE
位于 FE(幀錯誤檢測)功能;當(dāng)SMOD0 = 0,SCON的SM0/FE
用于 和SM1
組合指定串行口的工作模式。復(fù)位時 SMOD0 = 0
SCON
用于選擇串行通信的工作方式和某些控制功能,格式如下:
- SM0/FE:當(dāng)PCON寄存器的
SMOD0/FE
= 1時,該位用于幀錯誤檢測。當(dāng)檢測到一個無效停止位時,通過UART接收器設(shè)置該位,必須由軟件清零;當(dāng)SMOD0/FE
= 0,該位和SM1
組合指定工作模式
-
REN:
允許/禁止串行接收控制位
。由軟件置位,若REN = 1即允許串行接收數(shù)據(jù);REN = 0則禁止接收 -
SM2:
允許方式2或方式3多機通信控制位
。 -
TB8:
在方式2或方式3,為要發(fā)送的第9位數(shù)據(jù)
,按需要由軟件置位或清0??捎米鲾?shù)據(jù)的校驗位或多機通信中表示地址幀/數(shù)據(jù)幀的標(biāo)志位。 -
RB8:
在方式2或方式3,是接收到的第9位數(shù)據(jù)
。在方式1,若SM2=0,則RB8是接收到的停止位。方式0不用RB8。 -
TI:
發(fā)送中斷請求標(biāo)志位
。在方式0,當(dāng)串行發(fā)送數(shù)據(jù)第8位結(jié)束時,由內(nèi)部硬件置TI = 1,向主機請求中斷,響應(yīng)中斷后必須用軟件復(fù)位,即TI = 0。在其他方式中,則在停止位開始發(fā)送時由內(nèi)部硬件置位,必須用軟件復(fù)位TI = 0
-
RI:
接收中斷請求標(biāo)志位
。在方式0,當(dāng)串行接收到第8位結(jié)束時由內(nèi)部硬件自動置位 RI = 1 ,向主機請求中斷,響應(yīng)中斷后必須用軟件復(fù)位,即RI=0。在其他方式中,串行接收到停止位的中間時刻由內(nèi)部硬件置位,即RI=1,必須由軟件復(fù)位,即RI=0
IE
中斷允許寄存器
其中我們只關(guān)注EA
和 ES
- EA:CPU的總中斷允許控制位,EA = 1,CPU開放中斷;EA = 0,CPU屏蔽所有中斷請求
- ES:串行口中斷允許位,ES = 1,允許串行口中斷;ES = 0,禁止串行口中斷
注意:
接收中斷和發(fā)送中斷共用一個中斷,在中斷處理函數(shù)中還需要通過RI
和 TI
的置位判斷本次中斷是接收中斷還是發(fā)送中斷
最后我們回歸模式圖,講解一下串口通信的流程
發(fā)送數(shù)據(jù)
通過總線將數(shù)據(jù)寫入SBUF
,定時器1控制波特率。通過TXD發(fā)送數(shù)據(jù),當(dāng)發(fā)送數(shù)據(jù)結(jié)束時(方式0為發(fā)完8位數(shù)據(jù),其他方式為發(fā)送停止位時),將 TI = 1,發(fā)送中斷請求
接收數(shù)據(jù)
RXD接收數(shù)據(jù),通過定時器1控制波特率,對接收數(shù)據(jù)進行采樣,存放在SBUF
,當(dāng)接收數(shù)據(jù)結(jié)束(方式0當(dāng)串行發(fā)送數(shù)據(jù)第8位結(jié)束時,在其他方式中,則在停止位開始發(fā)送時由內(nèi)部硬件置位),將 RI = 1,發(fā)出中斷請求
注意
:TI
和 RI
都需要由軟件置0
編碼
經(jīng)過上述學(xué)習(xí),我們已經(jīng)對串口有了一定的了解,接下來就是實現(xiàn)串口通信
單片機通過串口發(fā)送數(shù)據(jù)
我們使用UART串口通信,首先要進行初始化,如:選擇工作方式,初始化中斷系統(tǒng),設(shè)置波特率,初始化定時器
選擇工作模式
涉及到SCON
和 PCON
首先是PCON的SMOD0
,當(dāng) SMOD0 = 0 時,SMOD 的 SM0 才會被用來選擇工作方式
其次,我們選擇 8位UART,波特率可變模式,即方式1,SCON的SM0 = 0,SM1 = 1
最后,如果要允許串口接收數(shù)據(jù),還需要置SCON的REN = 1
初始化中斷系統(tǒng)
首先,初始化串口收發(fā)數(shù)據(jù)的中斷請求標(biāo)志位,SCON的 TI
和 RI
,由硬件置1,我們初始化時清零即可:TI = 0, RI = 0
到此,SCON的設(shè)置就結(jié)束了
總結(jié)一下,SM0 = 0, SM1 = 1, REN = 1/0, TI = 0, RI = 0,其他默認(rèn)為0即可,所以SCON = 0x40/0x50
然后是中斷開關(guān)
ES = 1, EA = 1
SCON = 0x50; //選擇工作方式 & 允許串口接收數(shù)據(jù)
PCON |= 0x80; //使SM0為選擇工作方式
//中斷開關(guān)
ES = 1; //串口中斷開關(guān)
EA = 1; //總中斷開關(guān)
初始化定時器
初始化定時器可參看【51單片機】定時器
此處定時器1選擇工作模式2——8位自動重裝
8位自動重裝
一次只對TL1或TH1計數(shù)加一
當(dāng)一個溢出后,直接使用另一個計數(shù)單元的初值
//設(shè)置定時器1
TMOD &= 0x0F; //高4位清零
TMOD |= 0x20; //0010,模式3——8位自動重載
TR1 = 1; //啟用定時器T1
ET1 = 0; //禁止定時器T1中斷
//定時器初值
TL1 = 0xF3; //設(shè)定定時初值
TH1 = 0xF3; //設(shè)定定時器重裝值
設(shè)置波特率
我們設(shè)置波特率為4800
設(shè)置波特率需要通過設(shè)置定時器1的初始值
TL1 = 0xF3; //設(shè)定定時初值
TH1 = 0xF3; //設(shè)定定時器重裝值
講解一下為什么定時器初值是這個
假設(shè)系統(tǒng)頻率為12MHz,使用12T模式,則定時器頻率為12 / 12 = 1MHz,即每1us,計數(shù)單元加1。
使用8位自動重裝,256時會溢出,0xF3 = 243,256 - 243 = 13。所以定時器溢出需要13us
溢出率:1 / 13 = 0.07692
使用SMOD = 1,波特率加倍(不除2)
還需要 0.07692 / 16 = 0.0048076923MHz
轉(zhuǎn)化為Hz:4807.6923Hz,這個就是波特率
會存在一定誤差
也可參看如下計算
到此,串口的初始化就完成了
完整代碼如下:
/*** @brief 初始化串口* @parm 無* @retval 無*/
void UART_Init()
{//SCON高4位分別為SM0、SM1、SM2、REN//SM0和SM1控制串口模式,選擇01——8位UART,波特率可變//REN接收使能,REN = 0禁止接收,REN = 1允許接收//所以設(shè)置0101 0000SCON = 0x50;//PCON包含波特率和電源設(shè)置//前兩位為SMOD和SMOD0//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍//SMOD0是幀錯誤的,此處不用//所以設(shè)置1000 0000PCON |= 0x80;//設(shè)置定時器1TMOD &= 0x0F; //高4位清零TMOD |= 0x20; //0010,模式3——8位自動重載TR1 = 1; //啟用定時器T1ET1 = 0; //禁止定時器T1中斷 //定時器初值TL1 = 0xF4; //設(shè)定定時初值TH1 = 0xF4; //設(shè)定定時器重裝值//中斷開關(guān)ES = 1; //串口中斷開關(guān)EA = 1; //總中斷開關(guān)
}
博主的單片機系統(tǒng)頻率為11.0592MHz
可以使用STC-ICP
生成波特率設(shè)置代碼
注意:
配置一定要選擇正確;代碼中的AUXR寄存器
為高版本單片機才有的,低版本不認(rèn)識這個寄存器,可以直接刪掉
串口發(fā)送數(shù)據(jù)通過賦值SBUF
,數(shù)據(jù)發(fā)送完后,硬件置位TI = 1
,需要我們手動對TI清零
代碼如下:
/*** @brief 通過串口發(fā)送一個字節(jié)數(shù)據(jù)* @parm Byte:要發(fā)送字節(jié)數(shù)據(jù)* @retval 無*/
void UART_SendByte(unsigned char Byte)
{SBUF = Byte;while(TI == 0);//數(shù)據(jù)發(fā)送完,硬件置1TI = 0; //軟件置0
}
模塊化編程,完整代碼如下:
延時模塊——控制串口發(fā)送數(shù)據(jù)速率
Delay.h
#ifndef __DELAY_H__
#define __DELAT_H__void Delayms(unsigned int xms);//等待指定毫秒#endif
Delay.c
#include <INTRINS.h>
/*** @brief 延遲一定時間* @parm 延遲的時間,單位是毫秒,范圍:0 ~ 65535* @retval 無*/
void Delayms(unsigned int xms) //@11.0592MHz
{while(xms--){unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);}
}
UART串口模塊
UART.h
#ifndef __UART_H__
#define __UART_H__void UART_Init();
void UART_SendByte(unsigned char Byte);#endif
UART.c
#include <REGX52.H>
/*** @brief 初始化串口* @parm 無* @retval 無*/
void UART_Init()
{//SCON高4位分別為SM0、SM1、SM2、REN//SM0和SM1控制串口模式,選擇01——8位UART,波特率可變//REN接收使能,REN = 0禁止接收,REN = 1允許接收//所以設(shè)置0101 0000SCON = 0x50;//PCON包含波特率和電源設(shè)置//前兩位為SMOD和SMOD0//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍//SMOD0是幀錯誤的,此處不用//所以設(shè)置1000 0000PCON |= 0x80;//設(shè)置定時器1TMOD &= 0x0F; //高4位清零TMOD |= 0x20; //0010,模式3——8位自動重載TR1 = 1; //啟用定時器T1ET1 = 0; //禁止定時器T1中斷 //定時器初值TL1 = 0xF4; //設(shè)定定時初值TH1 = 0xF4; //設(shè)定定時器重裝值//中斷開關(guān)ES = 1; //串口中斷開關(guān)EA = 1; //總中斷開關(guān)
}
/*** @brief 通過串口發(fā)送一個字節(jié)數(shù)據(jù)* @parm Byte 要發(fā)送字節(jié)數(shù)據(jù)* @retval 無*/
void UART_SendByte(unsigned char Byte)
{SBUF = Byte;while(TI == 0);//數(shù)據(jù)發(fā)送完,硬件置1TI = 0; //軟件置0
}///**
// * @brief 接收數(shù)據(jù) 模版
// * @parm 無
// * @retval 無
// */
//void UART_Routine() interrupt 4
//{
// if(RI == 1)//檢測是否是接收數(shù)據(jù)中斷
// {
// RI = 0;//軟件置0
// }
//}
主程序——每隔一秒通過串口發(fā)送遞增數(shù)據(jù)
#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/*** @brief 通過串口每隔1s發(fā)送遞增的數(shù)據(jù) 范圍:0 ~ 255* @parm 無* @retval 無*/
void SendIncreasingNum()
{static unsigned char num;UART_SendByte(num++);Delayms(1000);
}void main()
{UART_Init();while(1){SendIncreasingNum();}
}
使用STC-IST
的 串口助手 查看效果
注意
:下面一行的配置要正確
電腦通過串口發(fā)送數(shù)據(jù)控制LED燈
電腦發(fā)送數(shù)據(jù)給單片機需要USB轉(zhuǎn)串口,自帶的USB線就已經(jīng)實現(xiàn)了這一轉(zhuǎn)換,所以我們直接編寫單片機通過串口接收數(shù)據(jù)的邏輯即可。
串口接收數(shù)據(jù)會存放在SBUF
,接收完畢后會將RI置1
,發(fā)出中斷請求,中斷號為4,然后需要手動清零RI
代碼如下:
void UART_Routine() interrupt 4
{if(RI == 1)//檢測是否是接收數(shù)據(jù)中斷{P2 = SBUF;RI = 0;//軟件置0}
}
注意
:P2
寄存器用于控制LED亮滅,為0亮起,為1熄滅
還可以將數(shù)據(jù)重新返回給電腦,同樣使用Delay
和 UART
模塊,只有main.c不同
main.c
#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/*** @brief 接收數(shù)據(jù),亮相應(yīng)的燈,并返回數(shù)據(jù)* @parm 無* @retval 無*/
void UART_Routine() interrupt 4
{if(RI == 1)//檢測是否是接收數(shù)據(jù)中斷{P2 = SBUF;UART_SendByte(SBUF);RI = 0;//軟件置0}
}
void main()
{UART_Init();while(1){}
}
效果如下:
我們通過串口助手,發(fā)送 0xAA = 1010 1010
LED燈效果如下:
以上就是本篇博客的所有內(nèi)容,感謝你的閱讀
如果覺得本篇文章對你有所幫助的話,不妨點個贊支持一下博主,拜托啦,這對我真的很重要。