红外遥控解码(NEC)
基本介绍
什么是红外线?
人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。其中红光的波长范围为0.62~0.76μm;紫光的波长范围为0.38~0.46μm。比紫光波长还短的光叫紫外线,比红光波长还长的光叫红外线.红外线遥控就是利用波长为0.76~1.5μm之间的近红外线来传送控制信号的。
红外线系统的组成
红外线系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置又可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由红外接收电路、红外解码芯片、电源和应用电路组成。通常为了使信号更好的被发射端发送出去,经常会将二进制数据信号调制成为脉冲信号,通过红外发射管发射。常用的有通过脉冲宽度来实现信号调制的脉宽调制(PWM)和通过脉冲串之间的时间间隔来实现信号调制的脉时调制(PPM)两种方法。
发射管和接收管
发送
是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为940nm左右,外形与普通φ5发光二极管相同。
接收
红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。
红外遥控发射(载波频率)
通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。
重要介绍
NEC协议
NEC功能特点包含:
1.地址码与地址反码,以及命令码与命令反码,具有数据校验作用,增强结果的准确性
2.脉冲距离调制,调制占空比
3.载波频率38KHZ
4.逻辑位时间为逻辑0:1.125ms或逻辑1:2.25ms
5.发送的信号与接收到的信号是反向的
数据格式(必看)
数据格式包括了引导码、用户码、用户反码、数据码和数据码反码,编码总占32位。 数据反码是数据码反相后的编码,编码时可用于对数据的纠错。注意:第二段的用户码也可以在遥控应用电路中被设置成第一段用户码的反码。
位定义(必看)
用户码或数据码中的每一个位可以是位 ‘1’ ,也可以是位 ‘0’。区分 ‘0’和 ‘1’是利用脉冲的时间间隔来区分,这种编码方式称为脉冲位置调制方式,英文简写PPM。
编写程序思路(2种)
1.这个红外一般都是用在中断的,像在我的板子是P3.2口,是外部中断0,中断我们知道可以设定为下降沿触发或者低电平触发。
方式一
第一种方式就是直接模拟数据格式的图来编写程序。(一次中断做完一帧的数据)
一开始 是9ms低电平 ——> 4.5高电平 ——> 0.56ms低电平——>位1/位0的高电平时间…
void ReadIr() interrupt 0
{
u8 j,k;
u16 err;
Time=0;
delay(700); //7ms
if(IRIN==0) //确认是否真的接收到正确的信号
{
err=1000; //1000*10us=10ms,超过说明接收到错误的信号
/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
侯,程序死在这里*/
while((IRIN==0)&&(err>0)) //等待前面9ms的低电平过去
{
delay(1);
err--;
}
if(IRIN==1) //如果正确等到9ms低电平
{
err=500;
while((IRIN==1)&&(err>0)) //等待4.5ms的起始高电平过去
{
delay(1);
err--;
}
for(k=0;k<4;k++) //共有4组数据
{
for(j=0;j<8;j++) //接收一组数据
{
err=60;
while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去
{
delay(1);
err--;
}
err=500;
while((IRIN==1)&&(err>0)) //计算高电平的时间长度。
{
delay(10); //0.1ms
Time++;
err--;
if(Time>30) //大于3ms 退出程序
{
return;
}
}
IrValue[k]>>=1; //k表示第几组数据
if(Time>=8) //如果高电平出现大于0.8ms,那么是1
{
IrValue[k]|=0x80;
}
Time=0; //用完时间要重新赋值
}
}
}
if(IrValue[2]!=~IrValue[3]) //对比 数据码 和数据反码 是否成立 不对则退出
{
return;
}
}
}
方式二
第二种是每触发中断就记录一次时间,通过记录时间的长短来确定是引导位还是数据位 位1还是位0(只记下降沿到下一次下降沿的时间)
void readIr() interrupt 0
{
count++;
if(count == 1)
{
irTime=0;
}
if(irTime > 40) //判断前导码是否接近9ms
{
bitNum = 0;
}
irData[bitNum] = irTime;
irTime = 0;
bitNum++;
if(bitNum == 33)
{
irDataOk = 1; //32位数据接收完标志位
count = 0; //次数清零
}
}
程序代码(在1602显示)
方式一
main.c
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include "lcd1602.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit IRIN=P3^2; //红外引脚
u8 IrValue[4] = {'\0'};
u8 Time;
/*延时函数,i=1时,大约延时10us*/
void delay(u16 i)
{
while(i--);
}
/*显示函数*/
void DigDisplay()
{
u8 i;
write_com(0x83);
for(i=0;i<4;i++)
{
if(IrValue[i]/16<10)
{
write_data(IrValue[i]/16 + 0x30);
}
else
{
write_data(IrValue[i]/16 + 0x37);
}
if(IrValue[i]%16<10)
{
write_data(IrValue[i]%16 + 0x30);
}
else
{
write_data(IrValue[i]%16 + 0x37);
}
}
}
/*配置外部中断0(初始化红外线接收)*/
void IrInit()
{
IT0=1;//下降沿触发
EX0=1;//打开中断0允许
EA=1; //打开总中断
IRIN=1;//初始化端口
}
/*主函数*/
void main()
{
IrInit(); //调用中断0(红外初始化)
init_lcd(); //初始化1602
while(1)
{
DigDisplay(); //显示
}
}
/*读取红外数值的中断函数*/
void ReadIr() interrupt 0
{
u8 j,k;
u16 err;
Time=0;
delay(700); //7ms
if(IRIN==0) //确认是否真的接收到正确的信号
{
err=1000; //1000*10us=10ms,超过说明接收到错误的信号
/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
侯,程序死在这里*/
while((IRIN==0)&&(err>0)) //等待前面9ms的低电平过去
{
delay(1);
err--;
}
if(IRIN==1) //如果正确等到9ms低电平
{
err=500;
while((IRIN==1)&&(err>0)) //等待4.5ms的起始高电平过去
{
delay(1);
err--;
}
for(k=0;k<4;k++) //共有4组数据
{
for(j=0;j<8;j++) //接收一组数据
{
err=60;
while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去
{
delay(1);
err--;
}
err=500;
while((IRIN==1)&&(err>0)) //计算高电平的时间长度。
{
delay(10); //0.1ms
Time++;
err--;
if(Time>30) //大于3ms 退出程序
{
return;
}
}
IrValue[k]>>=1; //k表示第几组数据
if(Time>=8) //如果高电平出现大于0.8ms,那么是1
{
IrValue[k]|=0x80;
}
Time=0; //用完时间要重新赋值
}
}
}
if(IrValue[2]!=~IrValue[3]) //对比 数据码 和数据反码 是否成立 不对则退出
{
return;
}
}
}
方式二
main.c
#include <reg52.h>
#include "lcd1602.h"
unsigned char irTime = 0; //电平时间
unsigned char bitNum = 0; //位数
unsigned char count = 0; //记录次数
unsigned char irDataOk = 0; //红外数据接收标志位
unsigned char irValueOk = 0; //红外编码完成标志位
unsigned char irValue[4] = {'\0'}; //存放键值
unsigned char irData[33] = {'\0'}; //存放电平时间
/*配置外部中断0 和 定时器0*/
void interrupt0Config()
{
EX0 = 1; //触发方式为负跳变
IT0 = 1; //打开中断0允许位
TMOD = 0x02; //定时器0 方式2(8位自动重装)
TH0 = 0x00; //200us
TL0 = 0x48;
ET0 = 1; //打开定时器0 允许位
TR0 = 1; //启动定时器0
EA = 1; //打开总开关
}
/********红外数据处理函数*********/
void irDataProcessing()
{
unsigned char i,j;
unsigned char k = 1;
unsigned char value = 0;
for(i=0;i<4;i++)
{
for(j=0;j<8;j++)
{
value>>=1; //右移一次
if(irData[k] > 7 ) // 8X200us=1.6ms 大于1.6则记为1
{
value |= 0x80;
}
k++;
}
irValue[i] = value;
}
irValueOk = 1;
irDataOk = 0;
}
/*********显示函数**********/
void display()
{
unsigned char i;
write_com(0x83);
if(irValueOk == 1 && irValue[2]==~irValue[3])//判断是否接收完和数据码有无错
{
for(i=0;i<4;i++)
{
if(irValue[i]/16<10)
{
write_data(irValue[i]/16 + 0x30);
}
else
{
write_data(irValue[i]/16 + 0x37);
}
if(irValue[i]%16<10)
{
write_data(irValue[i]%16 + 0x30);
}
else
{
write_data(irValue[i]%16 + 0x37);
}
}
irValueOk=0;
}
}
/*主函数*/
void main()
{
interrupt0Config(); //调用中断函数
init_lcd(); //初始化1602
while(1)
{
if(irDataOk == 1)
{
irDataProcessing(); //红外数据处理函数
}
display(); //显示函数
}
}
/*红外接收函数*/
void readIr() interrupt 0
{
count++;
if(count == 1)
{
irTime=0;
}
if(irTime > 40) //判断前导码是否接近9ms
{
bitNum = 0;
}
irData[bitNum] = irTime;
irTime = 0;
bitNum++;
if(bitNum == 33)
{
irDataOk = 1; //32位数据接收完标志位
count = 0; //次数清零
}
}
void time0() interrupt 1
{
irTime++;
}
LCD1602相关代码
lcd1602.c
#include <reg52.h>
#include "lcd1602.h"
#define LCD P0
sbit E = P2^7;
sbit RS = P2^6;
sbit RW = P2^5;
/******延迟5毫秒函数********/
void delay5ms() //误差 -0.000000000001us
{
unsigned char a,b;
for(b=15;b>0;b--)
for(a=152;a>0;a--);
}
/******LCD1602写命令函数********/
void write_com(unsigned char command)
{
RS = 0;
RW = 0; //高读低写
LCD = command;
delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
E = 1; //使能拉高
delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
E = 0;
}
/******LCD1602写数据函数********/
void write_data(unsigned char dat)
{
RS = 1;
RW = 0;
LCD = dat;
delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
E = 1; //使能拉高
delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
E = 0;
}
/******初始化LCD1602********/
void init_lcd()
{
write_com(0x06); //写入数据后光标自动右移 整屏不移动。 0x40(光标左移 整屏不移动)0x05(左移 整屏右移)0x07(右移 整屏右移)
write_com(0x0c); //开显示功能 无光标 不闪烁
write_com(0x38); //数据总线8位 16X2显示 5*7点阵
write_com(0x01); //清屏 0000 0001
}
lcd1602.h
#ifndef _LCD1602_H_
#define _LCD1602_H_
void write_com(unsigned char command); //写命令函数
void write_data(unsigned char dat); //写数据函数
void init_lcd(); //初始化LCD1602函数
void delay5ms(); //延时5ms函数
#endif