自制电脑红外遥控接收器(PC软解码)

简介: 网上有很多介绍红外遥控接收器制作的文章,但其中大部分是用单片进行红外解码,然后再通过串口或USB把解码后的按键信息传入到PC的。这样的电路制作起来,不仅造价相对偏高,而且需要对单片编程,这会令大部分软件开发爱好者望而却步

网上有很多介绍红外遥控接收器制作的文章,但其中大部分是用单片进行红外解码,然后再通过串口或USB把解码后的按键信息传入到PC的。这样的电路制作起来,不仅造价相对偏高,而且需要对单片编程,这会令大部分软件开发爱好者望而却步。

最近看到一篇仅需要7个简单元器件的红外接收器,只需拿起烙铁,不需硬件编程就可以制作完成,原理图如下:

image.png

 

由原理图我们可知,红外接收头把接收的红外信号转换为高低电平通过串口的DSR管脚传入到PC,PC软件通过对DSR高低电平信号的时间曲线进行分析,从而获得相对应的按键信息。

红外遥控器一般采用脉宽调制的串行码,经38kHz的载频把红外信号发射出去。其编码信息一般由三部分组成:引导码、地址码和数据码。一般信号长度大约100ms左右,持续按键则重复发送(中间会有10ms以上的间歇)。

常态下,红外接收头的输出(OUT)都是高电平,引导码信号首先会令红外接收头输出一个大约10ms左右的低电平(不同遥控器有不同的时延),这可令接收设备从容判定信号的到来,而后面的地址码和数据码其电平高低变化就相对较快了,大概在几十或几百个微秒之间。

PC红外遥控软件一般选用Girder,在使用之前需要安装“SFH-56 plugin for Girder”这个插件(文件名"igor SFH-56P lug.dll"),否则不能正常处理我们这种电路的红外接收器信号。可悲的是我至今没找到这个插件,网上提供的很多链接都是坏的。

即使找到了这个插件,要想在我们自己编写的程序中使用也是困难的,因为Girder并没有为我们开发者提供API接口。

既然Girder能用软件实现红外解码,我们为什么不能呢?凡事都要开动大脑,积极行动才对,下面就是我自己焊接的一个红外接收器(元器件是在中发买的,一共不到10元钱,还富裕好多电阻、电容!)

image.png

 

(图下方的红外遥控器的接收器是基于USB的,仅支持Vista以上版本,并且不支持个人开发,不过今天它终于发挥了它应有的作用。当然用电视或VCD遥控器也是可以的)

硬件有了,但程序该从何编起呢?

1、由于接收到的红外信号在微秒级别中变化,对系统实时性要求较高,所以具备垃圾回收功能,实时性没有保证的C#,似乎完不成这种信号的接收功能,所以我们选择的是VC,由它实现高优先级的线程去进行信号接收。

2、由于红外遥控信号是脉宽调制的串行码,所以我们需要采集信号的宽度,显然采用一般的时钟函数来获取时间间隔是不可行的,因为精度太低,所以我们需用采用多媒体时钟和高精度计时的API函数。

3、一般我们按键持续时间为几秒钟,并且由于按键发出前有一个10ms左右的引导信号,所以我们的程序很容易判断出信号起始点,这样我们一次仅需要接收一定量的原始数据就可以完成初步信号采集工作。

4、对于我们的红外接收程序来说并不需要实际解码出红外信号到底包含了那些具体的信息,只要其能够区分出红外遥控上的各个按键就行。

5、由于红接收器是通过串口RTS管脚供电,且通过DSR传递红外信号的,所以我们的程序即使不接收数据,也要打开串口,不过仅需要处理RTS和DSR管脚的信号即可。

好了,动手去做,下面是用VC实现的一个DLL,其功能就是接收并记录红外信号的持续时间。核心代码如下:

DWORD WINAPI ThreadProc(LPVOID pParam)

