红外是一种光, 对于人类是不可见的光,但摄像头可以捕捉到红外光.
红外可用不同的频谱来表二进制的0和1(高/低电平).
传输分成发送端(遥控器),接收端(红外接收头)
发送端发出表示高低电平的不同的光, 接收端收到红外光后还原成对应的高低电平来表示二进制的0和1.
发送端可由专门的红外发射芯片来实现,如图:
红外发射芯片会自动扫描矩阵键盘电路,根据不同的键码,生成一串二进制数据,再按每位的二进制数据用相应的红外光发出.
遥控器上的一个按键的数据:
其中引导码用于标识一个键码数据的开始。 用户编码由两个8位组成,用于标识遥控器的厂家。键数据码用于区别按下的键。键数据码的反码用于校验使用, 确保数据完整.
除了引导码外,共有32位数据位,每位数据不是二进制0就是二进制1.从低位开始传输.
引导码的表示, 数据0和数据1的表示,参考下图:
如上图,数据0的信号周期为1.12ms, 数据1的信号周期为2.24ms.
当同一键连续按下按键下,发出的数据都是完整的一帔键数据(有些发射芯片处理方式不同),如下图:
/
红外接收头收到光后,会把光还原成对应数据0/1的高低电平信号.
需要注意, 在接收头收到的信号的相位是相反的, 可能是发送端的使用三极管来放大信号的原因。
接收部分的图,用逻辑分析仪抓取出来的图:
注意:用的遥控器是benQ投影仪,并不是用发射芯片SC902。但除了引导码不同外,其它一样.
//
测试驱动代码:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/ktime.h>
//红外接收头,三个引脚
// 三个引脚向下, 突出的半圆面向自己,从左往右引脚分别是: 数据脚(接IO口), 地线(gnd), 电源线(vcc, 3.3v)
// 红外接收头的数据脚接的是PL11
#define IR_IO GPIOL(11)
int flag = 0; //表示数据帧的开始
int num = 0; //表示数据帧里的第几位数据
static long long prev = 0; //记录上次的时间
unsigned int times[40]; //记录每位数据的时间
irqreturn_t irq_func(int irqno, void *arg)
{
long long now = ktime_to_us(ktime_get());
unsigned int offset;
int i, j, tmp;
if (!flag) //数据开始
{
flag = 1;
prev = now;
return IRQ_HANDLED;
}
offset = now - prev;
prev = now;
if ((offset > 13000) && (offset < 14000)) //判断是否收到引导码,引导码13.5ms
{
num = 0;
return IRQ_HANDLED;
}
//不是引导码时间,数据位时间
if (num < 32)
times[num++] = offset;
if (num >= 32)
{
for (i = 0; i < 4; i++) //共4个字节
{
tmp = 0;
for (j = 0; j < 8; j++) //每字节8位
{
if (times[i*8+j] > 2000) //如果数据位的信号周期大于2ms, 则是二进制数据1
tmp |= 1<<j;
}
printk("%02x ", tmp);
}
printk("\n");
flag = 0; //重新开始帧
}
return IRQ_HANDLED;
}
static int __init test_init(void)
{
int ret;
ret = request_irq(gpio_to_irq(IR_IO), irq_func, IRQF_TRIGGER_FALLING, "myir", NULL);
if (ret < 0)
goto err0;
return 0;
err0:
return ret;
}
static void __exit test_exit(void)
{
free_irq(gpio_to_irq(IR_IO), NULL);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");