从单片机基础到程序框架(全集 2019pdf版) - 第757页
{ Gu8FinishF lag=1; //接收 完成标志“ 置 1” ,通知主函 数处理。 Gu8ReceSte p=0; //及时切换回 接头暗号的步 骤 } else //如果还没结 束,继续 切换到下一 个步骤,接收“ 其它数据” { Gu8ReceSte p=2; //切换到下一 个步骤 } } break; case 2: //其它数据 Gu8ReceBuf fer[Gu32R eceCnt]=SBUF ; //直接读取刚 …

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; //转换后的数据
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[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)
{