• 基于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

  • 相关阅读:
    字符串 CSV解析 表格 逗号分隔值 通讯录 电话簿 MD
    Context Application 使用总结 MD
    RxJava RxPermissions 动态权限 简介 原理 案例 MD
    Luban 鲁班 图片压缩 MD
    FileProvider N 7.0 升级 安装APK 选择文件 拍照 临时权限 MD
    组件化 得到 DDComponent JIMU 模块 插件 MD
    gradlew 命令行 build 调试 构建错误 Manifest merger failed MD
    protobuf Protocol Buffers 简介 案例 MD
    ORM数据库框架 SQLite 常用数据库框架比较 MD
    [工具配置]requirejs 多页面,多入口js文件打包总结
  • 原文地址:https://www.cnblogs.com/ToddleLobster/p/5873629.html
Copyright © 2020-2023  润新知