红外接收器的原理以及在STM32和51单片机中的应用

基本介绍:

        红外接收器是一种用于接收红外线信号的装置,常见于各种电子设备中,如电视遥控器、空调遥控器等。它能够接收来自发射器发送的红外信号,并将其转换成电信号,以便设备进行相应的操作。红外接收器通常包含红外光电二极管和相应的电路,用于检测和解码接收到的红外信号。

        在电子设备中,红外接收器扮演着接收用户输入指令的角色。当用户按下遥控器上的按钮时,遥控器会发送相应的红外信号,被设备中的红外接收器接收后,设备会根据接收到的信号执行相应的操作,比如调整音量、切换频道等。

        红外接收器的工作原理是利用红外光电二极管的特性,当红外线照射到光电二极管上时,会产生电流,该电流随着红外线强度的变化而变化,接收电路会将这些变化转换成数字信号或模拟信号,以供设备使用。

红外遥控介绍:

红外线简介:

        人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、 蓝、紫。其中红光的波长范围为 0.62~0.76μm;紫光的波长范围为 0.38~0.46 μm。比紫光波长还短的光叫紫外线,比红光波长还长的光叫红外线。红外线遥控就是利用波长为 0.76~1.5μm 之间的近红外线来传送控制信号的。

红外遥控的原理:

红外遥控的原理基于红外线的特性以及红外遥控器的工作原理。

1. 红外线特性:红外线是一种电磁辐射,波长长于可见光,人眼无法看到。它在电磁谱中介于可见光和微波之间。红外线能够穿透大气中的很多物质,并且可以在空间中传播,因此被广泛应用于通信和控制领域。

2. 红外遥控器的工作原理:红外遥控器内置了一个发射器,通常是红外发光二极管。当用户按下遥控器上的按钮时,遥控器会向外发射一系列的红外脉冲信号,每个按钮对应一个特定的红外码。这些红外码是事先编程好的,用来代表不同的功能或指令。

3. 红外接收器的工作原理:被控制的设备内部有一个红外接收器,通常也是红外光电二极管。当接收器接收到红外遥控器发送的信号时,它会将红外光转换成电信号,并传递给设备的控制电路。控制电路根据接收到的红外码来识别用户的操作,并执行相应的功能,比如调整音量、切换频道等。

4. 编码解码:红外遥控器和设备之间的通信需要一种编码解码的机制,以确保正确的指令被传递和执行。遥控器发送的红外信号是经过编码的,接收器需要将其解码成可识别的指令,从而执行相应的操作。

        总的来说,红外遥控的原理就是利用红外线作为通信媒介,通过遥控器发送编码的红外信号,设备内的红外接收器接收并解码这些信号,以实现对设备功能的控制。

红外发射装置:

红外发射装置,也就是通常我们说的红外遥控器是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管。 它实际上是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为 940nm 左右,外形与普通发光二极管相同。红外发光二极管有透明的,还有不透明的,在我们的红外遥控器上可以看到这个红外发光二极管。红外遥控器和红外发光二极管如下图所示:
        通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为 38kHz,这是由发射端所使用的 455kHz晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取 12,所以455kHz÷12≈37.9kHz≈38kHz。也有一些遥控系统采用36kHz、 40 kHz、 56 kHz等,一般由发射端晶振的振荡频率来决定。所以,通常的红外遥控器是将遥控信 号(二进制脉冲码)调制在 38KHz 的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。

        二进制脉冲码的形式有多种,其中最为常用的是 NEC Protocol 的 PWM 码(脉冲宽度调制)和 Philips RC-5 Protocol 的 PPM 码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。如果要开发红外接收设备,一定要知道红外遥控器的编码方式和载波频率,我们才可以选取一体化红外接收头和制定解码方案。我们的红外遥控器使用的是 NEC 协议,其特征如下:

