|
這是鴻哥的固定協(xié)議串口接收程序。
鴻哥寬廣的胸懷讓我敬佩不已,能夠拜讀鴻哥的大作是我的幸運(yùn),向鴻哥道謝、致敬!
程序功能如下:
(1)在上位機(jī)的串口助手里,發(fā)送一串?dāng)?shù)據(jù),控制蜂鳴器發(fā)出不同長(zhǎng)度的聲音。
(2)波特率 9600,校驗(yàn)位 NONE(無),數(shù)據(jù)位 8,停止位 1。
(3)十六進(jìn)制的數(shù)據(jù)格式如下:
EB 01 00 00 00 08 XX XX
其中 EB 是數(shù)據(jù)頭,01 是代表數(shù)據(jù)類型,00 00 00 08 代表數(shù)據(jù)長(zhǎng)度是 8 個(gè)(十進(jìn)制)。XX XX 代表
一個(gè) unsigned int 的數(shù)據(jù),此數(shù)據(jù)的大小決定了蜂鳴器發(fā)出聲音的長(zhǎng)度。比如:
讓蜂鳴器鳴叫 1000ms 的時(shí)間,發(fā)送十六進(jìn)制的: EB 01 00 00 00 08 03 E8
讓蜂鳴器鳴叫 100ms 的時(shí)間,發(fā)送十六進(jìn)制的: EB 01 00 00 00 08 00 64
*/
#include "REG52.H"
#define RECE_TIME_OUT 2000 //通信過程中字節(jié)之間的超時(shí)時(shí)間 2000ms
#define REC_BUFFER_SIZE 20 //接收數(shù)據(jù)的緩存數(shù)組的長(zhǎng)度
void usart(void); //串口接收的中斷函數(shù)
void T0_time(); //定時(shí)器的中斷函數(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ù)組的下標(biāo)
unsigned char Gu8ReceStep=0; //接收中斷函數(shù)里的步驟變量
unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作變量。
unsigned char Gu8ReceType=0; //接收的數(shù)據(jù)類型
unsigned long Gu32ReceDataLength=0; //接收的數(shù)據(jù)長(zhǎng)度
unsigned char Gu8FinishFlag=0; //是否已接收完成一串?dāng)?shù)據(jù)的標(biāo)志
unsigned long *pu32Data; //用于數(shù)據(jù)轉(zhuǎn)換的指針
volatile unsigned char vGu8ReceTimeOutFlag=0;//通信過程中字節(jié)之間的超時(shí)定時(shí)器的開關(guān)
volatile unsigned int vGu16ReceTimeOutCnt=0; //通信過程中字節(jié)之間的超時(shí)定時(shí)器,“喂狗”的對(duì)象
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
UsartTask(); //串口接收的任務(wù)函數(shù)
}
}
void usart(void) interrupt 4 //串口接發(fā)的中斷函數(shù),中斷號(hào)為 4
{
if(1==RI) //接收完一個(gè)字節(jié)后引起的中斷
{
RI = 0; //及時(shí)清零,避免一直無緣無故的進(jìn)入中斷。
/* 注釋一:
* 以下 Gu8FinishFlag 變量的用途。
* 此變量一箭雙雕,0 代表正處于接收數(shù)據(jù)的狀態(tài),1 代表已經(jīng)接收完畢并且及時(shí)通知主函數(shù)中的處理函數(shù)
* UsartTask()去處理新接收到的一串?dāng)?shù)據(jù)。除此之外,還起到一種“自鎖自保護(hù)”的功能,在新數(shù)據(jù)還
* 沒有被主函數(shù)處理完畢的時(shí)候,禁止接收其它新的數(shù)據(jù),避免新數(shù)據(jù)覆蓋了尚未處理的數(shù)據(jù)。
*/
if(0==Gu8FinishFlag) //1 代表已經(jīng)完成接收了一串新數(shù)據(jù),并且禁止接收其它新的數(shù)據(jù)
{
/* 注釋二:
* 以下 Gu8ReceFeedDog 變量的用途。
* 此變量是用來檢測(cè)并且識(shí)別通信過程中相鄰的字節(jié)之間是否存在超時(shí)的情況。
* 如果大家聽說過單片機(jī)中的“看門狗”這個(gè)概念,那么每接收到一個(gè)數(shù)據(jù)此變量就“置 1”一次,它的
* 作用就是起到及時(shí)“喂狗”的作用。每接收到一個(gè)數(shù)據(jù)此變量就“置 1”一次,在主函數(shù)里,相關(guān)
* 的定時(shí)器就會(huì)被重新賦值,只要這個(gè)定時(shí)器能不斷及時(shí)的被補(bǔ)充新的“能量”新的值,那么這個(gè)定時(shí)器
* 就永遠(yuǎn)不會(huì)變成 0,只要不變成 0 就不會(huì)超時(shí)。如果兩個(gè)字節(jié)之間通信時(shí)間超過了固定的長(zhǎng)度,就意味
* 著此定時(shí)器變成了 0,這時(shí)就需要把中斷函數(shù)里的接收步驟 Gu8Step 及時(shí)切換到“接頭暗號(hào)”的步驟。
*/
Gu8ReceFeedDog=1; //每接收到一個(gè)字節(jié)的數(shù)據(jù),此標(biāo)志就置 1 及時(shí)更新定時(shí)器的值。
switch(Gu8ReceStep)
{
case 0: //接頭暗號(hào)的步驟。判斷數(shù)據(jù)頭的步驟。
Gu8ReceBuffer[0]=SBUF; //直接讀取剛接收完的一個(gè)字節(jié)的數(shù)據(jù)。
if(0xeb==Gu8ReceBuffer[0]) //等于數(shù)據(jù)頭 0xeb,接頭暗號(hào)吻合。
{
Gu32ReceCnt=1; //接收緩存的下標(biāo)
Gu8ReceStep=1; //切換到下一個(gè)步驟,接收其它有效的數(shù)據(jù)
}
break;
case 1: //數(shù)據(jù)類型和長(zhǎng)度
Gu8ReceBuffer[Gu32ReceCnt]=SBUF; //直接讀取剛接收完的一個(gè)字節(jié)的數(shù)據(jù)。
Gu32ReceCnt++; //每接收一個(gè)字節(jié),數(shù)組下標(biāo)都自加 1,為接收下一個(gè)數(shù)據(jù)做準(zhǔn)備
if(Gu32ReceCnt>=6) //前 6 個(gè)數(shù)據(jù)。接收完了“數(shù)據(jù)類型”和“數(shù)據(jù)長(zhǎng)度”。
{
Gu8ReceType=Gu8ReceBuffer[1]; //提取“數(shù)據(jù)類型”
//以下的數(shù)據(jù)轉(zhuǎn)換,在第 62 節(jié)講解過的指針法
pu32Data=(unsigned long *)&Gu8ReceBuffer[2]; //數(shù)據(jù)轉(zhuǎn)換
Gu32ReceDataLength=*pu32Data; //提取“數(shù)據(jù)長(zhǎng)度”
if(Gu32ReceCnt>=Gu32ReceDataLength) //靠“數(shù)據(jù)長(zhǎng)度”來判斷是否完成
{
Gu8FinishFlag=1; //接收完成標(biāo)志“置 1”,通知主函數(shù)處理。
Gu8ReceStep=0; //及時(shí)切換回接頭暗號(hào)的步驟
}
else //如果還沒結(jié)束,繼續(xù)切換到下一個(gè)步驟,接收“其它數(shù)據(jù)”
{
Gu8ReceStep=2; //切換到下一個(gè)步驟
}
}
break;
case 2: //其它數(shù)據(jù)
Gu8ReceBuffer[Gu32ReceCnt]=SBUF; //直接讀取剛接收完的一個(gè)字節(jié)的數(shù)據(jù)。
Gu32ReceCnt++; //每接收一個(gè)字節(jié),數(shù)組下標(biāo)都自加 1,為接收下一個(gè)數(shù)據(jù)做準(zhǔn)備
//靠“數(shù)據(jù)長(zhǎng)度”來判斷是否完成。也不允許超過數(shù)組的最大緩存的長(zhǎng)度
if(Gu32ReceCnt>=Gu32ReceDataLength||Gu32ReceCnt>=REC_BUFFER_SIZE)
{
Gu8FinishFlag=1; //接收完成標(biāo)志“置 1”,通知主函數(shù)處理。
Gu8ReceStep=0; //及時(shí)切換回接頭暗號(hào)的步驟
}
break;
}
}
}
else //發(fā)送數(shù)據(jù)引起的中斷
{
TI = 0; //及時(shí)清除發(fā)送中斷的標(biāo)志,避免一直無緣無故的進(jìn)入中斷。
//以下可以添加一個(gè)全局變量的標(biāo)志位的相關(guān)代碼,通知主函數(shù)已經(jīng)發(fā)送完一個(gè)字節(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) //每被“喂一次狗”,就及時(shí)更新一次“超時(shí)檢測(cè)的定時(shí)器”的初值
{
Gu8ReceFeedDog=0;
vGu8ReceTimeOutFlag=0;
vGu16ReceTimeOutCnt=RECE_TIME_OUT;//更新一次“超時(shí)檢測(cè)的定時(shí)器”的初值
vGu8ReceTimeOutFlag=1;
}
else if(Gu8ReceStep>0&&0==vGu16ReceTimeOutCnt) //超時(shí),并且步驟不在接頭暗號(hào)的步驟
{
Gu8ReceStep=0; //串口接收數(shù)據(jù)的中斷函數(shù)及時(shí)切換回接頭暗號(hào)的步驟
}
if(1==Gu8FinishFlag) //1 代表已經(jīng)接收完畢一串新的數(shù)據(jù),需要馬上去處理
{
switch(Gu8ReceType) //接收到的數(shù)據(jù)類型
{
case 0x01: //驅(qū)動(dòng)蜂鳴器
//以下的數(shù)據(jù)轉(zhuǎn)換,在第 62 節(jié)講解過的指針法
pSu16Data=(unsigned int *)&Gu8ReceBuffer[6]; //數(shù)據(jù)轉(zhuǎn)換。
Su16Data=*pSu16Data; //提取“蜂鳴器聲音的長(zhǎng)度”
vGu8BeepTimerFlag=0;
vGu16BeepTimerCnt=Su16Data; //讓蜂鳴器鳴叫
vGu8BeepTimerFlag=1;
break;
}
Gu8FinishFlag=0; //上面處理完數(shù)據(jù)再清零標(biāo)志,為下一次接收新的數(shù)據(jù)做準(zhǔn)備
}
}
void T0_time() interrupt 1
{
VoiceScan();
if(1==vGu8ReceTimeOutFlag&&vGu16ReceTimeOutCnt>0) //通信過程中字節(jié)之間的超時(shí)定時(shí)器
{
vGu16ReceTimeOutCnt--;
}
TH0=0xfc;
TL0=0x66;
}
void SystemInitial(void)
{
unsigned char u8_TMOD_Temp=0;
//以下是定時(shí)器 0 的中斷的配置
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
//以下是串口接收中斷的配置
//串口的波特率與內(nèi)置的定時(shí)器 1 直接相關(guān),因此配置此定時(shí)器 1 就等效于配置波特率。
u8_TMOD_Temp=0x20; //即將把定時(shí)器 1 設(shè)置為:工作方式 2,初值自動(dòng)重裝的 8 位定時(shí)器。
TMOD=TMOD&0x0f; //此寄存器低 4 位是跟定時(shí)器 0 相關(guān),高 4 位是跟定時(shí)器 1 相關(guān)。先清零定時(shí)器 1。
TMOD=TMOD|u8_TMOD_Temp; //把高 4 位的定時(shí)器 1 填入 0x2,低 4 位的定時(shí)器 0 保持不變。
TH1=256-(11059200L/12/32/9600); //波特率為 9600。11059200 代表晶振 11.0592MHz,
TL1=256-(11059200L/12/32/9600); //L 代表 long 的長(zhǎng)類型數(shù)據(jù)。根據(jù)芯片手冊(cè)提供的計(jì)算公式。
TR1=1; //開啟定時(shí)器 1
SM0=0;
SM1=1; //SM0 與 SM1 的設(shè)置:選擇 10 位異步通信,波特率根據(jù)定時(shí)器 1 可變
REN=1; //允許串口接收數(shù)據(jù)
//為了保證串口中斷接收的數(shù)據(jù)不丟失,必須設(shè)置 IP = 0x10,相當(dāng)于把串口中斷設(shè)置為最高優(yōu)先級(jí),
//這個(gè)時(shí)候,串口中斷可以打斷任何其他的中斷服務(wù)函數(shù)實(shí)現(xiàn)嵌套,
IP =0x10; //把串口中斷設(shè)置為最高優(yōu)先級(jí),必須的。
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();
}
}
}
}
|
|