MCU 用c语言实现查询红外解码
红外解码的实现一般有定时器中断、外部中断+定时器、查询三种解码方案,其中查询方式解码在一些资源极少的MCU中或需要长时间关闭中断的系统中比较实用。下面给出的是用应广PMS150C单片机上实现的代码。
最近在用双元单通数字输出热释电红外传感器,感觉做出来的成本比现有方案有竞争优势,下次给大家写这个。
一.红外的编码格式
一般红外接收都采用市场上的集成接收头,它主要将38KHz的调制信号处理成电平信号,不过在一些应用中需要注意集成接收头有上电延时和接收延时,也不能长时间一直接收38KHz信号(会被接收头识别为干扰而被过滤)。
上图为编码格式:包括引导码+起始码+地址码+数据码+【结束码】其中结束码如不识别长按基本用不到!
上面为编码方式,当经过接收头的信号处理后得到的信号需要倒向,下面讲解都为倒向后的信号。
1.引导码和起始码
引导码为9ms低电平,起始码为4.5ms 高电平
注:在识别时需要考虑容错范围。
2.0码和1码
上图为0码和1码的编码方式,而实际接收到是倒向的。
3.地址码
地址码为2个八位表示 :如0xFF,0x00
其中一个码是另一个码的反码。一般应用的时候可以利用该码来屏蔽一些错误或遥控器。
4.数据码
数据码也是2个八位表示:如0x01,0xFE
2个字节也是互为反码。一般应用的时候可以了利用来屏蔽接收错误的情况。
5.结束码
如果红外遥控器的按键一直按着,一般会每隔108ms左右发送一次,这里具体的时间还需要实际测试一下!
注:上面每次的电平识别都需要考虑容错的问题!!!
二.各阶段主要代码介绍
1.空闲处理
接收初始假设是处于空闲状态,即接收端口一直为高电平。
//--------------idle
if(ir_rcvsta==0)//idle
{
if(ir_rcvio==1)
{
return_byte(0);//退出
}
ir_rcvsta++;
ir_rcvTimeCntL = 0;
ir_rcvTimeCntH = 0;
ir_rcvLevel_flag = 0;
}
2.引导码和起始码
一旦接收端口变为低电平后会进入该状态
//--------------boot
else if(ir_rcvsta == 1)
{
if(ir_rcvLevel_flag == 0)
{//low boot 9ms
if(ir_rcvio== 0)
{
ir_rcvTimeCntL++;
if(ir_rcvTimeCntL>=150)//>=15ms error
{//boot error
return_byte(0);
}
}
else
{
if(ir_rcvTimeCntL<45)//<4.5ms error
{
return_byte(0);
}
ir_rcvLevel_flag = 1;
}
}
else
{//high boot 4.5ms
if(ir_rcvio == 1)
{
ir_rcvTimeCntH++;
if(ir_rcvTimeCntH>=65)//>=6.5ms error
{//high level timeout
return_byte(0);
}
}
else
{
if(ir_rcvTimeCntH < 25)
{//low<4ms or high<2.5ms boot error
return_byte(0);
}
ir_rcvIndex = 0;
ir_rcvTimeCnt = 0;
ir_rcvsta++;
ir_rcvTimeCntL = 0;
ir_rcvTimeCntH = 0;
ir_rcvLevel_flag = 0;
}
}
}//1 end
3.地址码和数据码接收
数据码和地址码可以放在一起,因为他们都一样!只是在应用逻辑上定义不同而已。该接收方式只需要稍微改改,就可以接收任意多个数据,例如用在433的无线解码上。
else if(ir_rcvsta == 2)//address or data recive.
{
if(ir_rcvLevel_flag==0)
{//low
if(ir_rcvio==0)
{
ir_rcvTimeCntL++;
if(ir_rcvTimeCntL>=9)//low >=900us error
{
return_byte(0);
}
}
else
{
if(ir_rcvTimeCntL<=1)
{
return_byte(0);
}
ir_rcvLevel_flag = 1;
}
}
else
{//high
if(ir_rcvio == 1)
{
ir_rcvTimeCntH++;
if(ir_rcvTimeCntH>=25)//hig >=2.5MS error
{
return_byte(0);
}
}
else
{
if( ir_rcvTimeCntH <= 1 )
{
return_byte(0);//low<100us error
}
ir_rcvBuf <<= 1;
if(ir_rcvTimeCntH >10 )
{//1 ok
ir_rcvBuf.0 = 1;
}
else
{//0 ok
ir_rcvBuf.0 = 0;
}
ir_rcvTimeCnt++;
if(ir_rcvTimeCnt>=8)
{//recevie sucess a byte data
ir_rcvTimeCnt = 0;
array_set_u8_t(ir_rcvResult,ir_rcvIndex,ir_rcvBuf);
ir_rcvIndex++;
if(ir_rcvIndex >=4)
{
if(ir_rcvResult[0] != IR_ADRH)
{
return_byte(0);
}
if(ir_rcvResult[1] != IR_ADRL)
{
return_byte(0);
}
A = ~ir_rcvResult[2];
if( A != ir_rcvResult[3])
{
return_byte(0);
}
return_byte(1);//recevie sucess return
}
}
ir_rcvTimeCntL = 0;
ir_rcvTimeCntH = 0;
ir_rcvLevel_flag = 0;
}
}
}//2 end
fun_pcall(delay_20us,10);100us 这个100us延时函数比较重要,一定要测试
//MNOTB(DEBUG_OUT);
三 .解码说明
上面用到的fun_pcall(delay_20us,10); 只是一个延时函数,你可以改成你自己的。
return_byte(0); 相当于return 0;
A = ~ir_rcvResult[2];这里的A只是一个临时变量,你可以改成你自己的。
array_set_u8_t(ir_rcvResult,ir_rcvIndex,ir_rcvBuf); 保存一个数据。
有人说这里为什么要这样做,是因为应广单片机的编译器不支持函数带参调用和返回。
把上面的解码放在while(1){fun}下,做成函数形式,函数返回0表示接收错误或空闲,当函数返回1时表示接受到正确的数据,用户可以处理该数据了。
上面整个解码过程还是很简单,其中最重要的是容错的问题!!!!
四. 完整代码
代码连接如下:
https://download.csdn.net/download/wenyouren/12648084