红外遥控的底层

 简单介绍

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

        单片机作为控制中心,可以很好地控制红外遥控的发送和接收。红外遥控技术利用红外线通信技术进行信息传输。遥控器将按键信息编码后,通过红外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;
}

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

调用

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

#include "remote.h" #include "delay.h" #include "usart.h" ////////////////////////////////////////////////////////////////////////////////// //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK战舰STM32开发板 //红外遥控解码驱动 代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //修改日期:2012/9/12 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019 //All rights reserved ////////////////////////////////////////////////////////////////////////////////// u8 g_IR_RecFlag = 0; //红外接收到标志 //红外遥控初始化 //设置IO以及定时器4的输入捕获 void Remote_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PB9 输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO;_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9 TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出 TIM_TimeBaseStructure.TIM_Prescaler =(35-1); //预分频器,1M的计数频率,1us加1. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM;_TimeBaseStructure); //根据指定的参数初始化TIMx TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; // 选择输入端 IC4映射到TI4上 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //上升沿捕获..改为下降沿捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波 TIM_ICInit(TIM4, &TIM;_ICInitStructure);//初始化定时器输入捕获通道 TIM_Cmd(TIM4,ENABLE ); //使能定时器4 NVIC_InitStructure.NV
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值