1、8 位地址和 8 位指令长度;
2、地址和命令 2 次传输(确保可靠性)
3、PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;
4、载波频率为 38Khz;
5、位时间为 1.125ms 或 2.25ms;
        NEC 码的位定义:一个脉冲对应 560us 的连续载波, 一个逻辑 1 传输需要2.25ms(560us 脉冲+1680us 低电平),一个逻辑0的传输需要1.125ms(560us脉冲+560us 低电平) 。而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在 接收头端 收到的信号为: 逻辑 1 应该是 560us低+1680us 高,逻辑0应该是560us低+560us 高。 所以可以通过计算高电平时间判断接收到的数据是 0 还是 1。NEC 码位定义时序图如下图所示:
        NEC 遥控指令的数据格式为: 引导码、地址码、地址反码、控制码、控制反码。引导码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。数据格式如下:
        NEC 码还规定了连发码(由 9ms 低电平+2.5m 高电平+0.56ms 低电平+97.94ms 高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。
        这里写有一个点需要留意,无论发的是什么码,数据如何,是逻辑1或是逻辑0,接收端先接收到的一定是低电平,这个非常关键,写程序逻辑的时候非常关键。

 红外接收设备:

红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。近几年不论是业余制作还是正式产品,大多都采用成品红外接收头。成品红外接收头的封装大致有两种:一种采用铁皮屏蔽;一种是塑料封装。均有三只引脚,即电源正( VDD)、电源负(GND)和数据输出(VOUT)。其外观实物图如下图所示:

         

正对接收头的凸起处看,从左至右,管脚依次是 1:VOUT,2:GND,3:VDD。由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平, 所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断 接收到的数据是 0 还是 1。

   

软件设置:

51单片机:

本篇所要实现的功能是:数码管上显示红外解码遥控器键值。
我们这篇讲的是红外接收原理,我们无需在意数码管如何实现,我们只要正确解析出红外遥控器输入的按键值就可以,之后直接调用数码管函数来显示键值。
红外初始化函数:
/********************************************************************
***********
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断 0 配置
* 输 入 : 无
* 输 出 : 无
*********************************************************************
**********/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断 0 允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
因为我们使用外部中断 0 来解码红外遥控数据,所以需初始化配置外部中断0。

红外解码函数:

初始化外部中断后,中断就已经开启了,当 P32 引脚来一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据 0 和 1。具体代码如下:
void ired() interrupt 0 //外部中断 0 服务函数
{
    u8 ired_high_time=0;
    u16 time_cnt=0;
    u8 i=0,j=0;
    if(IRED==0)
    {
        time_cnt=1000;
        while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
        {
            delay_10us(1);//延时约 10us
            time_cnt--;
            if(time_cnt==0)return;
        }
        if(IRED)//引导信号 9ms 低电平已过,进入 4.5ms 高电平
        {
            time_cnt=500;
            while(IRED&&time_cnt)//等待引导信号 4.5ms 高电平结束,若超过 5ms 强制退出
            {
                delay_10us(1);
                time_cnt--;
                if(time_cnt==0)return;
            }
            for(i=0;i<4;i++)//循环 4 次,读取 4 个字节数据
            {
                for(j=0;j<8;j++)//循环 8 次读取每位数据即一个字节
                {
                    time_cnt=600;
                    while((IRED==0)&&time_cnt)//等待数据 1 或 0 前面的 0.56ms结束,若超过0.6ms强制退出
                    {
                        delay_10us(1);
                        time_cnt--;
                        if(time_cnt==0)return;
                    }
                    time_cnt=20;
                    while(IRED)//等待数据 1 或 0 后面的高电平结束,若超过 2ms 强制退出
                    {
                        delay_10us(10);//约 0.1ms
                        ired_high_time++;
                        if(ired_high_time>20)return;
                    }
                    gired_data[i]>>=1;//先读取的为低位,然后是高位
                    if(ired_high_time>=8)//如果高电平时间大于 0.8ms,数据则为1,否则为 0
                    gired_data[i]|=0x80;
                    ired_high_time=0;//重新清零,等待下一次计算时间
                }
            }
    }
    if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
    {
        for(i=0;i<4;i++)
        gired_data[i]=0;
        return;
    }
}

         进入中断函数,表示以来下降沿,然后判断管脚是否为低电平,如果为低电平则首先判断引导信号,根据前面 NEC 协议可知,引导信号有 9ms 的低电平和4.5ms 的高电平,因此通过 time_cnt 赋值 1000,然后在 while 循环内判断,time_cnt 每递减一次约 10us,1000 次则为 10ms,在解码时,这个时间要适当放 宽一点范围,因为不同传感器性能会有差异,所以此处以 10ms 的低电平为界限, 如果超过 10ms 则强制退出,防止系统死机。判断完引导信号的低电平,接着判断高电平,实现方法一样。当引导信号判断完成后进入地址码、地址反码、控制码及控制反码共4个字节的数据判断,也就是数据 0 和 1 的判断,实现方法也是 和前面判断引导信号一样,这里使用到了嵌套循环,外层循环次数是 4,表示读取 4 个字节,内层循环次数是 8,表示读取每个字节的 8 位。注意,红外遥控解码数据是从低位开始,最后是高位。最后将读取的 4 个字节数据存储在全局变量数组 gired_data 中,外部可直接使用这四个字节。

代码主要逻辑:

1、IRED其实就是红外接收端的引脚:

#define IRED P3^2

2、这里进去一次中断,如果说一切正常,因为一次按键的最长数据 = 引导码 + 32个数据"1" = 9+4.5+2.25*32 = 85.5ms,所以中断处理了 85.5ms才出来。

3、其实就是通过阻塞轮询模式,不断调用cpu,来判断红外接收端电平,来实现解码。

4、关键点就是超时判断和通过高电平时间来确认接收的是逻辑1还是逻辑0。

主函数:

        主函数代码很简单,首先调用使用的外设头文件,然后初始化红外,进入while 循环将红外解码数据转换为数码管段码数据,然后在数码管上显示。
void main()
{
    u8 ired_buf[3];
    ired_init();//红外初始化
    while(1)
    {
    ired_buf[0]=gsmg_code[gired_data[2]/16];//将控制码高 4 位转换为数码
    管段码
    ired_buf[1]=gsmg_code[gired_data[2]%16];//将控制码低 4 位转换为数码
    管段码
    ired_buf[2]=0X76;//显示 H 的段码
    smg_display(ired_buf,6);
    }
}

 实验现象:

 STM32:

总体思路其实都是一样,都是利用外部中断,然后判断高低电平的保持时间,来对红外进行解码。

这里红外接收端接的是PA10。

CubeMX配置该引脚为双边沿触发,完成红外接收初始化。可见比51简单不少。

解析中断回调函数里记录的时间序列,得到的device和key放入全局数组buf

/**********************************************************************
 * 函数名称: IRReceiver_IRQTimes_Parse
 * 功能描述: 解析中断回调函数里记录的时间序列,得到的device和key放入全局数组buf
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 0 - 成功, (-1) - 失败
 * 修改日期:      版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2024/04/21	     	  刻刻帝	      创建
 ***********************************************************************/
static int IRReceiver_IRQTimes_Parse(void)
{
	uint64_t time;
	int i;
	int m, n;
	unsigned char datas[4];
	unsigned char data = 0;
	int bits = 0;
	int byte = 0;

	/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲  */
	time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];
	if (time < 8000000 || time > 10000000)
	{
		return -1;
	}

	time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];
	if (time < 3500000 || time > 55000000)
	{
		return -1;
	}

	/* 2. 解析数据 */
	for (i = 0; i < 32; i++)
	{
		m = 3 + i*2;
		n = m+1;
		time = g_IRReceiverIRQ_Timers[n] - g_IRReceiverIRQ_Timers[m];
		data <<= 1;
		bits++;
		if (time > 1000000)
		{
			/* 得到了数据1 */
			data |= 1;
		}

		if (bits == 8)
		{
			datas[byte] = data;
			byte++;
			data = 0;
			bits = 0;
		}
	}

	/* 判断数据正误 */
	datas[1] = ~datas[1];
	datas[3] = ~datas[3];
	
	if ((datas[0] != datas[1]) || (datas[2] != datas[3]))
	{
        g_IRReceiverIRQ_Cnt = 0;
        return -1;
	}

	buf[0]=datas[0];
    buf[1]=datas[2];
	
    return 0;
}

 解析中断回调函数里记录的时间序列,判断是否重复码:

