haoDIY创好电子音响电脑科技DIY小制作发明移动版

主页 > 电脑DIY > 电脑DIY >

DIY电脑遥控器(4)


安装完毕后,安装目录里的文件有这些:
DIY电脑遥控器(4)

把遥控接收器插到电脑后面的串口上,然后双击“AeroPrism”即可启动上位机软件。启动后,需要设置好串口号,我的是COM1。
DIY电脑遥控器(4)
然后就可以使用了。

通过切换遥控器模式实现不同的功能。比如快捷运行,只要把快捷方式放到“Shortcuts”文件夹下,通过OSD就可以快速运行电脑上的任何文件了。活用这个功能,在听歌、看影片、看照片的时候就会很方便了。
DIY电脑遥控器(4)

因为是软解,这套DIY还可以变通把红外遥控的码显示出来,但是只限于NEC协议。
DIY电脑遥控器(4)




上位机解码部分代码

;==========================================================
;                     红外码串口接收
;
;==========================================================
;串口设备的缓存推荐值(系统用,和软件中的是两回事)
#MAX_WRITE_BUFFER = 1024
#MAX_READ_BUFFER  = 1024

#EVENT_CHAR = $ff      ;用来产生事件的字符(ANSI only, 1 Byte)

#FRAME_DURATION = 108  ;遥控器每帧数据时间(ms)
#MARGIN_OF_ERROR = 10  ;误差(ms)



;在何时接收到什么键码
Structure WhenWhatKey
    key_code.l     ;键码
    dwTimeFirst.l  ;接收到最初的32位码的时间
    dwTime.l       ;接收时间
    fBurst.l       ;是否连发码
EndStructure



Global m_hCommPort.l                       ;串口设备句柄
Global m_timeouts_orig.COMMTIMEOUTS        ;保存系统默认的串口超时值

Global m_dwEventFlags.l                    ;用于 WaitCommEvent 的串口事件
Global m_hEventReadExit.l                  ;读线程退出内核事件
Global m_hReadThread.l                     ;读线程句柄



;函数声明
Declare Read_StatusThread(*value)
Declare KeyCodePump(*pBuffer, cbData.l)
Declare PortEventHandler(dwCommEvent.l)


;串口设置字串
DataSection
    DCS:    
        Data.s "baud=9600 parity=N data=8 stop=1"
EndDataSection



