解码程序一:
/*-----------------------------------------------------------------------------------------
定时器0中断处理
-----------------------------------------------------------------------------------------*/
void tim0_isr (void) interrupt 1 using 1
{
irtime++; //用于计数2个下降沿之间的时间
}
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(startflag)
{
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
}
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
}
}
else
{
irtime=0;
startflag=1;
}
}
先来分析一个这个程序,这个程序用了两个中断,一个定时器中断,一个外断中断,程序的算法是让定时器中断不停的记数,外部中断在下降没来临的时候将这个记数值并将上一次记的数值存在一个数组中,不过这个程序有一个小Bug,就是那个startflag是没有起到关键性的作用的啊?!其实编这个程序的程序员想的是用startflag,这个方法可以过滤过那一个引导码,但是如果你足够细心就会发现他没有做startflag清0的操作,不过诡异的事情是即使这样也没有影响正常的解码程序的运行,其实只要你认真分析一下就会发现有没有startflag清0这一个判断的条件其实没有多大的意义,我的意思是有没有这一条语句都不会影响正常的解码,首先来分析原程序,也就是没有startflag清0这一语句的情况,第一个引导码来的时候,进入else语句,将第一个引导码过虑掉,进入else语句的就是后面有用的解值了,第一次会执行到if(irtime<54&&irtime>=50)这一条语句,因为引导码的长度刚好在这个范围内,所以会执行下会的一条i=0,也就是会把数组的下标清0,然后会把相应的32个红外编码存到数组中,注意这里的数组的长度是33,而不是32,因为irdata[0]是用来存放引导码的,还有一点非常值很注意,就是这32个有效的红外数据接收完了之后,会有一个重复码,这个重复码和引导码长得很相似,这个我用示波器看过,这个重复码可以看作是第34个波形,而且有一点非常值很注意,就是这个重复码只要你按键不松手的话是一直会发的,所以如何处理这个重复码的问题也是很关键的,至于这个问题我后面会有讲到,现有我们先来将这个重复码看成是第34个红外码,他的意义在于可以提供一个下降沿,有了这个下降沿就会将第32个红外码值存入data[32]中,所以最终的结果就是33个红外编码全部存入了容量为33的数组中,但是这时还有一个问题就是如果按键按下不放的话,后面还会有无数的波形啊,这时解码程序还在不停的处理,后面的红外编码会存入第34,35,36……等后面的数组中,这是不希望看到的,这也会导致一个更加可怕的问题,那就是程序的溢出,那天在防真器上出现了这个问题决对就是这个原因.因为数组只有33个元素,而在接收到这33个元素之后还没有停止对红外码的接收,后面的元素继续存入只有33个元素的数组中,必定会导致数据的溢出,也就是会弹出那样的一个溢出的界面,我还记得当天没有接红外接收管硬件的时候也会有这个溢出的窗口,那是因为当时没有在中断进入这后将中断标志位清0,所以会一直有中断进来,从而这33个元素的数组就会很快存满了,然后还在进入不停的进入中断,最后导致这33个元素的数组全部存满了还在不停的往里面存数据,所以也会导致程序的溢出.所以以后就有了一条经验,那就是如果程序溢出了有一种可能性就是往一个固定长度的数组中存入了大于其长度的数据.那么如何解决这个问题呢,一种可行的方法是这样的,待33个红外数据全部存完以后,置一个标志,后面接收到的数据只要不是引导码就做一次函数返回,当接收到这个正确的引导码以后才可以进行下一轮数据的接收.
上面的这个问题暂且放下(只要摇控器发出的波形只有34个脉冲就不会出现上面的问题 ,这种摇控器的特点就是只要你按下一个按键不论你是不松手,它都只会发出一帧数据,不会有后面的故虑),下面考虑第二帧数据的接收情况,当第二帧数据来临的时候,由于没有清0 startflag,程序会进入if(startflag)这一个分支,也就是会处理引导码的情况,这里会到下面的一条语句if(irtime<63&&irtime>=33),而这时irtime是一个随机的数,所以说这条语句有可能能被执行到也有可能不会被执行到,无论其是否能被执行到,都无所谓,等下一个下降沿到来的时候那个时候irtime存入的才是引导码的数据,也就是直到第二个下降沿到来的时候,irdata的下标才会清0,这时才会将引导码存入irdata的第0个格子里面.而后的数据会将先前的数据覆盖掉,也就消除了第一次接收到的数据的不稳定因素.[page]
如果有接收到第33个元素后将startflag清0.每次接收红外的时候就和第一次的一样了,还过即使是这样也不能摆脱那个接收到重复码的问题.下面是有startflag清0的程序.
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(startflag)
{
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
}
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
}
}
else
{
irtime=0;
startflag=1;
}
}
下面做一个解决重复码问题的程序:
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(statflag==1)
{
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正确的引导码,开始接收数据
}
else
{
return; //这个是33个红外码后面的重复码,过滤掉他
}
If(ir_flag==0) //如果接收到引导码才开始解码
{
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
ir_flag=1; //接收完33个数据,清0接收数据位,即不再接收数据
}
}
else
{
startflag==1
i=0;
return;
}
}
这个程序有一个关键的地方,那就是那个引导码的时间,程序就是通过这个时间来确定是否开始接收一帧数据的,真到接收到33个数据后将关闭数据的接收功能,直到有第二个引导码的到来,才开始接收第二帧数据.如果重复码我这个引导码很像,那么这个一点的时间的差别也是很关键的了,所以必须把握好这个时间,比如13.5ms,12M的晶振定时器计一次时间是256us比如把这个时间卡在13ms到14ms之间就应该是13000/256=50,14000/256=54,也就是说应该把两个时间卡在50和54之间,这样的精度才能保证将那些个重复码的脉冲过滤掉,这个卡的越准越好越小越好.也就这在逻辑上也是可行完美合理的程序.逻辑上就是要让一帧数据接收完成之后停止数据的接收功能.上面各全局变量的初始化情况为ir_flag=0;startflag=0;[page]
想一想如果这个程序像下面这样会有什么后果,即去掉startflag这个标置,会有什么后果?
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正确的引导码,开始接收数据
}
else
{
return; //这个是重复码,过滤掉他
}
If(ir_flag==0)
{
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
ir_flag=1; //接收完33个数据,清0接收数据位,即不再接收数据
}
}
}
这个程序没有用startflag这个标置,也就不能保证接收到的第一个脉冲是引导码了,这时会有一个随机的数存入irtime,如果这个随机数是和引导码的irtime相似的话,就会造成解码的开启,第二个码,也就是引导码会存入irdata[1]中,后面所有的码就会依次往后推移一次,这样的话第33个红外码就会丢失掉,也就去直接导致解码的失败.所以说完美的程序应该是上面的那个程序.(这段话说的不对,那个i=0的操作会把数组的下标做一下清0,也就不存在移位的问题了.)
特别纠正一下本文的错误之处,上面的两个程序都是有问题的,正确的程序应该是下面的程序,看完这个程序再来说明上面程序不正确的地方.
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正确的引导码,开始接收数据
}
if(ir_flag==1)
{
irtime=0;
return; //这个是重复码,过滤掉他
}
If(ir_flag==0)
{
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
ir_flag=1; //接收完33个数据,清0接收数据位,即不再接收数据
}
}
}
上面的那两个程序都不能按照正常的算法实现,关键在于有一个原因,那个else语句没有做irtime清零的操作,这样就会导致一个问题,就是下一个下降沿到来的时候不能反正确的两个下降沿之间的时间存入数中,存入的是不确定的数,所以说irtime清零这一步操作还是很有必要的,试想下一个正确的引导码到的的时候,没有irtime清0的操作也就不能识别能正确的引导码,也就不能够实现解码的开启,也就不能够实现正确的解码,不过上面的程序还是有一个问题,那就是他没有用startflag,也就会存在上面提到的那个问题,也说是说当有一个随机数,也就是下一帧数据开始接收时的第一个码,那个数就是一个随机数,如果那个数刚好是在引导码的时间区间内,那么就是恶梦的开始,也就是说会和上面的错误一样,这个随机数会存入irdata[0],引导码会存入irdata[1]中,下面的所有码都依次往下移一位,也说是说会有一位码没有存入这33个数据码中(包括引导码). (这段话说的不对,那个i=0的操作会把数组的下标做一下清0,也就不存在移位的问题了.)所以说这个程序可以说是一个比效完美的程序了[page]
下面是另一种编程的方法.
/*-----------------------------------------------------------------------------------------
外部中断0中断处理
-----------------------------------------------------------------------------------------*/
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag; //是否开始处理标志位
if(statflag==1)
{
if(irtime<=54&&irtime>=50)//引导码 TC9012的头码,9ms+4.5ms
{
i=0;
ir_flag=0; //接收到正确的引导码,开始接收数据
}
if(ir_flag==0)
{
irtime=0;
startflag=0;
return; //这个是33个红外码后面的重复码,过滤掉他
}
If(ir_flag==1) //如果接收到引导码才开始解码
{
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
startflag=0;
ir_flag=1; //接收完33个数据,清0接收数据位,即不再接收数据
}
}
else
{
startflag==1
i=0;
return;
}
}
这个程序的算法是那些重复码会隔一个进入解码程序一个,进处解码程序的那个又会被过滤掉,也说是说第一个重复码的下降沿会进入else starflag=1……的那个语句.下一个重复码的下降沿会被过滤掉,因为他不满足那个引导码时间的那个条件.所以他会被过滤掉.这就要求那个引导码的时间要足够的准确,范围也要足够的小,如果重复码和引导码比效相似的话那么事情就不妙了,那个条件就起不到过滤的作用了.这个严酷的条件也是这个程序的精髓.
上一篇:单片机开发中断及一些理解
下一篇:几种比较流行和成熟的红外解码程序做一下分析和总结2
推荐阅读最新更新时间:2024-03-16 13:53