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

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

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