/**********************************************************************
 * 函数名称: isRepeatedKey
 * 功能描述: 解析中断回调函数里记录的时间序列,判断是否重复码
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 1 - 是, (0) - 不是
 * 修改日期:      版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2024/04/21	     	  刻刻帝      创建
 ***********************************************************************/
static int isRepeatedKey(void)
{
	uint64_t time;

	/* 1. 判断重复码 : 9ms的低脉冲, 2.25ms高脉冲  */
	time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];
	if (time < 8000000 || time > 10000000)
	{
		return 0;
	}

	time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];
	if (time < 2000000 || time > 2500000)
	{
		return 0;
	}	

	return 1;
}

 红外接收器的中断回调函数,记录中断时刻:

/**********************************************************************
 * 函数名称: IRReceiver_IRQ_Callback
 * 功能描述: 红外接收器的中断回调函数,记录中断时刻
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期:      版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2024/04/21	    	  刻刻帝	      创建
 ***********************************************************************/
void IRReceiver_IRQ_Callback(void)
{
    uint64_t time;
    static uint64_t pre_time = 0;

        
	/* 1. 记录中断发生的时刻 */	
	time = system_get_ns();
    
    /* 一次按键的最长数据 = 引导码 + 32个数据"1" = 9+4.5+2.25*32 = 85.5ms
     * 如果当前中断的时刻, 举例上次中断的时刻超过这个时间, 以前的数据就抛弃
     */
    if (time - pre_time > 100000000) 
    {
        g_IRReceiverIRQ_Cnt = 0;
    }
    pre_time = time;
    
	g_IRReceiverIRQ_Timers[g_IRReceiverIRQ_Cnt] = time;

	/* 2. 累计中断次数 */
	g_IRReceiverIRQ_Cnt++;

	/* 3. 次数达标后, 解析数据, 放入buffer */
	if (g_IRReceiverIRQ_Cnt == 4)
	{
		/* 是否重复码 */
		if (isRepeatedKey())
		{
			/* device: 0, val: 0, 表示重复码 */
			buf[0]=0;
			buf[1]=0;
			g_IRReceiverIRQ_Cnt = 0;
		}
	}
	if (g_IRReceiverIRQ_Cnt == 68)
	{
		IRReceiver_IRQTimes_Parse();
		g_IRReceiverIRQ_Cnt = 0;
	}
}

代码解析;

1、这里采用双边沿触发中断,然后用time = system_get_ns();记录当前时刻。

2、如果是重复码的话,会经历四个边沿改变,我们通过直接判断四个边沿里面的时间,就可以知道是不是重复码。

3、如果不是重复码,我们一共会经历68个边沿改变,我们会把每次边沿改变的时间记录下来,之后会去调用 IRReceiver_IRQTimes_Parse(),来对按键进行解析。

4、前面提到的,不论是逻辑0还是逻辑1,接收端接收到的一定是低电平,所以代码不必去思考下一个bit开始是高电平还是低电平。

与51的区别:

1、这个代码没有采用阻塞cpu的方式,更能节省cpu资源。

2、不是一个中断就直接解决接收一个键值,是不断的中断,最后才判断键值。

最后,我们讲键值放在了buf[0]和buf[1]里面,之后该如何处理,取决于你该实现什么功能。

总结:

        无论是51还是32,处理红外接收的思路大体是一样的,通过外部中断,然后判断高低电平的持续时间,之后对他进行解析,得到发射器发送的键值。

如果觉得我写的还不错,可以给我点赞加关注,谢谢啦,这是对我最大的鼓励。

  • 36
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值