找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 409|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

鴻哥的 固定協(xié)議串口程序

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:299910 發(fā)表于 2025-3-15 10:47 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
這是鴻哥的固定協(xié)議串口接收程序。

鴻哥寬廣的胸懷讓我敬佩不已,能夠拜讀鴻哥的大作是我的幸運,向鴻哥道謝、致敬!



程序功能如下:

(1)在上位機的串口助手里,發(fā)送一串數(shù)據(jù),控制蜂鳴器發(fā)出不同長度的聲音。

(2)波特率 9600,校驗位 NONE(無),數(shù)據(jù)位 8,停止位 1。

(3)十六進制的數(shù)據(jù)格式如下:

EB 01 00 00 00 08 XX XX

其中 EB 是數(shù)據(jù)頭,01 是代表數(shù)據(jù)類型,00 00 00 08 代表數(shù)據(jù)長度是 8 個(十進制)。XX XX 代表

一個 unsigned int 的數(shù)據(jù),此數(shù)據(jù)的大小決定了蜂鳴器發(fā)出聲音的長度。比如:

讓蜂鳴器鳴叫 1000ms 的時間,發(fā)送十六進制的: EB 01 00 00 00 08 03 E8

讓蜂鳴器鳴叫 100ms 的時間,發(fā)送十六進制的: EB 01 00 00 00 08 00 64

*/

#include "REG52.H"

#define RECE_TIME_OUT 2000 //通信過程中字節(jié)之間的超時時間 2000ms

#define REC_BUFFER_SIZE 20 //接收數(shù)據(jù)的緩存數(shù)組的長度



void usart(void); //串口接收的中斷函數(shù)

void T0_time(); //定時器的中斷函數(shù)
void UsartTask(void); //串口接收的任務(wù)函數(shù),放在主函數(shù)內(nèi)

void SystemInitial(void) ;

void Delay(unsigned long u32DelayTime) ;

void PeripheralInitial(void) ;



void BeepOpen(void);

void BeepClose(void);

void VoiceScan(void);



sbit P3_6=P3^6;

sbit P0_0=P0^0;



volatile unsigned char vGu8BeepTimerFlag=0;

volatile unsigned int vGu16BeepTimerCnt=0;



unsigned char Gu8ReceBuffer[REC_BUFFER_SIZE]; //開辟一片接收數(shù)據(jù)的緩存

unsigned long Gu32ReceCnt=0; //接收緩存數(shù)組的下標

unsigned char Gu8ReceStep=0; //接收中斷函數(shù)里的步驟變量

unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作變量。

unsigned char Gu8ReceType=0; //接收的數(shù)據(jù)類型

unsigned long Gu32ReceDataLength=0; //接收的數(shù)據(jù)長度

unsigned char Gu8FinishFlag=0; //是否已接收完成一串數(shù)據(jù)的標志

unsigned long *pu32Data; //用于數(shù)據(jù)轉(zhuǎn)換的指針

volatile unsigned char vGu8ReceTimeOutFlag=0;//通信過程中字節(jié)之間的超時定時器的開關(guān)

volatile unsigned int vGu16ReceTimeOutCnt=0; //通信過程中字節(jié)之間的超時定時器,“喂狗”的對象



void main()

{

SystemInitial();

Delay(10000);

PeripheralInitial();

while(1)

{

UsartTask(); //串口接收的任務(wù)函數(shù)

}

}



void usart(void) interrupt 4 //串口接發(fā)的中斷函數(shù),中斷號為 4

