红外遥控的底层

 简单介绍

        红外遥控技术是一种基于红外线通信的无线遥控技术,具有体积小、传输距离远、传输速率快等特点,广泛应用于家用电器、车载电子、智能家居等领域。

        单片机作为控制中心,可以很好地控制红外遥控的发送和接收。红外遥控技术利用红外线通信技术进行信息传输。遥控器将按键信息编码后,通过红外LED发射出去,接收器接收到信号后进行解码并执行相应的命令。红外遥控编码格式有多种,常见的有NEC码、RC5码、RC6码等。本篇只介绍NEC码。

        单片机可以通过外部中断、定时器等方式进行红外遥控的接收和解码。接收到的红外信号需要进行解码,得到对应的按键信息,从而实现红外遥控功能。

        注意:通信方式为单工,异步

原理

 

        模式1:只有当两个信号波同时为低电平,才可以导通三极管,相当于两种波混合在一起,进行调制红外波形。

        为什么要这样做?答案是太阳光会影响,这是为了提高我们信号的抗干扰性

        之后将收到的信号波在右边的矩形框里进行解调(具体可以百度)!

 

         可以将模式2理解为模式1将波形处理后的结构,将IN引脚用于外部中断进行处理。

        时序可以用这张表作为参考!

        采集两个下降沿之间的时间,并按照上表进行模拟!

代码分析

上面这幅思维导图就是我们的代码逻辑,开始吧。

外部中断

void Int3_Init(void)
{
	IT3=1;
	IE3=0;
	EX3=1;
	EA=1;
	PX3=1;
}

        配置外部中断3(可以随便选)为下降沿触发,IE3也可以选择不写。

定时器

void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 0;		//定时器0不计时
}

        可以直接用软件生成代码,要注意选16位不自动重装载(所以TF0 = 0可以不加),因位普通51没有16位重装载模式,我们要用下降沿计数来模拟红外遥控时序,并且不开启定时器中断。因此我们要把定时器一些参数初值全初始化为0!

红外NEC时序模拟

void IR_Init(void)
{
	Timer0_Init();
	Int3_Init();
}

        相信初始化就没必要说了。

#include "main.h"

uint Time_Count = 0;

bit Recv_Repeat_Dat_Flag = 0;//数据接受标志位
bit Recv_Dat_Flag = 0;//数据帧写入标志位

uchar Dat[4] = {0};
uchar Dat_Index = 0;//  0 - 31

uchar IR_Cmd = 0;//红外按键命令
uchar IR_Address = 0;//红外按键地址

enum myenum
{
    idle_state,     //空闲状态
    buffer_state,   //缓冲状态
    collect_state,  //采集状态
};

//初始化红外
void IR_Init()
{
    Int3_Init();
    Timer0_Init();
}


void Int3_Isr(void) interrupt 7
{
    static uchar IR_State = idle_state;
    
    switch(IR_State)
    {
        case idle_state :
            
            Timer0_InitCount(0);    //数据从0开始
        
            Is_Timer0_Interrupt(1); //开启定时器
        
            IR_State = buffer_state;    //切到缓冲状态
        
        break;
        case buffer_state :
            
            Time_Count = Timer0_GetCount(); //获取上一次中断到此次中断的时间
        
            Timer0_InitCount(0);    //复位时间值
        
            //Start 如果计时为13.5ms,则接收到了Start信号
            if(Time_Count >= 13500 - 400 && Time_Count <= 13500 + 400)
                IR_State = collect_state;   //切到采集状态
            
            //Repeat 如果计时为11.25ms,则接收到了Repeat信号
            else if(Time_Count >= 11250 - 400 && Time_Count <= 11250 + 400)
            {
                Recv_Repeat_Dat_Flag = 1;   //置收到连发帧标志位为1
                
                IR_State = idle_state;  //回到空闲状态
                
                Is_Timer0_Interrupt(0); //关闭定时器                
            }
            
            //!Start &&!Repeat
            else
                IR_State = buffer_state;    //在缓冲状态里,继续判断Start || Repeat
                   
        break;        
        case collect_state :
            
            Time_Count = Timer0_GetCount(); //获取上一次中断到此次中断的时间
            
            Timer0_InitCount(0);    //复位时间值
        
            //写0
            if(Time_Count >= 1120 - 400 && Time_Count <= 1120 + 400)
            {
                Dat[Dat_Index / 8] &= ~(0x01 << (Dat_Index % 8));
                Dat_Index++;
            }
            
            //写1
            else if(Time_Count >= 2250 - 400 && Time_Count <= 2250 + 400)
            {
                Dat[Dat_Index / 8] |= (0x01 << (Dat_Index % 8));
                Dat_Index++;
            }
            
            //写入数据错误            
            else
            {
                Dat_Index = 0;//清除索引值
                IR_State = buffer_state;//还是在缓冲状态继续写数据              
            }
            
            if(Dat_Index >= 32) //说明一个指令发完
            {
                Dat_Index = 0;  //复位索引值
                
                if((Dat[0] == ~Dat[1]) && (Dat[2] == ~Dat[3]))   //数据有效,校验成功
                {
                    Recv_Dat_Flag = 1;//数据写入成功
                    
                    IR_Cmd = Dat[2];//写入命令
                    
                    IR_Address = Dat[0];//写入地址
                }
                
                Is_Timer0_Interrupt(0); //关闭定时器
                
                IR_State = idle_state;  //回到空闲状态
            }
        break;        
    }
}

分了三个状态:

        1、空闲状态:启动定时器开始做准备

        2、缓冲状态:获取两个下降沿时间,检测Start  或  Repeat 如果是Start,则进入采集状态,写数据;如果是Repeat状态,则回到空闲状态,沿用上一次的数据

        3、采集状态:写0  或  1,并直到校验完32位数据后,回到空闲状态,如果校验成功(地址码等于地址反码 且命令码等于命令反码)采集数据,校验失败,重新开始状态循环。

注意:时序判断要给定一个范围,不然条件太苛刻,时间刚好在那个时刻,很难进入if,同时给定的范围只能保证一个时序,不可与其他判断的时序重叠。

         这就是底层逻辑!

完善

        为了方便应用层调用,我们可以进行完善!

具体如下:

//判断是否重复接收数据
uchar IS_Recv_Repeat_Dat()
{
    if(Recv_Repeat_Dat_Flag)
    {
        Recv_Repeat_Dat_Flag = 0;
        return 1;
    }
    return 0;
}

//判断是否接收到数据
uchar IS_Recv_Dat()
{
    if(Recv_Dat_Flag)
    {
        Recv_Dat_Flag = 0;
        return 1;
    }
    return 0;
}

//获取地址
uchar IR_GetAddress()
{
    return IR_Address;
}

//获取命令
uchar IR_GetCommand()
{
    return IR_Cmd;
}

        这些就是底层接口,我们只需在应用层用这些函数即可,这就不用关心底层如何实现的了。

调用

 就这么用,也可以以此拓展更多业务。

  • 28
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值