Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819288
  • 博文数量: 54
  • 博客积分: 8076
  • 博客等级: 中将
  • 技术积分: 648
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-01 15:11
文章分类

全部博文(54)

分类: 嵌入式

2010-03-05 23:07:14

有关红外解码,很久前我博客转载一篇文章红外遥控系统原理及单片机软件解码实例。那篇文章介绍NEC红外遥控原理还是比较好的,但那个软件解码实在不敢恭维,有两个缺陷:1、代码是汇编的,维护性不好;2、对于脉冲宽度的计数居然用的是软件定时的方式。其中第2个几乎是致命的,主程序只能用来解码红外信号了。针对这些,我重写了解码过程,脉冲宽度用定时器中断计算,红外解码函数由INT0触发,这样主程序可以用来发送解码数据或者干别的工作了。
 
 
硬件环境:AT89S51
开发工具:KEIL C51+MedWin(编译环境)、progisp(程序下载工具)

 

原理图:

不方便给出,简单描述一下,基本与网上的参考电路一致,红外接收头输出接到单片机 P3.2。

 

工作原理描述:

红外接收头输出接到单片机 P3.2,这是一个中断脚/INT0,当接收到遥控按键,会触发中断,单片机程序开始运行并分析波形,如果是合法波形,就将里面的按键键值缓存起来。

遥控器采用NEC协议,关于协议部分不做详细介绍,网上很多原理性的描述。不过还是要给出一帧数据的脉冲波形,在比较timer的计时值从而确定脉冲宽度时要用到。

一帧数据大体波形如下:

 

引导码和连发码脉冲(注:如果键按下超过108ms 仍未松开, 接下来发射的连发码仅由起始码9ms和结束码2.5ms组成):

 

Bit0bit1脉冲:

 

 

源程序如下,已经有了很详细的注释,就不作单独的文字说明了。关键是红外脉冲宽度的计算,这些计算需要对51定时器的初值和工作模式有十分的了解。

值得一提的是:本来源码里除了红外解码之外,还有键码发送。现仅给出红外解码的实现,至于如何处理解码数据,就看具体需求了。
 

#include <reg52.h>
#include <stdio.h>
#include <absacc.h>
#include <intrins.h>

////////////////////////////////宏变量定义区//////////////////////////////////

/******************************* 基本数据类型 *******************************/
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long


#define crystal (11059200) //晶振频率
#define MS10 10 //t1中断时间10ms
#define TH1_MS10 (65536-((crystal*MS10/1000)/12))/256
#define TL1_MS10 (65536-((crystal*MS10/1000)/12))%256

/****************************** 遥控器协议相关 ******************************/
//Nec协议,引导码13.5ms,连发码11.25ms,bit1为2.25ms,bit0为1.125ms
//对计时值的计算,要了解定时器的初值计算方法,留意定时器的工作模式和开始设定的初值。
//这里以Bit0为例说明:65536-((22.219-1.125)*crystal/12)/1000=0xB3FD
#define TH0_MS22 (0xB0) //
#define TL0_MS22 (0x00)
//定时时间=(2^16-0xB000)*(12/crystal)=22.219ms

#define IR_GUILD_MAX (0xE1) //
#define IR_GUILD_MIN (0xDE)
//引导码=0xDFE2(只要th0比较就行了:0xDE<0xDF<0xE1)

#define IR_REPEAT_MAX (0xD9) //
#define IR_REPEAT_MIN (0xD6)
//连发码=0xD7E7

#define IR_BIT1_MAX (0xB9) //
#define IR_BIT1_MIN (0xB6) //码1=0xB7FB

#define IR_BIT0_MAX (0xB5) //
#define IR_BIT0_MIN (0xB2)
//码0=0xB3FD

#define IR_CUSTOM_CODE1 (0x80) //用户码1 BIT7~0 convert bit0~7
#define IR_CUSTOM_CODE2 (0xff)
//用户码2

#define IR_KEYVALUE_PWR1 (0x92) //待机键码值
#define IR_KEYVALUE_PWR2 (0x6d)
//待机键码反码

/****************************** 遥控器解码相关 ******************************/
#define BITS_ALL (32) //遥控器有效数据总位数
uchar data int0_interval; //int0中断之间的时间间隔,即遥控信号下降沿之间的宽度
uchar data bits_cnt; //有效数据位数计数
ulong data ir_data;
//32位的遥控器数据

/****************************** 发送数据缓存区 ******************************/
uchar data ir_val1, ir_val2; //16位键码,待发送的数据
uchar data ir_custom_code1, ir_custom_code2; //16位用号码,用于检查ir数据合法性
bit ir_data_ready;
//数据准备完成标志位,系统不断轮询

/////////////////////////////////功能函数区///////////////////////////////////