{

         LARGE_INTEGER litmp;

         LONGLONG QPart1,QPart2;

         double dfFreq;

    int iTime=0;  //微秒

 

         // 获得计数器的时钟频率

         QueryPerformanceFrequency(&litmp);

         dfFreq = (double)1000000.0/litmp.QuadPart;

            

         DWORD ModemState,oldModemState=MS_DSR_ON;

        

         //EV_BREAK or EV_CTS or EV_DSR or EV_ERR or EV_RING or EV_RLSD or EV_RXCHAR or EV_RXFLAG or EV_TXEMPTY

         //SetCommMask(HSC_COM_Handle,EV_DSR);

         //DWORD EvtMask,dwError;

         //COMSTAT cs;

  

         while(HSC_Thread_RunFlag)

         {

                   //等待DSR信号发生变化

        //WaitCommEvent(HSC_COM_Handle,&EvtMask,&HSC_Ovread);

             //ClearCommError(HSC_COM_Handle,&dwError,&cs);

 

                   //获得DSR的状态

                   GetCommModemStatus(HSC_COM_Handle,&ModemState);

                   ModemState = (ModemState & MS_DSR_ON);

                  

                   if(ModemState == oldModemState)  continue;

                   oldModemState=ModemState;

 

             //清计数

                   InterlockedExchange(&HSC_NUM,0);

      

                   //开始接收数据

                   if(HSC_State == 0 && ModemState == 0)

                   {

                      QueryPerformanceCounter(&litmp);

                QPart1 = litmp.QuadPart;

 

           HSC_State=1;

 

                      //复位计数

                      InterlockedExchange(&HSC_NUM,0);

           InterlockedExchange(&HSC_Index,0);

 

                      //开启定时器

                      HSC_TimerID = timeSetEvent(10,HSC_Accuracy,MMTimer,NULL,TIME_PERIODIC);

 

                      continue;

                   }

 

                   //接收数据状态

                   if(HSC_State == 1)

                   {

             QueryPerformanceCounter(&litmp);

                       QPart2 = litmp.QuadPart;

                            //--

                            if(ModemState == 0)

                            {                          

               iTime = (int)((QPart2-QPart1)*dfFreq);

                            }

                            else

                            {

               iTime = (int)((QPart1-QPart2)*dfFreq);

                            }

 

                            if(HSC_Index < HSC_BufferSize)

                                     *(HSC_Buffer+HSC_Index) = iTime;

                           

                            InterlockedIncrement(&HSC_Index);

            //--

            QPart1=QPart2;

                   }

         }

     return STILL_ACTIVE;

}

如果采用WaitCommEvent函数,你会发现CPU使用时间会很低,不过它会让接收程序无法正常退出,所以只好注释掉该函数了,此时你会发现CPU使用时间会很高。

原始数据一旦采集完毕,剩下的就由C#程序大显身手吧。

C#中DLL的接口函数如下:

     const string DllPath = @"YFHSCollect.dll";

     [DllImport(DllPath)]

     public static extern Int32 HSCStart(Int32 COM, Int32 delay, Int32 BufferSize);

     [DllImport(DllPath)]

     public static extern Int32 HSCEnd();

     [DllImport(DllPath)]

 public static extern Int32 HSCData(int[] intData);

我封装了一个类,一旦有按键信息,就会触发一个Click事件。此外程序还具备自学习功能,并且可以把学习后的结果序列化到一个XML文件中去,这样下次再按键就可以识别出键名了。

主程序中测试代码如下:

public partial class frmMain : Form

    {

        YFHWCollect hw =null;

        int[] hwData = null;

 

        public frmMain()

        {

            InitializeComponent();

            hw = new YFHWCollect(this, 1);

            hw.Click += new YFHWCollect.HWEventHandler(hw_Click);

        }

 

        void hw_Click(object sender, HWEventArgs e)

        {

            string strInfo = "";

            for (int i = 0; i < e.lstData.Count; i++)

            {

                for (int j = 0; j < e.lstData[i].Length; j++)

                {

                    strInfo += e.lstData[i][j].ToString() + " ";

                }

                strInfo += "/r/n";

            }

            txtInfo.Text = strInfo;

            lblKeyName.Text = e.KeyName+ " (" + (e.Interval /10).ToString() + "ms)";

            hwData = e.Data;

            picBar.Refresh();

        }

        private void btnCommand_Click(object sender, EventArgs e)

        {

            if (btnCommand.Text == "开始")

            {

                btnCommand.Text = "停止";

                hw.Start();

            }

            else

            {

                btnCommand.Text = "开始";

                hw.End();

            }

        }

 

        private void btnStudy_Click(object sender, EventArgs e)

        {

            hw.Study(txtKeyName.Text);

        }

 

        private void picBar_Paint(object sender, PaintEventArgs e)

        {

            int width = picBar.Width, height = picBar.Height;

            e.Graphics.DrawLine(new Pen(Color.Gray), 0, height / 2, width, height / 2);

 

            if (hwData != null)

            {

                float Len=0;

                foreach(int l in hwData)

                {

                   Len+=l;

                }

                float dx = width / Len,DX=0;

                Pen p = new Pen(Color.Green);

                float Y=0, Y1=height/4,Y2=(float)(height*3.0/4.0);

                float X=0;

                for(int i=0;i<hwData.Length;i++)

                {

                    Y = ((i % 2)==0 ? Y2:Y1);

                    DX = hwData[i] * dx;

                    e.Graphics.DrawLine(p, X, Y, X + DX, Y);

                    X += DX;

                    e.Graphics.DrawLine(p, X, Y1, X, Y2);

                }

            }

        }

}

