久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 18643|回復: 5
打印 上一主題 下一主題
收起左側

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

  [復制鏈接]
跳轉到指定樓層
樓主
ID:91442 發表于 2015-10-29 12:51 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
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')
根據實際情況,可以選擇不同的接收方式。

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

使用道具 舉報

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

使用道具 舉報

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

使用道具 舉報

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

使用道具 舉報

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

使用道具 舉報

6#
ID:584195 發表于 2022-4-12 13:02 | 只看該作者
謝謝樓主分享,非常感謝,我也是因為串口不夠用,所以想用iO摸擬。
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 中文字幕高清在线 | 99re6热在线精品视频播放 | 亚洲一区中文字幕 | 国产一区二区三区四区五区加勒比 | 九九热精品在线 | 亚洲视频一 | 亚洲欧美综合精品另类天天更新 | 粉嫩一区二区三区性色av | 久草网址| 日韩美香港a一级毛片免费 国产综合av | 视频在线亚洲 | 亚洲天堂一区 | 亚洲国产伊人 | 亚洲免费人成在线视频观看 | 亚洲午夜精品一区二区三区他趣 | 欧美一区二区三区在线免费观看 | 久久成人精品视频 | 在线中文视频 | 日韩福利| www.天堂av.com | 欧洲高清转码区一二区 | 蜜桃视频在线观看免费视频网站www | 亚洲成人免费观看 | 日韩中文字幕一区 | 青青久久久 | 精品成人一区二区 | 亚洲精品日韩综合观看成人91 | 999热在线视频 | 久久亚洲免费 | 欧美日韩一区二区三区四区 | 久热精品在线观看视频 | 日本淫视频 | 亚洲一区在线日韩在线深爱 | 国产精品久久国产精品 | 欧美久久视频 | 午夜欧美 | 国产网站在线免费观看 | 一级黄色片免费 | 91视频进入 | 亚洲欧洲一区 | 午夜男人的天堂 |