万能红外遥控解码模块
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/item.htm?id=7693624806
该模块采用5V电源供电,可以完成目前应用最广泛的多种红外遥控编码的解码,包括飞利浦(RC5)编码(典型编码芯片如SAA3010及兼容芯片如PT2210 等)和NEC编码(典型编码芯片如uPD6121,uPD6122, TC9012 )以及众多的兼容芯片型号,(如PT2221, PT2222, SC6121, SC6122,SC9012 等等),采用该模块,可以缩短开发时间,节约CPU 资源,降低总体成本。
特点
●使用简单、可靠
● 支持多种编码
● 兼容SPI 及UART(波特率9600)的串行输出
● 采用数字滤波技术,高抗干扰,无误码
● 接收有效指示输出
● 工业级温度范围
红外编码介绍
目前应用于家电等领域的红外线遥控装置,并没有统一的国际标准,目前市场上所见的红外线遥控编码芯片,超过10 种之多,分别由飞利浦公司、NEC 公司、SONY 公司、东芝公司、三菱公司、JVC 公司等生产,使用的编码方式各不相同。
目前应用最广泛、兼容产品最多的,是飞利浦公司(RC5编码)的和NEC 公司的编码芯片。
本模块可以完成这两种格式编码的解码工作。
RC5 编码:
RC5 编码由飞利浦公司推出,其编码芯片有SAA3010,SAA3006 等,是应用很
广泛的一种编码方式。
RC5 编码采用双相位编码方式,用不同相位分别代表“0”和“1”。
传送每一位的时间固定为1.778mS。
每一个指令包括1.5bits 的起始位(2 个逻辑1),1 个翻转位,5 位系统码(地址码),以及6 位命令码(键码),因此,最多可以支持64 个键。
翻转位在每次有新的按键按下去的时候翻转一次,这里指的新按键,也包括同一个键抬起后再次按下的情况。
如果某个键持续按下,则编码芯片会不断地重复发送同样的数据。
翻转位保持不变。
而如果该键中途抬起后再次按下,则再次按下后所发送的数据中的翻转位发生翻转,其它数据保持不变。
NEC 编码:
NEC 编码由NEC 公司推出,其典型编码芯片为uPD6121,uPD6122,除了NEC 公司的产品,市场上还有大量与之相兼容的产品,如PT2221, PT2222, SC6121, SC6122,SC9012 等等。
是应用最广泛的一种编码方式。
该编码方式采用脉冲位置编码方式,利用脉冲间的时间间隔来区分“0”和“1”。
每个指令包括32 位数据,包括16 位的用户码、以及8 位键数据码和键数据码的反码。
因为具有反码可以作为校验的依据,因此该种编码方式具有很低的误码率。
理论上该编码方式可以支持256 个键,实际的编码芯片一般可支持64 个
键。
uPD1621 等芯片支持组合按键,即某些键码只有在特定的2 个键同时按下的情况下才会发出,这个功能对于类似录像机“录像”键等需要防止误操作的场合非常有用。
模块的输出接口
ATC信号:模块的ACT 引脚作为接收有效指示输出,当模块芯片接收到有效的数据时,ACT 变为低电平。
对于RC5 和NEC 2种工作模式,ACT 信号的表现略有不同,这是由于两种编码制式的不同传输方式决定的。
下面分别介绍在两种工作模式下模块的输出情况:
RC5 模式:
RC5 的编码芯片在有持续按键的时候,会不断地重复发送相同的数据,因此,本模块也会不停地重复输出解码出的数据,而ACT 信号也会随着不停地跳变,每一个新的数据码到来时,都会输出一个低电平脉冲。
NEC 模式:
与RC5 模式的遥控器不同,NEC 格式的遥控芯片在有按键持续按下的情况下,不是重复地发出数据码,而是仅在第一次时传送一次数据,此后只是每108ms 发送一次引导信号,表示按键还持续有效。
因此,本模块在接受这样的信号时,也只会在最开始输出一次数据,而按键的保持情况,是通过ACT 信号的持续低电平来表示的,如果ACT 一直持续保持低电平,则表示该按键一直有效,按键抬起后,ACT 也随之恢复高电平。
(见下图)
如果用户需要判断遥控器的键是否被持续按下,对应RC5 模式和NEC 模式,应采用不同的方法,RC5 模式下,系统用翻转位来表示新的按键,用户可以将最后收到的键码数据中的翻转位(本模块芯片将翻转位置于键码数据的最高位BIT7)与上一次收到的数据中的翻转位相比较,如果两次翻转位相同,则表示是持续的按键,如果不同,则表示这是一个新的按键。
而对于NEC 模式,用户则可以通过监视ACT 信号来判断按键的情况,如果收到键码后,ACT 持续保持为低电平,则表示按键一直没有释放。
串行输出:模块串行数据输出使用三个引脚,分别用作SS(选通信号),CLK(时钟信号),DAT(串行数据)。
使用串行输出时,ACT 引脚电平也会发生变化, ACT 在数据开始输出前就跳变为低电平。
模块的串行输出,采用的是标准的3 线SPI 接口方式,不过,为了达到最大的兼容性,数据的传送速率(波特率)被特别设定为9600,因此,发出的数据也可以直接用于波特率为9600 的异步串行接口。
数据采用低位在前的方式。
输出时,SS 首先变为低电平,同时DAT 端也变为低电平,这个状态将保持104uS,这个时间正好是波特率9600 的异步串行口传送1 个BIT 所用的时间,如果接收数据方是UART,则DAT 保持低电平的这个104uS,相当于发送了1 个起始位(START BIT)。
随后第一个数据位在DAT 上输出,CLK 开始输出同步脉冲,每输出一位所用的时间为104uS,8 位数据的最后一位数据输出完成后,SS 恢复为高电平。
模块工作时,会将收到的用户码和按键键码一同输出,因此每次输出2-3 个字节,RC5 模式地址码只有5 位,因此输出2个字节,一个字节的地址码和一个字节的键码,地址码先输出。
NEC 模式有16 位用户码,因此将总共输出3 个字节,用户码高8 位在前,其次是用户码低8 位,最后是按键的键码。
下面用NEC 模式为例,说明其输出的波形:
模块的应用方式
1.与微处理器接口
(1)、使用UART 方式
很多的微处理器都提供片上的UART 接口,模块的串行输出兼容于“波特率9600,1 个起始位,1 个停止位,无奇偶校验位”的UART,使用的方式极其简单,只需要将模块的DAT引脚与微控制器的RX引脚相连即可。
这种接口方式只需要占用1 根口线,微处理器的数据接收可以由硬件完成,占用CPU 的资源很少。
(2)、使用SPI 方式
有的微处理器,比如ATMEL 公司的AVR 系列等,具有片上的可以工作于从机模式的SPI 接口,这时也可以利用SPI 接口,和UART 方式类似,这种方式数据传输也由硬件完成,占用CPU 资源很少。
以AVR 芯片为例,将模块的DAT与单片机的MOSI 引脚相连,CLK 与SCK 相连,SS 与SS 相连,设置AVR 芯片的SPI 接口工作于“从机、上升沿为起始沿(Clock Polarity=LOW)、起始沿采样、低位数据在前”模式即可。
(3)、使用外部中断读取串行数据
上面第三种方式占用的I/O 口比较多,如果希望减少I/O 的使用,可以采用外部中断读取串行数据的方式,这时,可以用SS 信号的下降沿或者CLK 信号的上升沿作为中断的触发条件。
使用SS 下降沿作为触发时,从中断触发到数据出现在DAT 引脚上,有104uS的时间,用户可以在中断处理程序中监视CLK 的状态,每次CLK 由低电平变为高电平,就读取一位数据。
因为每一位的时间都是104uS,加上开始时的104uS,整个中断处理程序(读取一个字节)需要耗时约900uS。
也可以采用CLK 的上升沿作为中断触发条件,每次中断服务程序只读取一位数据,这样可以减少一些读取数据的时间开销。
(4)、不使用中断的接口方式
上面的几种方式都使用了硬件的串行接口或者是中断资源,有的低成本的微
控制器没有这些资源,或者资源被其它程序占用,不能使用硬件接口和中断,则必须采取查询的方式。
查询方式的最大问题在于存在的丢失数据的可能性,因为程序除了查询模块状态外,还会有其他工作要做。
当程序正在执行其他任务,或者收到一个按键指令后进行处理尚未完成时,又有新的按键数据,则新来的数据就有可能因为未被CPU 察觉而丢失。
2. 与RS-232口相连
模块的DAT输出经过简单的电平转换,就可以直接用于RS-232 接口,可以直接被PC接收,配合适当的软件(如串口调试助手等),可以完成PC的遥控控制及键码显示。
如通过MAX232电平转换后,在PC上通过配置好的串口调试助手,如下图,就能读出相应遥控器的按键编码。
模块的示例程序
//模块的DAT端接51单片机RX端在LCD1602显示一款NEC编码遥控器数字0-9 #include<REG52.H>
#include <LCD1602.H>
#define uchar unsigned char
#define uint unsigned int
#define INBUF_LEN 3
uchar inbuf1[INBUF_LEN]; unsigned char i=0;
uchar c=0;
uchar ir_value;
sbit BZ=P3^5;//蜂鸣器
bit read_flag= 0 ;
uchar code a[10]={"0123456789"};
//延时ms
void Delay1ms(uint count)
{
uint i,j;
for(i=0;i<count;i++)
for(j=0;j<120;j++);
}
//串口初始化
void init_serialcomm( void ) {
TMOD=0x20;
TH1=0xfd; //设置波特率为9600
TL1=0xfd;
SCON=0x50;
TR1=1;
ES=1;
EA=1;
}
//串口接收3字节中断函数
void Rcv_INT(void) interrupt 4 {
if(RI)
{
RI=0;
inbuf1[i]=SBUF;
i++;
if(i==INBUF_LEN)
{
i=0;
read_flag=1;
}
}
}
//主函数
void main(void)
{
LCD_Initial();
Delay1ms(50);
GotoXY(0,0);
Print("The 1602LCD IR");
GotoXY(0,1);
Print("GO: by YU");
GotoXY(5,1);
LCD_Write(LCD_DATA,a[c]);
init_serialcomm(); //初始化串口
while ( 1 )
{
Delay1ms(50);//延时50MS开始接收
i=0; //忽略BC7210上电输出乱码
if(read_flag) //如果取数标志已置位,就将读到的数进行辗转处理
{
read_flag =0; //取数标志清0
if(inbuf1[0]==0x00&&inbuf1[1]==0xFF)//接收到的地址码高8位和低8位 {
BZ=!BZ;
Delay1ms(50);//蜂鸣器响一下表示接收到数据
BZ=!BZ;
ir_value=inbuf1[2];//接收到的1字节数据码
switch(ir_value)//判断数据码对应遥控器键盘的数据
{
case 0x12:{c=0;break;}//注意:遥控器的地址码和数据码事先通过串口调
//试助手在PC上读出,不同的遥控有不同的
//编码,见上节与RS-232口相连的内容 case 0x09:{c=1;break;}
case 0x1D:{c=2;break;}
case 0x1F:{c=3;break;}
case 0x0D:{c=4;break;}
case 0x19:{c=5;break;}
case 0x1B:{c=6;break;}
case 0x11:{c=7;break;}
case 0x15:{c=8;break;}
case 0x17:{c=9;break;}
default:{break;}
}
GotoXY(5,1);
LCD_Write(LCD_DATA,a[c]);
}
}
}
}
//以下是LCD1602.H文件
#ifndef LCD_CHAR_1602_2005_4_9
#define LCD_CHAR_1602_2005_4_9
#include <intrins.h>
//PortDefinitions**************************************************** sbit LcdRs = P1^0;
sbit LcdRw = P1^1;
sbit LcdEn = P1^2;
sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口
//内部等待函数***************************************************** unsigned char LCD_Wait(void)
{
LcdRs=0;
LcdRw=1; _nop_();
LcdEn=1; _nop_();
while(DBPort&0x80);//在用Proteus仿真时,注意用屏蔽此语句,在调用//GotoXY()时,会进入死循环,
//可能在写该控制字时,该模块没有返回写入完备命
//令,即DBPort&0x80==0x80
//实际硬件时打开此语句
LcdEn=0;
return DBPort;
}
//向LCD写入命令或数据****************************************
#define LCD_COMMAND 0 // Command
#define LCD_DATA 1 // Data
#define LCD_CLEAR_SCREEN 0x01 // 清屏
#define LCD_HOMING 0x02 // 光标返回原点
void LCD_Write(bit style, unsigned char input)
{
LcdEn=0;
LcdRs=style;
LcdRw=0; _nop_();
DBPort=input; _nop_();//注意顺序
LcdEn=1; _nop_();//注意顺序
LcdEn=0; _nop_();
LCD_Wait();
}
//设置显示模式***************************************************
#define LCD_SHOW 0x04 //显示开
#define LCD_HIDE 0x00 //显示关
#define LCD_CURSOR 0x02 //显示光标
#define LCD_NO_CURSOR 0x00 //无光标
#define LCD_FLASH 0x01 //光标闪动
#define LCD_NO_FLASH 0x00 //光标不闪动
void LCD_SetDisplay(unsigned char DisplayMode)
{
LCD_Write(LCD_COMMAND, 0x08|DisplayMode);
}
//设置输入模式****************************************************** #define LCD_AC_UP 0x02
#define LCD_AC_DOWN 0x00 // default
#define LCD_MOVE 0x01 // 画面可平移
#define LCD_NO_MOVE 0x00 //default
void LCD_SetInput(unsigned char InputMode)
{
LCD_Write(LCD_COMMAND, 0x04|InputMode);
}
//移动光标或屏幕***************************************************** /*
#define LCD_CURSOR 0x02
#define LCD_SCREEN 0x08
#define LCD_LEFT 0x00
#define LCD_RIGHT 0x04
void LCD_Move(unsigned char object, unsigned char direction)
{
if(object==LCD_CURSOR)
LCD_Write(LCD_COMMAND,0x10|direction);
if(object==LCD_SCREEN)
LCD_Write(LCD_COMMAND,0x18|direction);
}
*/
//初始化LCD********************************************************* void LCD_Initial()
{
LcdEn=0;
LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点阵LCD_Write(LCD_COMMAND,0x38);
LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标
LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); //清屏
LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动
}
//******************************************************************* void GotoXY(unsigned char x, unsigned char y)
{
if(y==0)
LCD_Write(LCD_COMMAND,0x80|x);
if(y==1)
LCD_Write(LCD_COMMAND,0x80|(x-0x40));
}
void Print(unsigned char *str)
{
while(*str!='\0')
{
LCD_Write(LCD_DATA,*str);
str++;
}
}
/*
void LCD_LoadChar(unsigned char user[8], unsigned char place)
{
unsigned char i;
LCD_Write(LCD_COMMAND,0x40|(place*8));
for(i=0; i<8; i++)
LCD_Write(LCD_DATA,user[i]);
}
*/
//******************************************************************* #endif。