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

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                【131.1 切換各種不同大小“接收內存”。】 很多32位的單片機,只要外掛SRAM或者SDRAM這類內存芯片,就可以輕松的把一個全局變量的數組開辟到幾百K甚至幾兆的容量。開辟這么大的數組,往往是用來處理一些文件類的大數據,比如串口接收一張480x272點陣大小的.BMP格式的圖片文件,就需要開辟一個幾百K的全局變量大數組。串口通信中,從接收內存的容量來劃分,常用有兩種數據類型,一種是常規控制類(容量小),一種是文件類(容量大),要能做到在這兩種“接收內存”中靈活切換,關鍵是用到“指針的中轉切換”技術。 “常規控制類內存”負責兩塊事務,一塊是接收“前部分的”\[數據頭,數據類型,數據長度\],另一塊是“后部分的”\[常規控制類的專用數據\]。 “文件類內存”只負責“后部分的”\[文件類的專用數據\],而“前部分的”\[數據頭,數據類型,數據長度\]是需要借助“常規控制類內存”來實現的。 本節破題的關鍵在于,根據不同的數據類型,利用“指針的中轉切換”實現不同接收內存的靈活切換。關鍵代碼是串口中斷函數這部分的處理,片段代碼的講解如下: unsigned char Gu8ReceBuffer\[20\]; //常規控制類的小內存 unsigned char Gu8FileBuffer\[40\]; //文件類的大內存 unsigned char \*pGu8ReceBuffer; //用來切換接收內存的“中轉指針” unsigned long Gu32ReceCntMax=20; //最大緩存(初始值20或者40都沒關系,因為后續會動態改變) void usart(void) interrupt 4 { if(1==RI) { RI = 0; if(0==Gu8FinishFlag) { Gu8ReceFeedDog=1; switch(Gu8ReceStep) { case 0: //“前部分的”數據頭。接頭暗號的步驟 Gu8ReceBuffer\[0\]=SBUF; if(0xeb==Gu8ReceBuffer\[0\]) { Gu32ReceCnt=1; Gu8ReceStep=1; } break; case 1: //“前部分的”數據類型和長度 Gu8ReceBuffer\[Gu32ReceCnt\]=SBUF; Gu32ReceCnt++; if(Gu32ReceCnt>=6) //前6個數據。接收完了“數據類型”和“數據長度”。 { Gu8ReceType=Gu8ReceBuffer\[1\]; //提取“數據類型” pu32Data=(unsigned long \*)&Gu8ReceBuffer\[2\]; Gu32ReceDataLength=\*pu32Data; //提取“數據長度” if(Gu32ReceCnt>=Gu32ReceDataLength) //靠“數據長度”來判斷是否完成 { Gu8FinishFlag=1; //接收完成標志“置1”,通知主函數處理。 Gu8ReceStep=0; //及時切換回接頭暗號的步驟 } else //如果還沒結束,繼續切換到下一個步驟,接收“有效數據” { //以下幾行代碼是本節的破題關鍵!!! if(0x02==Gu8ReceType) //如果是文件類,把指針關聯到Gu8FileBuffer { pGu8ReceBuffer=(unsigned char \*)&Gu8FileBuffer\[0\];//下標0 Gu32ReceCntMax=40+6; //最大緩存 } else //如果是常規類,繼續把指針關聯到Gu8ReceBuffer本身的數組 { pGu8ReceBuffer=(unsigned char \*)&Gu8ReceBuffer\[6\];//下標6 Gu32ReceCntMax=20; //最大緩存 } 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; //及時清除發送中斷的標志,避免一直無緣無故的進入中斷。 //以下可以添加一個全局變量的標志位的相關代碼,通知主函數已經發送完一個字節的數據了。 } } 【131.2 通信協議。】 數據頭(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,依次循環。這是數據校驗的一種方式。 異或(0B)。“異或”放在數據串的最后一個字節,是前面所有字節的異或結果(不包括自己本身的字節)。比如:本例子中,數據串是:EB 01 00 00 00 0B 03 E8 00 01 0B。其中最后一個字節0B就是“異或”字節,前面所有字節相“異或”等于十六進制的0B。驗證“異或”的方法,可以借用電腦“附件”自帶的“計算器”軟件來實現,打開“計算器”軟件后,在“查看”的下拉菜單里,選擇“程序員”,然后選擇“十六進制”,該計算器軟件的異或運算按鍵是“Xor”。不管是主機還是從機,每接收到一串數據后,都要自己計算一次“異或”,把自己計算得到的“異或”與接收到的最后一個字節的“異或”進行對比,來判斷接收到的數據是否發生了丟失或者錯誤。 【131.3 程序例程。】 **![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png)** 上圖131.3.1 有源蜂鳴器電路 ![](https://img.kancloud.cn/57/01/57010762abae4157797b57319514eff1_468x181.png) 上圖131.3.2 232串口電路 程序功能如下: (9) 單片機模擬從機,上位機的串口助手模擬主機。在上位機的串口助手里,發送一串數據,控制蜂鳴器發出不同長度的聲音。數據類型為01時,把“后部分的”數據發送給“常規控制類內存”;數據類型為02時,把“后部分的”數據發送給“文件類內存”。 (10) 本節因為還沒有講到數據發送的內容,因此應答“動態密匙”那部分的代碼暫時不寫,只寫驗證“異或”那部分的代碼。 (11) 波特率9600,校驗位NONE(無),數據位8,停止位1。 (12) 十六進制的數據格式: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 代表前面所有字節的異或結果。 比如: 數據類型01,“后部分的”數據發給“常規控制類內存”,讓蜂鳴器鳴叫1000毫秒,密匙為00 01,發送十六進制的:EB 01 00 00 00 0B 03 E8 00 01 0B 數據類型02,“后部分的”數據發給“文件類內存”,讓蜂鳴器鳴叫100毫秒, 密匙為00 02,發送十六進制的:EB 02 00 00 00 0B 00 64 00 02 84 \#include "REG52.H" \#define RECE\_TIME\_OUT 2000 //通信過程中字節之間的超時時間2000ms \#define REC\_BUFFER\_SIZE 20 //常規控制類數組的長度 \#define FILE\_BUFFER\_SIZE 40 //文件類數組的長度 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 char Gu8FileBuffer\[FILE\_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 int Gu16ReceYY=0; //接收的動態密匙 unsigned char Gu8ReceZZ=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(); //串口接收的任務函數 } } 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 //如果還沒結束,繼續切換到下一個步驟,接收“有效數據” { //以下幾行代碼是本節的破題關鍵!!! if(0x02==Gu8ReceType) //如果是文件類,把指針關聯到Gu8FileBuffer { pGu8ReceBuffer=(unsigned char \*)&Gu8FileBuffer\[0\];//下標0 Gu32ReceCntMax=FILE\_BUFFER\_SIZE+6; //最大緩存 } else //如果是常規類,繼續把指針關聯到Gu8ReceBuffer本身的數組 { pGu8ReceBuffer=(unsigned char \*)&Gu8ReceBuffer\[6\];//下標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; //及時清除發送中斷的標志,避免一直無緣無故的進入中斷。 //以下可以添加一個全局變量的標志位的相關代碼,通知主函數已經發送完一個字節的數據了。 } } void UsartTask(void) //串口接收的任務函數,放在主函數內 { static unsigned int \*pSu16Data; //數據轉換的指針 static unsigned int Su16Data; //轉換后的數據 static unsigned int i; static unsigned char Su8RecZZ=0; //計算的“異或” 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=Gu8ReceBuffer\[0\]; //提取數據串第“i=0”個數據作為異或的原始數據 for(i=1;i<(Gu32ReceDataLength-1);i++) //注意,這里是從第“i=1”個數據開始 { Su8RecZZ=Su8RecZZ^Gu8ReceBuffer\[i\]; //計算“異或” } if(Su8RecZZ==Gu8ReceZZ) //驗證“異或”,“計算的”與“接收的”是否一致 { pSu16Data=(unsigned int \*)&Gu8ReceBuffer\[6\]; //數據轉換。 Su16Data=\*pSu16Data; //提取“蜂鳴器聲音的長度” vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=Su16Data; //讓蜂鳴器鳴叫 vGu8BeepTimerFlag=1; } break; case 0x02: //文件類的大內存。驅動蜂鳴器。 //以下的數據轉換,在第62節講解過的指針法 pSu16Data=(unsigned int \*)&Gu8ReceBuffer\[Gu32ReceDataLength-3\]; //數據轉換 Gu16ReceYY=\*pSu16Data; //提取“動態密匙”。本例子中暫時不做返回應答的處理 //注意,請留意以下代碼文件類內存數組Gu8FileBuffer的下標位置 Gu8ReceZZ=Gu8FileBuffer\[Gu32ReceDataLength-1-6\]; //提取接收到的“異或” //前面6個字節是“前部分的”\[數據頭,數據類型,數據長度\] Su8RecZZ=Gu8ReceBuffer\[0\]; //提取數據串第“i=0”個數據作為異或的原始數據 for(i=1;i<6;i++) //注意,這里是從第“i=1”個數據開始 { Su8RecZZ=Su8RecZZ^Gu8ReceBuffer\[i\]; //計算“前部分的”“異或” } //6個字節之后是“后部分的”“文件類專用的數據” for(i=0;i<(Gu32ReceDataLength-1-6);i++) { Su8RecZZ=Su8RecZZ^Gu8FileBuffer\[i\]; //計算“后部分的”“異或” } if(Su8RecZZ==Gu8ReceZZ) //驗證“異或”,“計算的”與“接收的”是否一致 { pSu16Data=(unsigned int \*)&Gu8FileBuffer\[0\]; //數據轉換。此處下標0! 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>

                              哎呀哎呀视频在线观看