|
STC官方的模擬串口例程,看了很久,做了一點(diǎn)改動(dòng)。但是核心還是他家的。
/*
STC MCU STC15F204EA 模擬串口的示例程序
原始代碼來自 STCMCU的官方例程。
****************************************************************************
*** 重要說明:本程序不適用于硬件串口的產(chǎn)品,僅為無硬件UART的產(chǎn)品使用。******
****************************************************************************
本例程同時(shí)Timer0中斷,同步完成接受和發(fā)送的工作,屬于雙工首發(fā)。
示例中,由串口將收到的信息在下一個(gè)發(fā)送周期轉(zhuǎn)發(fā)出去。
示例中的收發(fā)波特率可變,其它設(shè)置為8位數(shù)據(jù)位,無奇偶校驗(yàn)位,1位停止位。
*/
//以下參數(shù)設(shè)置信息來自 STCMCU 官方例程。
//特別說明, MCU工作頻率FOSC設(shè)置為11.0592MHz。如果使用12.000MHz,24.000時(shí),會(huì)有頻差。
#include "reg51.h"
#include "intrins.h"
//波特率設(shè)定值計(jì)算公式:BAUD = 65536 - FOSC/3/BAUDRATE/M (1T:M=1; 12T:M=12)
//為了確保通信質(zhì)量,(FOSC/3/BAUDRATE) 必須大于 98,推薦大于110。
//這里每3次中斷,做一次輸出檢查或輸出。一般要求做連續(xù)16次采樣,取其開始,中間,
//結(jié)束3個(gè)數(shù)據(jù),如果穩(wěn)定,則可以認(rèn)為數(shù)據(jù)有效。
//3*16*2(?)=96,加上定時(shí)信號的不穩(wěn)定性,在98以上合理。 ???
//因此,中斷發(fā)生的頻率是波特率信號的3倍以上。
//按照主頻為11059200計(jì)算,如果選擇信號比大于110,則最大波特率為33512.
//如果選擇98,則為37616。選擇38400的通訊速率都不能保證接收的正確性。
//因此,最可靠的數(shù)據(jù)接收波特率應(yīng)該是19200。
//當(dāng)時(shí)鐘頻率提升到33.1174MHz時(shí),最大波特率為112848,依然小于115200. 使用115200時(shí),
//波特率誤差可能高達(dá)2%,出現(xiàn)數(shù)據(jù)差錯(cuò)。因此最大通訊速率選擇 57600.
//#define BAUD 0xF400 // 1200bps @ 11.0592MHz
//#define BAUD 0xFA00 // 2400bps @ 11.0592MHz
//#define BAUD 0xFD00 // 4800bps @ 11.0592MHz
//#define BAUD 0xFE80 // 9600bps @ 11.0592MHz
//#define BAUD 0xFF40 //19200bps @ 11.0592MHz
//#define BAUD 0xFFA0 //38400bps @ 11.0592MHz
//#define BAUD 0xEC00 // 1200bps @ 18.432MHz
//#define BAUD 0xF600 // 2400bps @ 18.432MHz
//#define BAUD 0xFB00 // 4800bps @ 18.432MHz
//#define BAUD 0xFD80 // 9600bps @ 18.432MHz
//#define BAUD 0xFEC0 //19200bps @ 18.432MHz
//#define BAUD 0xFF60 //38400bps @ 18.432MHz
//#define BAUD 0xE800 // 1200bps @ 22.1184MHz
//#define BAUD 0xF400 // 2400bps @ 22.1184MHz
//#define BAUD 0xFA00 // 4800bps @ 22.1184MHz
//#define BAUD 0xFD00 // 9600bps @ 22.1184MHz
//#define BAUD 0xFE80 //19200bps @ 22.1184MHz
//#define BAUD 0xFF40 //38400bps @ 22.1184MHz
#define BAUD 0xFF80 //57600bps @ 22.1184MHz
//后續(xù)程序段中,對于不同的設(shè)置,可能有如下的條件編譯指令
//在不同的通訊參數(shù)配置下,可能需要重新編譯,以降低代碼量。
#define DATALEN 8 //8位數(shù)據(jù)位
#define NOPARITY //無校驗(yàn)位
//#define EVENPARITY //偶校驗(yàn)
//#define ODDPARITY //奇校驗(yàn)
#define STOP1BIT //1位停止位
//#define STOP1P5BITS //停止位1.5位
//#define STOP2BITS //停止位2位
#ifdef EVENPARITY //偶校驗(yàn)
#endif
#ifdef ODDPARITY //奇校驗(yàn)
#endif
#ifdef NOPARITY //無校驗(yàn)
#endif
#ifdef STOP1BIT //停止位1位
#endif
#ifdef STOP1P5BITS //停止位1.5位
#endif
#ifdef STOP2BITS //停止位2位
#endif
#define YES 1
#define NO 0
sfr AUXR = 0x8E;
sbit RXB = P3^0; //UART RX 管腳
sbit TXB = P3^1; //UART TX 管腳
//數(shù)據(jù)發(fā)送相關(guān)的中間數(shù)據(jù)
unsigned char nDataToSend; //要發(fā)送的字符
unsigned char nTmpTxData; //發(fā)送過程的臨時(shí)數(shù)據(jù)
unsigned char nTransmitCount; //輸出定時(shí)計(jì)數(shù)器。每三次輸出一個(gè)數(shù)據(jù)。
unsigned char nTxBitCount; //發(fā)送剩余位計(jì)數(shù)器
unsigned char pTx; //發(fā)送數(shù)據(jù)指針
bit tData; //***新增加:準(zhǔn)備發(fā)送的位數(shù)據(jù)(使用CY作為中間數(shù)據(jù)位不可靠,可能會(huì)被修改)
bit bIsTransmitting;
bit bTransmitCompleted;
//數(shù)據(jù)接收相關(guān)的中間數(shù)據(jù)
unsigned char nReceiveCount; //輸入電平多次采樣計(jì)數(shù)器
//說明:這里默認(rèn)的發(fā)送和接收過程都是在3次中斷發(fā)生后采樣輸入信號或發(fā)送數(shù)據(jù)。
unsigned char nReceivedData; //最終接收的數(shù)據(jù)
unsigned char nTmpRcvData; //接收過程的臨時(shí)數(shù)據(jù)(未完成的數(shù)據(jù))
unsigned char nRxBitCount; //接收剩余位計(jì)數(shù)器
unsigned char pRx; //接收數(shù)據(jù)指針
bit bIsReceiving;
bit bReceiveCompleted;
//數(shù)據(jù)接收緩沖區(qū)的定義
#define BUFFLEN 0x10 //字符緩沖區(qū)長度
#define CYCLEMASK 0x0F //緩沖區(qū)循環(huán)掩模字碼,=BUFFLEN-1。如長度為0x10,則掩模為0x0F,如果長度為0x20,則掩模為0x1F。
unsigned char buf[BUFFLEN];
void UartInit(void);
//基本輸入輸出函數(shù),其它類型數(shù)據(jù)的處理,可以通過標(biāo)準(zhǔn)庫的轉(zhuǎn)換實(shí)現(xiàn),如sprintf,atoi等。
void SendChar(unsigned char c); //發(fā)送一個(gè)字符
void SendStr(char *str); //發(fā)送一個(gè)字符串(長度不能超過BUFFLEN)
unsigned char uGetChar(void); //獲取一個(gè)字符
char *uGetStr(void); //獲取字符串,結(jié)果指向默認(rèn)的通用緩沖區(qū)buf[BUFFLEN].
void SendChar(unsigned char c)
{
while (!bTransmitCompleted); //如果發(fā)送未完成,則等待
nDataToSend = c; //準(zhǔn)備要發(fā)送的字符
bTransmitCompleted = 0;
bIsTransmitting = 1; //標(biāo)識正在發(fā)送標(biāo)志
}
void SendStr(char *str)
{
while (*str) SendChar(*str++);
}
unsigned char uGetChar(void)
{
while (!bReceiveCompleted); //如果數(shù)據(jù)接收未完成,則等待
bReceiveCompleted = 0; //允許開始接收下一個(gè)字符
return nReceivedData; //返回接收到的字符
}
char *uGetStr(void)
{
/*
字符串接收停止的標(biāo)志:\r\n(0xD 0xA),或者 '\0'?
*/
unsigned char p; //用數(shù)字標(biāo)號
unsigned char c;
p = 0; //指針指向緩沖區(qū)開頭
c = uGetChar();
while (c != 0x0) {
buf[p] = c;
p++;
p &= CYCLEMASK; //防止指針溢出,自動(dòng)從頭到尾循環(huán),但是會(huì)丟失前面的字符。
//if (p = CYCLEMASK) break; //第二策略:如果溢出,則自動(dòng)停止接收。未測試。
c = uGetChar();
}
buf[p] = 0; //字符串結(jié)尾
return buf;
}
void main(void)
{
/*
程序的初始狀態(tài)準(zhǔn)備。
*/
bIsTransmitting = 0; //尚未開始發(fā)送
bIsReceiving = 0; //尚未開始接受
bTransmitCompleted = 1; //緩沖區(qū)數(shù)據(jù)已發(fā)送完成(沒數(shù)據(jù)可以發(fā)送)
bReceiveCompleted = 0; //尚未接收到數(shù)據(jù)
nTransmitCount = 0; //發(fā)送過程的T0中斷次數(shù)計(jì)數(shù)器為0
nReceiveCount = 0; //接收過程的T0中斷次數(shù)計(jì)數(shù)器為0
pRx = 0; //接收指針指向起始位置
pTx = 0; //發(fā)送指針指向起始位置
UartInit(); //設(shè)置中斷條件(工作模式、計(jì)數(shù)值等,并打開中斷)
while (1)
{
//SendChar(uGetChar()); //測試1:邊收邊發(fā)。具體波形見下圖1.
//測試2:按字符串進(jìn)行收發(fā),具體波形見下圖2.
uGetStr();
SendStr(buf);
/*
以下程序段是 STC的官方例程。
完成接收一個(gè)字符后,由Tx再次轉(zhuǎn)發(fā)出去的動(dòng)作。
當(dāng)沒有數(shù)據(jù)輸入時(shí),也不會(huì)有數(shù)據(jù)輸出。
數(shù)據(jù)的輸出最少比輸入要晚一個(gè)字節(jié)。
*/
/*
if (bReceiveCompleted) //如果數(shù)據(jù)接收完成
{
buf[pRx] = nReceivedData; //將已接收的字符存入緩沖區(qū)中。
pRx++; //接收指針指向下一個(gè)字符位置。
pRx &= CYCLEMASK; //修訂為按緩沖區(qū)長度循環(huán)的指針。
bReceiveCompleted = 0; //可以再次接收下一個(gè)字符了。
}
if (bTransmitCompleted) //如果數(shù)據(jù)發(fā)送完成
{
if (pTx != pRx) //只有在接收指針的位置和發(fā)送指針的位置不同時(shí),才開始發(fā)送。
{
nDataToSend = buf[pTx];
pTx++;
pTx &= CYCLEMASK;
bTransmitCompleted = 0;
bIsTransmitting = 1;
}
}
*/
}
}
//定時(shí)器0中斷,用于接收和發(fā)送字符。
void Timer0ISR(void) interrupt 1 using 1
{
//數(shù)據(jù)接收過程
if (bIsReceiving) //如果正在接收過程中,則完成以下動(dòng)作。
{
nReceiveCount--; //接收中斷計(jì)數(shù)器值減1
if (nReceiveCount == 0) //如果接收中斷計(jì)數(shù)器值為0,則開始判定輸入數(shù)據(jù)。
{
nReceiveCount = 3; //重置接收中斷計(jì)數(shù)器的值為3。每發(fā)生3次中斷,接收1位數(shù)據(jù)。
nRxBitCount--; //當(dāng)前要接受的數(shù)據(jù)剩余位計(jì)數(shù)器減1。說明接收完次數(shù)據(jù)后,還需要接收多少位。
if (nRxBitCount == 0) //如果剩余位計(jì)數(shù)器為0,說明當(dāng)前數(shù)據(jù)已經(jīng)接收完成。
{
nReceivedData = nTmpRcvData; //保存所接收的數(shù)據(jù)到nReceivedData中。
bIsReceiving = 0; //停止繼續(xù)接收。(下一個(gè)數(shù)據(jù)要重新開始對齊起始位)
bReceiveCompleted = 1; //數(shù)據(jù)接收完成標(biāo)志置1。
}
else //如果還有數(shù)據(jù)要接收
{
nTmpRcvData >>= 1; //當(dāng)前接收的臨時(shí)數(shù)據(jù)右移一位,最高位自動(dòng)補(bǔ)0。
if (RXB) nTmpRcvData |= 0x80; //如果當(dāng)前輸入位是1(RXB=1),則將臨時(shí)數(shù)據(jù)的最高位修改為1。
}
}
}
else if (!RXB) //如果不是當(dāng)前數(shù)據(jù)的接收過程,但是出現(xiàn) RXB=0,則表明是數(shù)據(jù)的起始位,新的輸入開始。
{
bIsReceiving = 1; //設(shè)置標(biāo)志,表明開始新的數(shù)據(jù)接收過程
nReceiveCount = 4; //因?yàn)橥V刮灰呀?jīng)做了響應(yīng),后續(xù)的判斷點(diǎn)設(shè)置在3次判定點(diǎn)的中間,
//因此在接受到起始位以后,需要4次中斷才開始下一個(gè)數(shù)據(jù)判定。
nRxBitCount = 9; //剩余未接收的數(shù)據(jù)位數(shù)(8個(gè)數(shù)據(jù)位 + 0個(gè)校驗(yàn)位 + 1個(gè)停止位,共9位)
//****注意:沒有編寫2個(gè)停止位的程序。
}
//數(shù)據(jù)的發(fā)送過程:每次中斷均可發(fā)生。因此數(shù)據(jù)的發(fā)送和接收幾乎是同時(shí)進(jìn)行。
//在main()例程中,數(shù)據(jù)的發(fā)送是等待緩沖區(qū)中至少一個(gè)數(shù)據(jù)接收完成后,才開始發(fā)送動(dòng)作,因此延遲一個(gè)字符的時(shí)間。
//如果不考慮判定一個(gè)完整字符,可以同步進(jìn)行接收和發(fā)送的動(dòng)作(直接轉(zhuǎn)發(fā),不解釋,也不判定),延遲時(shí)間是以上指令的執(zhí)行時(shí)間。
nTransmitCount--; //發(fā)送中斷計(jì)數(shù)器減1
if (nTransmitCount == 0) //如果發(fā)送中斷計(jì)數(shù)器為0,則開始發(fā)送動(dòng)作。
{
nTransmitCount = 3; //重設(shè)發(fā)送中斷計(jì)數(shù)器的初值,每3次做一個(gè)發(fā)送動(dòng)作。
if (bIsTransmitting) //如果當(dāng)前字符尚未發(fā)送完成,正在發(fā)送過程中
{
if (nTxBitCount == 0) //如果尚未發(fā)送的位計(jì)數(shù)值是0,說明當(dāng)前數(shù)據(jù)還未開始發(fā)送(此變量的含義跟接收過程的剩余位計(jì)數(shù)器意義不同)。
{
TXB = 0; //發(fā)送起始位
nTmpTxData = nDataToSend; //加在要發(fā)送的數(shù)據(jù)(把 nDataToSend 存放到 nTmpTxData 中,實(shí)際發(fā)送的是 nTmpTxData。)
#ifdef STOP1BIT
nTxBitCount = 9; //初始化剩余位計(jì)數(shù)器(這里是8個(gè)數(shù)據(jù)位 + 0個(gè)奇偶校驗(yàn)位 + 1 個(gè)停止位)
#endif
#ifdef STOP2BITS
nTxBitCount = 10; //初始化剩余位計(jì)數(shù)器(這里是8個(gè)數(shù)據(jù)位 + 0個(gè)奇偶校驗(yàn)位 + 2 個(gè)停止位)
#endif
}
else //如果剩余位計(jì)數(shù)值不等于0,說明已經(jīng)有數(shù)據(jù)在發(fā)送中
{
tData = (nTmpTxData & 0x1); //****注意:此處做了修改,防止CY被后續(xù)程序改變而出錯(cuò)。獲取要發(fā)送數(shù)據(jù)的最低位
nTmpTxData >>= 1; //數(shù)據(jù)右移到 CY 中,最高位自動(dòng)補(bǔ) 0。
//這里要用到特殊位CY,最好查看匯編后的程序,否則可能出錯(cuò)。增加位變量 tData后則無此影響。
nTxBitCount--; //剩余位計(jì)數(shù)器值減1。
#ifdef STOP1BIT
if (nTxBitCount == 0) //如果已經(jīng)發(fā)送完成
{
TXB = 1; //發(fā)送1位停止位。如果是2位停止位,還需要一次。如果是1.5位停止位,如何做?
bIsTransmitting = 0; //表示當(dāng)前數(shù)據(jù)的所有位發(fā)送完成
bTransmitCompleted = 1; //表示整個(gè)字符的傳送完成,設(shè)置標(biāo)志為1。
}
#endif
#ifdef STOP2BITS
if (nTxBitCount < 2 ) //這個(gè)操作是否會(huì)影響到 CY的值?
{
TXB = 1; //發(fā)送停止位。如果是2位停止位,還需要一次。如果是1.5位停止位,如何做?
if (nTxBitCount == 0) //如果已經(jīng)發(fā)送完成
{
bIsTransmitting = 0; //表示當(dāng)前數(shù)據(jù)的所有位發(fā)送完成
bTransmitCompleted = 1; //表示整個(gè)字符的傳送完成,設(shè)置標(biāo)志為1。
}
}
#endif
else //剩余位計(jì)數(shù)器不為0,還需要繼續(xù)發(fā)送數(shù)據(jù)。
{
//TXB = CY; //TX的數(shù)據(jù)來自 CY 寄存器,是循環(huán)右移指令的結(jié)果。
TXB = tData; //改用標(biāo)準(zhǔn)數(shù)據(jù)
}
}
}
}
}
//定時(shí)器0初始化,用于虛擬串口初始化
void UartInit(void)
{
TMOD = 0x00; //Timer0設(shè)置為16位自動(dòng)重載模式。
AUXR = 0x80; //Timer0工作在1T模式
TL0 = BAUD; //Timer0重載值的低8位
TH0 = BAUD>>8; //Timer0重載值得高8位
TR0 = 1; //tiemr0啟動(dòng)
ET0 = 1; //允許Timer0中斷
PT0 = 1; //Timer0中斷優(yōu)先級為高
EA = 1; //總中斷開關(guān)開啟
}
以上程序使用 Keil C51 V9.52 編譯通過。其中 STC15F204EA的主頻設(shè)置為22.1184MHz,通訊波特率為 57600,8位數(shù)據(jù)位,使用1或2位停止位,無校驗(yàn)的方式,可以順利通訊。
使用串口調(diào)試助手,從PC向單片機(jī)發(fā)送數(shù)據(jù)(下圖中通道1),由單片機(jī)在接收后進(jìn)行發(fā)送(下圖中通道0)。對兩種工作方式進(jìn)行測試:
1. 使用邊收邊發(fā)時(shí)的波形如下(SendChar(uGetChar());):

圖1. 接收一個(gè)字符后就進(jìn)行轉(zhuǎn)發(fā)的例子
2. 使用整個(gè)字符串(以字符'\0'結(jié)尾)接收完成后再進(jìn)行發(fā)送的波形如下(uGetStr(); SendStr(buf);):

圖2. 接收完一個(gè)字符串后再轉(zhuǎn)發(fā)的例子(去掉了末尾的'\0')
根據(jù)實(shí)際情況,可以選擇不同的接收方式。
|
|