基于STC12系列单片机的通用红外遥控信号分析程序(一)

前言

  最近学51单片机学习到红外遥控解码与发送部分,开发板的相关教程只有NEC协议的解码,基本的解码套路是1838接收头输出管脚接单片机外部中断0,当接收到红外信号时产生下降沿触发中断,在中断函数中先延时9ms判断电平再延时4.5ms判断电平,从而跳过引导码;再分别延时560us、1690us左右不等的时间判断电平来解码“0”或“1”,直到结束;红外发送思路就是根据NEC协议及红外码值的二进制码分别控制高低电平,并延时相应的时间。但存在这么几个问题:

  1. 解码逻辑写死在中断处理函数中,不方便扩展、移植;

  2. 只能解码NEC协议的红外遥控信号,如果拿一款别的遥控器来,编码协议未知,整个程序就无能为力了;

  3. 接收到信号时实时解码,没有保存红外波形信息,不能输出波形进行分析;

  4. 只能发送NEC协议的红外遥控信号。

  恰逢外地出差,带了开发板却没带NEC的红外遥控器,手边只有空调、电视遥控器和一个带红外遥控功能的手机,于是就想利用51单片机做一个通用的红外遥控信号录波、解码、发送为一体的程序,直接录制红外波形,发送时也是直接按原波形发送,这样就做到了万能红外信号的学习与发送。同时将录制的波形数据发送到上位机进行显示、分析,这样就算拿到一款未知红外协议的遥控器,也可以做它的协议分析了。PS:本人没有示波器、逻辑分析仪,有这些装备的同学请随便看看。

红外遥控基本原理

  红外发射和接收的原理就不细说了,网上很多,也可以参见这篇文章《全面了解红外遥控(中文版)》,这是一个歪果仁写的,网友翻译,讲了基本原理,也介绍了各种常见的协议。

  需注意的是通常的介绍协议时说的表示逻辑“0”或“1”的高低电平是针对发射端的,而常用的一体化红外接收头如HS0038、VX1838等在无红外信号是输出高电平,有红外信号时输出低电平,也就是与发射端时相反的——发射端高电平发射红外线,接收端接收后产生低电平。这在解码时必须注意。

红外遥控录波硬件系统

  为了尽可能的提高录波时的分辨率,采用了1T模式的STC12C5A60S2单片机,之所以用STC12系列而没用更快的STC15系列时因为12系列DIP40封装与传统8051完全兼容,直接插51开发板上就能用。红外遥控接收头为HS0038,输出管脚接P3.2口(原理图中红外接收头只是随便找了个相近的元件做示意)。采用LCD1602做简单显示。原理图如下:

  

红外遥控录波程序实现

  本文章内只贴出关键程序,完整程序请点击下载,编译环境Keil4。

  原理:HS0038输出管脚接INT0中断,下降沿触发。当接收到红外信号后,HS0038输出管脚为低电平,进入中断处理函数,立即启动定时器0,等待红外输出管脚变为高电平,记录低电平时间;然后重置定时器0,等待红外输出管脚变为低电平,记录高电平时间;如此往复,直到某次等待超时或记录时间的数组已用完。

  录制的波形数据保存到一个unsigned char数组中,两两一组,以低电平开始(针对接收端而言),交替表示低电平、高电平的持续时间。格式为:

0x04, 0x24,    //低字节在前,实际数据为0x2404,低电平持续时间的计数值
0x84, 0x11,    //低字节在前,实际数据为0x1184,高电平持续时间的计数值
...

  该段程序不仅可以录制红外波形,还可以做简易的逻辑分析仪使用。录制波形时定时器0的计时时间为1us,所以该段程序的录波理论最小分辨率为1us,但由于中间计算过程等耗时会产生误差,所以最好用来录制电平持续时间大于10us的脉冲波形。

  录波的流程图如下:

//硬件
//@单片机          :    STC12C5A60S2
//@晶振            :    12.0MHz
void InitTimer0()    //定时器0初始化
{
    ET0 = 1;
    AUXR &= 0x7f;        //定时器时钟12T模式,1us
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置16位定时器
    TL0 = 0x00;            //设置定时器初值
    TH0 = 0x00;            //设置定时器初值
    TF0 = 0;            //清除TF0标志
    TR0 = 0;            //暂不开启定时器0计时
    PT0 = 1;            //高优先级,必须,否则在外部中断0中就不能执行定时器0是否超时溢出
}

