一、红外遥控简介

红外遥控,顾名思义,就是利用红外线实现遥控。这里就不单独对红外线做介绍了,红外线的波长再可见光范围外,所以人眼是看不到的。红外遥控的原理就是利用红外线进行通讯,比如生活中常用的电视遥控器,空调遥控器等,大多都是红外通讯实现的遥控功能。


二、红外遥控的原理

红外遥控是一种非接触,无线控制技术。具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等特点。红外遥控系统一般有红外发射装置和红外接收设备两大部分组成。


红外发射装置就是我们常见的遥控器,由键盘电路,红外编码电路,电源电路和红外发射电路组成。

红外接收设备是由红外接收电路,红外解码,电源和应用电路组成。红外接收装置有三个引脚,VDD,GND和数据输出VOUT。通常正对接收头凸起处看,从左到右引脚顺序为VOUT,GND,VDD

image.png


通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以为38kHz。

也有一些遥控系统采用36kHz、40 kHz、56 kHz等,一般由发射端晶振的振荡频率来决定。所以,通常的红外遥控器是将遥控信号(二进制脉冲码) 调制在38KHz的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。


三、二进制脉冲编码

二进制脉冲码的形式有多种,其中最为常用的是NEC Protocol 的PWM码(脉冲宽度调制)和 Philips RC-5 Protoco 的 PPM码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。如果要开发红外接收设备,一定要知道红外遥控器的编码方式和载波频率,

才可以选取一体化红外接收头和制定解码方案。这里针对NEC编码形式做一个详细介绍。NEC编码形式有以下特点


8 位地址和 8 位指令长度

地址和命令 2次传输(确保可靠性)

PWM 脉冲位置调制,以发射红外载波的占空比代表“ 0”和“1”

载波频率为 38Khz

位时间为 1.125ms 或 2.25ms

3.1 NEC码的位定义

一个脉冲对应 560us 的连续载波,一个逻辑1传输需要 2.25ms(560us脉冲+1680us 低电平) ,一个逻辑 0的传输需要 1.125ms (560us脉冲+560us 低电平)。而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,

我们在接收头端收到的信号为: 逻辑 1应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。所以可以通过计算高电平时间判断接收到的数据是0还是1。NEC码位定义时序图如下

image.png



特别标注一下,上图逻辑0和逻辑1的时序图对应的是接收端的时序图。


3.2 NEC遥控指令的数据格式

NEC遥控指令的数据格式为:引导码、地址码、地址反码、控制码控制反码。引导码由一个 9ms 的低电平和一个 4.5ms 的高电平组成地址码、地址反码、控制码、控制反码均是8 位数据格式。按照低位在前,高位在后的顺序发送。

采用反码是为了增加传输的可靠性 (可用于校验)。数据格式如下


image.png


特别标注一下,上图为发送端的时序图。


NEC码还规定了连发码(由 9ms 低电平+2.5m 高电平+0.5ms 低电平+97.94ms 高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。


四、红外遥控程序设计思路

红外发射装置只需要按键按下即可产生红外信号,我们只需要针对红外接收设备编写程序即可。上面介绍了,红外接收设备在收到脉冲的时候为低电平,在没有脉冲的时候为高电平。根据“0”和“1”的时序图可知,我们只需要监测红外接收

设备的数据输出引脚的高电平持续时间就可以判断接收到的是“0”还是“1”。


另外,没有按键按下时,也就是没有发红外信号,没有脉冲,红外接收设备的数据输出引脚一直为高电平。只有接收到脉冲时,说明有按键按下,此时红外接收设备的数据输出引脚为低电平。因此,可以利用外部中断的下降沿出发来判断是否有

按键按下,在中断中测量高电平持续时间来判断接收到的是“0”还是“1”。


五、红外遥控程序设计

5.1 红外遥控初始化程序

需要初始化GPIO和外部中断,GPIO配置为上拉输入模式

/*
 *==============================================================================
 *函数名称:Drv_Hw_Init
 *函数功能:初始化红外遥控模块
 *输入参数:无
 *返回值:无
 *备  注:红外端口初始化函数,时钟端口及外部中断初始化
 *==============================================================================
 */
void Drv_Hw_Init (void)
{
// 结构体定义
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);

// 配置GPIO结构体
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;   // 红外接收
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;   // 上拉输入模式
GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);   // 选择GPIO管脚用作外部中断线路
EXTI_ClearITPendingBit(EXTI_Line11);

// 配置外部中断
EXTI_InitStructure.EXTI_Line=EXTI_Line11;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure); 

// 配置NVIC结构体
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   // 抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;   // 响应优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   // 使能
NVIC_Init(&NVIC_InitStructure);
}

5.2 记录高电平持续时间函数

利用记录延时次数的方法测量高电平持续时间

