先来说下制作这个DEMO的所经历的曲折以及知识点:

  • 学习NEC协议;
  • 学习AVR的定时与中断,因为Arduino是在AVR的基础上实现的;
  • 编程实现NEC解码;
  • 解码未成功,最终使用Ken Shirriff的解码类库,成功实现DEMO;

下面进入正题

1. 首先介绍下红外接收头

红外接收头有三个引脚如下图

红外遥控 Arduino 实例

三个引脚含义上图标的非常清晰:VOUT接模拟口,GND接GND,VCC接电源。

红外遥控器发出的信号是一连串的二进制脉冲码。为了使其在无线传输过程中免受其他红外信号的干扰,通常都是先将其调制在特定的载波频率上,然后再经红外发射二极管发射出去,而红外线接收装置则要滤除其他杂波,只接收该特定频率的信号并将其还原成二进制脉冲码,也就是解调.

工作原理:内置接收管将红外发射管发射出来的光信号转换为微弱的电信号,此信号经由IC内部放大器进行放大,然后通过自动增益控制、带通滤波、解调发、波形整形后还原为遥控器发射出的原始编码,经由接收头的信号输出脚输入到电器上的编码识别电路。

2. NEC协议

要想对某一遥控器进行解码必须要了解该遥控器的编码方式。我们的这个DEMO使用的遥控器的编码方式为:NEC协议。

特点:
(1)8位地址位,8位命令位
(2)为了可靠性地址位和命令位被传输两次
(3)脉冲位置调制
(4)载波频率38khz
(5)每一位的时间为1.125ms或2.25ms 

逻辑0 和1 的定义如下图:

红外遥控 Arduino 实例

按键按下立刻松开的发射脉冲:

红外遥控 Arduino 实例

上面的图片显示了NEC的协议典型的脉冲序列。注意:这是首先发送LSB(最低位)的协议。在上面的脉冲传输的地址为0x59命令为0x16。一个 消息是由一个9ms的高电平开始,随后有一个4.5ms的低电平,(这两段电平组成引导码)然后由地址码和命令码。地址和命令传输两次。第二次所有位都取 反,可用于对所收到的消息中的确认使用。总传输时间是恒定的,因为每一点与它取反长度重复。如果你不感兴趣,你可以忽略这个可靠性取反,也可以扩大地址和 命令,以每16位!

按键按下一段时间才松开的发射脉冲:

红外遥控 Arduino 实例

一个命令发送一次,即使在遥控器上的按键仍然按下。当按键一直按下时,第一个110ms的脉冲不上图一样,随后每110ms重复代码传输一次。这个重复代码是由一个9ms的高电平脉冲和一个2.25ms低电平和560μs的高电平组成。

·重复脉冲:

红外遥控 Arduino 实例

注意:脉冲波形进入一体化接收头以后,因为一体化接收头里要进行解码、信号放大和整形,故要注意:在没有红外信号时,其输出端为高电平,有信号时为低电平,故其输出信号电平正好和发射端相反。接收端脉冲大家可以通过示波器看到,结合看到的波形理解程序。

3. 实现的效果以及器材

器材及数量:
     红外遥控器:1个;
     红外接收头:1个;
     LED灯:1个;
     220Ω电阻:1个;
     多彩面包线:若干;

实现效果:按下遥控器的EQ键盘LED亮,按下电源键LED灭。

红外遥控 Arduino 实例

4. 编码实现

根据NEC 特点和接收端的波形,将接收端的波形分成四部分:引导码(9ms 和4.5ms 的脉冲)、地址码16 位(包括8 位的地址码和8 位的地址的取反)、命令码16 位(包括8 位命令位和8 位命令位的取反)、重复码(9ms、2.25ms、560us 脉冲组成)。

利用定时器对接收到的波形的高电平段和低电平段进行测量,根据测量到的时间来区分:逻辑“0”、逻辑“1”、引导脉冲、重复脉冲。引导码和地址码只要判断是正确的脉冲即可,不用存储,但是命令码必须存储,因为每个按键的命令码都不同,根据命令码来执行相应的动作。

