新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 單片機模擬串口發送和波特率問題

        單片機模擬串口發送和波特率問題

        作者: 時間:2016-11-27 來源:網絡 收藏
        傳統的8051系列單片機一般都配備一個串口,而STC 89C52RC增強型單片機也不例外,只有一個串口可供使用,這樣就出問題了,假如當前單片機系統要求二個串口或多個串口進行同時通信,8051系列單片機只有一個串口可供通信就顯得十分尷尬,但是在實際的應用中,有兩種方法可以選擇。
        方法1:使用能夠支持多串口通信的單片機,不過通過更換其他單片機來代替8051系列單片機,這樣就會直接導致成本的增加,優點就是編程簡單,而且通信穩定可靠。
        方法2:在IO資源比較充足的情況下,可以通過IO來模擬串口的通信,雖然這樣會增加編程的難度,模擬串口的波特率會比真正的串口通信低一個層次,但是唯一優點就是成本上得到控制,而且通過不同的IO組合可以實現更加之多的模擬串口,在實際應用中往往會采用模擬串口的方法來實現多串口通信。
        普遍使用串口通信的數據流都是1位起始位、8位數據位、1位停止位的格式的,如表1。
        表1
        起始位8位數據位停止位
        0Bit0Bit1Bit2Bit3Bit4Bit5Bit6Bit71

        要注意的是,起始位作為識別是否有數據到來,停止位標志數據已經發送完畢。起始位固定值為0,停止位固定值為1,那么為什么起始位要是0,停止位要是1呢?這個很好理解,假設停止位固定值為1,為了更加易識別數據的到來,電平的跳變最為簡單也最容易識別,那么當有數據來的時候,只要在規定的時間內檢測到發送過來的第一位的電平是否0值,就可以確定是否有數據到來;另外停止位為1的作用就是當沒有收發數據之后引腳置為高電平起到抗干擾的作用。
        在平時使用紅外無線收發數據時,一般都采用模擬串口來實現的,但是有個問題要注意,波特率越高,傳輸距離越近;波特率越低,傳輸距離越遠。對于這些通過模擬串口進行數據傳輸,波特率適宜為1200b/s來進行數據傳輸。
        例子:在使用單片機的串口接收數據實驗當中,使用串口調試助手發送16字節數據,單片機采用模擬串口的方法將接收到的數據返發到PC機。
        模擬串口實驗代碼:



        1#include"stc.h"
        2
        3#defineRXD P3_0//宏定義:接收數據的引腳
        4#defineTXD P3_1//宏定義:發送數據的引腳
        5#defineRECEIVE_MAX_BYTES 16//宏定義:最大接收字節數
        6
        7#defineTIMER_ENABLE() {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
        8#defineTIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
        9#defineTIMER_WAIT() {while(!fTimeouts);fTimeouts=0;}//等待T/C超時
        10
        11
        12unsignedcharfTimeouts=0;//T/C超時溢出標志位
        13unsignedcharRecvBuf[16];//接收數據緩沖區
        14unsignedcharRecvCount=0;//接收數據計數器
        15
        16
        17
        23voidSendByte(unsignedcharb)
        24{
        25unsignedchari=8;
        26
        27TXD=0;
        28
        29TIMER_ENABLE();
        30TIMER_WAIT();
        31
        32
        33while(i--)
        34{
        35if(b&1)TXD=1;
        36elseTXD=0;
        37
        38TIMER_WAIT();
        39
        40b>>=1;
        41
        42}
        43
        44
        45TXD=1;
        46
        47TIMER_WAIT();
        48TIMER_DISABLE();
        49}
        50
        56unsignedcharRecvByte(void)
        57{
        58unsignedchari;
        59unsignedcharb=0;
        60
        61TIMER_ENABLE();
        62TIMER_WAIT();
        63
        64for(i=0;i<8;i++)
        65{
        66if(RXD)b|=(1<67
        68TIMER_WAIT();
        69}
        70
        71TIMER_WAIT();//等待結束位
        72TIMER_DISABLE();
        73
        74returnb;
        75
        76}
        77
        83voidPrintfStr(char*pstr)
        84{
        85while(pstr&&*pstr)
        86{
        87SendByte(*pstr++);
        88}
        89}
        90
        96voidTimerInit(void)
        97{
        98TMOD=0x02;
        99TR0=0;
        100TF0=0;
        101TH0=(256-99);
        102TL0=TH0;
        103ET0=1;
        104EA=1;
        105}
        106
        112unsignedcharStartBitCome(void)
        113{
        114return(RXD==0);
        115}
        116
        122voidmain(void)
        123{
        124unsignedchari;
        125
        126TimerInit();
        127
        128PrintfStr("Hello 8051rn");
        129
        130while(1)
        131{
        132if(StartBitCome())
        133{
        134RecvBuf[RecvCount++]=RecvByte();
        135
        136if(RecvCount>=RECEIVE_MAX_BYTES)
        137{
        138RecvCount=0;
        139
        140for(i=0;i141{
        142SendByte(RecvBuf[i]);
        143}
        144}
        145}
        146
        147}
        148}
        149
        155voidTimer0IRQ(void) interrupt1using0
        156{
        157fTimeouts=1;
        158}
        159

        代碼分析
        在模擬串口實驗代碼中,宏的使用占用了相當的一部分。
        #define RXD P3_0//宏定義:接收數據的引腳
        #define TXD P3_1//宏定義:發送數據的引腳
        #define TIMER_ENABLE(){TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
        #define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
        #define TIMER_WAIT(){while(!fTimeouts);fTimeouts=0;}//等待T/C超時

        模擬串口接收引腳為P3.0,發送引腳為P3.1。為了達到精確的定時,減少模擬串口時收發數據的累積誤差,有必要通過對T/C進行頻繁的使能和禁止等操作。例如宏TIMER_ENABLE為使能T/C,宏TIMER_DISABLE禁止T/C,宏TIMER_WAIT等待T/C超時。
        模擬串口的工作波特率為9600b/s,在串口收發的數據流當中,每一位的時間為1/9600≈104us,
        若單片機工作在12MHz頻率下,使用T/C0工作在方式2,那么為了達到104us的定時時間,TH0、TL0的初值為256-104=152,在實際的模擬串口中,往往出現收發數據不正確的現象。原因就在于TH0、TL0的初值,或許很多人會疑惑,按道理來說,計算T/C0的初值是沒有錯的。對,是沒有錯,但是在SendByte和Recv的函數當中,執行每一行代碼都要消耗一定的時間,這就是所謂的“累積誤差”導致收發數據出現問題,因此我們必須通過實際測試得到TH0、TL0的初值,最佳值256-99=157。那么在T/C初始化TimerInit函數中,TH0、TL0的初值不能夠按照常規來計算得到,實際初值在正常初值附近,可以通過實際測試得到。
        模擬串口主要復雜在模擬串口發送與接收,具體實現函數在SendByte和RecvByte函數,這兩個函數必須要遵循“1位起始位、8位數據位、1位停止位”的數據流。
        SendByte函數用于模擬串口發送數據,以起始位“0”作為移位傳輸的起始標志,然后將要發送的自己從低字節到高字節移位傳輸,最后以停止位“1”作為移位傳輸的結束標志。
        RecvByte函數用于模擬串口接收數據,一旦檢測到起始位“0”,就立刻將接收到的每一位移位存儲,最后以判斷停止位“1”結束當前數據的接收。
        main函數完成T/C的初始化,在while(1)死循環以檢測起始位“0”為目的,當接收到的數據達到宏RECEIVE_MAX_BYTES的個數時,將接收到的數據返發到外設。

        波特率的研究


        通常情況下,8051系列單片機外接晶振頻率一般是12MHz、24MHz、48MHz如圖7-6-1,為什么會這樣選取呢?從前面的章節已經介紹8051系列單片機的每12個時鐘周期為一個指令周期,當8051系列單片機外接12MHz晶振時,指令周期=12/12MHz=1us;若外接24MHz晶振時,指令周期=12/24MHz=0.5us;若外接48MHz晶振時,指令周期=12/48MHz=0.25us。8051系列單片機外接能夠被除盡的晶振,在使用單片機內部的定時器/計數器資源時作定時器使用時能夠得到精確定時應用;當使用匯編語言編程時,可以清楚知道當前每一行代碼執行的時間。
        8051系列單片機外接能夠被除盡的晶振即12MHz、24MHz、48MHz這些晶振時,波特率的精確性就得不到保證。

        假若現在單片機外接的晶振為12MHz時,以T/C2作波特率發生器,根據波特率公式:
        波特率=Fosc/2x16x(65536-t)
        9600=12MHz/2x16x(65536-t)
        t=65496.9375
        “65496.9375”不是一個整數值,是一個帶有小數點的數值。對于常用的8位、9位、11位一幀的數據接收與傳輸,最大的允許誤差分別是6.25%、5.56%、4.5%。雖然波特率允許誤差,但是這樣通信時便會產生積累誤差,進而影響數據的正確性。唯一的解決辦法就是更改單片機外接的晶振頻率,更改為常用于產生精確波特率的晶振如11.0592MHz、22.1184MHz。
        假若現在單片機外接的晶振為11.0592MHz時,以T/C2作波特率發生器,根據波特率公式:
        波特率=Fosc/2x16x(65536-t)
        9600=11.0592MHz/2x16x(65536-t)
        t=65500=0xFFDC

        雖然使用11.0592MHz、22.1184MHz的晶振能夠產生精確的波特率,但是用于系統精確的定時服務不是十分的理想。例如單片機外接11.0592MHz晶振時,指令周期=12/11.0592MHz≈1.085us,是一個無限循環的小數。當單片機外接22.1184MHz晶振時,指令周期=12/22.1184MHz≈0.5425us,也是一個無限循環的小數。

        串口工作在方式1時分別采用T/C1和T/C2產生常用波特率初值表如下。
        波特率
        (11.0592MHz)
        初值波特率
        (12MHz)
        初值
        TH1、TL1
        (SMOD=0)
        TH1、TL1
        (SMOD=1)
        TH1、TL1
        (SMOD=0)
        TH1、TL1
        (SMOD=1)
        12000xE70xD012000xE50xCB
        24000xF30xE724000xF20xE5
        48000xF90xF348000xF90xF2
        96000xFC0xF996000xFC0xF9
        144000xFD0xFB144000xFD0xFB
        192000xFE0xFC192000xFE0xFC


        波特率
        (11.0592MHz)
        初值波特率
        (12MHz)
        初值
        RCAL2HRCAL2LRCAL2HRCAL2L
        12000xFE0xE012000xFE0xC8
        24000xFF0x7024000xFF0x64
        48000xFF0xD848000xFF0xB2
        96000xFF0xDC96000xFF0xD9
        144000xFF0xE8144000xFF0xE6
        192000xFF0xEE192000xFF0xED

        如果大家想通過設置不同的晶振獲取更加多的波特率的值,可以下載以下工具進行計算:
        軟件下載地址:http://files.cnblogs.com/wenziqi/單片機多功能助手.rar



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 准格尔旗| 琼中| 丹江口市| 高州市| 成都市| 巫山县| 托里县| 宜章县| 东海县| 彰化县| 石林| 满城县| 南昌市| 梓潼县| 邮箱| 葫芦岛市| 余江县| 乐陵市| 德昌县| 濮阳县| 屏东市| 都江堰市| 休宁县| 紫云| 西林县| 山东省| 沧源| 武陟县| 蛟河市| 布拖县| 西林县| 东乌珠穆沁旗| 双桥区| 凉山| 成都市| 天峻县| 光泽县| 盐源县| 新和县| 尖扎县| 和平区|