单片机IO口模拟串口程序(发送+接收
生活随笔
收集整理的這篇文章主要介紹了
单片机IO口模拟串口程序(发送+接收
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
單片機IO口模擬串口程序(發送+接收)【轉】 qcmc?發表于?-?2011-6-23 0:42:00
| 前一陣一直在做單片機的程序,由于串口不夠,需要用IO口來模擬出一個串口。經過若干曲折并參考了一些現有的資料,基本上完成了。現在將完整的測試程序,以及其中一些需要總結的部分貼出來。 程序硬件平臺:11.0592M晶振,STC單片機(兼容51) /*************************************************************** *????在單片機上模擬了一個串口,使用P2.1作為發送端 *????把單片機中存放的數據通過P2.1作為串口TXD發送出去 ***************************************************************/ #i nclude?<reg51.h> #i nclude?<stdio.h> #i nclude?<string.h> typedef?unsigned?char?uchar; int?i; uchar?code?info[]?=? { 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55 }; sbit?newTXD?=?P2^1;//模擬串口的發送端設為P2.1 void?UartInit() { ???? SCON??=?0x50;???//?SCON:?serail?mode?1,?8-bit?UART ???? TMOD?|=?0x21;???// T0工作在方式1,十六位定時 ???? PCON?|=?0x80;???//?SMOD=1; ???? TH0??????=?0xFE;????//?定時器0初始值,延時417us,目的是令模擬串口的波特率為2400bps?fosc=11.0592MHz ????TL0???=?0x7F;????//?定時器0初始值,延時417us,目的是令模擬串口的波特率為2400bps?fosc=11.0592MHz //????TH0??????=?0xFD;????//?定時器0初始值,延時417us,目的是令模擬串口的波特率為2400bps?fosc=18.432MHz //????TL0???=?0x7F;????//?定時器0初始值,延時417us,目的是令模擬串口的波特率為2400bps?fosc=18.432MHz } void?WaitTF0(void) { ???? while(!TF0); ???? TF0=0; ???? TH0=0xFE;????//?定時器重裝初值?fosc=11.0592MHz ???? TL0=0x7F;????//?定時器重裝初值?fosc=11.0592MHz ???? //????TH0??????=?0xFD;????//?定時器重裝初值?fosc=18.432MHz ???? //????TL0???=?0x7F;????//?定時器重裝初值?fosc=18.432MHz } void?WByte(uchar?input) { ???? //發送啟始位 ?????uchar?j=8; ???? TR0=1; ???? newTXD=(bit)0; ???? WaitTF0(); ???? //發送8位數據位 ?????while(j--) ???? { ???? ??? newTXD=(bit)(input&0x01);??????//先傳低位 ?????????WaitTF0(); ???? ??? input=input>>1; ???? } ???? //發送校驗位(無) ?????//發送結束位 ?????newTXD=(bit)1; ???? WaitTF0(); ???? TR0=0; }???? void?Sendata() { ???? for(i=0;i<sizeof(info);i++)//外層循環,遍歷數組 ????{ ???? ???? WByte(info[i]); ???? } } void?main() { ???? UartInit(); ???? while(1) ???? { ???? ???? Sendata(); ???? } } ############################################################################## /*************************************************************** *???????模擬接收程序,這個程序的作用從模擬串口接收數據,然后將這些數據發送到實際串口 *????在單片機上模擬了一個串口,使用P3.2作為發送和接收端 *????以P3.2模擬串口接收端,從模擬串口接收數據發至串口 ***************************************************************/ #i nclude<reg51.h> #i nclude<stdio.h> #i nclude<string.h> typedef?unsigned?char?uchar?; //這里用來切換晶振頻率,支持11.0592MHz和18.432MHz //#define?F18_432 #define?F11_0592? uchar?tmpbuf2[64]={0}; //用來作為模擬串口接收數據的緩存 struct? { ???? uchar?recv?:6?;//tmpbuf2數組下標,用來將模擬串口接收到的數據存放到tmpbuf2中 ?? ?? uchar?send?:6?;//tmpbuf2數組下標,用來將tmpbuf2中的數據發送到串口 }tmpbuf2_point={0,0}; sbit?newRXD=P3^2?;//模擬串口的接收端設為P3.2 void?UartInit() { ???? SCON=0x50?;//?SCON:?serail?mode?1,?8-bit?UART ???? TMOD|=0x21?;//?TMOD:?timer?1,?mode?2,?8-bit?reload,自動裝載預置數(自動將TH1送到TL1);T0工作在方式1,十六位定時 ???? PCON|=0x80?;//?SMOD=1; ???? ???? #ifdef?F11_0592? ???? TH1=0xE8?;//?Baud:2400??fosc=11.0592MHz?2400bps為從串口接收數據的速率 ???? TL1=0xE8?;//?計數器初始值,fosc=11.0592MHz?因為TH1一直往TL1送,所以這個初值的意義不大 ???? TH0=0xFF?;//?定時器0初始值,延時208us,目的是令模擬串口的波特率為9600bps?fosc=11.0592MHz ?????? TL0=0xA0?;//?定時器0初始值,延時208us,目的是令模擬串口的波特率為9600bps?fosc=11.0592MHz ??????? #endif? ???? #ifdef?F18_432? ???? TH1=0xD8?;???? //?Baud:2400??fosc=18.432MHz?2400bps為從串口接收數據的速率 ???? TL1=0xD8?;???? //?計數器初始值,fosc=18.432MHz?因為TH1一直往TL1送,所以這個初值的意義不大 ??????? TH0=0xFF?;//?定時器0初始值,延時104us,目的是令模擬串口的波特率為9600bps?fosc=18.432MHz ??????? TL0=0x60?;//?定時器0初始值,延時104us,目的是令模擬串口的波特率為9600bps?fosc=18.432MHz ??????? #endif? ???? IE|=0x81?;//?中斷允許總控制位EA=1;使能外部中斷0 ???? TF0=0?; ???? IT0=1?;//?設置外部中斷0為邊沿觸發方式 ???? TR1=1?;//?啟動TIMER1,用于產生波特率 } void?WaitTF0(void) { ???? while(!TF0); ???? TF0=0?; ???? #ifdef?F11_0592? ???? TH0=0xFF?;//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? TL0=0xA0?;//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? #endif? ???? #ifdef?F18_432? ???? TH0=0xFF?; ???? //?定時器重裝初值?fosc=18.432MHz ???? TL0=0x60?; ???? //?定時器重裝初值?fosc=18.432MHz ???? #endif? } //接收一個字符 uchar?RByte() { ???? uchar?Output=0?; ???? uchar?i=8?; ???? TR0=1?;???? //啟動Timer0 ???? ???? #ifdef?F11_0592? ???? TH0=0xFF?;//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? TL0=0xA0?;//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? #endif? ???? #ifdef?F18_432? ???? TH0=0xFF?;//?定時器重裝初值?fosc=18.432MHz ???? TL0=0x60?;//?定時器重裝初值?fosc=18.432MHz ???? #endif? ???? TF0=0?; ???? WaitTF0();//等過起始位 ???? //接收8位數據位 ???? while(i--) ???? { ???? ???? Output>>=1?; ???? ???? if(newRXD)Output|=0x80?;//先收低位 ???????????? WaitTF0();//位間延時 ???? } ???? TR0=0?;//停止Timer0 ???? return?Output?; } //向COM1發送一個字符 void?SendChar(uchar?byteToSend) { ???? SBUF=byteToSend?; ???? while(!TI); ???? TI=0?; } void?main() { ???? UartInit(); ???? while(1) ???? { ???? ???? if(tmpbuf2_point.recv!=tmpbuf2_point.send)//差值表示模擬串口接收數據緩存中還有多少個字節的數據未被處理(發送至串口) ???? ???? { ???? ???? ???? SendChar(tmpbuf2[tmpbuf2_point.send++]); ???? ???? } ???? } } //外部中斷0,說明模擬串口的起始位到來了 void?Simulated_Serial_Start()interrupt?0? { ???? EX0=0?;???? //屏蔽外部中斷0 ???? tmpbuf2[tmpbuf2_point.recv++]=RByte();???? //從模擬串口讀取數據,存放到tmpbuf2數組中 ???? IE0=0?;???? //防止外部中斷響應2次,防止外部中斷函數執行2次 ???? EX0=1?;???? //打開外部中斷0 } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 以上是兩個獨立的測試程序,分別是模擬串口發送的測試程序和接收的測試程序 上面兩個程序在編寫過程中參考了這篇文章《51單片機模擬串口的三種方法》(在后文中簡稱《51》),但在它的基礎上做了一些補充,下面是若干總結的內容: 1、《51》在接收數據的程序中,采用的是循環等待的方法來檢測起始位(見《51》的“附:51 IO口模擬串口通訊C源程序(定時器計數法)” 部分),這種方法在較大程序中,可能會錯過起始位(比如起始位到來的時候程序正好在干別的,而沒有處于判斷起始位到來的狀態),或者一直在檢測起始位,而沒有辦法完成其他工作。為了避免這個問題,在本接收程序中采用了外部中斷的方法,將外部中斷0引腳作為模擬串口的接收端,設IT0=1(將外部中斷0設為邊緣觸發)。這樣當起始位(低電平)到來時,就會引發外部中斷,然后在外部中斷處理函數中接收余下的數據。這種方法可以保證沒數據的時候程序該干什么干什么,一旦模擬串口接收端有數據,就可以立即接收到。 2、加入了模擬串口接收緩沖區。在較大程序中,單片機要完成的工作很多,在模擬串口接收到了數據之后立即處理的話,有可能處理不過來造成丟失數據,或者影響程序其他部分執行。本程序中加入了64個字節的緩沖區,從模擬串口接收到的數據先存放在緩沖區中。這樣就算程序一時沒工夫處理這些數據,騰出手來之后也能在緩沖區中找到它們。 3、《51》文中的WByte函數和RByte函數中都先打開計數器后關閉計數器。如果使用本文的外部中斷法來接收數據,并且外部中斷處理函數里外都調用了WByte或RByte的話,需要將這兩個函數中的TR0=1,TR0=0操作的語句除去,并在UartInit()中加入一句TR0=1;即讓TR0始終開著就可以。 由于之前沒有意識到這個問題,因此在具體應用時出現了奇怪的問題:表現為中斷處理函數執行完畢之后,似乎回不到主程序,程序停在了一個不知道的地方。后來經過排查后找到了問題所在,那個程序的中斷處理函數中用了RByte,中斷處理函數外用到了WByte,而這兩個函數的最后都有TR0=0。這樣當中斷處理函數執行完畢后,TR0實際上是為0的,返回主程序后(中斷前的主程序可能正好處于其他的WByte或RByte執行中),原先以來定時器0溢出改變TF0才能執行下去的WByte函數就無法進行下去,從而導致整個程序停下來不動。(在本文的接收測試程序中不存在這個問題,因為中斷處理程序中雖調用了RByte,但中斷處理程序外卻沒有調用RByte或WByte) 下面是修改后的RByte、WByte和WaitTF0函數,僅供參考: /********************************************** *????????????定時器0溢出后重裝初值 **********************************************/ void?WaitTF0(void) { ???? TF0=0?; ???? #ifdef?F11_0592? ???? TH0=0xFF?;//?定時器重裝初值?fosc=11.0592MHz ???? TL0=0xA0?;//?定時器重裝初值?fosc=11.0592MHz ???? #endif? ???? #ifdef?F18_432? ???? TH0=0xFF?;//?定時器重裝初值?fosc=18.432MHz ???? TL0=0x60?;//?定時器重裝初值?fosc=18.432MHz ???? #endif? ???? while(!TF0); ???? TF0=0?; } /********************************************** *????????????從串口B接收一個字符 **********************************************/ uchar?RByte() { ???? uchar?Output=0?; ???? uchar?i=8?; ???? //????TR0=1;?????????????????????????????//啟動Timer0 /* ???? #ifdef?F11_0592 ???? TH0??????=?0xFF;????//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? TL0???=?0xA0;????//?定時器重裝初值?模擬串口的波特率為9600bps?fosc=11.0592MHz ???? #endif ???? #ifdef?F18_432 ???? TH0??????=?0xFF;????//?定時器重裝初值?fosc=18.432MHz ???? TL0???=?0x60;????//?定時器重裝初值?fosc=18.432MHz ???? #endif */ ???? WaitTF0();//等過起始位 ???? ???? //接收8位數據位 ???? while(i--) ???? { ???? ???? Output>>=1?; ???? ???? if(newRXD)Output|=0x80?;???? ???? //先收低位 ???????????? WaitTF0();//位間延時 ???? } ???? //??while(!TF0)?if(newRXD)?break;????//此句和下一句不能加,如果加上了將導致耗時過長,影響下一個字節的接收 ???? //????WaitTF0();????????????????????????//等過結束位 ???? //????TR0=0;?????????????????????????????//停止Timer0 ???? ???? return?Output?; } /********************************************** *????????????發送一個字節到串口B **********************************************/ void?WByte(uchar?input) { ???? //發送啟始位 ???? uchar?j=8?; ???? //TR0=1; ???? newTXD=(bit)0?; ???? WaitTF0(); ???? //發送8位數據位 ???? while(j--) ???? { ???? ???? newTXD=(bit)(input&0x01);//先傳低位 ???????????? WaitTF0(); ???? ???? input=input>>1?; ???? } ???? //發送校驗位(無) ???? //發送結束位 ???? newTXD=(bit)1?; ???? WaitTF0(); ???? //TR0=0; } 4、在上面的新修改后的RByte()函數中,有被注釋掉的如下兩句: //??while(!TF0)?if(newRXD)?break;????//此句和下一句不能加,如果加上了將導致耗時過長,影響下一個字節的接收 //????WaitTF0();????????????????????????//等過結束位 這兩句在《51》文中的程序是存在的,但是使用中斷接收法后,加上這兩句后出現了問題。表現為接收到的下一個字節的數據不完整或直接接收不到,似乎這兩句占用了過多的時間。 看這兩句的目的似乎是要延時以跳過結束位,但是我感覺這個結束位可以不用管它,反正結束位是個高電平,不會妨礙下一個字節是否到來的判斷(下一個字節的起始位是低電平)。那就由它去吧,沒有必要為了它而占用CPU的時間。 在本文的程序中,去掉這兩句后程序執行正確,如果其他朋友在使用時真的出現問題,可以試著再把它們加上試一下 |
轉載于:https://blog.51cto.com/2942350/607348
總結
以上是生活随笔為你收集整理的单片机IO口模拟串口程序(发送+接收的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么更改QQ实名认证
- 下一篇: 蔚来宣布完成阿布扎比主权基金 7.385