小米红外遥控器如何适配到其他应用设备之上
最近合作方公司需要匹配小米最新款的红外遥控器到一款电暖设备上,鬼知道为何要使用一个明显的TV遥控器去控制电暖器。不过这都无所谓,客户是衣食父母嘛,尽管他们有的时候会想孩子一样胡闹......所以首先我去了解了一下小米的红外传输协议,很不幸,它并不是传统的NEC或者类NEC,当然我早已经有了心理准备的,毕竟MI这样的一家科技公司怎么可能去使用人尽可驭的NEC呢,那样一点都不酷,不会用NEC的,这辈子都不会用。
言归正传。我们先来看看MI的红外传输协议:
下面进行分析,首先一段时间的低电平信号和一段时间的高电平信号表示一位,所以从左向右一直到Stop bit结束这是一帧信号,一共12位,如图依次是 Start bit,2,0,1,2,0,0,3,3,0,1,Stop bit. 每一位的脉冲宽度是这样定义的:
此图仅作为参考,不同厂家可能有细微的出入,所以我的工作就是根据接收到的脉冲宽度来区分0,1,2,3的对应关系达成解码的最终目的。下面我们看一下每一个按键对应的键码值:
3CCC | 空 | |
8605 | ||
860B | 860D | 860C |
8606 | ||
8608 | 8607 | 8604 |
860E | ||
860F |
让我们回到MiBox协议格式那张图,可以看到该图对应的键码为0x860F,也就是最下面的那个按键。在这里有些小伙伴可能会疑惑0x860F是什么呢?其实MiBox协议进行了两次数据转换,如果没有接触过就会迷惑我们。首先我们知道根据脉冲宽度MiBox协议为只有0,1,2,3,这属于四进制吧,我们使用两位二进制就可以完整表示。那么正是如此:
我们将数据位排列对比一下,
2 0 1 2 0 0 3 3 0 1 这一行相当于四进制的表示 10 00 01 10 00 00 11 11 00 01 这是转换后的二进制表示,最后我们四位合成一个16进制数
8 6 0 F 1
这下小伙伴们应该就明白了,至于后面的1我们就忽略掉,毕竟我们只需要判断前四位就可以区分键值了,没必要增加额外的工作量。
那么最后我们要如何写程序呢?这里我分享一下我的代码,算是抛砖引玉了,有什么不合理的地方也请大家指正。
首先我们需要忽略掉Start bit位,然后将后面的8位数据位依次接收并存储到数组,但是有一个问题,似乎Start bit位和后面的数据位的脉冲宽度的界限很难区分。我们需要做一个处理,索性我们就丢掉第一帧,从第二帧开始接收并使用前一帧的Stop bit位做为判断是否开始接收的标志,幸运的是Mibox正是一种循环发送机制,长按会不停发送同一组码值。那么可以得出数组长度即为10
**上代码(避免误导小伙伴,这里仅贴出跟红外接收相关的代码,如有疑问,请在评论区留言咨询。)
volatile uint16_t ir_data = 0;
uint8_t irdata_num=0,mcsj=0;
unsigned char xdata sj[10] = {0}; //脉冲时长数组
bit self_lock_flag = 0; //红外接收标志位
void T1_init(void) //100us中断一次
{
TIMER1_MODE2_ENABLE;
CKCON &= (~b4_msk);
TH1=0xA4;
TL1=0xA4;
ET1=1;
TR1=1;
}
void T1_ISR(void) interrupt 3
{
mcsj++; //脉冲时间计数
}
void INT1_ISR(void) interrupt 2/*下降沿中断*/
{
// if(error_be_found)
// {
if(mcsj>50) //判断是不是引导码
{
self_lock_flag = 1;
irdata_num=0;
}
if(self_lock_flag)
{
sj[irdata_num]=mcsj; //脉冲时间存入数组
mcsj=0; //清空这一数据,准备接收下一个数据
irdata_num++;
if(irdata_num==10) //判断是否接收完
{
self_lock_flag = 0;
JS = 1; //接收完成标志位
MC=0; //红外脉冲结束
irdata_num=0;
TR1=0;
}
}
mcsj=0;
//}
}
/**红外管脚电平检测,每1ms检测一次**/
if(0==IR_input)
{
key_lock_10=0;
key_count_10=0;
}
else if(0==key_lock_10)
{
key_count_10++;
if(key_count_10>=19)
{
TR1=1;
}
}
void hwjm(void)
{
uint8_t k;
uint8_t value = 0;
for(k=2;k<10;k++)
{
if(sj[k]<13)
{
ir_data<<=2;
ir_data+=0x00;
}
else if(sj[k]>13&&sj[k]<16)
{
ir_data<<=2;
ir_data+=0x01;
}
else if(sj[k]>16&&sj[k]<19)
{
ir_data<<=2;
ir_data+=0x02;
}
else if(sj[k]>19)
{
ir_data<<=2;
ir_data+=0x03;
}
}
JS=0;
JM=1;
}
让我们来梳理一下,首先定义T1来捕捉脉冲宽度,每100us中断一次并且mscj++,接着判断该脉冲宽度的范围,这里采用50,如果到达停止位则开始接收,依次将mscj的值存放的数组内。最后hwjm()根据脉冲宽度来进行解码。
通常按键都会发送多帧数据,在某些场合我们需要屏蔽这些重复的数据,所以如程序所示,在接收完成第一个10位数据之后关闭的T1定时器,那么何时打开呢?这里我采用了循环检测红外引脚的电平状态,如果按键一直处于被按下的状态,那么IR的电平就一直在变化,除非松开了按键才会保持一种状态,此时即可以打开T1定时器。