GPIO 模拟红外发射(NEC)

  前段时间做过一个通过gpio口来模拟红外loop out 的功能。整理记录一下.


一.概念和NEC协议:

1.基本概念:

GPIO:General Purpose Input Output (通用输入/输出)详情可查看Kernel 中的 GPIO 定义和控制

NEC:一种红外遥控器传输协议,由NEC开发.

从网上找到的概念图,很形象,借用一下:

红外遥控通常是由发送端和接收端两个部分组成。发送端将要发送的二进制信号编码成一系列的脉冲信号,然后通过红外发射管发射红外信号。

接收端完成对红外信号的接收,放大,整形,并解调出遥控编码脉冲。这个过程如下:

GPIO 模拟红外发射(NEC)_第1张图片 


2.NEC协议:

1.从上面图中可以看到 首先发送端是需要发送二进制信号编码,二进制信号编码由 Customer Code (客户码) 和 Data Code (数位码) 两部分构成。


2.一般都采用载波提高红外信频率,提高发射效率,载波频率为38KHZ:

用38KHZ的高频信号去调制然后通过红外二极管发射出去,那么就是在信号高电平期间加载波

38K调制波形:

GPIO 模拟红外发射(NEC)_第2张图片



3.协议中一个bit的表示:

0:一个560us高电平+560us的低电平 合计 1.12ms

1:一个560us高电平+1690us的低电平 合计 2.25ms


如下图:代表一个1然后紧跟一个0



4.按键一次的NEC波形组成:


一个完整的NEC波形如下,它主要由5个部分组成:前导码(Head Code)+ 客户码(Customer Code)+ 客户反码 + 数位码(Data Code) + 数位反码

前导码也叫引导码 代表一次传输的起始标志:


9ms 高电平+4.5ms 低电平。


完整波形如图:

发的数据是:0x59 + 0xA6 + 0x16 +0xE9

都是从低位开始,后面会详细写发送过程。


5.重复按键NEC波形:

重复按键的引导码如下:

9ms 高电平+2.25ms 低电平。

它是以一个9ms的Head Code + 2.25ms的低电平 + 560us的高电平组成(必须拉高电平以区分2.25ms的低电平结束),

如下图正常发送一次nec之后一直重复:



6.软件解码模式图:

GPIO 模拟红外发射(NEC)_第3张图片


当有IR波形发过来时,内部计数器(Counter)会将两个相邻的正边沿触发(
如图中红色箭头所示,这两次的正边沿触发就是引导码的开始与结束位置的时间差值放到寄存器中。
与此同时,正边沿触发一个ISR,去读取这个寄存器的值,并以数组的形式保存起来。
等波形获取完毕后,再通过解析这个数组的Data来解析出key值




二.GPIO口模拟NEC发射:

通过对IO口的拉高拉低,高低电平产生符合协议的波形,我的实现主函数如下:

void sendkeytodvb(U8 u8key)
{

printk("ir out put key for stb==%x\n",u8key);

mdelay(100);
U8 u8datOpposite = 0;
u8datOpposite=~u8key;
sendcarrier(9000,CARRIERNUM1); //delay 9ms

mdelay(4); //delay 4.5 ms
udelay(500);

sendirbyte(GPIO_IR_HEADER_CODE0);
sendirbyte(GPIO_IR_HEADER_CODE1);

sendirbyte(u8key);
sendirbyte(u8datOpposite);
sendcarrier(560,CARRIERNUM2);

}

u8key 是需要发送的 数位码,也就是遥控器的键值。延时调用了kernel里面的delay忙延时函数。

以下是需要用到的GPIO口控制接口以及相关宏:

extern void MDrv_GPIO_Set_Low(U8 u8IndexGPIO);
extern void MDrv_GPIO_Set_High(U8 u8IndexGPIO);
#define GPIO_IR_HEADER_CODE0		0x08//0x10//0x00    /* Custom 0*/
#define GPIO_IR_HEADER_CODE1		0xF7//0xEF//0xFF    /* Custom 1*/
#define CARRIERNUM1  90000/264
#define CARRIERNUM2  5600/264



高电平需要38KHZ载波调制:

void sendcarrier(U32 utime, U32 unum)
{
	U32 icount = 0;
	while (1)
	{

		if (icount == unum)
			break;

		MDrv_GPIO_Set_High(gpioindex);
		udelay(13);
		MDrv_GPIO_Set_Low(gpioindex);
		udelay(13);
		ndelay(400);
		icount++;

	}
	MDrv_GPIO_Set_Low(gpioindex);
}


因为考虑在liunx下对gpio口的操作耗时,想要做到完全和NEC协议一样的精确是很难实现的,细微的偏差是可以的,所以我在icount==unum时就break出来.


通过sendirbyte(...),来发送一个byte:


void sendirbyte(U8 u8keydata)
{
	U8 u8Mask = 0x01;

	while (u8Mask)
	{

		sendbit(u8keydata & u8Mask);

		u8Mask <<= 1;
	}
}

通过移位与,从低位开始发送八位二进制:


void sendbit(U8 byte)
{

	sendcarrier(560, CARRIERNUM2); //delay 560us

	udelay(byte == 0x00 ? 560 : 1690);
}

高低电平保持时间遵循NEC协议中的0和1的标准。


撰写不易,转载请注明出处 http://blog.csdn.net/jscese/article/details/21398961













你可能感兴趣的:(kernel,byte,NEC,GPIO,38KHZ)