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