{



if(1==RI) //接收完一個字節(jié)后引起的中斷

{

RI = 0; //及時清零,避免一直無緣無故的進入中斷。



/* 注釋一:

* 以下 Gu8FinishFlag 變量的用途。

* 此變量一箭雙雕,0 代表正處于接收數(shù)據(jù)的狀態(tài),1 代表已經(jīng)接收完畢并且及時通知主函數(shù)中的處理函數(shù)

* UsartTask()去處理新接收到的一串數(shù)據(jù)。除此之外,還起到一種“自鎖自保護”的功能,在新數(shù)據(jù)還

* 沒有被主函數(shù)處理完畢的時候,禁止接收其它新的數(shù)據(jù),避免新數(shù)據(jù)覆蓋了尚未處理的數(shù)據(jù)。

*/

if(0==Gu8FinishFlag) //1 代表已經(jīng)完成接收了一串新數(shù)據(jù),并且禁止接收其它新的數(shù)據(jù)

{



/* 注釋二:

* 以下 Gu8ReceFeedDog 變量的用途。

* 此變量是用來檢測并且識別通信過程中相鄰的字節(jié)之間是否存在超時的情況。

* 如果大家聽說過單片機中的“看門狗”這個概念,那么每接收到一個數(shù)據(jù)此變量就“置 1”一次,它的

* 作用就是起到及時“喂狗”的作用。每接收到一個數(shù)據(jù)此變量就“置 1”一次,在主函數(shù)里,相關(guān)

* 的定時器就會被重新賦值,只要這個定時器能不斷及時的被補充新的“能量”新的值,那么這個定時器

* 就永遠不會變成 0,只要不變成 0 就不會超時。如果兩個字節(jié)之間通信時間超過了固定的長度,就意味

* 著此定時器變成了 0,這時就需要把中斷函數(shù)里的接收步驟 Gu8Step 及時切換到“接頭暗號”的步驟。

*/

Gu8ReceFeedDog=1; //每接收到一個字節(jié)的數(shù)據(jù),此標志就置 1 及時更新定時器的值。

switch(Gu8ReceStep)

{

case 0: //接頭暗號的步驟。判斷數(shù)據(jù)頭的步驟。

Gu8ReceBuffer[0]=SBUF; //直接讀取剛接收完的一個字節(jié)的數(shù)據(jù)。

if(0xeb==Gu8ReceBuffer[0]) //等于數(shù)據(jù)頭 0xeb,接頭暗號吻合。

{

Gu32ReceCnt=1; //接收緩存的下標

Gu8ReceStep=1; //切換到下一個步驟,接收其它有效的數(shù)據(jù)

}

break;



case 1: //數(shù)據(jù)類型和長度

Gu8ReceBuffer[Gu32ReceCnt]=SBUF; //直接讀取剛接收完的一個字節(jié)的數(shù)據(jù)。

Gu32ReceCnt++; //每接收一個字節(jié),數(shù)組下標都自加 1,為接收下一個數(shù)據(jù)做準備

if(Gu32ReceCnt>=6) //前 6 個數(shù)據(jù)。接收完了“數(shù)據(jù)類型”和“數(shù)據(jù)長度”。

{

Gu8ReceType=Gu8ReceBuffer[1]; //提取“數(shù)據(jù)類型”

//以下的數(shù)據(jù)轉(zhuǎn)換,在第 62 節(jié)講解過的指針法

pu32Data=(unsigned long *)&Gu8ReceBuffer[2]; //數(shù)據(jù)轉(zhuǎn)換

Gu32ReceDataLength=*pu32Data; //提取“數(shù)據(jù)長度”

if(Gu32ReceCnt>=Gu32ReceDataLength) //靠“數(shù)據(jù)長度”來判斷是否完成



{

Gu8FinishFlag=1; //接收完成標志“置 1”,通知主函數(shù)處理。

Gu8ReceStep=0; //及時切換回接頭暗號的步驟

}

else //如果還沒結(jié)束,繼續(xù)切換到下一個步驟,接收“其它數(shù)據(jù)”

{

Gu8ReceStep=2; //切換到下一個步驟

}

}

break;



case 2: //其它數(shù)據(jù)

Gu8ReceBuffer[Gu32ReceCnt]=SBUF; //直接讀取剛接收完的一個字節(jié)的數(shù)據(jù)。

Gu32ReceCnt++; //每接收一個字節(jié),數(shù)組下標都自加 1,為接收下一個數(shù)據(jù)做準備



//靠“數(shù)據(jù)長度”來判斷是否完成。也不允許超過數(shù)組的最大緩存的長度

if(Gu32ReceCnt>=Gu32ReceDataLength||Gu32ReceCnt>=REC_BUFFER_SIZE)

{

Gu8FinishFlag=1; //接收完成標志“置 1”,通知主函數(shù)處理。

Gu8ReceStep=0; //及時切換回接頭暗號的步驟

}

break;

}

}

}

else //發(fā)送數(shù)據(jù)引起的中斷

{

TI = 0; //及時清除發(fā)送中斷的標志,避免一直無緣無故的進入中斷。

//以下可以添加一個全局變量的標志位的相關(guān)代碼,通知主函數(shù)已經(jīng)發(fā)送完一個字節(jié)的數(shù)據(jù)了。

}

}