/*
 *==============================================================================
 *函数名称:Drv_Hw_RecordHightTime
 *函数功能:记录高电平持续时间并返回
 *输入参数:无
 *返回值:t:高电平持续时间
 *备  注:无
 *==============================================================================
 */
u8 Drv_Hw_RecordHightTime (void)
{
u8 t = 0;
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 1)   // 高电平
{
t ++;
delay_us(20);
if(t >= 250)
{
return t;   // 超时溢出
}
}
return t;
}

 

5.3 中断服务函数

中断服务函数中接收数据,接收完成后对应的接收完成标志位置1

/*
 *==============================================================================
 *函数名称:EXTI15_10_IRQHandler
 *函数功能:外部中断服务函数
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
 */
void EXTI15_10_IRQHandler(void)   // 红外遥控外部中断
{
u8 Tim = 0,Ok = 0,Data,Num = 0;

   while(1)
   {
    if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 1)
{
Tim = Drv_Hw_RecordHightTime();   //获得此次高电平时间

if(Tim>=250)
{
break;   // 不是有用的信号
}

if(Tim >= 200 && Tim < 250)
{
Ok = 1;   // 收到起始信号
}
else if(Tim >= 60 && Tim < 90)
{
Data = 1;   // 收到数据 1
}
else if(Tim >= 10 && Tim < 50)
{
Data = 0;   // 收到数据 0
}
 
if(Ok == 1)
{
hw_jsm <<= 1;
hw_jsm += Data;

// 接收完成
if(Num >= 32)
{
hw_jsbz = 1;
  break;
}
}
Num ++;
}
   }
 
// 清除中断标志位
   EXTI_ClearITPendingBit(EXTI_Line11);
}

5.4 读取键值

判断键盘上按下的是哪个按键。不同的遥控器,按键对应的红外编码可能不同,可以通过串口打印接收到的键值的方法来读出每一个按键的键值。

/*
 *==============================================================================
 *函数名称:Med_Hw_ReadKeyValue
 *函数功能:读取键值
 *输入参数:无
 *返回值:红外键值
 *备  注:每一个键值是测试出来的,不同遥控器键值可能不同
 *==============================================================================
 */
u8 Med_Hw_ReadKeyValue (void)
{
if(hw_jsm == 0x00FF9867)
{
return 0;
}
else if(hw_jsm == 0x00FFA25D)
{
return 1;
}
else if(hw_jsm == 0x00FF629D)
{
return 2;
}
else if(hw_jsm == 0x00FFE21D)
{
return 3;
}
else if(hw_jsm == 0x00FF22DD)
{
return 4;
}
else if(hw_jsm == 0x00FF02FD)
{
return 5;
}
else if(hw_jsm == 0x00FFC23D)
{
return 6;
}
else if(hw_jsm == 0x00FFE01F)
{
return 7;
}
else if(hw_jsm == 0x00FFA857)
{
return 8;
}
else if(hw_jsm == 0x00FF906F)
{
return 9;
}
else if(hw_jsm == 0x00FF6897)   // 按键*按下
{
return 10;
}
else if(hw_jsm == 0x00FFB04F)   // 按键#按下
{
return 11;
}
else if(hw_jsm == 0x00FF38C7)   // 按键OK按下
{
return 12;
}

else if(hw_jsm == 0x00FF18E7)   // 按键"上"按下
{
return 13;
}
else if(hw_jsm == 0x00FF4AB5)   // 按键"下"按下
{
return 14;
}
else if(hw_jsm == 0x00FF10EF)   // 按键"左"按下
{
return 15;
}
else if(hw_jsm == 0x00FF5AA5)   // 按键"右"按下
{
return 16;
}
return 17;
}

 

5.5 参数定义

定义了以下两个参数

u32 hw_jsm;   // 定义一个32位数据变量,保存接收码
u8 hw_jsbz;  // 定义一个8位数据的变量,用于指示接收标志

 

六、应用实例

利用红外遥控控制LED的亮灭,按下“1”点亮,按下“0”熄灭。每次执行完对应按键的内容后,需要清除接收完成标志位和接收码。main函数如下

u8 gHwKeyValue = 0;   // 红外键值

int main(void)
{
Med_Mcu_Iint();   // 系统初始化

while(1)
  {
if (hw_jsbz == 1)
{
// 获取红外键值
gHwKeyValue = Med_Hw_ReadKeyValue();

// 按下按键“1”
if (gHwKeyValue == 1)
{
// 点亮LED
Med_Led_StateCtrl(LED1,LED_ON);
}
// 按下按键“0”
if (gHwKeyValue == 0)
{
// 熄灭LED
Med_Led_StateCtrl(LED1,LED_OFF);
}

hw_jsbz = 0;   // 清除接收完成标志位
hw_jsm = 0;   // 清零接收码
}
}
}

————————————————

版权声明:本文为CSDN博主「二土电子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_45217381/article/details/131425665