代码如下:

  1 #define LED 7//LED灯
  2 #define IR_IN 8 //红外接收
  3 int Pulse_Width=0;//存储脉宽
  4 int ir_code=0x00;//命令值
  5 
  6 void timer1_init(void)//定时器初始函数
  7 {
  8     TCCR1A = 0X00;
  9     TCCR1B = 0X05;//给定时器时钟源
 10     TCCR1C = 0X00;
 11     TCNT1 = 0X00;
 12     TIMSK1 = 0X00; //禁止定时器溢出中断
 13 }
 14 
 15 void remote_decode(void)//译码函数
 16 {
 17     TCNT1=0X00;
 18     while(digitalRead(8))//是高就等待
 19     {
 20         if(TCNT1>=1563) //当高电平持续时间超过100ms,表明此时没有按键按下
 21         {
 22             ir_code = 0xff00;
 23             return;
 24         }
 25     }
 26     //如果高电平持续时间不超过100ms
 27     TCNT1=0X00;
 28     while(!(digitalRead(8))); //低等待
 29 
 30     Pulse_Width=TCNT1;
 31     TCNT1=0;
 32 
 33     if(Pulse_Width>=140&&Pulse_Width<=141)//9ms
 34     {
 35         while(digitalRead(8));//是高就等待
 36         
 37         Pulse_Width=TCNT1;
 38         TCNT1=0;
 39         if(Pulse_Width>=68&&Pulse_Width<=72)//4.5ms
 40         {
 41             pulse_deal();
 42             return;
 43         }else if(Pulse_Width>=34&&Pulse_Width<=36)//2.25ms
 44         {
 45             while(!(digitalRead(8)));//低等待
 46             Pulse_Width=TCNT1;
 47             TCNT1=0;
 48             if(Pulse_Width>=7&&Pulse_Width<=10)//560us
 49             {
 50                 return;
 51             }
 52         }
 53     }
 54 }
 55 
 56 void pulse_deal()//接收地址码和命令码脉冲函数
 57 {
 58     int i;
 59 
 60     //执行8个0
 61     for(i=0; i<8; i++)
 62     {
 63         if(logic_value() != 0) //不是0
 64         return;
 65     }
 66 
 67     //执行6个1
 68     for(i=0; i<6; i++)
 69     {
 70         if(logic_value()!= 1) //不是1
 71         return;
 72     }
 73     
 74     //执行1个0
 75     if(logic_value()!= 0) //不是0
 76         return;
 77     
 78     //执行1个1
 79     if(logic_value()!= 1) //不是1
 80         return;
 81     
 82     //解枂遥控器编码中的command指令
 83     ir_code=0x00;//清零
 84 
 85     for(i=0; i<16;i++ ){
 86         if(logic_value() == 1){ir_code |=(1<<i);}
 87     }
 88 }
 89 
 90 
 91 void remote_deal(void)//执行译码结果函数
 92 {
 93     switch(ir_code)
 94     {
 95         case 0xff00://停止
 96         digitalWrite(LED,LOW);//LED亮
 97         break;
 98         case 0xfe01://VOL+
 99         digitalWrite(LED,HIGH);//LED灭
100         break;
101     }
102 }
103 
104 char logic_value()//判断逻辑值“0”和“1”子函数
105 {
106     while(!(digitalRead(8))); //低等待
107     Pulse_Width=TCNT1;
108     TCNT1=0;
109     
110     if(Pulse_Width>=7&&Pulse_Width<=10)//低电平560us
111     {
112         while(digitalRead(8));//是高就等待
113         Pulse_Width=TCNT1;
114         TCNT1=0;
115     
116         if(Pulse_Width>=7&&Pulse_Width<=10)//接着高电平560us
117             return 0;
118         else if(Pulse_Width>=25&&Pulse_Width<=27) //接着高电平1.7ms
119         return 1;
120     }
121     return -1;
122 }
123 
124 
125 
126 void setup()
127 {
128     unsigned char i;
129     pinMode(LED,OUTPUT);//设置与LED连接的引脚为输出模式
130     pinMode(IR_IN,INPUT);//设置红外接收引脚为输入
131 }
132 
133 void loop()
134 {
135     timer1_init();//定时器初始化
136     while(1)
137     {
138         remote_decode(); //译码
139         remote_deal(); //执行译码结果
140     }
141 }
View Code

相关文章: