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

          新聞中心

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

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

          作者: 時間:2016-11-22 來源:網(wǎng)絡 收藏
          開場白:

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

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

          這一節(jié)要教大家四個知識點:

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

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

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

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

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

          (1)硬件平臺:

          基于朱兆祺51單片機學習板。當把程序下載到單片機之后,要做以下跳線處理:

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

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

          波特率是:9600 。

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

          例如:

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

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

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

          單片機返回: 05 07 EE A8 F9

          (3)源代碼講解如下:

          #include "REG52.H"

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

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

          #define const_receive_time 5 //如果超過這個時間沒有串口數(shù)據(jù)過來,就認為一串數(shù)據(jù)已經(jīng)全部接收完,這個時間根據(jù)實際情況來調整大小

          /* 注釋一:

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

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

          * 以下的時序脈沖延時參數(shù)是需要一步一步慢慢調的。我一開始的時候先編寫一個簡單的發(fā)送數(shù)據(jù)測試程序,

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

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

          void main()

          {

          initial_myself();

          delay_long(100);

          initial_peripheral();

          while(1)

          {

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

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

          delay_long(300);

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

          delay_long(300);

          }

          }

          */

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

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

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

          #define const_r_2 9 //接收時序延時2 第二步再調出此數(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); //細分度最小的延時,用char類型一個字節(jié)

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

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

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

          void eusart_send(unsigned char ucSendData);

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

          sbit beep_dr=P2^7; //蜂鳴器的驅動IO口

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

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

          unsigned int uiSendCnt=0; //用來識別串口是否接收完一串數(shù)據(jù)的計時器

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

          unsigned int uiRcregTotal=0; //代表當前緩沖區(qū)已經(jīng)接收了多少個數(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(); //串口服務程序

          }

          }

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

          {

          unsigned char i=0;

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

          {

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

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

          for(i=0;i

          {

          eusart_send(ucRcregBuf[i]);

          }

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

          }

          }

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

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

          {

          unsigned char i=8;

          EA=0; //關總中斷

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

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

          while(i--)

          {

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

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

          ucSendData=ucSendData>>1;

          }

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

          delay_short(400); //每個字節(jié)之間的延時,這里非常關鍵,也是最容易出錯的地方。延時的大小請根據(jù)實際項目來調整

          EA=1; //開總中斷

          }

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

          unsigned char read_eusart_byte()

          {

          unsigned char ucReadData=0;

          unsigned char i=8;

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

          while(i--)

          {

          ucReadData >>=1;

          if(ri_sr==1)

          {

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

          }

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

          {

          ;

          }

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

          }

          return ucReadData;

          }

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

          {

          TF0=0; //清除中斷標志

          TR0=0; //關中斷

          if(uiSendCnt

          {

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

          ucSendLock=1; //開自鎖標志

          }

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

          TL0=0x0b;

          TR0=1; //開中斷

          }

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

          {

          EX0=0; //禁止外部0中斷 這個只是我個人的編程習慣,也可以不關閉

          ++uiRcregTotal;

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

          {

          uiRcregTotal=const_rc_size;

          }

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

          uiSendCnt=0; //及時喂狗,雖然main函數(shù)那邊不斷在累加,但是只要串口的數(shù)據(jù)還沒發(fā)送完畢,那么它永遠也長不大,因為每個中斷都被清零。

          /* 注釋二:

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

          */

          IE0=0; //外部中斷0標志位清零,必須的!

          EX0=1; //打開外部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ù)量

          {

          ; //一個分號相當于執(zhí)行一條空語句

          }

          }

          }

          void delay_short(unsigned int uiDelayShort)

          {

          unsigned int i;

          for(i=0;i

          {

          ; //一個分號相當于執(zhí)行一條空語句

          }

          }

          /* 注釋三:

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

          */

          void delay_minimum(unsigned char ucDelayMinimum) //細分度最小的延時,用char類型一個字節(jié)

          {

          unsigned char i;

          for(i=0;i

          {

          ; //一個分號相當于執(zhí)行一條空語句

          }

          }

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

          {

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

          //配置定時器

          TMOD=0x01; //設置定時器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; //設置外部中斷0為最高優(yōu)先級,可以打斷低優(yōu)先級中斷服務。實現(xiàn)中斷嵌套功能

          EA=1; //開總中斷

          ET0=1; //允許定時中斷

          TR0=1; //啟動定時中斷

          }

          總結陳詞:

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



          評論


          技術專區(qū)

          關閉
          看屁屁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); })();