从单片机基础到程序框架(全集 2019pdf版).pdf - 第794页
Gu8ReceBuf fer_B[Gu3 2ReceCnt_B]= SBUF; Gu32ReceCn t_B++; //记录 当前缓存 B 的接收字节 数 } } } else //发送数据引 起的中 断 { TI = 0; //及时 清除发送中断 的标志,避免 一直无 缘无故的进入 中断。 //以下可以添 加一个全局变 量的标志位的 相关代 码,通知主函 数已经发送完 一个字节的数 据了。 } } void Usart Task(vo…

unsigned char Gu8ReceBuffer_B[RECE_BUFFER_SIZE]; //双缓存其中之一的缓存 B
unsigned long Gu32ReceCnt_B=0; //缓存 B 的数组下标与计数器,必须初始化为 0,做好接收准备
unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作变量。
unsigned char Gu8FinishFlag=0; //接收完成标志。0 代表还没有完成,1 代表已经完成了一次接收
volatile unsigned char vGu8ReceTimeOutFlag=0;//通信过程中字节之间的超时定时器的开关
volatile unsigned int vGu16ReceTimeOutCnt=0; //通信过程中字节之间的超时定时器,“喂狗”的对象
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
UsartTask(); //串口接收的任务函数
}
}
void usart(void) interrupt 4
{
if(1==RI)
{
RI = 0;
Gu8FinishFlag=0; //此处也清零,意味深长,当主函数正在处理数据时,可以兼容多次接收完成
Gu8ReceFeedDog=1; //看门狗的“喂狗”操作,给软件定时器继续“输血”
if(0==Gu8CurrentReceBuffer_Sec) //0 代表选择缓存 A
{
if(Gu32ReceCnt_A<RECE_BUFFER_SIZE)
{
Gu8ReceBuffer_A[Gu32ReceCnt_A]=SBUF;
Gu32ReceCnt_A++; //记录当前缓存 A 的接收字节数
}
}
else //1 代表选择缓存 B
{
if(Gu32ReceCnt_B<RECE_BUFFER_SIZE)
{

Gu8ReceBuffer_B[Gu32ReceCnt_B]=SBUF;
Gu32ReceCnt_B++; //记录当前缓存 B 的接收字节数
}
}
}
else //发送数据引起的中断
{
TI = 0; //及时清除发送中断的标志,避免一直无缘无故的进入中断。
//以下可以添加一个全局变量的标志位的相关代码,通知主函数已经发送完一个字节的数据了。
}
}
void UsartTask(void) //串口接收的任务函数,放在主函数内
{
static unsigned char *pSu8ReceBuffer; //“指针切换关联”中的指针,切换内存
static unsigned char Su8Lock=0; //用来避免一直更新的临时变量
static unsigned long i; //用在数据处理中的循环变量
static unsigned long Su32ReceSize=0; //接收到的数据大小的临时变量
if(1==Gu8ReceFeedDog) //每被“喂一次狗”,就及时更新一次“超时检测的定时器”的初值
{
Gu8ReceFeedDog=0;
Su8Lock=0; //解锁。用来避免一直更新的临时变量
//以下三行代码是看门狗中的“喂狗”操作。继续给软件定时器“输血”
vGu8ReceTimeOutFlag=0;
vGu16ReceTimeOutCnt=DOG_TIME_OUT;//正在通信时,两个字节间隔的最大时间,本节选用 20ms
vGu8ReceTimeOutFlag=1;
}
else if(0==Su8Lock&&0==vGu16ReceTimeOutCnt) //超时,代表一串数据已经接收完成
{
Su8Lock=1; //避免一直进来更新
Gu8FinishFlag=1; //两个字节之间的时间超时,因此代表了一串数据已经接收完成
}
if(1==Gu8FinishFlag) //1 代表已经接收完毕一串新的数据,需要马上去处理
{
if(0==Gu8CurrentReceBuffer_Sec)
{
Gu8CurrentReceBuffer_Sec=1; //以最快的速度先切换接收内存,避免丢失新发过来的数据

//Gu32ReceCnt_B=0;//这里不能清零缓存 B 的计数器,意味深长,避免此处临界点发生中断
Gu8FinishFlag=0; //尽可能以最快的速度清零本次完成的标志,为下一次新数据做准备
pSu8ReceBuffer=(unsigned char *)&Gu8ReceBuffer_A[0]; //关联刚刚接收的数据缓存
Su32ReceSize=Gu32ReceCnt_A; //记录当前缓存的有效字节数
Gu32ReceCnt_A=0; //及时把当前缓存计数清零,为一次切换接收缓存做准备。意味深长。
}
else
{
Gu8CurrentReceBuffer_Sec=0; //以最快的速度先切换接收内存,避免丢失新发过来的数据
//Gu32ReceCnt_A=0;//这里不能清零缓存 A 的计数器,意味深长,避免此处临界点发生中断
Gu8FinishFlag=0; //尽可能以最快的速度清零本次完成的标志,为下一次新数据做准备
pSu8ReceBuffer=(unsigned char *)&Gu8ReceBuffer_B[0]; //关联刚刚接收的数据缓存
Su32ReceSize=Gu32ReceCnt_B; //记录当前缓存的有效字节数
Gu32ReceCnt_B=0; //及时把当前缓存计数清零,为一次切换接收缓存做准备。意味深长。
}
//Gu8FinishFlag=0; //之所以不选择在这里清零,是因为在上面清零更及时快速。意味深长。
//开始处理刚刚接收到的一串新数据,直接“统一”处理 pSu8ReceBuffer 指针为代表的数据即可
for(i=0;i<Su32ReceSize;i++)
{
if(0x02==pSu8ReceBuffer[i]&&
0x03==pSu8ReceBuffer[i+1]&&
0x04==pSu8ReceBuffer[i+2]) //连续三个数是 0x02 0x03 0x04
{
vGu8BeepTimerFlag=0;
vGu16BeepTimerCnt=100; //让蜂鸣器“短叫”100ms
vGu8BeepTimerFlag=1;
return; //直接退出当前函数
}
if(0x06==pSu8ReceBuffer[i]&&
0x07==pSu8ReceBuffer[i+1]&&
0x08==pSu8ReceBuffer[i+2]&&
0x09==pSu8ReceBuffer[i+3]) //连续四个数是 0x06 0x07 0x08 0x09
{
vGu8BeepTimerFlag=0;
vGu16BeepTimerCnt=2000; //让蜂鸣器“长叫”2000ms
vGu8BeepTimerFlag=1;
return; //直接退出当前函数
}
}