Bootstrap

无线遥控红外通信

无线遥控红外通信

红外发射装置一般是指红外遥控器由 键盘电路红外编码电路 电源电路 和红外发射 电路组成
一般的红外线波长为940nm左右,外形与普通发光二极管相同
红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHZ,这个是由发射端所使用的的455kHZ晶振决定的
在发射端要对晶振进行整数分频,分频系数一般取 12,所以 455kHz÷12≈37.9kHz≈38kHz。
载波:是具有一定频率的电磁波

工作方式

通常红外遥控器是将遥控信号(二进制脉冲码),调制在38kHZ的载波上,经过缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。二进制脉冲码的形式有多种,其中最为常用的NEC Protocol 的PWM 码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)

在这里插入图片描述
发射端负责产生和发送信号,而接收端则负责接收和解码信号

工作原理

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 高。
在这里插入图片描述
NEC 遥控指令的数据格式为:引导码、地址码、地址反码、控制码、控制反码。引导码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是 8 位数据格式。(LSB低位优先发送模式)
在这里插入图片描述
控制码是接收端最后用来处理响应操作的。
NEC 码还规定了连发码(由 9ms 低电平+2.5m 高电平+0.56ms 低电平 +97.94ms 高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。

红外接收设备

红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。

主要有三根引脚
正对接收头的凸起处看,从左至右,管脚依次是 1:VOUT,2:GND,3:VDD。

在这里插入图片描述
Vout引脚负责将接收到的红外信号转化为电信号后的输出。
为了提高接收的灵敏度,输出的高低电平和发射端可能是反相的。
由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平, 所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断 接收到的数据是 0 还是 1。

原理图

在这里插入图片描述

代码实现

本章所要实现的功能是:数码管上显示红外解码遥控器键值。
程序框架如下:
(1)编写数码管显示功能
(2)编写红外解码函数
(3)编写主函数

  1. 初始化红外接收器
    ● 设置引脚:将红外接收器的引脚配置为输入模式,以便能够检测红外信号。
    ● 配置中断:配置外部中断0以检测红外信号的下降沿触发。
    ● 打开中断:启用外部中断0和总中断允许,以便在接收到红外信号时能够进入中断服务函数。
  2. 等待并验证引导信号
    ● 等待低电平:在中断服务函数中,首先等待一个约9ms的低电平信号,这是红外信号的引导码的开始。
    ● 时间限制:设置一个时间限制(如10ms),如果超过这个时间仍未检测到高电平,则放弃接收并退出。
    ● 验证高电平:在9ms低电平后,检查是否紧接着有一个约4.5ms的高电平信号。如果不是,则放弃接收并退出。
  3. 接收数据位
    ● 循环接收:循环四次以接收四个字节的数据(地址码、地址反码、控制码和控制反码)。
    ○ 内部循环:在每次循环中,再次循环八次以接收一个字节的八位数据。
    ● 等待位开始:对于每个数据位,首先等待一个约0.56ms的低电平信号,这表示数据位的开始。
    ○ 时间限制:同样设置一个时间限制(如6ms),如果超过这个时间仍未检测到高电平,则放弃接收并退出。
    ● 测量高电平时间:接下来,测量高电平持续时间以确定数据位的值。
    ○ 判断逻辑:如果高电平时间大于或等于0.8ms,则该位为1;否则,该位为0。
    ● 存储数据:将接收到的位按正确顺序组合成一个字节,并存储在gired_data数组中。
  4. 数据校验
    ● 检查反码:在接收完四个字节后,检查控制码(gired_data[2])和它的反码(gired_data[3])是否匹配。
    ● 错误处理:如果不匹配,则表明数据接收错误,将gired_data数组清零并退出。
  5. 后续处理
    ● 解码数据:如果数据接收并校验成功,则可以根据需要对数据进行解码。
    ● 触发动作:根据解码后的数据执行相应的操作,如调整音量、切换频道等。
    ● 清除中断:清除外部中断0的标志位,以便能够接收下一个红外信号。
    注意事项
    ● 时间精度:上述步骤中的时间值(如9ms、4.5ms、0.56ms和0.8ms)是基于常见的红外编码格式(如NEC编码)的。在实际应用中,需要根据具体的硬件和编码格式进行调整。
    ● 错误处理:在接收过程中,应充分考虑各种可能的错误情况,并设置相应的错误处理机制。
    ● 优化性能:为了提高接收的灵敏度和准确性,可以考虑使用更精确的计时方法和更高效的解码算法。

ired.h

#ifndef _IRED_H_
#define _IRED_H_

#include "public.h"
sbit IRED = P3 ^ 2;    // 红外接收头接到P3.2
extern u8 gIrd_code[]; // NEC码(地址码,地址反码,键值码,键值反码)

void IRED_Init(void); // 初始化红外接收头

#endif

ired.c

#include "ired.h"
u8 gIrd_code[4]; // NEC码(地址码,地址反码,键值码,键值反码)
void IRED_Init(void)
{
    IRED = 1; // 红外端口空闲状态 这里其实默认就是高电平
}
//中断0因为是P3^2的引脚我们就是中断0来写
void Ired_inter(void) interrupt 0 // 红外解码NEC
{
    u8 i, j;            // i控制编码个数,j控制编码位数
    u8 time_out = 1000; //设置一个时间限制10ms  因为我们使用10us为一次循环那这里就初始值设置1000
    if (IRED == 0)//电平拉低了 中断信号开始了,要执行校验红外的任务了
    {
        // 分析引导码
        while (!IRED && time_out) // 等待引导码10ms低电平,若超时则跳出循环,10ms内还没有拉高证明不是这个引导码退出
        {
            time_out--;
            delay_10us(1);
            if (time_out == 0)
                return; // 信号超时
        }
        if (IRED)//证明引导码被拉高了,是这个,那么再次验证高电平5ms左右,看中间有没有波动被拉低了
        {
            time_out = 500;          // 5ms的超时
            while (IRED && time_out) // 等待数据5ms低电平,若超时则跳出循环
            {
                time_out--;
                delay_10us(1);
                if (time_out == 0)
                    return; // 5ms信号超时
            }
			//一高一低在规定时间内都符合,那就开始分析数据了
            // 分析32位数据
            //外层循环控制类型码数组,总共4个字节分别是地址码,地址反,控制码,控制反,0,1,2,3
            //控制码就是按键键值,到这里接收数据信号的时候是低电平
            for (i = 0; i < 4; i++)  
            {
                gIrd_code[i] = 0; // 将所有编码初始化
                for (j = 0; j < 8; j++) //取数值
                {
                    // 首先低电平0.6ms  600us开始  IRED =0
                    time_out = 60;
                    while (!IRED && time_out)
                    {
                        time_out--;
                        delay_10us(1);
                        if (time_out == 0)
                            return; // 0.6ms信号超时
                    }
                    //电平信号改变了,是这个数据,高电平IRED =1 
                    // 看时序图可以发现逻辑1是2.25ms。560+1680 低电平,逻辑0是560+560低电平,这里假设定义一个2ms的
                   //上面走了0.6ms的脉冲,下面要判断是0还是1只需要再走一个560us的低电平就行
                    time_out = 20;
                    while (IRED && time_out)
                    {
                        time_out--;
                        delay_10us(10);
                        if (time_out == 0)
                            return; // 2ms信号超时
                    }
                   	//由于上面时间20-6约等于14  所以在14以内就是0,560us以内是逻辑0,超过就是1
                    if (time_out < 8) 
                    {
                        /* code */
                        gIrd_code[i] |= 1 << j;
                    }
                }
            }
            // 校验数据 数组2跟3是反码直接取反就可以,如果不等就重新取
            if (gIrd_code[2] != ~gIrd_code[3])
            {
                gIrd_code[2] = 0; // 校验失败
                gIrd_code[3] = 0; // 校验失败
            }
        }
    }
}

main.c

/**
 * 红外遥控器按下数码管显示实验以16进制显示 结尾为H
 *
 */
#include "public.h"
#include "smg.h"
#include "exit.h"
#include "ired.h"
#include "lcd1602.h"
#include "stdio.h"
void main(void)
{
	u8 u8code[8] = {0}; // 数码管显示的数字
	u8 hexStr[5];		// 存储转换后的字符串

	Int0_Init(); // 初始化定时器0
	IRED_Init(); // 初始化红外接收器
	SMG_Init();	 // 初始化数码管显示
	LCD_Init();	 // 初始化LCD液晶显示屏

	while (1)
	{
		// LCD显示
		sprintf(hexStr, "%02X", (u16)gIrd_code[2]); // 这里必须把红外接收到的数值转化为16进制不然会出现bug
		LCD_ShowString(0, 0, hexStr);

		// 数码管显示
		// u8code[0] = smg_code[gIrd_code[2] / 16]; // 十位
		// u8code[1] = smg_code[gIrd_code[2] % 16]; // 个位
		// u8code[2] = 0x76;						 // H
		// SMG_Display(u8code);					 // 显示数码管
	}
}

void Uart_Isp(void) interrupt 4
{
}

// void Int0(void) interrupt 0
// {
// }

void Int1(void) interrupt 2
{
}

void Time0(void) interrupt 1
{
}

void Time1(void) interrupt 3
{
}

注意上面有两种显示方式一种是在数码管显示,一种是在lcd1602上面显示,由于keil上面的sprintf有bug那么我们就需要将其转换成16位显示,不然会显示出倒着的数据

总结实现流程:

  • 首先由于初始是高电平->
  • 拉低代表开始了-> IRED =0
  • 拉低引导10ms,循环判断是否拉高引导,如果10ms还没反应就退出,反之-> IRED =1 往下走
  • 然后在拉高,拉高5ms ,看有没有做出反应拉低,在5ms内被拉低证明有反应有引导,反之-》 IRED =0 往下走
  • 一低一高都引导了 再来处理数据 ->外层是数组的4个码,内层是数据的8位
  • 首先数组4个元素清零, 取数值继续判断,首先是0.6ms的低电平,看下有没有波动被拉高了,被拉高了证明这是数据是对的 IRED =1 往下走
  • 然后持续起码最低0.6ms的高电平 有个阈值 高于0.6ms就是1,反之是0
  • 最后校验数组的第二个跟第三个元素互反
;