经过几天早晨的学习,终于把遥控器的红外信号给搞通了,特此记录一下;其实说白了,红外遥控就是高低电平的信号,用时间来区分是二进制的0还是1;然后把这些0或1,在组装成一个32位的数基本就算是完事了,最后程序里判断你按下的这个遥控按键的值是不是你预先设定的值就好了,执行自己想要的动作就OK了。
下面是NEC红外码的编码时序:
NEC 码的位定义: 一个脉冲对应 560us 的连续载波, 一个逻辑 1 传输需要2.25ms(560us 脉冲+1680us 低电平) , 一个逻辑 0 的传输需要 1.125ms(560us脉冲+560us 低电平) 。 而红外接收头在收到脉冲的时候为低电平, 在没有脉冲的时候为高电平, 这样, 我们在接收头端收到的信号为: 逻辑 1 应该是 560us 低+1680us 高, 逻辑 0 应该是 560us 低+560us 高。 所以可以通过计算高电平时间判断接收到的数据是 0 还是 1。
知道了0和1的编码方式,剩下的就简单了,发送的话就是控制一个IO口560us的低电平560us的高电平就是发送一个0. 控制IO口560us的低电平1680微妙的高电平就是发送一个1.
用个for循环发送8次就是发送了一个字节。还是很简单的。当然这个IO口外面需要接一个红外发光二极管,高电平的时候就点亮,低电平的时候就熄灭。
接收的时候也是一样,外面接一个红外接收二极管,红外接收二极管的一端接IO口,一端接地,这个IO口设置成上拉输入的模式,并开启下降沿中断,每次接收到红外信号后这个接收二极管就会导通,拉低IO口电平,也就会进入中断,在中断中统计时间就能区分出是0还是1了。由于不论是0还是1低电平的时间都是560us,所以只统计高电平的时间就能知道是0还是1了。这样就省去了一半的事情了。
还有就是NEC码还规定了最前面还有一个引导码,这也很简单的:
前面的低电平9ms,4.5ms的高电平就是引导码了,其余的32位不是0就是1了。整体的区分也是只需要区分高电平的时间就行了,4.5ms的是引导码,560us的是0,1680us的是1,还有就是时间上不用卡的那么死,毕竟晶振的震动频率也不见得就那么精准的,有点误差也很正常,所以判断的时候只要给个范围就好了:
比如说: 4ms到5ms的高电平就可以认为是引导码了
200us到800us的高电平就可以认为是数据0了
1000us到1800us的高电平就可以认为是数据1了
有了上面这些理论知识,编写程序就简单多了,先来看看统计高电平的函数要怎么写:
u8 HW_JSSJ(void) //红外接收时间的首字母
{
u8 t=0;
while(HWJS) //HWJS红外接收是一个位带操作,查看连接红外就收二极管的IO的电平 如果是高电平进入
{
t++;
Delay_us(20); //t每次加1就延时20us
if(t>=250) return t; //如果大于了250,也就是5ms了,超出了NEC码中的最高的高电平时间了,再统计也没有什么意义了
}
return t;
}
有了统计高电平时间的统计函数,就可以写中断函数了,每次引导码时都是首先得低电平,所以就以低电平触发中断开始,进入中断就获取高电平时间,然后判断时间范围,是引导码就写个引导码的标志置1,是0或1就把0或1写入8位的变量中,还是直接看程序吧:打字太多了,懒了,5点半了,要去跑步了:
#include "hwjs.h"
void HWJS_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource12);
EXTI_ClearITPendingBit(EXTI_Line12);
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line12;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStruct);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
u8 HW_JSSJ(void) //红外接收时间的首字母
{
u8 t=0;
while(HWJS) //HWJS红外接收是一个位带操作,查看连接红外就收二极管的IO的电平 如果是高电平进入
{
t++;
Delay_us(20); //t每次加1就延时20us
if(t>=250) return t; //如果大于了250,也就是5ms了,超出了NEC码中的最高的高电平时间了,再统计也没有什么意义了
}
return t;
}
u32 HWJSM=0;
u8 HW_JSBZ=0;
void EXTI15_10_IRQHandler(void)
{
u8 time=0, YD=0, dat=0, Num=0;
while(1)
{
if(HWJS)
{
time = HW_JSSJ();
if(time >= 250) break;
if(time >= 200 && time < 250)
{
YD=1;
}
else if(time >= 10 && time < 40)
{
dat = 0;
}
else if(time >= 50 && time < 90)
{
dat = 1;
}
if( YD )
{
HWJSM <<= 1;
HWJSM |= dat;
Num++;
if(Num >= 31)
{
HW_JSBZ = 1;
break;
}
}
}
}
EXTI_ClearITPendingBit(EXTI_Line12);
}
.h文件:
#ifndef _hwjs_H
#define _hwjs_H
#include "system.h"
#include "Delay.h"
extern u32 HWJSM;
extern u8 HW_JSBZ;
#define HWJS PBin(12)
void HWJS_Init(void);
#endif
主函数:
#include "led.h"
#include "Serail1.h"
#include "OLED.h"
#include "hwjs.h"
u8 i=0;
int main(void)
{
Serail1_Init();
Serail2_Init();
led_Init();
OLED_Init();
HWJS_Init();
while(1)
{
if(HW_JSBZ)
{
i++;
OLED_ShowHexNum(1,1,HWJSM,8);
OLED_ShowNum(2,1,i,3);
HW_JSBZ=0;
HWJSM=0;
}
}
}
手头上只有空调遥控器,也是能精准识别的,红外二极管也是随便淘宝的:
接收管长脚接地,短脚接IO口就能识别的,翻过了不行,总共就两种可能性,试试就出来了,很简单的。好了去跑步5公里走起!