void UsartTask(void) //串口接收的任務(wù)函數(shù),放在主函數(shù)內(nèi)

{

static unsigned int *pSu16Data; //數(shù)據(jù)轉(zhuǎn)換的指針

static unsigned int Su16Data; //轉(zhuǎn)換后的數(shù)據(jù)



if(1==Gu8ReceFeedDog) //每被“喂一次狗”,就及時更新一次“超時檢測的定時器”的初值

{

Gu8ReceFeedDog=0;



vGu8ReceTimeOutFlag=0;

vGu16ReceTimeOutCnt=RECE_TIME_OUT;//更新一次“超時檢測的定時器”的初值

vGu8ReceTimeOutFlag=1;



}

else if(Gu8ReceStep>0&&0==vGu16ReceTimeOutCnt) //超時,并且步驟不在接頭暗號的步驟

{

Gu8ReceStep=0; //串口接收數(shù)據(jù)的中斷函數(shù)及時切換回接頭暗號的步驟

}



if(1==Gu8FinishFlag) //1 代表已經(jīng)接收完畢一串新的數(shù)據(jù),需要馬上去處理

{

switch(Gu8ReceType) //接收到的數(shù)據(jù)類型

{

case 0x01: //驅(qū)動蜂鳴器

//以下的數(shù)據(jù)轉(zhuǎn)換,在第 62 節(jié)講解過的指針法

pSu16Data=(unsigned int *)&Gu8ReceBuffer[6]; //數(shù)據(jù)轉(zhuǎn)換。

Su16Data=*pSu16Data; //提取“蜂鳴器聲音的長度”



vGu8BeepTimerFlag=0;

vGu16BeepTimerCnt=Su16Data; //讓蜂鳴器鳴叫

vGu8BeepTimerFlag=1;

break;

}



Gu8FinishFlag=0; //上面處理完數(shù)據(jù)再清零標志,為下一次接收新的數(shù)據(jù)做準備

}

}



void T0_time() interrupt 1

{

VoiceScan();



if(1==vGu8ReceTimeOutFlag&&vGu16ReceTimeOutCnt>0) //通信過程中字節(jié)之間的超時定時器

{

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;



//以下是串口接收中斷的配置

//串口的波特率與內(nèi)置的定時器 1 直接相關(guān),因此配置此定時器 1 就等效于配置波特率。

u8_TMOD_Temp=0x20; //即將把定時器 1 設(shè)置為:工作方式 2,初值自動重裝的 8 位定時器。

TMOD=TMOD&0x0f; //此寄存器低 4 位是跟定時器 0 相關(guān),高 4 位是跟定時器 1 相關(guān)。先清零定時器 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 的長類型數(shù)據(jù)。根據(jù)芯片手冊提供的計算公式。

TR1=1; //開啟定時器 1



SM0=0;

SM1=1; //SM0 與 SM1 的設(shè)置:選擇 10 位異步通信,波特率根據(jù)定時器 1 可變

REN=1; //允許串口接收數(shù)據(jù)



//為了保證串口中斷接收的數(shù)據(jù)不丟失,必須設(shè)置 IP = 0x10,相當于把串口中斷設(shè)置為最高優(yōu)先級,

//這個時候,串口中斷可以打斷任何其他的中斷服務(wù)函數(shù)實現(xiàn)嵌套,

IP =0x10; //把串口中斷設(shè)置為最高優(yōu)先級,必須的。



ES=1; //允許串口中斷

EA=1; //允許總中斷

}



void Delay(unsigned long u32DelayTime)

{

for(;u32DelayTime>0;u32DelayTime--);

}



void PeripheralInitial(void)

{


}

void BeepOpen(void)

{

P3_6=1;

P0_0=0;


}

void BeepClose(void)

{

P3_6=0;

P0_0=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();

}

}

}

}
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩
回復(fù)

使用道具 舉報

沙發(fā)
ID:462629 發(fā)表于 2025-5-6 10:34 | 只看該作者
08 是怎么計算出來的啊,應(yīng)該不是xx xx的吧
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復(fù) 返回頂部 返回列表