<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                【129.1 “累加和”與“動態密匙”。】 上一節講了串口基本的程序框架,但是沒有講到校驗。校驗在很多通信項目中是必不可少的。比如,在事關金融或者生命安全的項目,是不允許有任何的數據丟失或錯誤的;在容易受干擾的工業環境,或者在無線通信的項目中,這些項目往往容易丟失數據;還有一種常見的人為過失是,在編寫程序的層面,因為超時重發的時間與從機不匹配,導致反饋的信息延時而造成數據丟失,如果這種情況也加上校驗,通信會穩定可靠很多。 上一節講到“數據頭,數據類型,數據長度,其它數據”這四個元素,本節在此基礎上,增加兩個校驗的元素,分別是“動態密匙”與“累加和”。“動態密匙”占用2個字節,“累加和”占用1個字節,因此,這兩個元素一共占用最后面的3個字節。分析如下: 數據頭(EB):占1個字節,作為“起始字節”,起到“接頭暗號”的作用,平時用來過濾無關的數據。 數據類型(01):占用1個字節。數據類型是用來定義這串數據的用途。 數據長度(00 00 00 0B):占4個字節。用來告訴通信的對方,這串數據一共有多少個字節。 其它數據(03 E8):此數據根據不同的“數據類型”可以用來做不同的用途,根據具體的項目而定。 動態密匙(00 01):這兩個字節代表一個unsigned int類型的數據,數據范圍是從0到65535,但是考慮到數據更加安全可靠,一般丟棄了首尾的0(十六進制的00 00)與65535(十六進制的FF FF),只保留從1到65534的變化。大部分的通信模型都是主機對從機的“一問一應答”模式,也就是,主機每發送一條指令給從機,從機才返回一條消息作為應答。如果主機發送了信息后,在規定的時間內,沒有收到從機的應答指令,主機就繼續發送信息給從機,但是此時,從機本來應該應答主機當前指令的,可能因為某種情況導致反饋的信息發生了延時,導致此時應答的數據是主機的上一條指令,從而造成“一問一應答”的數據幀發送了錯位,這種情況加上“動態密匙”就能使問題得到有效的解決。主機每發送一條信息,信息里都攜帶了2個字節的“動態密匙”,從機每收到主機的一條信息,在應答此信息時都把收到的“動態密匙”原封不動的反饋給主機,主機再查看發送的“動態密匙”與接收到的“動態密匙”是否一致,以此來判斷應答數據是否有效。“動態密匙”像流水號一樣,每發送一次指令后都累加1,不斷發生變化,從1到65534,依次循環。這是數據校驗的一種方式。 累加和(E3)。“累加和”放在數據串的最后一個字節,是前面所有字節的累加之和(不包括自己本身的字節),累加的結果高于一個字節的那部分自動溢出丟掉,只保留低8位的一個字節的數據。比如:本例子中,數據串是:EB 01 00 00 00 0B 03 E8 00 01 E3。其中最后一個字節E3就是“累加和”,前面所有字節相加等于十六進制的0x1E3,只保留低8位的一個字節的數據,因此為十六進制的0xE3。驗證“累加和”的方法,可以借用電腦“附件”自帶的“計算器”軟件來實現,打開“計算器”軟件后,在“查看”的下拉菜單里,選擇“程序員”,然后選擇“十六進制”。不管是主機還是從機,每接收到一串數據后,都要自己計算一次“累加和”,把自己計算得到的“累加和”與接收到的最后一個字節的“累加和”進行對比,來判斷接收到的數據是否發生了丟失或者錯誤。 【129.2 程序例程。】 **![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png)** 上圖129.2.1 有源蜂鳴器電路 ![](https://img.kancloud.cn/57/01/57010762abae4157797b57319514eff1_468x181.png) 上圖129.2.2 232串口電路 程序功能如下: (1)單片機模擬從機,上位機的串口助手模擬主機。在上位機的串口助手里,發送一串數據,控制蜂鳴器發出不同長度的聲音。 (2)本節因為還沒有講到數據發送的內容,因此應答“動態密匙”那部分的代碼暫時不寫,只寫驗證“累加和”那部分的代碼。 (3)波特率9600,校驗位NONE(無),數據位8,停止位1。 (4) 十六進制的數據格式:EB 01 00 00 00 0B XX XX YY YY ZZ 。其中: EB是數據頭。 01是代表數據類型。 00 00 00 0B代表數據長度是11個(十進制)。 XX XX代表一個unsigned int的數據,此數據的大小決定了蜂鳴器發出聲音的長度。 YY YY代表一個unsigned int的動態密匙,每收發一條指令,此數據累加一次1,范圍從1到65534。 ZZ 代表前面所有字節的累加和。 比如: 讓蜂鳴器鳴叫1000毫秒,密匙為00 01,發送十六進制的:EB 01 00 00 00 0B 03 E8 00 01 E3 讓蜂鳴器鳴叫100毫秒, 密匙為00 02,發送十六進制的:EB 01 00 00 00 0B 00 64 00 02 5D \#include "REG52.H" \#define RECE\_TIME\_OUT 2000 //通信過程中字節之間的超時時間2000ms \#define REC\_BUFFER\_SIZE 20 //接收數據的緩存數組的長度 void usart(void); //串口接收的中斷函數 void T0\_time(); //定時器的中斷函數 void UsartTask(void); //串口接收的任務函數,放在主函數內 void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); void BeepClose(void); void VoiceScan(void); sbit P3\_4=P3^4; volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; unsigned char Gu8ReceBuffer\[REC\_BUFFER\_SIZE\]; //開辟一片接收數據的緩存 unsigned long Gu32ReceCnt=0; //接收緩存數組的下標 unsigned char Gu8ReceStep=0; //接收中斷函數里的步驟變量 unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作變量。 unsigned char Gu8ReceType=0; //接收的數據類型 unsigned int Gu16ReceYY=0; //接收的動態密匙 unsigned char Gu8ReceZZ=0; //接收的累加和,必須是unsigned char的數據類型 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(); //串口接收的任務函數 } } void usart(void) interrupt 4 //串口接發的中斷函數,中斷號為4 { if(1==RI) //接收完一個字節后引起的中斷 { RI = 0; //及時清零,避免一直無緣無故的進入中斷。 /\* 注釋一: \* 以下Gu8FinishFlag變量的用途。 \* 此變量一箭雙雕,0代表正處于接收數據的狀態,1代表已經接收完畢并且及時通知主函數中的處理函數 \* UsartTask()去處理新接收到的一串數據。除此之外,還起到一種“自鎖自保護”的功能,在新數據還 \* 沒有被主函數處理完畢的時候,禁止接收其它新的數據,避免新數據覆蓋了尚未處理的數據。 \*/ if(0==Gu8FinishFlag) //1代表已經完成接收了一串新數據,并且禁止接收其它新的數據 { /\* 注釋二: \* 以下Gu8ReceFeedDog變量的用途。 \* 此變量是用來檢測并且識別通信過程中相鄰的字節之間是否存在超時的情況。 \* 如果大家聽說過單片機中的“看門狗”這個概念,那么每接收到一個數據此變量就“置1”一次,它的 \* 作用就是起到及時“喂狗”的作用。每接收到一個數據此變量就“置1”一次,在主函數里,相關 \* 的定時器就會被重新賦值,只要這個定時器能不斷及時的被補充新的“能量”新的值,那么這個定時器 \* 就永遠不會變成0,只要不變成0就不會超時。如果兩個字節之間通信時間超過了固定的長度,就意味 \* 著此定時器變成了0,這時就需要把中斷函數里的接收步驟Gu8Step及時切換到“接頭暗號”的步驟。 \*/ 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 //如果還沒結束,繼續切換到下一個步驟,接收“其它數據” { Gu8ReceStep=2; //切換到下一個步驟 } } break; case 2: //其它數據 Gu8ReceBuffer\[Gu32ReceCnt\]=SBUF; //直接讀取剛接收完的一個字節的數據。 Gu32ReceCnt++; //每接收一個字節,數組下標都自加1,為接收下一個數據做準備 //靠“數據長度”來判斷是否完成。也不允許超過數組的最大緩存的長度 if(Gu32ReceCnt>=Gu32ReceDataLength||Gu32ReceCnt>=REC\_BUFFER\_SIZE) { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } break; } } } else //發送數據引起的中斷 { TI = 0; //及時清除發送中斷的標志,避免一直無緣無故的進入中斷。 //以下可以添加一個全局變量的標志位的相關代碼,通知主函數已經發送完一個字節的數據了。 } } void UsartTask(void) //串口接收的任務函數,放在主函數內 { static unsigned int \*pSu16Data; //數據轉換的指針 static unsigned int Su16Data; //轉換后的數據 static unsigned int i; static unsigned char Su8RecZZ=0; //計算的“累加和”,必須是unsigned char的數據類型 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: //驅動蜂鳴器 //以下的數據轉換,在第62節講解過的指針法 pSu16Data=(unsigned int \*)&Gu8ReceBuffer\[Gu32ReceDataLength-3\]; //數據轉換 Gu16ReceYY=\*pSu16Data; //提取“動態密匙”。本例子中暫時不做返回應答的處理 Gu8ReceZZ=Gu8ReceBuffer\[Gu32ReceDataLength-1\]; //提取“累加和” Su8RecZZ=0; for(i=0;i<(Gu32ReceDataLength-1);i++) { Su8RecZZ=Su8RecZZ+Gu8ReceBuffer\[i\]; //計算“累加和” } if(Su8RecZZ==Gu8ReceZZ) //驗證“累加和”,“計算的”與“接收的”是否一致 { pSu16Data=(unsigned int \*)&Gu8ReceBuffer\[6\]; //數據轉換。 Su16Data=\*pSu16Data; //提取“蜂鳴器聲音的長度” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=Su16Data; //讓蜂鳴器鳴叫 vGu8BeepTimerFlag=1; } break; } Gu8FinishFlag=0; //上面處理完數據再清零標志,為下一次接收新的數據做準備 } } void T0\_time() interrupt 1 { VoiceScan(); 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) { } 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(); } } } }
                  <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>

                              哎呀哎呀视频在线观看