<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                【134.1 應用層的“半雙工”和“全雙工”。】 應用層的“半雙工”。主機與從機在程序應用層采用“一問一答”的查詢模式,主機是主動方,從機是被動方,主機問一句從機答一句,“聊天對話“的氛圍很無趣很呆板。從機沒有發言權,當從機想主動給主機發送一些數據時就“憋得慌”。半雙工適用于大多數單向通訊的場合。 應用層的“全雙工”。主機與從機在程序應用層可以實現任意雙向的通訊,這時從機也可變為主機,主機也可變為從機,就像兩個人平時聊天,無所謂誰是從機誰是主機,也無所謂非要對方對我每句話都要應答附和(只要對方能聽得清我講什么就可以),“聊天對話”的氛圍很生動很活潑。全雙工適用于通訊更復雜的場合。 本節從“半雙工“開始講,讓初學者先熟悉雙機通訊的基本程序框架,下一節再講“全雙工“。 【134.2 雙機通訊的三類核心函數。】 雙機通訊在程序框架層面有三類核心的涵數,它們分別是:通訊過程的控制涵數,發送的隊列驅動涵數,接收數據后的處理涵數。 “通訊過程的控制涵數”的數量可以不止1個,每一個通訊事件都對應一個獨立的“通訊過程的控制涵數”,根據通訊事件的數量,一個系統往往有N個“通訊過程的控制涵數”。顧名思義,它負責過程的控制,無論什么項目,凡是過程控制我都首選switch語句。此函數是屬于上層應用的函數,它的基礎底層是“發送的隊列驅動涵數”和“接收數據后的處理涵數”這兩個函數。 “發送的隊列驅動涵數”在系統中只有1個“發送的隊列驅動涵數”,負責“通訊管道的占用”的分配,負責數據的具體發送。當同時存在很多“待發送”的請求指令時,此函數會根據“if ,else if...”的優先級,像隊列一樣安排各指令發送的先后順序,確保各指令不會發生沖突。此函數屬于底層的驅動函數。 “接收數據后的處理涵數”在系統中只有1個,負責處理當前接收到的數據,它既屬于“底層函數”也屬于“應用層函數”,二者成分皆有。 我們一旦深刻地領悟了這三類函數各自的分工與關聯方式,將來應付再復雜的通訊系統都會脈絡清析,游刃有余。 【134.3 例程的功能需求。】 上位機與下位機都有一個一模一樣的57個字節的大數組。在上位機端按下獨立按鍵K1后,上位機開始與下位機建立通訊,上位機的目的是讀取下位機的那57個字節的大數組,分批讀取,每批讀取10個字節,最后一批讀取的是余下的7個字節。讀取完畢后,上位機把讀取到的大數組與自己的大數組進行對比:如果相等,表示通訊正確,蜂鳴器“長鳴”一聲;如果不相等,表示通訊錯誤,蜂鳴器“短鳴”一聲。在通訊過程中,如果出現通信異常(比如因為接收超時或者接收某批次數據錯誤而導致重發的次數超過最大限制的次數)也表示通訊錯誤,蜂鳴器也會發出“短鳴”一聲的提示。 【134.4 例程的電路圖。】 兩個單片機進行232串口通訊,一共需要3根線:1根作為共地線,其它2根是交叉的收發數據線(上位機的“接收線”連接下位機的“發送線”,上位機的“發送線”連接下位機的“接收線”),如下圖所示: ![](https://img.kancloud.cn/07/c7/07c71ef5d2261aa2417c7dc4e2a24fe1_811x339.png) 上圖134.4.1 雙機通訊的232串口接線圖 ![](https://img.kancloud.cn/a2/3d/a23df87ac21f61d2182864f67461b009_359x103.png) 上圖134.4.2 上位機的獨立按鍵 ![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png) 上圖134.4.3 上位機的有源蜂鳴器 【134.5 例程的通訊協議。】 (1) 通訊參數。波特率9600,校驗位NONE(無),數據位8,停止位1。 (二)上位機讀取下位機的數組容量的大小的指令。 (1)上位機發送十六進制的數據:EB 01 00 00 00 07 ED。 EB是數據頭。 01是指令類型,01代表請求下位機返回大數組的容量大小。 00 00 00 07代表整個指令的數據長度。 ED是前面所有字節數據的異或結果,用來作為校驗數據。 (2)下位機返回十六進制的數據:EB 01 00 00 00 0C XX XX XX XX ZZ。 EB是數據頭。 01是指令類型,01代表返回大數組的容量大小。 00 00 00 0B代表整個指令的數據長度 XX XX XX XX代表大數組的容量大小 ZZ是前面所有字節數據的異或結果,用來作為校驗數據。 (三)上位機讀取下位機的大數組的分段數據的指令。 (1)上位機發送十六進制的數據:EB 02 00 00 00 0F RR RR RR RR YY YY YY YY ZZ EB是數據頭 02是指令類型,02代表請求下位機返回當前分段的數據。 00 00 00 0F代表整個指令的數據長度 RR RR RR RR代表請求下位機返回的數據的“請求起始地址” YY YY YY YY代表請求下位機從“請求起始地址”一次返回的數據長度 ZZ是前面所有字節數據的異或結果,用來作為校驗數據。 (2)下位機返回十六進制的數據:EB 02 TT TT TT TT RR RR RR RR YY YY YY YY HH ...HH ZZ EB是數據頭 02是指令類型,02代表返回大數組當前分段的數據 TT TT TT TT 代表整個指令的數據長度 RR RR RR RR代表下位機返回數據時的“請求起始地址” YY YY YY YY代表下位機從“請求起始地址”一次返回的數據長度 HH ...HH代表中間有效的數據內容 ZZ是前面所有字節數據的異或結果,用來作為校驗數據。 【134.6 解決本節例程編譯不過去的方法。】 因為本節用到的全局變量比較多,如果有初學者在編譯的時候出現“error C249: 'DATA': SEGMENT TOO LARGE”的提示,請按下圖的窗口提示來設置一下編譯的環境。 ![](https://img.kancloud.cn/0b/4c/0b4cf94661c684369773a2110577eb4b_415x279.png) 上圖134.5.1 設置編譯的環境 【134.7 例程的上位機程序。】 \#include "REG52.H" \#define RECE\_TIME\_OUT 2000 //通訊過程中字節之間的超時時間2000ms \#define REC\_BUFFER\_SIZE 30 //常規控制類數組的長度 \#define KEY\_FILTER\_TIME 25 //按鍵濾波的“穩定時間” void usart(void); //串口接收的中斷函數 void T0\_time(); //定時器的中斷函數 void BigBufferUsart(void); //讀取下位機大數組的“通訊過程的控制涵數”。三大核心函數之一 void QueueSend(void); //發送的隊列驅動涵數。三大核心函數之一 void ReceDataHandle(void); //接收數據后的處理涵數。三大核心函數之一 void UsartTask(void); //串口收發的任務函數,放在主函數內 unsigned char CalculateXor(const unsigned char \*pCu8Buffer, //異或的算法函數 unsigned long u32BufferSize); //比較兩個數組的是否相等。返回1代表相等,返回0代表不相等 //u32BufferSize是參與對比的數組的大小 unsigned char CmpTwoBufferIsSame(const unsigned char \*pCu8Buffer\_1, const unsigned char \*pCu8Buffer\_2, unsigned long u32BufferSize); void UsartSendByteData(unsigned char u8SendData); //發送一個字節的底層驅動函數 //發送帶協議的函數 void UsartSendMessage(const unsigned char \*pCu8SendMessage,unsigned long u32SendMaxSize); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); void BeepClose(void); void VoiceScan(void); void KeyScan(void); void KeyTask(void); sbit P3\_4=P3^4; //蜂鳴器的驅動輸出口 sbit KEY\_INPUT1=P2^2; //K1按鍵識別的輸入口。 //下面表格數組的數據與下位機的表格數據一模一樣,目的用來檢測接收到的數據是否正確 code unsigned char Cu8TestTable\[\]= { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A, 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A, 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A, 0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A, 0x51,0x52,0x53,0x54,0x55,0x56,0x57 }; unsigned char Gu8ReceTable\[57\]; //從下位機接收到的表格數據的數組 //把一些針對某個特定事件的全局變量放在一個結構體內,可以讓全局變量的分類更加清晰 struct StructBigBufferUsart //控制讀取大數組的通訊過程的結構體 { unsigned char u8Status; //通訊過程的狀態 0為初始狀態 1為通訊成功 2為通訊失敗 unsigned char u8ReSendCnt; //重發計數器 unsigned char u8Step; //通訊過程的步驟 unsigned char u8Start; //通訊過程的啟動 unsigned long u32NeedSendSize; //一共需要發送的全部數據量 unsigned long u32AlreadySendSize; //實際已經發送的數據量 unsigned long u32CurrentAddr; //當前批次需要發送的起始地址 unsigned long u32CurrentSize; //當前批次從起始地址開始發送的數據量 unsigned char u8QueueSendTrig;//隊列驅動函數的發送的啟動 unsigned char u8QueueSendBuffer\[30\]; //隊列驅動函數的發送指令的數組 unsigned char u8QueueStatus; //隊列驅動函數的通訊狀態 0為初始狀態 1為通訊成功 2為通訊失敗 }; unsigned char Gu8QueueReceUpdate=0; //1代表“隊列發送數據后,收到了新的數據” struct StructBigBufferUsart GtBigBufferUsart;//此結構體變量專門用來控制讀取大數組的通訊事件 volatile unsigned char vGu8BigBufferUsartTimerFlag=0; //過程控制的超時定時器 volatile unsigned int vGu16BigBufferUsartTimerCnt=0; volatile unsigned char vGu8QueueSendTimerFlag=0; //隊列發送的超時定時器 volatile unsigned int vGu16QueueSendTimerCnt=0; volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; volatile unsigned char vGu8KeySec=0; unsigned char Gu8SendByteFinish=0; //發送一個字節完成的標志 unsigned char Gu8ReceBuffer\[REC\_BUFFER\_SIZE\]; //常規控制類的小內存 unsigned char \*pGu8ReceBuffer; //用來切換接收內存的“中轉指針” unsigned long Gu32ReceCntMax=REC\_BUFFER\_SIZE; //最大緩存 unsigned long Gu32ReceCnt=0; //接收緩存數組的下標 unsigned char Gu8ReceStep=0; //接收中斷函數里的步驟變量 unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作變量。 unsigned char Gu8ReceType=0; //接收的數據類型 unsigned char Gu8Rece\_Xor=0; //接收的異或 unsigned long Gu32ReceDataLength=0; //接收的數據長度 unsigned char Gu8FinishFlag=0; //是否已接收完成一串數據的標志 unsigned long \*pu32Data; //用于數據轉換的指針 volatile unsigned char vGu8ReceTimeOutFlag=0;//通訊過程中字節之間的超時定時器的開關 volatile unsigned int vGu16ReceTimeOutCnt=0; //通訊過程中字節之間的超時定時器,“喂狗”的對象 void main() { SystemInitial(); Delay(10000); PeripheralInitial(); while(1) { UsartTask(); //串口收發的任務函數 KeyTask(); } } void KeyTask(void) //按鍵任務函數,放在主函數內 { if(0==vGu8KeySec) { return; //按鍵的觸發序號是0意味著無按鍵觸發,直接退出當前函數,不執行此函數下面的代碼 } switch(vGu8KeySec) //根據不同的按鍵觸發序號執行對應的代碼 { case 1: //1號按鍵。K1的獨立按鍵 //GtBigBufferUsart.u8Start在開機初始化函數里必須初始化為0!這一步很關鍵! if(0==GtBigBufferUsart.u8Start) //只有在還沒有啟動的情況下,才能啟動 { GtBigBufferUsart.u8Status=0; //通訊過程的狀態 0為初始狀態 GtBigBufferUsart.u8Step=0; //通訊過程的步驟 0為從當前開始的步驟 GtBigBufferUsart.u8Start=1; //通訊過程的啟動 } vGu8KeySec=0; //響應按鍵服務處理程序后,按鍵編號必須清零,避免一致觸發 break; } } /\* 注釋一: \* 每一個通訊事件都對應的一個獨立的“通訊過程的控制涵數”,一個系統中有多少個通訊事件,就存在 \* 多少個“通訊過程的控制涵數”。該函數負責某個通訊事件從開始到結束的整個過程。比如本節項目, \* 在通訊過程中,如果發現接收到的數據錯誤,則繼續啟動重發的機制。當發現接收到的累加字節數等于 \* 預期想要接收的數量時,則結束這個通訊的事件。 \*/ void BigBufferUsart(void) //讀取下位機大數組的“通訊過程的控制涵數” { static const unsigned char SCu8ReSendCntMax=3; //重發的次數 static unsigned long \*pSu32Data; //用于數據與數組轉換的指針 switch(GtBigBufferUsart.u8Step) //過程控制,我首選switch語句! { case 0: if(1==GtBigBufferUsart.u8Start) //通訊過程的啟動 { //根據實際項目需要,在此第0步驟里可以添加一些初始化相關的數據 GtBigBufferUsart.u8ReSendCnt=0; //重發計數器清零 GtBigBufferUsart.u8Step=1; //切換到下一步 } break; //-----------先發送“讀取下位機的數組容量的大小的指令”--------------------- //-----------EB 01 00 00 00 07 ED --------------------- case 1: GtBigBufferUsart.u8QueueSendBuffer\[0\]=0xeb; //數據頭 GtBigBufferUsart.u8QueueSendBuffer\[1\]=0x01; //數據類型 讀取數組容量大小 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2\]; \*pSu32Data=7; //數據長度 本條指令的數據總長是7個字節 //異或算法的函數 GtBigBufferUsart.u8QueueSendBuffer\[6\]=CalculateXor(GtBigBufferUsart.u8QueueSendBuffer, 6) ; //最后一個字節不納入計算 //隊列驅動函數的狀態 0為初始狀態 1為通訊成功 2為通訊失敗 GtBigBufferUsart.u8QueueStatus=0; //隊列驅動函數的通訊狀態 GtBigBufferUsart.u8QueueSendTrig=1;//隊列驅動函數的發送的啟動 vGu8BigBufferUsartTimerFlag=0; vGu16BigBufferUsartTimerCnt=2000; vGu8BigBufferUsartTimerFlag=1; //過程控制的超時定時器的啟動 GtBigBufferUsart.u8Step=2; //切換到下一步 break; case 2: //發送之后,等待下位機返回的數據的狀態 if(1==GtBigBufferUsart.u8QueueStatus) //當前批次的接收到的數據成功 { GtBigBufferUsart.u8ReSendCnt=0; //重發計數器清零 GtBigBufferUsart.u32AlreadySendSize=0; //實際已經發送的數據量清零 GtBigBufferUsart.u32CurrentAddr=0; //當前批次需要發送的起始地址 GtBigBufferUsart.u32CurrentSize=10; //從當前批次起始地址開始發送的數據量 GtBigBufferUsart.u8Step=3; //切換到下一步 } else if(2==GtBigBufferUsart.u8QueueStatus) //當前批次的接收到的數據失敗 { GtBigBufferUsart.u8ReSendCnt++; if(GtBigBufferUsart.u8ReSendCnt>=SCu8ReSendCntMax) //大于最大的重發次數 { GtBigBufferUsart.u8Step=0; GtBigBufferUsart.u8Start=0; //結束當前的過程通訊 GtBigBufferUsart.u8Status=2; //對外宣布“通訊失敗” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=30; //讓蜂鳴器“短鳴”一聲 vGu8BeepTimerFlag=1; } else { GtBigBufferUsart.u8Step=1; //返回上一步,重發當前段的數據 } } else if(0==vGu16BigBufferUsartTimerCnt) //當前批次在等待接收返回數據時,超時 { GtBigBufferUsart.u8ReSendCnt++; if(GtBigBufferUsart.u8ReSendCnt>=SCu8ReSendCntMax) //大于最大的重發次數 { GtBigBufferUsart.u8Step=0; GtBigBufferUsart.u8Start=0; //結束當前的過程通訊 GtBigBufferUsart.u8Status=2; //對外宣布“通訊失敗” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=30; //讓蜂鳴器“短鳴”一聲 vGu8BeepTimerFlag=1; } else { GtBigBufferUsart.u8Step=1; //返回上一步,重發當前段的數據 } } break; //-----------接著發送“讀取下位機的大數組的分段數據的指令”--------------------- //-----------EB 02 00 00 00 0F RR RR RR RR YY YY YY YY ZZ --------------------- case 3: GtBigBufferUsart.u8QueueSendBuffer\[0\]=0xeb; //數據頭 GtBigBufferUsart.u8QueueSendBuffer\[1\]=0x02; //數據類型 讀取分段數據 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2\]; \*pSu32Data=15; //數據長度 本條指令的數據總長是15個字節 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2+4\]; \*pSu32Data=GtBigBufferUsart.u32CurrentAddr; //當前批次需要發送的起始地址 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2+4+4\]; \*pSu32Data=GtBigBufferUsart.u32CurrentSize; //從當前批次起始地址發送的數據量 //異或算法的函數 GtBigBufferUsart.u8QueueSendBuffer\[14\]=CalculateXor(GtBigBufferUsart.u8QueueSendBuffer, 14); //最后一個字節不納入計算 //隊列驅動函數的狀態 0為初始狀態 1為通訊成功 2為通訊失敗 GtBigBufferUsart.u8QueueStatus=0; //隊列驅動函數的通訊狀態 GtBigBufferUsart.u8QueueSendTrig=1;//隊列驅動函數的發送的啟動 vGu8BigBufferUsartTimerFlag=0; vGu16BigBufferUsartTimerCnt=2000; vGu8BigBufferUsartTimerFlag=1; //過程控制的超時定時器的啟動 GtBigBufferUsart.u8Step=4; //切換到下一步 break; case 4: //發送之后,等待下位機返回的數據的狀態 if(1==GtBigBufferUsart.u8QueueStatus) //當前批次的接收到的數據成功 { //更新累加當前實際已經發送的字節數 GtBigBufferUsart.u32AlreadySendSize=GtBigBufferUsart.u32AlreadySendSize+ GtBigBufferUsart.u32CurrentSize; //更新下一步起始的發送地址 GtBigBufferUsart.u32CurrentAddr=GtBigBufferUsart.u32CurrentAddr+ GtBigBufferUsart.u32CurrentSize; //更新下一步從起始地址開始發送的字節數 if((GtBigBufferUsart.u32CurrentAddr+GtBigBufferUsart.u32CurrentSize)> GtBigBufferUsart.u32NeedSendSize) //最后一段數據的臨界點的判斷 { GtBigBufferUsart.u32CurrentSize=GtBigBufferUsart.u32NeedSendSize- GtBigBufferUsart.u32CurrentAddr; } else { GtBigBufferUsart.u32CurrentSize=10; } //判斷是否已經把整個大數組的57個字節都已經接收完畢。如果已經接收完畢,則 //結束當前通信;如果還沒結束,則繼續請求下位機發送下一段新數據。 if(GtBigBufferUsart.u32AlreadySendSize>=GtBigBufferUsart.u32NeedSendSize) { GtBigBufferUsart.u8Step=0; GtBigBufferUsart.u8Start=0; //結束當前的過程通訊 if(1==CmpTwoBufferIsSame(Cu8TestTable, //如果接收的數據與存儲的相等 Gu8ReceTable, 57)) { vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=1000; //讓蜂鳴器“長鳴”一聲 vGu8BeepTimerFlag=1; GtBigBufferUsart.u8Status=1; //對外宣布“通訊成功” } else { vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=30; //讓蜂鳴器“短鳴”一聲 vGu8BeepTimerFlag=1; GtBigBufferUsart.u8Status=2; //對外宣布“通訊失敗” } } else { GtBigBufferUsart.u8ReSendCnt=0; //重發計數器清零 GtBigBufferUsart.u8Step=3; //返回上一步,繼續發下一段的新數據 } } else if(2==GtBigBufferUsart.u8QueueStatus) //當前批次的接收到的數據失敗 { GtBigBufferUsart.u8ReSendCnt++; if(GtBigBufferUsart.u8ReSendCnt>=SCu8ReSendCntMax) //大于最大的重發次數 { GtBigBufferUsart.u8Step=0; GtBigBufferUsart.u8Start=0; //結束當前的過程通訊 GtBigBufferUsart.u8Status=2; //對外宣布“通訊失敗” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=30; //讓蜂鳴器“短鳴”一聲 vGu8BeepTimerFlag=1; } else { GtBigBufferUsart.u8Step=3; //返回上一步,重發當前段的數據 } } else if(0==vGu16BigBufferUsartTimerCnt) //當前批次在等待接收返回數據時,超時 { GtBigBufferUsart.u8ReSendCnt++; if(GtBigBufferUsart.u8ReSendCnt>=SCu8ReSendCntMax) //大于最大的重發次數 { GtBigBufferUsart.u8Step=0; GtBigBufferUsart.u8Start=0; //結束當前的過程通訊 GtBigBufferUsart.u8Status=2; //對外宣布“通訊失敗” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=30; //讓蜂鳴器“短鳴”一聲 vGu8BeepTimerFlag=1; } else { GtBigBufferUsart.u8Step=3; //返回上一步,重發當前段的數據 } } break; } } /\* 注釋二: \* 整個項目中只有一個“發送的隊列驅動涵數”,負責“通訊管道的占用”的分配,負責數據的具體發 \* 送。當同時存在很多“待發送”的請求指令時,此函數會根據“if ,else if...”的優先級,像隊列一 \* 樣安排各指令發送的先后順序,確保各指令不會發生沖突。 \*/ void QueueSend(void) //發送的隊列驅動涵數 { static unsigned char Su8Step=0; switch(Su8Step) { case 0: //分派即將要發送的任務 if(1==GtBigBufferUsart.u8QueueSendTrig) { GtBigBufferUsart.u8QueueSendTrig=0; //及時清零。驅動層,不管結果,只發一次。 Gu8QueueReceUpdate=0; //接收應答數據的狀態恢復初始值 //發送帶指令的數據 UsartSendMessage((const unsigned char \*)&GtBigBufferUsart.u8QueueSendBuffer\[0\], 30); vGu8QueueSendTimerFlag=0; vGu16QueueSendTimerCnt=2000; vGu8QueueSendTimerFlag=1; //隊列發送的超時定時器 Su8Step=1; } // else if(...) //當有其它發送的指令時,可以在此處繼續添加判斷,越往下優先級越低 // else if(...) //當有其它發送的指令時,可以在此處繼續添加判斷,越往下優先級越低 break; case 1: //發送之后,等待下位機的應答。驅動層,只管有沒有應答,不管應答對不對。 if(1==Gu8QueueReceUpdate) //如果“接收數據后的處理涵數”接收到應答數據 { Su8Step=0; //返回上一步繼續處理其它“待發送的指令” } if(0==vGu16QueueSendTimerCnt) //發送指令之后,等待應答超時 { Su8Step=0; //返回上一步繼續處理其它“待發送的指令” } break; } } /\* 注釋三: \* 整個項目中只有一個“接收數據后的處理涵數”,負責即時處理當前接收到的數據。 \*/ void ReceDataHandle(void) //接收數據后的處理涵數 { static unsigned long \*pSu32Data; //數據轉換的指針 static unsigned long i; static unsigned char Su8Rece\_Xor=0; //計算的“異或” static unsigned long Su32CurrentAddr; //讀取的起始地址 static unsigned long Su32CurrentSize; //讀取的發送的數據量 if(1==Gu8ReceFeedDog) //每被“喂一次狗”,就及時更新一次“超時檢測的定時器”的初值 { Gu8ReceFeedDog=0; vGu8ReceTimeOutFlag=0; vGu16ReceTimeOutCnt=RECE\_TIME\_OUT;//更新一次“超時檢測的定時器”的初值 vGu8ReceTimeOutFlag=1; } else if(Gu8ReceStep>0&&0==vGu16ReceTimeOutCnt) //超時,并且步驟不在接頭暗號的步驟 { Gu8ReceStep=0; //串口接收數據的中斷函數及時切換回接頭暗號的步驟 } if(1==Gu8FinishFlag) //1代表已經接收完畢一串新的數據,需要馬上去處理 { switch(Gu8ReceType) //接收到的數據類型 { case 0x01: //讀取下位機的數組容量的大小 Gu8QueueReceUpdate=1; //告訴“隊列驅動函數”收到了新的應答數據 Gu8Rece\_Xor=Gu8ReceBuffer\[Gu32ReceDataLength-1\]; //提取接收到的“異或” Su8Rece\_Xor=CalculateXor(Gu8ReceBuffer,Gu32ReceDataLength-1); //計算“異或” if(Gu32ReceDataLength>=11&& //接收到的數據長度必須大于或者等于11個字節 Su8Rece\_Xor==Gu8Rece\_Xor) //驗證“異或”,“計算的”與“接收的”是否一致 { pSu32Data=(unsigned long \*)&Gu8ReceBuffer\[6\]; //數據轉換。 GtBigBufferUsart.u32NeedSendSize=\*pSu32Data; //提取將要接收數組的大小 GtBigBufferUsart.u8QueueStatus=1; //告訴“過程控制函數”,當前通訊成功 } else { GtBigBufferUsart.u8QueueStatus=2; //告訴“過程控制函數”,當前通訊失敗 } break; case 0x02: //讀取下位機的分段數據 Gu8QueueReceUpdate=1; //告訴“隊列驅動函數”收到了新的應答數據 Gu8Rece\_Xor=Gu8ReceBuffer\[Gu32ReceDataLength-1\]; //提取接收到的“異或” Su8Rece\_Xor=CalculateXor(Gu8ReceBuffer,Gu32ReceDataLength-1); //計算“異或” pSu32Data=(unsigned long \*)&Gu8ReceBuffer\[6\]; //數據轉換。 Su32CurrentAddr=\*pSu32Data; //讀取的起始地址 pSu32Data=(unsigned long \*)&Gu8ReceBuffer\[6+4\]; //數據轉換。 Su32CurrentSize=\*pSu32Data; //讀取的發送的數據量 if(Gu32ReceDataLength>=11&& //接收到的數據長度必須大于或者等于11個字節 Su8Rece\_Xor==Gu8Rece\_Xor&& //驗證“異或”,“計算的”與“接收的”是否一致 Su32CurrentAddr==GtBigBufferUsart.u32CurrentAddr&& //驗證“地址”,相當于驗證“動態密匙” Su32CurrentSize==GtBigBufferUsart.u32CurrentSize) //驗證“地址”,相當于驗證“動態密匙” { for(i=0;i<Su32CurrentSize;i++) { //及時把接收到的數據存儲到Gu8ReceTable數組 Gu8ReceTable\[Su32CurrentAddr+i\]=Gu8ReceBuffer\[6+4+4+i\]; } GtBigBufferUsart.u8QueueStatus=1; //告訴“過程控制函數”,當前通訊成功 } else { GtBigBufferUsart.u8QueueStatus=2; //告訴“過程控制函數”,當前通訊失敗 } break; } Gu8FinishFlag=0; //上面處理完數據再清零標志,為下一次接收新的數據做準備 } } void UsartTask(void) //串口收發的任務函數,放在主函數內 { BigBufferUsart(); //讀取下位機大數組的“通訊過程的控制涵數” QueueSend(); //發送的隊列驅動涵數 ReceDataHandle(); //接收數據后的處理涵數 } void usart(void) interrupt 4 //串口接發的中斷函數,中斷號為4 { if(1==RI) //接收完一個字節后引起的中斷 { RI = 0; //及時清零,避免一直無緣無故的進入中斷。 if(0==Gu8FinishFlag) //1代表已經完成接收了一串新數據,并且禁止接收其它新的數據 { Gu8ReceFeedDog=1; //每接收到一個字節的數據,此標志就置1及時更新定時器的值。 switch(Gu8ReceStep) { case 0: //“前部分的”數據頭。接頭暗號的步驟。 Gu8ReceBuffer\[0\]=SBUF; //直接讀取剛接收完的一個字節的數據。 if(0xeb==Gu8ReceBuffer\[0\]) //等于數據頭0xeb,接頭暗號吻合。 { Gu32ReceCnt=1; //接收緩存的下標 Gu8ReceStep=1; //切換到下一個步驟,接收其它有效的數據 } break; case 1: //“前部分的”數據類型和長度 Gu8ReceBuffer\[Gu32ReceCnt\]=SBUF; //直接讀取剛接收完的一個字節的數據。 Gu32ReceCnt++; //每接收一個字節,數組下標都自加1,為接收下一個數據做準備 if(Gu32ReceCnt>=6) //前6個數據。接收完了“數據類型”和“數據長度”。 { Gu8ReceType=Gu8ReceBuffer\[1\]; //提取“數據類型” //以下的數據轉換,在第62節講解過的指針法 pu32Data=(unsigned long \*)&Gu8ReceBuffer\[2\]; //數據轉換 Gu32ReceDataLength=\*pu32Data; //提取“數據長度” if(Gu32ReceCnt>=Gu32ReceDataLength) //靠“數據長度”來判斷是否完成 { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } else //如果還沒結束,繼續切換到下一個步驟,接收“有效數據” { //本節只用到一個接收數組,把指針關聯到Gu8ReceBuffer本身的數組 pGu8ReceBuffer=(unsigned char \*)&Gu8ReceBuffer\[6\]; Gu32ReceCntMax=REC\_BUFFER\_SIZE; //最大緩存 Gu8ReceStep=2; //切換到下一個步驟 } } break; case 2: //“后部分的”數據 pGu8ReceBuffer\[Gu32ReceCnt-6\]=SBUF; //這里的指針就是各種不同內存的化身!!! Gu32ReceCnt++; //每接收一個字節,數組下標都自加1,為接收下一個數據做準備 //靠“數據長度”來判斷是否完成。也不允許超過數組的最大緩存的長度 if(Gu32ReceCnt>=Gu32ReceDataLength||Gu32ReceCnt>=Gu32ReceCntMax) { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } break; } } } else //發送數據引起的中斷 { TI = 0; //及時清除發送中斷的標志,避免一直無緣無故的進入中斷。 Gu8SendByteFinish=1; //從0變成1通知主函數已經發送完一個字節的數據了。 } } void UsartSendByteData(unsigned char u8SendData) //發送一個字節的底層驅動函數 { static unsigned int Su16TimeOutDelay; //超時處理的延時計時器 Gu8SendByteFinish=0; //在發送以字節之前,必須先把此全局變量的標志清零。 SBUF =u8SendData; //依靠寄存器SBUF作為載體發送一個字節的數據 Su16TimeOutDelay=0xffff; //超時處理的延時計時器裝載一個相對合理的計時初始值 while(Su16TimeOutDelay>0) //超時處理 { if(1==Gu8SendByteFinish) { break; //如果Gu8SendByteFinish為1,則發送一個字節完成,退出當前循環等待。 } Su16TimeOutDelay--; //超時計時器不斷遞減 } //Delay();//在實際應用中,當連續發送一堆數據時如果發現丟失數據,可以嘗試在此增加延時 } //發送帶協議的函數 void UsartSendMessage(const unsigned char \*pCu8SendMessage,unsigned long u32SendMaxSize) { static unsigned long i; static unsigned long \*pSu32; static unsigned long u32SendSize; pSu32=(const unsigned long \*)&pCu8SendMessage\[2\]; u32SendSize=\*pSu32; //從帶協議的數組中提取整包數組的有效發送長度 if(u32SendSize>u32SendMaxSize) //如果“有效發送長度”大于“最大限制的長度”,數據異常 { return; //數據異常,直接退出當前函數,預防數組越界 } for(i=0;i<u32SendSize;i++) //u32SendSize為發送的數據長度 { UsartSendByteData(pCu8SendMessage\[i\]); //基于“發送單字節的最小接口函數”來實現的 } } unsigned char CalculateXor(const unsigned char \*pCu8Buffer, //此處加const代表數組“只讀” unsigned long u32BufferSize) //參與計算的數組的大小 { unsigned long i; unsigned char Su8Rece\_Xor; Su8Rece\_Xor=pCu8Buffer\[0\]; //提取數據串第“i=0”個數據作為異或的原始數據 for(i=1;i<u32BufferSize;i++) //注意,這里是從第“i=1”個數據開始 { Su8Rece\_Xor=Su8Rece\_Xor^pCu8Buffer\[i\]; //計算“異或” } return Su8Rece\_Xor; //返回運算后的異或的計算結果 } //比較兩個數組的是否相等。返回1代表相等,返回0代表不相等 unsigned char CmpTwoBufferIsSame(const unsigned char \*pCu8Buffer\_1, const unsigned char \*pCu8Buffer\_2, unsigned long u32BufferSize) //參與對比的數組的大小 { unsigned long i; for(i=0;i<u32BufferSize;i++) { if(pCu8Buffer\_1\[i\]!=pCu8Buffer\_2\[i\]) { return 0; //只要有一個不相等,則返回0并且退出當前函數 } } return 1; //相等 } void KeyScan(void) //此函數放在定時中斷里每1ms掃描一次 { static unsigned char Su8KeyLock1; //1號按鍵的自鎖 static unsigned int Su16KeyCnt1; //1號按鍵的計時器 //1號按鍵 if(0!=KEY\_INPUT1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位 { Su8KeyLock1=0; //按鍵解鎖 Su16KeyCnt1=0; //按鍵去抖動延時計數器清零,此行非常巧妙,是全場的亮點。 } else if(0==Su8KeyLock1)//有按鍵按下,且是第一次被按下。 { Su16KeyCnt1++; //累加定時中斷次數 if(Su16KeyCnt1>=KEY\_FILTER\_TIME) //濾波的“穩定時間”KEY\_FILTER\_TIME,長度是25ms。 { Su8KeyLock1=1; //按鍵的自鎖,避免一直觸發 vGu8KeySec=1; //觸發1號鍵 } } } void T0\_time() interrupt 1 { VoiceScan(); KeyScan(); if(1==vGu8BigBufferUsartTimerFlag&&vGu16BigBufferUsartTimerCnt>0) //過程控制的超時定時器 { vGu16BigBufferUsartTimerCnt--; } if(1==vGu8QueueSendTimerFlag&&vGu16QueueSendTimerCnt>0) //隊列發送的超時定時器 { vGu16QueueSendTimerCnt--; } if(1==vGu8ReceTimeOutFlag&&vGu16ReceTimeOutCnt>0) //通訊過程中字節之間的超時定時器 { vGu16ReceTimeOutCnt--; } TH0=0xfc; TL0=0x66; } void SystemInitial(void) { unsigned char u8\_TMOD\_Temp=0; //以下是定時器0的中斷的配置 TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; //以下是串口接收中斷的配置 //串口的波特率與內置的定時器1直接相關,因此配置此定時器1就等效于配置波特率。 u8\_TMOD\_Temp=0x20; //即將把定時器1設置為:工作方式2,初值自動重裝的8位定時器。 TMOD=TMOD&0x0f; //此寄存器低4位是跟定時器0相關,高4位是跟定時器1相關。先清零定時器1。 TMOD=TMOD|u8\_TMOD\_Temp; //把高4位的定時器1填入0x2,低4位的定時器0保持不變。 TH1=256-(11059200L/12/32/9600); //波特率為9600。11059200代表晶振11.0592MHz, TL1=256-(11059200L/12/32/9600); //L代表long的長類型數據。根據芯片手冊提供的計算公式。 TR1=1; //開啟定時器1 SM0=0; SM1=1; //SM0與SM1的設置:選擇10位異步通訊,波特率根據定時器1可變 REN=1; //允許串口接收數據 //為了保證串口中斷接收的數據不丟失,必須設置IP = 0x10,相當于把串口中斷設置為最高優先級, //這個時候,串口中斷可以打斷任何其他的中斷服務函數實現嵌套, IP =0x10; //把串口中斷設置為最高優先級,必須的。 ES=1; //允許串口中斷 EA=1; //允許總中斷 } void Delay(unsigned long u32DelayTime) { for(;u32DelayTime>0;u32DelayTime--); } void PeripheralInitial(void) { GtBigBufferUsart.u8Start=0; //通訊過程的啟動變量必須初始化為0!這一步很關鍵! } void BeepOpen(void) { P3\_4=0; } void BeepClose(void) { P3\_4=1; } void VoiceScan(void) { static unsigned char Su8Lock=0; if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0) { if(0==Su8Lock) { Su8Lock=1; BeepOpen(); } else { vGu16BeepTimerCnt--; if(0==vGu16BeepTimerCnt) { Su8Lock=0; BeepClose(); } } } } 【134.8 例程的下位機程序。】 下位機作為從機應答上位機的指令,程序相對簡化了很多。不需要“通訊過程的控制涵數”,直接在 “接收數據后的處理涵數”里啟動“發送的隊列驅動涵數”來發送應答的數據即可。發送應答數據后,也不用等待上位機的應答數據。 \#include "REG52.H" \#define RECE\_TIME\_OUT 2000 //通訊過程中字節之間的超時時間2000ms \#define REC\_BUFFER\_SIZE 30 //常規控制類數組的長度 void usart(void); //串口接收的中斷函數 void T0\_time(); //定時器的中斷函數 void QueueSend(void); //發送的隊列驅動涵數 void ReceDataHandle(void); //接收數據后的處理涵數 void UsartTask(void); //串口收發的任務函數,放在主函數內 unsigned char CalculateXor(const unsigned char \*pCu8Buffer, //異或的算法的函數 unsigned long u32BufferSize); void UsartSendByteData(unsigned char u8SendData); //發送一個字節的底層驅動函數 //發送帶協議的函數 void UsartSendMessage(const unsigned char \*pCu8SendMessage,unsigned long u32SendMaxSize); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; //下面表格數組的數據與上位機的表格數據一模一樣,目的用來讓上位機檢測接收到的數據是否正確 code unsigned char Cu8TestTable\[\]= { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A, 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A, 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A, 0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A, 0x51,0x52,0x53,0x54,0x55,0x56,0x57 }; //把一些針對某個特定事件的全局變量放在一個結構體內,可以讓全局變量的分類更加清晰 struct StructBigBufferUsart //應答讀取大數組的通訊過程的結構體 { unsigned char u8QueueSendTrig;//隊列驅動函數的發送的啟動 unsigned char u8QueueSendBuffer\[30\]; //隊列驅動函數的發送指令的數組 unsigned char u8QueueStatus; //隊列驅動函數的通訊狀態 0為初始狀態 1為通訊成功 2為通訊失敗 }; unsigned char Gu8QueueReceUpdate=0; //1代表“隊列發送數據后,收到了新的數據” struct StructBigBufferUsart GtBigBufferUsart;//此結構體變量專門用來應答讀取大數組的通訊事件 volatile unsigned char vGu8QueueSendTimerFlag=0; //隊列發送的超時定時器 volatile unsigned int vGu16QueueSendTimerCnt=0; unsigned char Gu8SendByteFinish=0; //發送一個字節完成的標志 unsigned char Gu8ReceBuffer\[REC\_BUFFER\_SIZE\]; //常規控制類的小內存 unsigned char \*pGu8ReceBuffer; //用來切換接收內存的“中轉指針” unsigned long Gu32ReceCntMax=REC\_BUFFER\_SIZE; //最大緩存 unsigned long Gu32ReceCnt=0; //接收緩存數組的下標 unsigned char Gu8ReceStep=0; //接收中斷函數里的步驟變量 unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作變量。 unsigned char Gu8ReceType=0; //接收的數據類型 unsigned char Gu8Rece\_Xor=0; //接收的異或 unsigned long Gu32ReceDataLength=0; //接收的數據長度 unsigned char Gu8FinishFlag=0; //是否已接收完成一串數據的標志 unsigned long \*pu32Data; //用于數據轉換的指針 volatile unsigned char vGu8ReceTimeOutFlag=0;//通訊過程中字節之間的超時定時器的開關 volatile unsigned int vGu16ReceTimeOutCnt=0; //通訊過程中字節之間的超時定時器,“喂狗”的對象 void main() { SystemInitial(); Delay(10000); PeripheralInitial(); while(1) { UsartTask(); //串口收發的任務函數 } } /\* 注釋一: \* 整個項目中只有一個“發送的隊列驅動涵數”,負責“通訊管道的占用”的分配,負責數據的具體發 \* 送。當同時存在很多“待發送”的請求指令時,此函數會根據“if ,else if...”的優先級,像隊列一 \* 樣安排各指令發送的先后順序,確保各指令不會發生沖突。 \*/ void QueueSend(void) //發送的隊列驅動涵數 { static unsigned char Su8Step=0; switch(Su8Step) { case 0: //分派即將要發送的任務 if(1==GtBigBufferUsart.u8QueueSendTrig) { GtBigBufferUsart.u8QueueSendTrig=0; //及時清零。驅動層,不管結果,只發一次。 Gu8QueueReceUpdate=0; //接收應答數據的狀態恢復初始值 //發送帶指令的數據 UsartSendMessage((const unsigned char \*)&GtBigBufferUsart.u8QueueSendBuffer\[0\], 30); //注意,這里是從機應答主機的數據,不需要等待返回的數據,因此不需要切換Su8Step } // else if(...) //當有其它發送的指令時,可以在此處繼續添加判斷,越往下優先級越低 // else if(...) //當有其它發送的指令時,可以在此處繼續添加判斷,越往下優先級越低 break; case 1: //發送之后,等待下位機的應答。驅動層,只管有沒有應答,不管應答對不對。 if(1==Gu8QueueReceUpdate) //如果“接收數據后的處理涵數”接收到應答數據 { Su8Step=0; //返回上一步繼續處理其它“待發送的指令” } if(0==vGu16QueueSendTimerCnt) //發送指令之后,等待應答超時 { Su8Step=0; //返回上一步繼續處理其它“待發送的指令” } break; } } /\* 注釋二: \* 整個項目中只有一個“接收數據后的處理涵數”,負責即時處理當前接收到的數據。 \*/ void ReceDataHandle(void) //接收數據后的處理涵數 { static unsigned long \*pSu32Data; //數據轉換的指針 static unsigned long i; static unsigned char Su8Rece\_Xor=0; //計算的“異或” static unsigned long Su32CurrentAddr; //讀取的起始地址 static unsigned long Su32CurrentSize; //讀取的發送的數據量 if(1==Gu8ReceFeedDog) //每被“喂一次狗”,就及時更新一次“超時檢測的定時器”的初值 { Gu8ReceFeedDog=0; vGu8ReceTimeOutFlag=0; vGu16ReceTimeOutCnt=RECE\_TIME\_OUT;//更新一次“超時檢測的定時器”的初值 vGu8ReceTimeOutFlag=1; } else if(Gu8ReceStep>0&&0==vGu16ReceTimeOutCnt) //超時,并且步驟不在接頭暗號的步驟 { Gu8ReceStep=0; //串口接收數據的中斷函數及時切換回接頭暗號的步驟 } if(1==Gu8FinishFlag) //1代表已經接收完畢一串新的數據,需要馬上去處理 { switch(Gu8ReceType) //接收到的數據類型 { case 0x01: //返回下位機的數組容量的大小 Gu8Rece\_Xor=Gu8ReceBuffer\[Gu32ReceDataLength-1\]; //提取接收到的“異或” Su8Rece\_Xor=CalculateXor(Gu8ReceBuffer,Gu32ReceDataLength-1); //計算“異或” if(Su8Rece\_Xor!=Gu8Rece\_Xor) //驗證“異或”,如果不相等,退出當前switch { break; //退出當前switch } GtBigBufferUsart.u8QueueSendBuffer\[0\]=0xeb; //數據頭 GtBigBufferUsart.u8QueueSendBuffer\[1\]=0x01; //數據類型 返回數組容量的大小 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2\]; \*pSu32Data=11; //數據長度 本條指令的數據總長是11個字節 //提取數組容量的大小 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2+4\]; \*pSu32Data=sizeof(Cu8TestTable);//相當于\*pSu32Data=57;sizeof請參考第69節 //異或算法的函數 GtBigBufferUsart.u8QueueSendBuffer\[10\]=CalculateXor(GtBigBufferUsart.u8QueueSendBuffer, 10); //最后一個字節不納入計算 //隊列驅動函數的狀態 0為初始狀態 1為通訊成功 2為通訊失敗 GtBigBufferUsart.u8QueueStatus=0; //隊列驅動函數的通訊狀態 GtBigBufferUsart.u8QueueSendTrig=1;//隊列驅動函數的發送的啟動 Gu8QueueReceUpdate=1; //告訴“隊列驅動函數”此發送指令無需等待上位機的應答 break; case 0x02: //返回下位機的分段數據 Gu8Rece\_Xor=Gu8ReceBuffer\[Gu32ReceDataLength-1\]; //提取接收到的“異或” Su8Rece\_Xor=CalculateXor(Gu8ReceBuffer,Gu32ReceDataLength-1); //計算“異或” if(Su8Rece\_Xor!=Gu8Rece\_Xor) //驗證“異或”,如果不相等,退出當前switch { break; //退出當前switch } pSu32Data=(unsigned long \*)&Gu8ReceBuffer\[6\]; //數據轉換。 Su32CurrentAddr=\*pSu32Data; //讀取的起始地址 pSu32Data=(unsigned long \*)&Gu8ReceBuffer\[6+4\]; //數據轉換。 Su32CurrentSize=\*pSu32Data; //讀取的發送的數據量 GtBigBufferUsart.u8QueueSendBuffer\[0\]=0xeb; //數據頭 GtBigBufferUsart.u8QueueSendBuffer\[1\]=0x02; //數據類型 返回分段數據 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2\]; \*pSu32Data=6+4+4+Su32CurrentSize+1; //數據總長度 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2+4\]; \*pSu32Data=Su32CurrentAddr; //返回接收到的起始地址 pSu32Data=(unsigned long \*)&GtBigBufferUsart.u8QueueSendBuffer\[2+4+4\]; \*pSu32Data=Su32CurrentSize; //返回接收到的當前批次的數據量 for(i=0;i<Su32CurrentSize;i++) { //裝載即將要發送的分段數據 GtBigBufferUsart.u8QueueSendBuffer\[6+4+4+i\]=Cu8TestTable\[Su32CurrentAddr+i\]; } //異或算法的函數 GtBigBufferUsart.u8QueueSendBuffer\[6+4+4+Su32CurrentSize\]= CalculateXor(GtBigBufferUsart.u8QueueSendBuffer, 6+4+4+Su32CurrentSize); //隊列驅動函數的狀態 0為初始狀態 1為通訊成功 2為通訊失敗 GtBigBufferUsart.u8QueueStatus=0; //隊列驅動函數的通訊狀態 GtBigBufferUsart.u8QueueSendTrig=1;//隊列驅動函數的發送的啟動 Gu8QueueReceUpdate=1; //告訴“隊列驅動函數”此發送指令無需等待上位機的應答 break; } Gu8FinishFlag=0; //上面處理完數據再清零標志,為下一次接收新的數據做準備 } } void UsartTask(void) //串口收發的任務函數,放在主函數內 { QueueSend(); //發送的隊列驅動涵數 ReceDataHandle(); //接收數據后的處理涵數 } void usart(void) interrupt 4 //串口接發的中斷函數,中斷號為4 { if(1==RI) //接收完一個字節后引起的中斷 { RI = 0; //及時清零,避免一直無緣無故的進入中斷。 if(0==Gu8FinishFlag) //1代表已經完成接收了一串新數據,并且禁止接收其它新的數據 { Gu8ReceFeedDog=1; //每接收到一個字節的數據,此標志就置1及時更新定時器的值。 switch(Gu8ReceStep) { case 0: //“前部分的”數據頭。接頭暗號的步驟。 Gu8ReceBuffer\[0\]=SBUF; //直接讀取剛接收完的一個字節的數據。 if(0xeb==Gu8ReceBuffer\[0\]) //等于數據頭0xeb,接頭暗號吻合。 { Gu32ReceCnt=1; //接收緩存的下標 Gu8ReceStep=1; //切換到下一個步驟,接收其它有效的數據 } break; case 1: //“前部分的”數據類型和長度 Gu8ReceBuffer\[Gu32ReceCnt\]=SBUF; //直接讀取剛接收完的一個字節的數據。 Gu32ReceCnt++; //每接收一個字節,數組下標都自加1,為接收下一個數據做準備 if(Gu32ReceCnt>=6) //前6個數據。接收完了“數據類型”和“數據長度”。 { Gu8ReceType=Gu8ReceBuffer\[1\]; //提取“數據類型” //以下的數據轉換,在第62節講解過的指針法 pu32Data=(unsigned long \*)&Gu8ReceBuffer\[2\]; //數據轉換 Gu32ReceDataLength=\*pu32Data; //提取“數據長度” if(Gu32ReceCnt>=Gu32ReceDataLength) //靠“數據長度”來判斷是否完成 { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } else //如果還沒結束,繼續切換到下一個步驟,接收“有效數據” { //本節只用到一個接收數組,把指針關聯到Gu8ReceBuffer本身的數組 pGu8ReceBuffer=(unsigned char \*)&Gu8ReceBuffer\[6\]; Gu32ReceCntMax=REC\_BUFFER\_SIZE; //最大緩存 Gu8ReceStep=2; //切換到下一個步驟 } } break; case 2: //“后部分的”數據 pGu8ReceBuffer\[Gu32ReceCnt-6\]=SBUF; //這里的指針就是各種不同內存的化身!!! Gu32ReceCnt++; //每接收一個字節,數組下標都自加1,為接收下一個數據做準備 //靠“數據長度”來判斷是否完成。也不允許超過數組的最大緩存的長度 if(Gu32ReceCnt>=Gu32ReceDataLength||Gu32ReceCnt>=Gu32ReceCntMax) { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } break; } } } else //發送數據引起的中斷 { TI = 0; //及時清除發送中斷的標志,避免一直無緣無故的進入中斷。 Gu8SendByteFinish=1; //從0變成1通知主函數已經發送完一個字節的數據了。 } } void UsartSendByteData(unsigned char u8SendData) //發送一個字節的底層驅動函數 { static unsigned int Su16TimeOutDelay; //超時處理的延時計時器 Gu8SendByteFinish=0; //在發送以字節之前,必須先把此全局變量的標志清零。 SBUF =u8SendData; //依靠寄存器SBUF作為載體發送一個字節的數據 Su16TimeOutDelay=0xffff; //超時處理的延時計時器裝載一個相對合理的計時初始值 while(Su16TimeOutDelay>0) //超時處理 { if(1==Gu8SendByteFinish) { break; //如果Gu8SendByteFinish為1,則發送一個字節完成,退出當前循環等待。 } Su16TimeOutDelay--; //超時計時器不斷遞減 } //Delay();//在實際應用中,當連續發送一堆數據時如果發現丟失數據,可以嘗試在此增加延時 } //發送帶協議的函數 void UsartSendMessage(const unsigned char \*pCu8SendMessage,unsigned long u32SendMaxSize) { static unsigned long i; static unsigned long \*pSu32; static unsigned long u32SendSize; pSu32=(const unsigned long \*)&pCu8SendMessage\[2\]; u32SendSize=\*pSu32; //從帶協議的數組中提取整包數組的有效發送長度 if(u32SendSize>u32SendMaxSize) //如果“有效發送長度”大于“最大限制的長度”,數據異常 { return; //數據異常,直接退出當前函數,預防數組越界 } for(i=0;i<u32SendSize;i++) //u32SendSize為發送的數據長度 { UsartSendByteData(pCu8SendMessage\[i\]); //基于“發送單字節的最小接口函數”來實現的 } } unsigned char CalculateXor(const unsigned char \*pCu8Buffer, //此處加const代表數組“只讀” unsigned long u32BufferSize) //參與計算的數組的大小 { unsigned long i; unsigned char Su8Rece\_Xor; Su8Rece\_Xor=pCu8Buffer\[0\]; //提取數據串第“i=0”個數據作為異或的原始數據 for(i=1;i<u32BufferSize;i++) //注意,這里是從第“i=1”個數據開始 { Su8Rece\_Xor=Su8Rece\_Xor^pCu8Buffer\[i\]; //計算“異或” } return Su8Rece\_Xor; //返回運算后的異或的計算結果 } void T0\_time() interrupt 1 { if(1==vGu8QueueSendTimerFlag&&vGu16QueueSendTimerCnt>0) //隊列發送的超時定時器 { vGu16QueueSendTimerCnt--; } if(1==vGu8ReceTimeOutFlag&&vGu16ReceTimeOutCnt>0) //通訊過程中字節之間的超時定時器 { vGu16ReceTimeOutCnt--; } TH0=0xfc; TL0=0x66; } void SystemInitial(void) { unsigned char u8\_TMOD\_Temp=0; //以下是定時器0的中斷的配置 TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; //以下是串口接收中斷的配置 //串口的波特率與內置的定時器1直接相關,因此配置此定時器1就等效于配置波特率。 u8\_TMOD\_Temp=0x20; //即將把定時器1設置為:工作方式2,初值自動重裝的8位定時器。 TMOD=TMOD&0x0f; //此寄存器低4位是跟定時器0相關,高4位是跟定時器1相關。先清零定時器1。 TMOD=TMOD|u8\_TMOD\_Temp; //把高4位的定時器1填入0x2,低4位的定時器0保持不變。 TH1=256-(11059200L/12/32/9600); //波特率為9600。11059200代表晶振11.0592MHz, TL1=256-(11059200L/12/32/9600); //L代表long的長類型數據。根據芯片手冊提供的計算公式。 TR1=1; //開啟定時器1 SM0=0; SM1=1; //SM0與SM1的設置:選擇10位異步通訊,波特率根據定時器1可變 REN=1; //允許串口接收數據 //為了保證串口中斷接收的數據不丟失,必須設置IP = 0x10,相當于把串口中斷設置為最高優先級, //這個時候,串口中斷可以打斷任何其他的中斷服務函數實現嵌套, IP =0x10; //把串口中斷設置為最高優先級,必須的。 ES=1; //允許串口中斷 EA=1; //允許總中斷 } void Delay(unsigned long u32DelayTime) { for(;u32DelayTime>0;u32DelayTime--); } void PeripheralInitial(void) { }
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看