<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 第67節(jié):利用外部中斷實(shí)現(xiàn)模擬串口數(shù)據(jù)的收發(fā)

          第67節(jié):利用外部中斷實(shí)現(xiàn)模擬串口數(shù)據(jù)的收發(fā)

          作者: 時(shí)間:2016-11-22 來(lái)源:網(wǎng)絡(luò) 收藏
          開(kāi)場(chǎng)白:

          鴻哥曾經(jīng)親自用外部中斷做過(guò)紅外遙控器的數(shù)據(jù)接收,步進(jìn)電機(jī)圓周運(yùn)動(dòng)的光電反饋信號(hào)檢測(cè),輸液器里瞬間即逝的水滴信號(hào),以及本節(jié)的模擬串口數(shù)據(jù)的接收,其實(shí)這些項(xiàng)目的原理都大同小異,會(huì)一樣即可觸類(lèi)旁通其它的。

          本文引用地址:http://www.ex-cimer.com/article/201611/319741.htm

          這一節(jié)要教大家四個(gè)知識(shí)點(diǎn):

          第一個(gè):如何利用外部中斷實(shí)現(xiàn)模擬串口數(shù)據(jù)的收發(fā)。

          第二個(gè):在退出外部中斷函數(shù)時(shí),必須通過(guò)軟件把外部中斷標(biāo)志位IE0清零,否則在接收到的數(shù)據(jù)包最后面會(huì)多收到一個(gè)無(wú)效的字節(jié)0xFF。

          第三個(gè):實(shí)際做項(xiàng)目的時(shí)候,盡量利用單片機(jī)內(nèi)部自帶的集成串口,不到萬(wàn)不得已盡量不要用自制的模擬串口,如果非要用本節(jié)講的模擬串口,那么一次接收的數(shù)據(jù)包不要太長(zhǎng),盡可能越短越好,因?yàn)樽约鹤龅哪M串口在穩(wěn)定性上肯定比不上單片機(jī)自帶的串口。這種模擬串口在批量生產(chǎn)時(shí)容易因?yàn)榫д竦恼`差,以及外界各地溫度的溫差而影響產(chǎn)品的一致性,是有隱患的。

          第四個(gè):用模擬串口時(shí),盡量不要選用動(dòng)態(tài)數(shù)碼管的顯示方案,因?yàn)閱纹瑱C(jī)在收發(fā)串口數(shù)據(jù)時(shí),只能專(zhuān)心干一件事,此時(shí)不能中途被動(dòng)態(tài)數(shù)碼管掃描程序占用。而動(dòng)態(tài)數(shù)碼管得不到均勻掃描,就會(huì)產(chǎn)生略微閃爍的現(xiàn)象瑕疵。

          具體內(nèi)容,請(qǐng)看源代碼講解。

          (1)硬件平臺(tái):

          基于朱兆祺51單片機(jī)學(xué)習(xí)板。當(dāng)把程序下載到單片機(jī)之后,要做以下跳線(xiàn)處理:

          單片機(jī)原來(lái)的P3.1引腳是TI串口輸出引腳,P3.0是RI串口輸入引腳,分別把P3.1和P3.0的黃顏色跳冒去掉,同時(shí)也把外部中斷0的引腳P3.2和一根IO口P1.0引腳的換顏色跳冒去掉,把P3.2跳冒的右針連接到P3.0跳冒的左針,作為模擬串口的接收數(shù)據(jù)線(xiàn)。把P1.0跳冒的右針連接到P3.1跳冒的左針,作為模擬串口的發(fā)送數(shù)據(jù)線(xiàn)。

          (2)實(shí)現(xiàn)功能:

          波特率是:9600 。

          通過(guò)電腦串口調(diào)試助手模擬上位機(jī),往單片機(jī)任意發(fā)送一串不超過(guò)10個(gè)的數(shù)據(jù)包,單片機(jī)如實(shí)地返回接收到的整包數(shù)據(jù)給上位機(jī)。

          例如:

          (a)上位機(jī)發(fā)送數(shù)據(jù):01 02 03 04 05 06 07 08 09 0A

          單片機(jī)返回: 01 02 03 04 05 06 07 08 09 0A

          (b)上位機(jī)發(fā)送數(shù)據(jù): 05 07 EE A8 F9

          單片機(jī)返回: 05 07 EE A8 F9

          (3)源代碼講解如下:

          #include "REG52.H"

          #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間

          #define const_rc_size 20 //接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組大小

          #define const_receive_time 5 //如果超過(guò)這個(gè)時(shí)間沒(méi)有串口數(shù)據(jù)過(guò)來(lái),就認(rèn)為一串?dāng)?shù)據(jù)已經(jīng)全部接收完,這個(gè)時(shí)間根據(jù)實(shí)際情況來(lái)調(diào)整大小

          /* 注釋一:

          * 以下時(shí)序脈沖延時(shí)參數(shù)我是在keil uVision2 平臺(tái)下,Memory Model在small模式,Code Rom Size在Large模式下編譯的,

          * 如果在不同keil版本,不同的模式下,編譯出來(lái)的程序有可能此參數(shù)會(huì)不一樣。

          * 以下的時(shí)序脈沖延時(shí)參數(shù)是需要一步一步慢慢調(diào)的。我一開(kāi)始的時(shí)候先編寫(xiě)一個(gè)簡(jiǎn)單的發(fā)送數(shù)據(jù)測(cè)試程序,

          * 先確調(diào)試出合適的發(fā)送時(shí)序延時(shí)數(shù)據(jù)。然后再編寫(xiě)串口接收數(shù)據(jù)的程序,從而調(diào)試出接收時(shí)序的延時(shí)參數(shù)。

          * 比如:我第一步發(fā)送數(shù)據(jù)的測(cè)試程序是這樣的:

          void main()

          {

          initial_myself();

          delay_long(100);

          initial_peripheral();

          while(1)

          {

          // usart_service(); //串口服務(wù)程序

          eusart_send(0x08); //測(cè)試程序,讓它不斷發(fā)送數(shù)據(jù)給上位機(jī)觀察,確保發(fā)送延時(shí)時(shí)序的參數(shù)準(zhǔn)確性

          delay_long(300);

          eusart_send(0xE5); //測(cè)試程序,讓它不斷發(fā)送數(shù)據(jù)給上位機(jī)觀察,確保發(fā)送延時(shí)時(shí)序的參數(shù)準(zhǔn)確性

          delay_long(300);

          }

          }

          */

          #define const_t_1 10 //發(fā)送時(shí)序延時(shí)1 第一步先調(diào)出此數(shù)據(jù)

          #define const_t_2 9 //發(fā)送時(shí)序延時(shí)2 第一步先調(diào)出此數(shù)據(jù)

          #define const_r_1 7 //接收時(shí)序延時(shí)1 第二步再調(diào)出此數(shù)據(jù)

          #define const_r_2 9 //接收時(shí)序延時(shí)2 第二步再調(diào)出此數(shù)據(jù)

          void initial_myself(void);

          void initial_peripheral(void);

          void delay_long(unsigned int uiDelaylong);

          void delay_short(unsigned int uiDelayShort);

          void delay_minimum(unsigned char ucDelayMinimum); //細(xì)分度最小的延時(shí),用char類(lèi)型一個(gè)字節(jié)

          void T0_time(void); //定時(shí)中斷函數(shù)

          void INT0_int(void); //外部0中斷函數(shù),在本系統(tǒng)中是模擬串口的接收中斷函數(shù)。

          void usart_service(void); //串口服務(wù)程序,在main函數(shù)里

          void eusart_send(unsigned char ucSendData);

          unsigned char read_eusart_byte();//從串口讀一個(gè)字節(jié)

          sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動(dòng)IO口

          sbit ti_dr=P1^0; //模擬串口發(fā)送數(shù)據(jù)的IO口

          sbit ri_sr=P3^2; //模擬串口接收數(shù)據(jù)的IO口 也是外部中斷0的復(fù)用IO口

          unsigned int uiSendCnt=0; //用來(lái)識(shí)別串口是否接收完一串?dāng)?shù)據(jù)的計(jì)時(shí)器

          unsigned char ucSendLock=1; //串口服務(wù)程序的自鎖變量,每次接收完一串?dāng)?shù)據(jù)只處理一次

          unsigned int uiRcregTotal=0; //代表當(dāng)前緩沖區(qū)已經(jīng)接收了多少個(gè)數(shù)據(jù)

          unsigned char ucRcregBuf[const_rc_size]; //接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組

          unsigned char ucTest=0;

          void main()

          {

          initial_myself();

          delay_long(100);

          initial_peripheral();

          while(1)

          {

          usart_service(); //串口服務(wù)程序

          }

          }

          void usart_service(void) //串口服務(wù)程序,在main函數(shù)里

          {

          unsigned char i=0;

          if(uiSendCnt>=const_receive_time&&ucSendLock==1) //說(shuō)明超過(guò)了一定的時(shí)間內(nèi),再也沒(méi)有新數(shù)據(jù)從串口來(lái)

          {

          ucSendLock=0; //處理一次就鎖起來(lái),不用每次都進(jìn)來(lái),除非有新接收的數(shù)據(jù)

          //下面的代碼進(jìn)入數(shù)據(jù)協(xié)議解析和數(shù)據(jù)處理的階段

          for(i=0;i

          {

          eusart_send(ucRcregBuf[i]);

          }

          uiRcregTotal=0; //清空緩沖的下標(biāo),方便下次重新從0下標(biāo)開(kāi)始接受新數(shù)據(jù)

          }

          }

          //往串口發(fā)送一個(gè)字節(jié)

          void eusart_send(unsigned char ucSendData) //往上位機(jī)發(fā)送一個(gè)字節(jié)的函數(shù)

          {

          unsigned char i=8;

          EA=0; //關(guān)總中斷

          ti_dr=0; //發(fā)送啟始位

          delay_minimum(const_t_1); //發(fā)送時(shí)序延時(shí)1 delay_minimum是本程序細(xì)分度最小的延時(shí)

          while(i--)

          {

          ti_dr=ucSendData&0x01; //先傳低位

          delay_minimum(const_t_2); //發(fā)送時(shí)序延時(shí)2 delay_minimum是本程序細(xì)分度最小的延時(shí)

          ucSendData=ucSendData>>1;

          }

          ti_dr=1; //發(fā)送結(jié)束位

          delay_short(400); //每個(gè)字節(jié)之間的延時(shí),這里非常關(guān)鍵,也是最容易出錯(cuò)的地方。延時(shí)的大小請(qǐng)根據(jù)實(shí)際項(xiàng)目來(lái)調(diào)整

          EA=1; //開(kāi)總中斷

          }

          //從串口讀取一個(gè)字節(jié)

          unsigned char read_eusart_byte()

          {

          unsigned char ucReadData=0;

          unsigned char i=8;

          delay_minimum(const_r_1); //接收時(shí)序延時(shí)1 。作用是等過(guò)起始位 delay_minimum是本程序細(xì)分度最小的延時(shí)

          while(i--)

          {

          ucReadData >>=1;

          if(ri_sr==1)

          {

          ucReadData|=0x80; //先收低位

          }

          if(ri_sr==0) //此處空指令,是為了讓驅(qū)動(dòng)時(shí)序的時(shí)間保持一致性

          {

          ;

          }

          delay_minimum(const_r_2); //接收時(shí)序延時(shí)2 delay_minimum是本程序細(xì)分度最小的延時(shí)

          }

          return ucReadData;

          }

          void T0_time(void) interrupt 1 //定時(shí)中斷

          {

          TF0=0; //清除中斷標(biāo)志

          TR0=0; //關(guān)中斷

          if(uiSendCnt

          {

          uiSendCnt++; //表面上這個(gè)數(shù)據(jù)不斷累加,但是在串口中斷里,每接收一個(gè)字節(jié)它都會(huì)被清零,除非這個(gè)中間沒(méi)有串口數(shù)據(jù)過(guò)來(lái)

          ucSendLock=1; //開(kāi)自鎖標(biāo)志

          }

          TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b

          TL0=0x0b;

          TR0=1; //開(kāi)中斷

          }

          void INT0_int(void) interrupt 0 //INT0外部中斷函數(shù)

          {

          EX0=0; //禁止外部0中斷 這個(gè)只是我個(gè)人的編程習(xí)慣,也可以不關(guān)閉

          ++uiRcregTotal;

          if(uiRcregTotal>const_rc_size) //超過(guò)緩沖區(qū)

          {

          uiRcregTotal=const_rc_size;

          }

          ucRcregBuf[uiRcregTotal-1]=read_eusart_byte(); //將串口接收到的數(shù)據(jù)緩存到接收緩沖區(qū)里

          uiSendCnt=0; //及時(shí)喂狗,雖然main函數(shù)那邊不斷在累加,但是只要串口的數(shù)據(jù)還沒(méi)發(fā)送完畢,那么它永遠(yuǎn)也長(zhǎng)不大,因?yàn)槊總€(gè)中斷都被清零。

          /* 注釋二:

          * 注意,此處必須把IE0中斷標(biāo)志清零,否則在接收到的數(shù)據(jù)包最后面會(huì)多收到一個(gè)無(wú)效的字節(jié)0xFF。

          */

          IE0=0; //外部中斷0標(biāo)志位清零,必須的!

          EX0=1; //打開(kāi)外部0中斷

          }

          void delay_long(unsigned int uiDelayLong)

          {

          unsigned int i;

          unsigned int j;

          for(i=0;i

          {

          for(j=0;j<500;j++) //內(nèi)嵌循環(huán)的空指令數(shù)量

          {

          ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句

          }

          }

          }

          void delay_short(unsigned int uiDelayShort)

          {

          unsigned int i;

          for(i=0;i

          {

          ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句

          }

          }

          /* 注釋三:

          * 由于IO口模擬的串口時(shí)序要求很高,所以用的延時(shí)函數(shù)盡可能細(xì)分度越高越好,以下用一個(gè)字節(jié)的延時(shí)計(jì)時(shí)器

          */

          void delay_minimum(unsigned char ucDelayMinimum) //細(xì)分度最小的延時(shí),用char類(lèi)型一個(gè)字節(jié)

          {

          unsigned char i;

          for(i=0;i

          {

          ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句

          }

          }

          void initial_myself(void) //第一區(qū) 初始化單片機(jī)

          {

          beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時(shí)不叫。

          //配置定時(shí)器

          TMOD=0x01; //設(shè)置定時(shí)器0為工作方式1

          TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b

          TL0=0x0b;

          }

          void initial_peripheral(void) //第二區(qū) 初始化外圍

          {

          EX0=1; //允許外部中斷0

          IT0=1; //下降沿觸發(fā)外部中斷0 如果是0代表低電平中斷

          IP=0x01; //設(shè)置外部中斷0為最高優(yōu)先級(jí),可以打斷低優(yōu)先級(jí)中斷服務(wù)。實(shí)現(xiàn)中斷嵌套功能

          EA=1; //開(kāi)總中斷

          ET0=1; //允許定時(shí)中斷

          TR0=1; //啟動(dòng)定時(shí)中斷

          }

          總結(jié)陳詞:

          這節(jié)講完了外部中斷的應(yīng)用例子,下一節(jié)我會(huì)開(kāi)始講單片機(jī)C語(yǔ)言的多文件編程技巧。很多人也把多文件編程稱(chēng)作模塊化編程,其實(shí)我覺(jué)得叫多文件編程會(huì)更加符合實(shí)際一些。多文件編程有兩個(gè)最大的好處,一個(gè)是給我們的程序增加了目錄,方便我們查找。另外一個(gè)好處是方便移植別人已經(jīng)做好的功能程序模塊,利用這個(gè)特點(diǎn),特別適合團(tuán)隊(duì)一起做大型項(xiàng)目。很多初學(xué)者剛開(kāi)始學(xué)多文件編程時(shí),會(huì)經(jīng)常遇到重復(fù)定義等問(wèn)題,想知道怎么解決這些問(wèn)題嗎?欲知詳情,請(qǐng)聽(tīng)下回分解----單片機(jī)C語(yǔ)言的多文件編程技巧。



          評(píng)論


          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();