void Timer0Interrupt(void) interrupt 1 using 1
{
    timer0Overflow = true;    //超时溢出标志
}

void InitINT0()
{
    EX0 = 1;    //打开中断0
    IT0 = 1;    //1——下降沿触发;0——低电平触发
}

void INT0Interrupt() interrupt 0 using 2
{
    UINT8 i;
    UINT8  cH, cL;
    TR0 = 1;     //定时器0开始计数
    EX0 = 0;    //关闭外部中断0的中断响应

    usedLength = 1;    //如果没有接收到有效信号,串口发送1次共4字节数据,用来跟串口收发失败的情况区分
    for (i = 0; i < MAX_BUFFER_LENGTH / 4; i++)
    {
        while (!IR_In)
        {
            if (timer0Overflow)
            {
                usedLength = i;
                goto endfor; //65ms,超时,跳出循环
            }
        }
        TR0 = 0;
        cL = TL0;    //取定时数据
        cH = TH0;
        TL0 = 0x00;    //初始化
        TH0 = 0x00;
        timer0Overflow = false;
        TR0 = 1;     //定时器0开始计数

        waveData[4 * i + 0] = cL;
        waveData[4 * i + 1] = cH;

        while (IR_In)
        {
            if (timer0Overflow)
            {
                usedLength = i;
                goto endfor; //65ms,超时,跳出循环
            }
        }
        TR0 = 0;
        cL = TL0;    //取定时数据
        cH = TH0;
        TL0 = 0x00;    //初始化
        TH0 = 0x00;
        timer0Overflow = false;
        TR0 = 1;     //定时器0开始计数

        waveData[4 * i + 2] = cL;
        waveData[4 * i + 3] = cH;

        usedLength = i;
    }
endfor:
    TR0 = 0;    //关闭定时器0
    timer0Overflow = false;
    TL0 = 0x00;
    TH0 = 0x00;

    if (usedLength > 2)    //至少录制了一组有效数据,显示录制的数据长度
    {
        Lcd1602Clear();

        setPos(0, 0);
        writeData('L');
        writeData(':');
        writeData((usedLength + 1) * 4 / 100 + '0');
        writeData((usedLength + 1) * 4 / 10 % 10 + '0');
        writeData((usedLength + 1) * 4 % 10 + '0');
    }

    IE0 = 0;    //若接收信号过程中产生了下降沿,IE0则为1,此处需清除外部中断0的中断标志
    EX0 = 1;    //打开外部中断0的中断响应
}
View Code

   按键发送数据,同时添加了一个按键做清空缓存数组用,程序如下:

void main()
{
    UINT16 n;

    InitSys();

    while (1)
    {
        Key_Send = 1;
        if ( Key_Send != 1)
        {
            DelayX10ms(1);
            Key_Send = 1;
            if (Key_Send != 1)
            {
                for (n = 0; n < usedLength; n++)
                {
                    //将波形数据串口发送到上位机
                    UartSendByte(waveData[4 * n + 0]);
                    UartSendByte(waveData[4 * n + 1]);
                    UartSendByte(waveData[4 * n + 2]);
                    UartSendByte(waveData[4 * n + 3]);
                }

                while (!Key_Send);    //等待弹起
            }
        }

        Key_Clear = 1;
        if ( Key_Clear != 1)
        {
            DelayX10ms(1);
            Key_Clear = 1;
            if (Key_Clear != 1)
            {
                //清空波形缓存数组
                for (n = 0; n < MAX_BUFFER_LENGTH; n++)
                {
                    waveData[n] = 0;
                }
                SystemReady();
                while (!Key_Send);
            }
        }
    }
}
View Code

 

上位机红外波形分析

  录制完波形后,波形数据通过串口发送到上位机,得到类似下图的十六进制数据,进行数据处理后就可以进行分析解码了。

  为方便分析,我用C#简单写了个小程序,可以很方便的绘制波形,并将每帧的2字节数据直接转换为时间长度,方便对照各种红外协议分析。如下图,该段红外信号已9220us的高电平开始,紧随一个4484us的低电平,与NEC协议中“9ms高电平+4.5ms低电平”的引导码格式相符,分析其后面的电平持续时间,可知这段红外信号为NEC格式信号。

上位机程序及源码下载

 

 欢迎关注本人的个人博客YoungCoding.top

原文地址:https://www.cnblogs.com/ToddleLobster/p/5873629.html