51单片机红外遥控 C语言入门编程

目录

红外发射装置:

 NEC码:

红外接收设备:

1.红外遥控:数码管上显示红外解码遥控器键值

        红外遥控方法一:外部中断+延时

2.红外遥控:红外遥控控制LCD1602画面上的值

        红外遥控方法二:外部中断+定时器


红外发射装置:

        也就是通常我们说的红外遥控器是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管。 它实际上是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因 而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为940nm左右,外形与普通发光二极管相同。红外发光二极管有透明的,还有不透明的,在我们的红外遥控器上可以看到这个红外发光二极管。

        通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以455kHz÷12≈37.9kHz≈38kHz。通常的红外遥控器是将遥控信号(二进制脉冲码)调制在38KHz的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。

        二进制脉冲码的形式有多种,其中最为常用的是NEC Protocol 的PWM码 (脉冲宽度调制)和Philips RC-5 Protocol的PPM码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。我们配套的红外遥控器使用的是NEC协议。

 NEC码:

        NEC码的位定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms(560us脉冲+560us低电平)。而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑1应该是560us低 +1680us高,逻辑0应该是560us低+560us高。所以可以通过计算高电平时间判断接收到的数据是0还是1。

        NEC遥控指令的数据格式为:引导码、地址码、地址反码、控制码、控制反码。引导码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反 码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。

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

红外接收设备:

        红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥 控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、 限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。成品红外接收头的封装大致有两种:一种采用铁皮屏蔽;一种是塑料封装。均有三只引脚,正对接收头的凸起处看,从左至右,管脚依次是 1:VOUT,2:GND,3:VDD。

        由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平,所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断接收到的数据是0还是1。

1.红外遥控:数码管上显示红外解码遥控器键值

        红外遥控方法一:外部中断+延时

        红外遥控函数

#include <REGX52.H>
#include "DELAY.h"
#include "Int0.h"

sbit VOUT=P3^2;

unsigned char IR_Data[4];//全局变量 红外解码数组 地址码、地址反码、控制码、控制反码

void IR_Init(void)//初始化
{
    Int0_Init();
}

void Int0_Routine() interrupt 0
{
    unsigned char i,j;
    unsigned int IR_Count=0,IR_HighTime=0;
    if(VOUT==0)
    {
        IR_Count=1000;
        while(!VOUT&&IR_Count)//等待引导信号9ms低电平结束,若超过10ms强制退出
        {
            delay10us(1);//1000*10us
            IR_Count--;
            if(IR_Count==0)return;
        }
        if(VOUT)//引导信号9ms低电平已过,进入4.5ms高电平
        {
            IR_Count=500;
            while(VOUT&&IR_Count)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
            {
                delay10us(1);
                IR_Count--;
                if(IR_Count==0)return;
            }
            for(i=0;i<4;i++)//循环4次,读取4个字节数据
            {
                for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
                {
                    IR_Count=600;
                    while(!VOUT&&IR_Count)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
                    {
                        delay10us(1);
                        IR_Count--;
                        if(IR_Count==0)return;
                    }
                    IR_Count=20;
                    while(VOUT)//等待数据1或0后面的高电平结束,若超过2ms强制退出
                    {
                        delay10us(10);
                        IR_HighTime++;
                        if(IR_HighTime>20)return;//20*100us
                    }
                    IR_Data[i]>>=1;//先读取的为低位,然后是高位
                    if(IR_HighTime>8)//如果高电平时间大于0.8ms,数据则为1,否则为0
                        IR_Data[i]|=0x80;
                    IR_HighTime=0;//重新清零,等待下一次计算时间
                }
            }
        }
    }
    if(IR_Data[2]!=~IR_Data[3])//校验控制码与反码,错误则返回
    {
        for(i=0;i<4;i++)
            IR_Data[i]=0;
        return;	
    }
}

        主函数

#include <REGX52.H>
#include "IR.h"
#include "shumaguan.h"

void main()
{
    IR_Init();
    while(1)
    {
        shumaguan(1,IR_Data[0]/16);
        shumaguan(2,IR_Data[0]%16);
        shumaguan(3,IR_Data[1]/16);
        shumaguan(4,IR_Data[1]%16);
        shumaguan(5,IR_Data[2]/16);
        shumaguan(6,IR_Data[2]%16);
        shumaguan(7,IR_Data[3]/16);
        shumaguan(8,IR_Data[3]%16);
    }
}

2.红外遥控:红外遥控控制LCD1602画面上的值

        红外遥控方法二:外部中断+定时器

        定时器函数

#include <REGX52.H>//定时器函数

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

void timer0_setcounter(unsigned char value)//设置定时器初值
{
    TH0=value/256;
    TL0=value%256;
}

unsigned int timer0_getcounter(void)//读取定时器的值
{
    return (TH0<<8)|TL0;
}