测试程序运行结果如下:

image.png

 

(上面显示的数据为高电平和低电平的持续时间(低高低高…),单位为1/10毫秒)

注意事项:

1、红外遥控器按键偶数次和奇数次的编码是不同的,程序需要学习两次,才能正常识别按键信息。

2、普通的USB转串口由于仅连接了2、3、5管脚,所以不能正常使用,对比较好的USB转串口(比如Moxa的三百多一根),虽然所有的管脚都引出了,但是由于是通过USB转换的,所以响应时间很是问题,我就因为这个差一点功亏一篑,幸好把程序又在PC机跑了一遍。

//获得DSR的状态

GetCommModemStatus(HSC_COM_Handle,&ModemState);

上面的指令如果采用的是USB转串口,运行时间会是7ms左右,而用主板自带串口仅是几个微秒,相差实在太大了。所以上面的红外接收器程序在没有自带串口的笔记本上是无法正常工作的。

 

源码下载地址:http://www.sky-walker.com.cn/yefan/SourceCode/YFHSCollectTest.rar

相关文章
Win系统 - WIN10 更新后蓝牙音箱无法连接成 Stereo 模式(立体声)
Win系统 - WIN10 更新后蓝牙音箱无法连接成 Stereo 模式(立体声)
856 0
Win系统 - WIN10 更新后蓝牙音箱无法连接成 Stereo 模式(立体声)
|
1天前
|
编解码 Android开发 芯片
RK3288 主板上的RT5651芯片SPK音频无声音问题解决方案
RK3288 主板上的RT5651芯片SPK音频无声音问题解决方案
8 2
|
7月前
|
Android开发 开发者
Android平台GB28181设备接入模块之球机/云台控制探究
好多开发者在做GB28181设备接入的时候,问云台控制是否可以处理(亦或拉取外部RTSP摄像头,通过命令中转的方式,控制摄像头),实际上云台控制命令相对来说还是比较好处理的。协议规范有明确说明,云台控制命令不需要发送应答命令,实现相对简单,和我们之前做的远程启动命令(TeleBoot)类似。
|
7月前
|
Android开发 开发者
Android平台GB28181设备接入端语音广播如何实现实时音量调节
Android平台GB28181设备接入,语音广播功能非常重要,本文要介绍的,不是语音广播的流程,语音广播流程,之前的blog也有非常详细的分享,感兴趣的可以参考官方规范书的交互流程:
|
传感器
手持便携VH501TC混合信号采集仪的常见问题
不能开机 检查电池是否有电,检查电池安装极性是否正确。
手持便携VH501TC混合信号采集仪的常见问题
西门子S7-200 SMART如何用存储卡复位CPU出厂设置、固件升级、程序传输
上篇文章中我们学习了西门子S7-200 SMART的全局变量和局部变量以及如何编写带参数子程序并调用,本篇我们来介绍西门子S7-200 SMART使用存储卡复位CPU到出厂设置、固件升级和程序传输。S7-200 SMART CPU使用FAT32文件系统格式,支持容量为4G至32G范围内的标准商用MicroSD HC卡。
西门子S7-200 SMART如何用存储卡复位CPU出厂设置、固件升级、程序传输
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
176 0
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
|
芯片
单片机:蜂鸣器发声和自动关闭一站式教程:(附赠如何控制声音大小和音调)软件编程+硬件原理+注意事项
单片机:蜂鸣器发声和自动关闭一站式教程:(附赠如何控制声音大小和音调)软件编程+硬件原理+注意事项
472 0
单片机:蜂鸣器发声和自动关闭一站式教程:(附赠如何控制声音大小和音调)软件编程+硬件原理+注意事项
|
存储 网络协议 数据安全/隐私保护
示波器如何直连电脑进行波形读取?
示波器如何直连电脑进行波形读取?
550 0
示波器如何直连电脑进行波形读取?
|
Web App开发 数据采集 Shell
树莓派/PC实现实时摄像头数据共享—最优方法(搭建网络摄像头)
树莓派/PC实现实时摄像头数据共享—最优方法(搭建网络摄像头)
树莓派/PC实现实时摄像头数据共享—最优方法(搭建网络摄像头)

热门文章

最新文章