; 试一下串口端口是否可用
Procedure.i IsPortAvailable(nPort.l)
    Dim sPortString.c(8)
    *p = @sPortString()
    CopyMemoryString(@"COM", @*p)
    CopyMemoryString(Str(nPort))
    hComm.l = CreateFile_(sPortString(), #GENERIC_READ, 0, #Null, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL | #FILE_FLAG_OVERLAPPED, #Null)                                        
        
        If hComm = #INVALID_HANDLE_VALUE
            ProcedureReturn #False
        Else
            CloseHandle_(hComm)
            ProcedureReturn #True
        EndIf
EndProcedure


Procedure ConfigurePort(hComm.l)
    ;设置 DCB
    dcb.DCB
    GetCommState_(hComm, @dcb)
    sSettings.s
    Restore DCS
    Read.s sSettings
    BuildCommDCB_(@sSettings, @dcb) ;波特率、奇偶校验、停止位
    dcb\EvtChar = #EVENT_CHAR       ;事件字符
    dcb\fbits | (1 << 1)            ;奇偶校验
    SetCommState_(hComm, @dcb)

    ;设置超时参数
    GetCommTimeouts_(hComm, @m_timeouts_orig) ;先保存原来的
    ct.COMMTIMEOUTS
    With ct
        \ReadIntervalTimeout = 20
        \ReadTotalTimeoutMultiplier = 0
        \ReadTotalTimeoutConstant = 0
    EndWith    
        SetCommTimeouts_(hComm, @ct)
    
    ;设置缓存大小
    SetupComm_(hComm, #MAX_READ_BUFFER, #MAX_WRITE_BUFFER)
EndProcedure


Procedure SetPortEvent(dwEvent.l)
    m_dwEventFlags = dwEvent
EndProcedure


Procedure StartupComm(nPort.l) 
    If GetFileType_(m_hCommPort) = #FILE_TYPE_CHAR
            ProcedureReturn
        EndIf
        
    ;打开串口设备
    Dim sPortString.c(8)
    *p = @sPortString()
    CopyMemoryString(@"COM", @*p)
    CopyMemoryString(Str(nPort))
    m_hCommPort = CreateFile_(sPortString(), #GENERIC_READ, 0, #Null, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL | #FILE_FLAG_OVERLAPPED, #Null)                                        
        If m_hCommPort = #INVALID_HANDLE_VALUE
            ProcedureReturn
        EndIf

    ;配置串口参数
    ConfigurePort(m_hCommPort)
    
    ;设置 WaitCommEvent 的串口事件
    SetPortEvent(#EV_ERR)
    
    ;建立线程退出事件
    m_hEventReadExit = CreateEvent_(#Null, 0, 0, #Null)
    
    ;启动读线程
    m_hReadThread = ThreadID(CreateThread(@Read_StatusThread(), 0))
    SetThreadPriority_(m_hReadThread, #THREAD_PRIORITY_HIGHEST)
EndProcedure


Procedure BreakdownComm()
    If GetFileType_(m_hCommPort) <> #FILE_TYPE_CHAR
        ProcedureReturn
    EndIf

    ;结束读线程
    SetEvent_(m_hEventReadExit)
    WaitForSingleObject_(m_hReadThread, #INFINITE)
    CloseHandle_(m_hEventReadExit)
    CloseHandle_(m_hReadThread)
    
    ;还原超时参数
    SetCommTimeouts_(m_hCommPort, @m_timeouts_orig)

    CloseHandle_(m_hCommPort)
    m_hCommPort = #Null 
EndProcedure


; 改变串口设备
Procedure AlterComm(nPort.l)
    ;打开新串口
    Dim sPortString.c(8)
    *p = @sPortString()
    CopyMemoryString(@"COM", @*p)
    CopyMemoryString(Str(nPort))
    hComm.l = CreateFile_(sPortString(), #GENERIC_READ, 0, #Null, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL | #FILE_FLAG_OVERLAPPED, #Null)                                        
        If hComm = #INVALID_HANDLE_VALUE
        ProcedureReturn
        EndIf

    ;关闭旧串口
    If GetFileType_(m_hCommPort) = #FILE_TYPE_CHAR
        ;结束读线程
        SetEvent_(m_hEventReadExit)
        WaitForSingleObject_(m_hReadThread, #INFINITE)
        CloseHandle_(m_hReadThread)
        
        ;还原超时参数
        SetCommTimeouts_(m_hCommPort, @m_timeouts_orig)

        ;关闭内核文件对象
        CloseHandle_(m_hCommPort)
        m_hCommPort = #Null
        
    Else ;之前没有串口设备打开,需要建立相关同步对象
        ;建立线程退出事件
        m_hEventReadExit = CreateEvent_(#Null, 0, 0, #Null)
    EndIf
    
    m_hCommPort = hComm
    
    ;配置新串口
    ConfigurePort(m_hCommPort)
    
    ;设置 WaitCommEvent 的串口事件
    SetPortEvent(#EV_ERR)
    
    ;启动读线程
    m_hReadThread = ThreadID(CreateThread(@Read_StatusThread(), 0))
    SetThreadPriority_(m_hReadThread, #THREAD_PRIORITY_HIGHEST)
EndProcedure


; 串口读与串口事件线程
Procedure Read_StatusThread(*value)
    #READ_STATUS_CHECK_TIMEOUT = #INFINITE
    #BYTES_OF_READ_BUFFER = 40

    Define.l cbRead, cbDummy
    dwWaitRst.l
    Define.l dwCommEvent, dwStoredEvtFlags = ~0
    Define.i fThreadDone = #False, fWaitingOnRead = #False, fWaitingOnStat = #False

    ; 建立接收缓存
    Dim buffer.a(#BYTES_OF_READ_BUFFER - 1)
    
    ; 建立内核对象
    Define.OVERLAPPED osReader, osStatus
    osReader\hEvent = CreateEvent_(#Null, 1, 0, #Null)
    osStatus\hEvent = CreateEvent_(#Null, 1, 0, #Null)
    
    ; 需要侦测下面几个事件:
    ; Read event
    ; Status event
    ; Thread exit event
    ;
    Dim hArray.l(2)
    hArray(0) = osReader\hEvent
    hArray(1) = osStatus\hEvent
    hArray(2) = m_hEventReadExit

    While Not fThreadDone
        ; 若无读操作处于等待中,新建一个读操作
        If Not fWaitingOnRead
            If Not ReadFile_(m_hCommPort, buffer(), #BYTES_OF_READ_BUFFER, @cbRead, @osReader)
                If GetLastError_() <> #ERROR_IO_PENDING
                    ; 出错了
                EndIf
                fWaitingOnRead = #True
            Else ; 读操作立即完成 
                If cbRead <> 0
                    KeyCodePump(buffer(), cbRead)
                EndIf
            EndIf
        EndIf

        ;更新 WaitCommEvent 的事件
        If dwStoredEvtFlags <> m_dwEventFlags
            dwStoredEvtFlags = m_dwEventFlags
            SetCommMask_(m_hCommPort, dwStoredEvtFlags)
        EndIf
         
        ; 若无串口事件处于等待中,建立串口事件等待
        If Not fWaitingOnStat
            If Not WaitCommEvent_(m_hCommPort, @dwCommEvent, @osStatus)
                If GetLastError_() <> #ERROR_IO_PENDING
                    ; 出错了
                Else
                    fWaitingOnStat = #True
                EndIf    
            Else ; WaitCommEvent 立即返回
                PortEventHandler(dwCommEvent)
            EndIf
        EndIf

        ;等待未决操作完成
        If fWaitingOnStat And fWaitingOnRead
            dwWaitRst = WaitForMultipleObjects_(3, hArray(), 0, #READ_STATUS_CHECK_TIMEOUT)
            
            Select dwWaitRst
            Case #WAIT_OBJECT_0 ; 读操作结束
                If Not GetOverlappedResult_(m_hCommPort, @osReader, @cbRead, 0)
                    If GetLastError_() = #ERROR_OPERATION_ABORTED
                        ; 读操作放弃
                    Else
                        ; 出错了
                    EndIf
                Else ;读操作成功完成  
                    If cbRead <> 0
                        KeyCodePump(buffer(), cbRead)
                    EndIf
                EndIf

                fWaitingOnRead = #False
                
            Case #WAIT_OBJECT_0 + 1 ;有串口事件
                If Not GetOverlappedResult_(m_hCommPort, @osStatus, @cbDummy, 0)
                    If GetLastError_() = #ERROR_OPERATION_ABORTED
                        ; WaitCommEvent 放弃
                    Else
                        ; 出错了
                    EndIf
                Else  ; 成功
                    PortEventHandler(dwCommEvent) ;WaitCommEvent 仍会填充 dwCommEvent
                EndIf
                
                fWaitingOnStat = #False

            Case #WAIT_OBJECT_0 + 2 ; 线程退出 
                GetOverlappedResult_(m_hCommPort, @osReader, @cbRead, #False)
                PurgeComm_(m_hCommPort, #PURGE_RXABORT | #PURGE_RXCLEAR) ;停止异步读取(线程退出后,接收缓存就不存在了)
                fThreadDone = #True

            Case #WAIT_TIMEOUT ; 超时
                ;
                ; 用这个机会检查串口状态、清除错误等
                ;
            EndSelect
        EndIf
    Wend

    ; 释放内核对象
    CloseHandle_(osReader\hEvent)
    CloseHandle_(osStatus\hEvent)
EndProcedure


;响应串口事件
Procedure PortEventHandler(dwCommEvent.l)
    Define.l fCTS, fDSR, fERR, fRING, fRLSD, fBREAK, fRXCHAR, fRXFLAG, fTXEMPTY
    fCTS = #EV_CTS & dwCommEvent
    fDSR = #EV_DSR & dwCommEvent
    fERR = #EV_ERR & dwCommEvent
    fRING = #EV_RING & dwCommEvent
    fRLSD = #EV_RLSD & dwCommEvent
    fBREAK = #EV_BREAK & dwCommEvent
    fRXCHAR = #EV_RXCHAR & dwCommEvent
    fRXFLAG = #EV_RXFLAG & dwCommEvent
    fTXEMPTY = #EV_TXEMPTY & dwCommEvent
    
    If fERR ;有错误发生
        dwErrs.l
        cs.COMSTAT
        ClearCommError_(m_hCommPort, @dwErrs, @cs)
    EndIf
EndProcedure


;把收到的数据取出,检验合法性,投入队列
Procedure KeyCodePump(*pBuffer, cbData.l)
    Static s_previous_key.WhenWhatKey   ;之前接收到的键码
    Static Dim s_buffer.a(33)           ;按键数据缓存
    Static s_offset.l                   ;偏移
    ks.KeyInstruction
    
    ;获得当前时间
    dwTime.l = timeGetTime_()
    
    ;把收到的数据放到缓存中
    If s_offset + cbData <= 34
        CopyMemory(*pBuffer, @s_buffer() + s_offset, cbData)
        s_offset + cbData
    Else  ;之前接收过乱码?
        If cbData = 34  ;丢弃之前的,本次的算作一次接收
            CopyMemory(*pBuffer, @s_buffer(), 34)
            s_offset = 34
        Else
            s_offset = 0
            FillMemory(@s_previous_key, SizeOf(WhenWhatKey), 0)
            ProcedureReturn
        EndIf
    EndIf
    
    Select s_offset
    Case 2  ;连发码
        If PeekU(s_buffer()) = 0 And dwTime - s_previous_key\dwTime <= #FRAME_DURATION + #MARGIN_OF_ERROR ;有效的连发码
            s_previous_key\dwTime = dwTime
            s_previous_key\fBurst = 1
            
            If dwTime - s_previous_key\dwTimeFirst >= g_kb_delay
                ks\key_code = s_previous_key\key_code
                ks\fStatus = #KS_BURST
                EnterCriticalSection_(@g_csQKI)
                EnterQueue(@g_queueKeyIns, @ks)
                LeaveCriticalSection_(@g_csQKI)
                SetEvent_(g_hEventKeySignal)
            EndIf
        EndIf
        
        s_offset = 0
        
    Case 34  ;键码
        Dim key_code.a(3)  ;a(3) -- ID7 ~ ID0
                           ;a(2) -- ID7 ~ ID0 (NOT)
                           ;a(1) -- FUN7 ~ FUN0
                           ;a(0) -- FUN7 ~ FUN0 (NOT)
                           ;红外码低位在前发送
        Define.i i, j, k
        If s_buffer(0) = 0 And s_buffer(33) = 0
            k = 1
            For i = 3 To 0 Step -1
                Select s_buffer(k)
                Case $0        ;逻辑1
                    key_code(i) | $80
                Case $e0, $f0  ;逻辑0
                    key_code(i) | $0
                Default        ;按0处理
                    key_code(i) | $0
                EndSelect
                k + 1
                For j = 1 To 7
                    key_code(i) >> 1
                    Select s_buffer(k)
                    Case $0        ;逻辑1
                        key_code(i) | $80
                    Case $e0, $f0  ;逻辑0
                        key_code(i) | $0
                    Default        ;按0处理
                        key_code(i) | $0
                    EndSelect
                    k + 1
                Next j
            Next i

            If key_code(0) ! key_code(1) = $ff ;And key_code(2) ! key_code(3) = $ff  ;有效键码 
                If s_previous_key\fBurst = 0 And s_previous_key\key_code = PeekL(key_code()) And dwTime - s_previous_key\dwTime <= g_dbclick_time  ;认为是双击
                    ks\fStatus = #KS_DBCLICK
                Else
                    s_previous_key\key_code = PeekL(key_code())
                    s_previous_key\fBurst = 0
                    ks\fStatus = #KS_PRESSED
                EndIf
                s_previous_key\dwTime = dwTime
                s_previous_key\dwTimeFirst = dwTime
                ks\key_code = PeekL(key_code())
                EnterCriticalSection_(@g_csQKI)
                EnterQueue(@g_queueKeyIns, @ks)
                LeaveCriticalSection_(@g_csQKI)
                SetEvent_(g_hEventKeySignal)
                
                PostThreadMessage_(g_idMainThread, #TM_IRKEYCODE, ks\key_code, 0)
            EndIf
        EndIf
        
        s_offset = 0
    EndSelect
EndProcedure

(责任编辑:admin)
haoDIY创好电子 版权所有,电话微信13977534587