void timer0_run(unsigned char flag)//设置定时器是否启动 1开始0停止
{
    TR0=flag;
}

        外部中断函数

#include <REGX52.H>

void int0_init ()//外部中断下降沿启动
{
	IT0=1;
	IE0=0;
	EX0=1;
	EA=1;
	PX0=1;
}
/*中断函数模板
void int0_routine (void)  interrupt 0
{
	
}
*/

        红外遥控函数

#include <REGX52.H>
#include "timer0.h"
#include "int0.h"

unsigned int ir_time;//红外读取定时器的时间
unsigned char ir_state;//红外接收阶段的模式

unsigned char ir_data[4];//四组数据
unsigned char ir_pdata;//8位数据为一组

unsigned char ir_dataflag;//连发标志位
unsigned char ir_repeatflag;//收到的连发标志位
unsigned char ir_address;//红外地址
unsigned char ir_command;//红外命令

void ir_init(void)//红外遥控初始化
{
    timer0_init();
    int0_init();
}

unsigned char ir_getdataflag(void)//红外遥控获取收到数据帧标志位
{
    if(ir_dataflag)
    {
        ir_dataflag=0;
        return 1;//是否收到数据帧,1为收到,0为未收到
    }
    return 0;
}

unsigned char ir_getrepeatflag(void)//红外遥控获取收到连发帧标志位
{
    if(ir_repeatflag)
    {
        ir_repeatflag=0;
        return 1;//是否收到连发帧,1为收到,0为未收到
    }
    return 0;
}

unsigned char ir_getaddress(void)//红外遥控获取收到的地址数据
{
    return ir_address;
}

unsigned char ir_getcommand(void)//红外遥控获取收到的命令数据
{
    return ir_command;
}

void int0_Routine(void) interrupt 0
{
    if(ir_state==0)//当下降沿触发时 模式为0
    {
        timer0_setcounter(0);//定时计数器清0
        timer0_run(1);//定时器启动
        ir_state=1;//将模式置为1
    }
    else if(ir_state==1)//模式1,等待Start信号或Repeat信号
    {
        ir_time=timer0_getcounter();//获取上一次中断到此次中断的时间
        timer0_setcounter(0);//定时计数器清0
        //如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
        if(ir_time>12442-500&&ir_time<12442+500)
        {
            ir_state=2;//将模式置为2
        }
        //如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
        else if(ir_time>10368-500&&ir_time<10368+500)
        {
            ir_repeatflag=1;//置收到连发帧标志位为1
            timer0_run(0);//定时器停止
            ir_state=0;//将模式置为0
        }
        else//接收出错
        {
            ir_state=1;//置模式为1
        }
    }
    else if(ir_state==2)//模式2,接收数据
    {
        ir_time=timer0_getcounter();//获取上一次中断到此次中断的时间
        timer0_setcounter(0);//定时计数器清0
        //如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
        if((ir_time>1032-500) && (ir_time<1032+500))
        {
            ir_data[ir_pdata/8] &= ~(0x01<<(ir_pdata%8));//数据对应位清0
            ir_pdata++;//数据位置指针自增
        }
        //如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
        else if(ir_time>2074-500 && ir_time<2074+500)
        {
            ir_data[ir_pdata/8] |= (0x01<<(ir_pdata%8));//数据对应位置1
            ir_pdata++;//数据位置指针自增
        }
        else//接收出错
        {
            ir_pdata=0;//数据位置指针清0
            ir_state=1;//模式置为1
        }
        if(ir_pdata>=32)//如果接收到了32位数据
        {
            ir_pdata=0;//数据位置指针清0
            if((ir_data[0]==~ir_data[1])&&(ir_data[2]==~ir_data[3]))//数据验证
            {
                ir_address=ir_data[0];//转存数据
                ir_command=ir_data[2];
                ir_dataflag=1;//置收到连发帧标志位为1				
            }
            timer0_run(0);//定时器停止
            ir_state=0;//置状态为0
        }
    }
}

        主函数

#include <REGX52.H>
#include "LCD1602.h"
#include "ir.h"

unsigned char num;
unsigned char address,command;

void main()
{
    LCD_Init();
    LCD_ShowString(1,1,"ADDR  CMD  NUM");
    LCD_ShowString(2,1," 00   00   000");
    ir_init();
    while(1)
    {
        if(ir_getdataflag()||ir_getrepeatflag())
        {
            address=ir_getaddress();
            command=ir_getcommand();
            LCD_ShowHexNum(2,2,address,2);
            LCD_ShowHexNum(2,7,command,2);
            if(command==0x15)//IR_VOL_MINUS
            {
                num--;
            }
            if(command==0x09)//IR_VOL_ADD
            {
                num++;
            }
            LCD_ShowNum(2,12,num,3);
        }
    }
}

物联沃分享整理
物联沃-IOTWORD物联网 » 51单片机红外遥控 C语言入门编程

发表评论