/******************************** 初始化系列函数 ****************************/
void mcu_init(void)
{
    //MCU    Initialization
    EA = 0;    
//disable all interrupt

    //PCON = 0x80; //串口0 double baud,
    //SCON = 0X50; //serial0 10 bit 异步方式
    TCON = 0x05;
//int0-1下降触发

    /*Timer 2 is being used to generate baud rates(9600bps).*/
    TMOD = 0x11;     //T0 16bit counter and T1 timer 8bit reload
    TH1 = TH1_MS10; TL1 = TL1_MS10;
    TH0 = TH0_MS22; TL0 = TL0_MS22;
//th0=0xb0,tl0=0x00

    
//T2CON = 0X30; //timer2 as serial0's time_base

    IE = 0; //EA=0,s0 int enable,t1 int enable,x0/x1 int enable
    EX0 = 1; //int0 enable
    EX1 = 1; //int1 enable
    ET1 = 1;
    TR0 = 1;
    TR1 = 1;
    //TR2 = 1;
}

void ir_init()
{
    ir_data_ready = 0;
    ir_val1 = ir_val2 = 0;
    ir_custom_code1 = ir_custom_code2 = 0;
  
    int0_interval = 0;
    bits_cnt = 0;
    ir_data = 0;
}

void sys_init()
{
    ir_init();
}

/******************************** 发送相关函数 ******************************/
uchar data_convert(uchar dat)
{
    //将一个byte的数据位反置。bit7:0转换成bit0:7
    uchar i;
    uchar src;
    uchar dsn;
    src = dat;
    dsn = src & 0x1;;
    for(i=0; i<7; i++)
    {
        src >>= 1;
        dsn <<= 1;
        dsn |= src & 0x1;
    }
    return dsn;
}

void check_ir_key()
{
    //检测数据是否准备好
    if(ir_data_ready)
    {
        //检查数据合法性
        if( (data_convert(ir_custom_code1) == IR_CUSTOM_CODE1 ) &&
            (data_convert(ir_custom_code2) == IR_CUSTOM_CODE2 ))
        {
            //TODO: Send ir-code to host machine
        }
        ir_data_ready = 0;
    }
}

/******************************** 遥控器数据解码 ****************************/
void ir_decode()
{
    if (int0_interval>IR_GUILD_MIN && int0_interval<IR_GUILD_MAX)
    {
        //引导码,准备接收数据,初始化data和位数计数
        ir_data = 0;
        bits_cnt = 0;
    }
    else if (int0_interval>IR_BIT1_MIN && int0_interval<IR_BIT1_MAX)
    {
        //bit-1
        ir_data = (ir_data<<1) + 1;
        bits_cnt++;
    }
    else if (int0_interval>IR_BIT0_MIN && int0_interval<IR_BIT0_MAX)
    {
        //bit-0
        ir_data = (ir_data<<1);
        bits_cnt++;
    }
    else if (int0_interval>IR_REPEAT_MIN && int0_interval<IR_REPEAT_MAX)
    {
        //连发码,初始化data和位数计数,置数据准备标志位为true
        ir_data = 0;
        bits_cnt = 0;
        ir_data_ready = 1;//根据具体协议来裁定
    }
    else
    {
        //其它,不合法波形
        ir_data = 0;
        bits_cnt = BITS_ALL+1;
    }

    if (bits_cnt == BITS_ALL)
    {
        //已经接收了32位数据,将数据分解并保存到发送缓存区
        ir_custom_code1 = (uchar)(ir_data>>24);
        ir_custom_code2 = (uchar)(ir_data>>16);
        ir_val1 = (uchar)(ir_data>>8);
        ir_val2 = (uchar)(ir_data);
        ir_data = 0;
        ir_data_ready = 1; //数据接收完毕,置标志位为1
    }
}

///////////////////////////////////中断函数区/////////////////////////////////

void int0_serv(void) interrupt 0 using 0
{
    //int0中断,遥控信号到达时下降沿触发,根据t0的计时值计算脉冲宽度,解码
    EX0 = 0; //disable int 0
    TR0 = 0; //disable timer 0
        
    int0_interval = TH0; //获取当前的计时值,用于计算两个下降沿之间的宽度

    TH0 = TH0_MS22; TL0 = TL0_MS22; //
    TR0 = 1; //enable timer 0
    EX0 = 1; //enable int 0 
      
    ir_decode(); //decode ir signal     
}

void int1_serv(void) interrupt 2 using 2
{
    //TODO
}

void timer0(void) interrupt 1 using 1
{
    //TODO
}

void timer1(void) interrupt 3 using 3
{
    //TODO
}

void time2(void) interrupt 5
{
    //TODO
}

void serial0_ssio(void) interrupt 4
{
    //TODO
}

//////////////////////////////////////Main////////////////////////////////////

void main(void)
{
    EA = 0;
    mcu_init();
    sys_init();
    EA = 1;

    while(1)
    {
        check_ir_key();
    }
}


阅读(3920) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~