找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 18780|回復(fù): 5
打印 上一主題 下一主題
收起左側(cè)

STC單片機(jī)官方的模擬串口程序解析(詳細(xì)注釋+圖解)

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:91442 發(fā)表于 2015-10-29 12:51 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
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í)際情況,可以選擇不同的接收方式。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏14 分享淘帖 頂1 踩

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:343681 發(fā)表于 2018-6-2 19:59 | 只看該作者
感謝樓主  很有幫助
回復(fù)

使用道具 舉報(bào)

板凳
ID:386669 發(fā)表于 2018-8-18 17:08 | 只看該作者
SPI和IIC都好弄,只有Uart我一直嫌麻煩,只用芯片自帶的資源,剛看到這個(gè)帖子,覺得太有用處了,留名以后看。
回復(fù)

使用道具 舉報(bào)

地板
ID:184868 發(fā)表于 2020-1-9 14:36 | 只看該作者
感謝樓主  很有幫助
回復(fù)

使用道具 舉報(bào)

5#
ID:67274 發(fā)表于 2020-3-20 22:57 | 只看該作者
STC范例程序就是這個(gè),理解起來好繞腦。
回復(fù)

使用道具 舉報(bào)

6#
ID:584195 發(fā)表于 2022-4-12 13:02 | 只看該作者
謝謝樓主分享,非常感謝,我也是因?yàn)榇诓粔蛴,所以想用iO摸擬。
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表