转---------------wince串口线程、中断等相关学习(作者:wogoyixikexie@gliet)

来源:互联网 发布:怎么在淘宝上挑选衣服 编辑:程序博客网 时间:2024/05/20 18:54

前面发了好几篇wince串口的学习的文章,由于是学习性质的,弄的比较乱,还请网友们原谅。以前只是搞懂了大体框架,对这个中断线程等底层的东西还没有了解,现在来来学习一下。

 
VOID
SerialEventHandler(PHW_INDEP_INFO       pSerialHead)
{
    PHW_VTBL            pFuncTbl = pSerialHead->pHWObj->pFuncTbl;
    PVOID               pHWHead = pSerialHead->pHWHead;
    ULONG               CharIndex;
    ULONG               RoomLeft = 0;
    ULONG               TotalLeft = 0;
    INTERRUPT_TYPE      it = INTR_NONE;
    BOOL                RxDataAvail = FALSE;
    DEBUGMSG (ZONE_THREAD, (TEXT("+SerialEventHandler, pHead 0x%X/r/n"),
                            pSerialHead));
    if ( pSerialHead->KillRxThread ||
         !pSerialHead->hSerialEvent ) {
        DEBUGMSG (ZONE_THREAD, (TEXT("Exitting thread/r/n")));
        SetEvent(pSerialHead->hKillDispatchThread);
        ExitThread(0);
    }
// NOTE - This one is a little tricky.  If the only owner is a monitoring task
// then I don't have an owner for read/write, yet I might be in this routine
// due to a change in line status.  Lets just do the best we can and increment
// the count for the access owner if available.
    if ( pSerialHead->pAccessOwner )
        COM_INC_USAGE_CNT(pSerialHead->pAccessOwner);
    while ( 1 ) {
        if ( !(it = pFuncTbl->HWGetIntrType(pHWHead)) ) {
            DEBUGMSG (ZONE_THREAD,
                      (TEXT("SerialEventHandler, No Interrupt./r/n")));
            break;
        }
        DEBUGMSG (ZONE_THREAD,
                  (TEXT("SerialEventHandler, Interrupts 0x%X/r/n"), it));
        if ( it & INTR_RX ) {
            // It's read data event. Optimize the read by reading chunks
            // if the user has not specified using xflow control
            // or event/error/eof characters. Ack the receive,
            // unmask the interrupt, get the current data pointer
            // and see if data is available.
            // Note: We have to copy RxRead and RxWrite index to local in order to make it atomic.
            register DWORD RxWIndex=RxWrite(pSerialHead), RxRIndex=RxRead(pSerialHead);
            DEBUGMSG (ZONE_THREAD|ZONE_READ , (TEXT("Rx Event/r/n")));
            if ( RxRIndex == 0 ) {
                // have to leave one byte free.
                RoomLeft = RxLength(pSerialHead) - RxWIndex - 1;
            } else {
                RoomLeft = RxLength(pSerialHead) - RxWIndex;
            }
            if ( RxRIndex > RxWIndex ) {
                RoomLeft = RxRIndex - RxWIndex - 1;
            }
            if ( RoomLeft ) {
                pSerialHead->DroppedBytesPDD +=
                pFuncTbl->HWRxIntrHandler(pHWHead,
                                          RxBuffWrite(pSerialHead),
                                          &RoomLeft);
            } else {
                BYTE    TempBuf[16];
                RoomLeft = 16;
                pFuncTbl->HWRxIntrHandler(pHWHead,
                                          TempBuf,
                                          &RoomLeft);
                pSerialHead->DroppedBytesMDD += RoomLeft;
                DEBUGMSG (ZONE_WARN|ZONE_READ, (TEXT("Tossed %d bytes/r/n"),
                                                RoomLeft));
                RoomLeft = 0;
            }
            DEBUGMSG (ZONE_READ ,
                      (TEXT("After HWGetBytes, Fifo(R=%d,W=%d,BA=%d,L=%d) ByteRead=%d/r/n"),
                       RxRead(pSerialHead), RxWrite(pSerialHead),
                       RxBytesAvail(pSerialHead), RxLength(pSerialHead),
                       RoomLeft));
            // If flow control enabled then we need to scan for XON/XOFF
            // characters
            if ( pSerialHead->XFlow ) {
                for ( CharIndex=0; CharIndex < RoomLeft; ) {
                    if ( RxBuffWrite(pSerialHead)[CharIndex] ==
                         pSerialHead->DCB.XoffChar ) {
                        DEBUGMSG (ZONE_FLOW, (TEXT("Received XOFF/r/n")));
                        pSerialHead->StopXmit = 1;
                        memmove (RxBuffWrite(pSerialHead)+CharIndex,
                                 RxBuffWrite(pSerialHead)+CharIndex+1,
                                 RoomLeft - CharIndex);
                        RoomLeft--;
                        continue;
                    } else if ( RxBuffWrite(pSerialHead)[CharIndex] ==
                                pSerialHead->DCB.XonChar ) {
                        pSerialHead->StopXmit = 0;
                        DEBUGMSG (ZONE_FLOW, (TEXT("Received XON/r/n")));
                        memmove (RxBuffWrite(pSerialHead)+CharIndex,
                                 RxBuffWrite(pSerialHead)+CharIndex+1,
                                 RoomLeft - CharIndex);
                        RoomLeft--;
                        // We disabled TX on XOFF, so now we need to start sending
                        // again. Easiest way is to pretend we saw a TX interrupt
                        it |= INTR_TX;
                        continue;
                    }
                    CharIndex++;
                }
            }
            pSerialHead->RxBytes += RoomLeft;
            RxWrite(pSerialHead) = 
                (RxWrite(pSerialHead)+RoomLeft<RxLength(pSerialHead)? RxWrite(pSerialHead)+RoomLeft: RxWrite(pSerialHead)+RoomLeft-RxLength(pSerialHead));
            if ( RoomLeft ) {
                RxDataAvail = TRUE;
            }
            /* Support DTR_CONTROL_HANDSHAKE/RTS_CONTROL_HANDSHAKE
             * signal is cleared when the input buffer is more than 3/4 full.
             */
            if ( (pSerialHead->DCB.fDtrControl == DTR_CONTROL_HANDSHAKE) &
                 (!pSerialHead->DtrFlow) && 
                 (4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
                DEBUGMSG (ZONE_READ|ZONE_FLOW,
                          (TEXT("DTR_CONTROL_HANDSHAKE Clearing DTR/r/n")));
                pSerialHead->DtrFlow = 1;
                pFuncTbl->HWClearDTR(pHWHead);
            }
            if ( (pSerialHead->DCB.fRtsControl == RTS_CONTROL_HANDSHAKE) &
                 (!pSerialHead->RtsFlow) &
                 (4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
                DEBUGMSG (ZONE_READ|ZONE_FLOW,
                          (TEXT("RTS_CONTROL_HANDSHAKE Clearing RTS/r/n")));
                pSerialHead->RtsFlow = 1;
                pFuncTbl->HWClearRTS(pHWHead);
            }
            /* If Xon/Xoff flow control is desired. check the limit against
             * the remaining room and act accordingly.
             */
            if ( pSerialHead->DCB.fInX && !(pSerialHead->SentXoff) &
                 ( pSerialHead->DCB.XoffLim >=
                   (RxLength(pSerialHead) - RxBytesAvail(pSerialHead))) ) {
                DEBUGMSG (ZONE_FLOW, (TEXT("Sending XOFF/r/n")));
                pFuncTbl->HWXmitComChar(pHWHead, pSerialHead->DCB.XoffChar);
                pSerialHead->SentXoff = 1;
                if ( !pSerialHead->DCB.fTXContinueOnXoff ) {
                    pSerialHead->StopXmit = 1;
                }
            }
        }
        //-与中断相关的操作
        if ( it & INTR_TX ) {
            DEBUGMSG (ZONE_THREAD|ZONE_WRITE , (TEXT("Tx Event/r/n")));
            DoTxData( pSerialHead );//收到发送中断后发送数据
        }
        if ( (it & INTR_MODEM) ) {
            DEBUGMSG (ZONE_THREAD, (TEXT("Other Event, it:%x/r/n"), it));
            /* Call low level status clean up code.
             */
            pFuncTbl->HWModemIntrHandler(pHWHead);//这个函数会对应下层函数实现
        }
        if ( it & INTR_LINE ) {
            DEBUGMSG (ZONE_THREAD, (TEXT("Line Event, it:%x/r/n"), it));
            /* Call low level line status clean up code.
             * Then unmask the interrupt
             */
            pFuncTbl->HWLineIntrHandler(pHWHead);//这个是干什么的?
        }
    }
    // We kept this till the end to optimize the above loop
    if ( RxDataAvail ) {
        // Signal COM_Read that bytes are available.
        SetEvent(pSerialHead->hReadEvent);
        EvaluateEventFlag(pSerialHead, EV_RXCHAR);
    }
    DEBUGMSG (ZONE_THREAD ,
              (TEXT("-SerialEventHandler, Fifo(R=%d,W=%d,L=%d)/r/n"),
               RxRead(pSerialHead), RxWrite(pSerialHead),
               RxLength(pSerialHead)));
    if ( pSerialHead->pAccessOwner )
        COM_DEC_USAGE_CNT(pSerialHead->pAccessOwner);
    return;
}
                        

    从这个SerialEventHandler的实现代码来看,这个SerialEventHandler几乎包括了所有串口功能的操作。把读写线程等都集成在一起了。通过判断中断类型来执行不同的函数。

   SerialEventHandler函数被两个地方调用

   一、在49行处调用,

+ expand sourceview plaincopy to clipboardprint?
/* 
 @doc INTERNAL 
 @func  ULONG | SerialDispatchThread | Main serial event dispatch thread code. 
 *  This is the reading and dispatching thread. It gets the 
 *  event associated with the logical interrupt dwIntID and calls 
 *  hardware specific routines to determine whether it's a receive event 
 *  or a transmit event. If it's a transmit event, it calls the HW tx handler. 
 *  If it's a receive event, it calls for the number of characters and calls 
 *  atomic GetByte() to extract characters and put them into the drivers 
 *  buffer represented by pSerialHead->pTargetBuffer, managing waiting 
 *  for events and checking to see if those signals correspond to reading. 
 *  It relies on NK masking the interrupts while it does it's thing, calling 
 *  InterruptDone() to unmask them for each of the above cases. 
 * 
 *  Not exported to users. 
 * 
 @rdesc This thread technically returns a status, but in practice, doesn't return 
 *      while the device is open. 
 */ 
static DWORD WINAPI  
SerialDispatchThread(  
                    PVOID   pContext    /* @parm [IN] Pointer to main data structure. */ 
                    )  
{  
    PHW_INDEP_INFO      pSerialHead    = (PHW_INDEP_INFO)pContext;  
    ULONG               WaitReturn;  
 
    DEBUGMSG (ZONE_THREAD, (TEXT("Entered SerialDispatchThread %X/r/n"),  
                            pSerialHead));  
 
    // It is possible for a PDD to use this routine in its private thread, so  
    // don't just assume that the MDD synchronization mechanism is in place.  
    if ( pSerialHead->pHWObj->BindFlags & THREAD_IN_MDD ) {  
        DEBUGMSG(ZONE_INIT,  
                 (TEXT("Spinning in dispatch thread %X %X/n/r"), pSerialHead, pSerialHead->pHWObj));  
        while ( !pSerialHead->pDispatchThread ) {  
            Sleep(20);  
        }  
    }  
 
    /* Wait for the event that any serial port action creates. 
     */ 
    while ( !pSerialHead->KillRxThread ) {  
        DEBUGMSG (ZONE_THREAD, (TEXT("Event %X, %d/r/n"),  
                                pSerialHead->hSerialEvent,  
                                pSerialHead->pHWObj->dwIntID ));  
        WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);  
 
        SerialEventHandler(pSerialHead);//开始执行串口线程  
        InterruptDone(pSerialHead->pHWObj->dwIntID);  
    }  
 
    DEBUGMSG (ZONE_THREAD, (TEXT("SerialDispatchThread %x exiting/r/n"),  
                            pSerialHead));  
    return(0);  

/*
 @doc INTERNAL
 @func ULONG | SerialDispatchThread | Main serial event dispatch thread code.
 * This is the reading and dispatching thread. It gets the
 * event associated with the logical interrupt dwIntID and calls
 * hardware specific routines to determine whether it's a receive event
 * or a transmit event. If it's a transmit event, it calls the HW tx handler.
 * If it's a receive event, it calls for the number of characters and calls
 * atomic GetByte() to extract characters and put them into the drivers
 * buffer represented by pSerialHead->pTargetBuffer, managing waiting
 * for events and checking to see if those signals correspond to reading.
 * It relies on NK masking the interrupts while it does it's thing, calling
 * InterruptDone() to unmask them for each of the above cases.
 *
 * Not exported to users.
 *
 @rdesc This thread technically returns a status, but in practice, doesn't return
 *  while the device is open.
 */
static DWORD WINAPI
SerialDispatchThread(
                    PVOID   pContext    /* @parm [IN] Pointer to main data structure. */
                    )
{
    PHW_INDEP_INFO      pSerialHead    = (PHW_INDEP_INFO)pContext;
    ULONG               WaitReturn;

    DEBUGMSG (ZONE_THREAD, (TEXT("Entered SerialDispatchThread %X/r/n"),
                            pSerialHead));

    // It is possible for a PDD to use this routine in its private thread, so
    // don't just assume that the MDD synchronization mechanism is in place.
    if ( pSerialHead->pHWObj->BindFlags & THREAD_IN_MDD ) {
        DEBUGMSG(ZONE_INIT,
                 (TEXT("Spinning in dispatch thread %X %X/n/r"), pSerialHead, pSerialHead->pHWObj));
        while ( !pSerialHead->pDispatchThread ) {
            Sleep(20);
        }
    }

    /* Wait for the event that any serial port action creates.
     */
    while ( !pSerialHead->KillRxThread ) {
        DEBUGMSG (ZONE_THREAD, (TEXT("Event %X, %d/r/n"),
                                pSerialHead->hSerialEvent,
                                pSerialHead->pHWObj->dwIntID ));
        WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

        SerialEventHandler(pSerialHead);//开始执行串口线程
        InterruptDone(pSerialHead->pHWObj->dwIntID);
    }

    DEBUGMSG (ZONE_THREAD, (TEXT("SerialDispatchThread %x exiting/r/n"),
                            pSerialHead));
    return(0);
}
 

        SerialDispatchThread函数是等待线程启动的作用,微软的代码还是写的比较清晰的,来看看SerialDispatchThread函数在哪里调用了。

+ expand sourceview plaincopy to clipboardprint?
// ****************************************************************  
//  
//  @doc INTERNAL  
//  @func       BOOL | StartDispatchThread | Start thread if requested by PDD.  
//  
//  @parm       ULONG  | pSerialHead  
//  
//   @rdesc     TRUE if success, FALSE if failed.  
//  
BOOL 
StartDispatchThread(  
                   PHW_INDEP_INFO  pSerialHead  
                   )  
{  
    // Initialize the interrupt to be associated with the hSerialEvent  
    // event. GetByte waits on this event and acts as a second  
    // level decoder determining the type of serial event. If this return  
    // fails, then another process has registered for the interrupt, so  
    // fail the init and set the hSerialEvent to NULL.  
    DEBUGMSG(ZONE_INIT,  
             (TEXT("Initializing interrupt 0x%X, 0x%X/n/r"),  
              pSerialHead->pHWObj->dwIntID, pSerialHead->hSerialEvent));  
//中断绑定串口线程SerialEventHandler,这个dwIntID并不是什么中断  
//而是注册表中的DeviceArrayIndex的值why?  
    if ( !InterruptInitialize(pSerialHead->pHWObj->dwIntID,  
                              pSerialHead->hSerialEvent,  
                              NULL,  
                              0) ) {  
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,  
                 (TEXT("Error initializing interrupt/n/r")));  
        return(FALSE);  
    }  
 
    InterruptDone(pSerialHead->pHWObj->dwIntID);  
 
    // Set up the dispatch thread and it's kill flag. Note that the thread  
    // fills in its own handle in pSerialHead.  
    pSerialHead->KillRxThread = 0;  
    pSerialHead->pDispatchThread = NULL;  
 
    DEBUGMSG(ZONE_INIT,  
             (TEXT("Spinning thread%X/n/r"), pSerialHead));  
//创建启动线程函数  
    pSerialHead->pDispatchThread = CreateThread(NULL,0, SerialDispatchThread,  
                                                pSerialHead, 0,NULL);  
    if ( pSerialHead->pDispatchThread == NULL ) {  
        DEBUGMSG(ZONE_INIT|ZONE_ERROR,  
                 (TEXT("Error creating dispatch thread (%d)/n/r"),  
                  GetLastError()));  
        return(FALSE);  
    }  
 
    DEBUGMSG (ZONE_INIT, (TEXT("Created receive thread %X/r/n"),  
                          pSerialHead->pDispatchThread));       
    return(TRUE);  

// ****************************************************************
//
// @doc INTERNAL
// @func  BOOL | StartDispatchThread | Start thread if requested by PDD.
//
// @parm   ULONG  | pSerialHead
//
//  @rdesc  TRUE if success, FALSE if failed.
//
BOOL
StartDispatchThread(
                   PHW_INDEP_INFO  pSerialHead
                   )
{
    // Initialize the interrupt to be associated with the hSerialEvent
    // event. GetByte waits on this event and acts as a second
    // level decoder determining the type of serial event. If this return
    // fails, then another process has registered for the interrupt, so
    // fail the init and set the hSerialEvent to NULL.
    DEBUGMSG(ZONE_INIT,
             (TEXT("Initializing interrupt 0x%X, 0x%X/n/r"),
              pSerialHead->pHWObj->dwIntID, pSerialHead->hSerialEvent));
//中断绑定串口线程SerialEventHandler,这个dwIntID并不是什么中断
//而是注册表中的DeviceArrayIndex的值why?
    if ( !InterruptInitialize(pSerialHead->pHWObj->dwIntID,
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) ) {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,
                 (TEXT("Error initializing interrupt/n/r")));
        return(FALSE);
    }

    InterruptDone(pSerialHead->pHWObj->dwIntID);

    // Set up the dispatch thread and it's kill flag. Note that the thread
    // fills in its own handle in pSerialHead.
    pSerialHead->KillRxThread = 0;
    pSerialHead->pDispatchThread = NULL;

    DEBUGMSG(ZONE_INIT,
             (TEXT("Spinning thread%X/n/r"), pSerialHead));
//创建启动线程函数
    pSerialHead->pDispatchThread = CreateThread(NULL,0, SerialDispatchThread,
                                                pSerialHead, 0,NULL);
    if ( pSerialHead->pDispatchThread == NULL ) {
        DEBUGMSG(ZONE_INIT|ZONE_ERROR,
                 (TEXT("Error creating dispatch thread (%d)/n/r"),
                  GetLastError()));
        return(FALSE);
    }

    DEBUGMSG (ZONE_INIT, (TEXT("Created receive thread %X/r/n"),
                          pSerialHead->pDispatchThread));    
    return(TRUE);
}
 

再来看这个StartDispatchThread被谁调用了

(1)COM_Init调用了

  if ( pSerialHead->pHWObj->BindFlags & THREAD_AT_INIT ) {
        // Hook the interrupt and start the associated thread.
        if ( ! StartDispatchThread( pSerialHead ) ) {
            // Failed on InterruptInitialize or CreateThread.  Bail.
            COM_Deinit(pSerialHead);
            return(NULL);       
        }

    }

(2)COM_Open调用了

 if ( pSerialHead->pHWObj->BindFlags & THREAD_AT_OPEN ) {
            DEBUGMSG(ZONE_INIT|ZONE_OPEN,
                     (TEXT("COM_Open: Starting DispatchThread x%X/n/r"),
                      pOpenHead));
            // Hook the interrupt and start the associated thread.
            if ( ! StartDispatchThread( pSerialHead ) ) {
                // Failed on InterruptInitialize or CreateThread.  Bail.
                DEBUGMSG(ZONE_INIT|ZONE_OPEN,
                         (TEXT("COM_Open: Failed StartDispatchThread x%X/n/r"),
                          pOpenHead));
                goto OpenFail;
            }
        }

二、NotifyPDDInterrupt调用了SerialEventHandler函数

//NotifyPDDInterrupt在PDD和中间层被调用了
BOOL CSerialPDD::NotifyPDDInterrupt(INTERRUPT_TYPE interruptType)
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    m_dwInterruptFlag |= (DWORD)interruptType;
    m_InterruptLock.Unlock();
    if (IsPowerResumed ( )) {
        if (m_lOpenCount) { // If application is opened.
            EventCallback( EV_POWER );
        }
        else {
            if (GetModemStatus() & MS_RLSD_ON)
                CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL);
        }
    }
    SerialEventHandler(m_pMdd);
    return TRUE;
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 现在来看看这个中断到底怎么回事,感觉和4.2的区别太大了。

 ———————————————————————————————————————————————

我找了好久,终于找到PDD线程相关的地方,这里值得注意的是C++语言的构造函数和析构函数的作用,在第一次

建类对象的时候会调用构造函数(构造函数可以用来初始化对象),而清除类对象的时候调用析构函数(如果不懂

C++的,比如我,找了好久才知道在析构函数关闭了串口线程句柄,惭愧。)

//构造函数,当创建对象的时候会自动调用

CPdd2440Uart::CPdd2440Uart (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj )
:   CSerialPDD(lpActivePath,pMdd, pHwObj)
,   m_ActiveReg(HKEY_LOCAL_MACHINE,lpActivePath)
,   CMiniThread (0, TRUE)  
{
    m_pReg2440Uart = NULL;
    m_pINTregs = NULL;
    m_dwIntShift = 0;
    m_dwSysIntr = MAXDWORD;
    m_hISTEvent = NULL;
    m_dwDevIndex = 0;
    m_pRegVirtualAddr = NULL;
    m_XmitFlushDone =  CreateEvent(0, FALSE, FALSE, NULL);
    m_XmitFifoEnable = FALSE;
    m_dwWaterMark = 8 ;
}

//析构函数,当清除类对象的时候会自动调用
CPdd2440Uart::~CPdd2440Uart()
{
    InitModem(FALSE);
    if (m_hISTEvent) {
        m_bTerminated=TRUE;
        ThreadStart();
        SetEvent(m_hISTEvent);
        ThreadTerminated(1000);
        InterruptDisable( m_dwSysIntr );   //禁止中断     
        CloseHandle(m_hISTEvent);//关闭串口线程句柄
    };
    if (m_pReg2440Uart)
        delete m_pReg2440Uart;
    if (m_XmitFlushDone)
        CloseHandle(m_XmitFlushDone);
    if (m_pRegVirtualAddr != NULL) {
        MmUnmapIoSpace((PVOID)m_pRegVirtualAddr,0UL);//释放虚拟内存
    }
    if (m_pINTregs!=NULL) {
        MmUnmapIoSpace((PVOID)m_pINTregs,0UL));//释放虚拟内存

    }
       
}

//---------至于更进一步分析,明天做个比较完整的总结吧,看来年前的时间都用来学C++比较合适

————————————上面都是比较乱,现在总结一下一个正常的线程架构——————————————

首先是创建线程 pSerialHead->hSerialEvent = CreateEvent(0,FALSE,FALSE,NULL);

InterruptInitialize(pSerialHead->pHWObj->dwIntID,//这个中断和PDD的中断以及相关线程到底有什么不同?
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

 SetEvent(pSerialHead->hSerialEvent);

WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

SerialEventHandler(pSerialHead);//开始执行串口线程

CloseHandle(pSerialHead->hSerialEvent);//关闭线程句柄

—————————————————————————————————————————————————

WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。在这里举个例子:

先创建一个全局Event对象g_event:
CEvent g_event;
在程序中可以通过调用CEvent::SetEvent设置事件为有信号状态。

在驱动程序中,因为线程往往和硬件中断相关,中断产生就执行线程,中断关闭就不执行线程,所以串口提供了

两种执行线程的方法(1)产生中断.(2)SetEvent(线程句柄)

—————————————————————————————————————————————————

MDD中的中断是怎么回事?

在SerialEventHandler函数中调用了HWGetIntrType函数(其实是调用了转换函数表的SerGetInterruptType函数)

INTERRUPT_TYPE
SerGetInterruptType(
                   PVOID pHead      // Pointer to hardware head
                   )
{
    CSerialPDD * pSerialPDD  = ( CSerialPDD * )pHead;
    INTERRUPT_TYPE interrupts=INTR_NONE;
    DEBUGMSG (ZONE_EVENTS,(TEXT("+SerGetInterruptType 0x%X/r/n"), pHead));
    if (pSerialPDD)
        interrupts= pSerialPDD->GetInterruptType();
    DEBUGMSG (ZONE_EVENTS,(TEXT("-SerGetInterruptType (0x%X) return %X/r/n"), pHead, interrupts));
    return interrupts;
}

——————————

INTERRUPT_TYPE CSerialPDD::GetInterruptType()
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    INTERRUPT_TYPE lIntrFlagRet= (INTERRUPT_TYPE )m_dwInterruptFlag;
    m_dwInterruptFlag = INTR_NONE;
    m_InterruptLock.Unlock();
    return lIntrFlagRet;
}

——


BOOL CSerialPDD::NotifyPDDInterrupt(INTERRUPT_TYPE interruptType)
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    m_dwInterruptFlag |= (DWORD)interruptType;
    m_InterruptLock.Unlock();
    if (IsPowerResumed ( )) {
        if (m_lOpenCount) { // If application is opened.
            EventCallback( EV_POWER );
        }
        else {
            if (GetModemStatus() & MS_RLSD_ON)
                CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL);
        }
    }
    SerialEventHandler(m_pMdd);
    return TRUE;
}

——看看这个NotifyPDDInterrupt被谁调用了就知道怎么回事了。

 在PDD层的ThreadRun函数调用了NotifyPDDInterrupt函数

DWORD CPdd2440Uart::ThreadRun()
{
    while ( m_hISTEvent!=NULL && !IsTerminated()) {
        if (WaitForSingleObject( m_hISTEvent,m_dwISTTimeout)==WAIT_OBJECT_0) {
            m_HardwareLock.Lock();   
            while (!IsTerminated() ) {
                DWORD dwData = (GetInterruptStatus() & (S2440UART_INT_RXD|S2440UART_INT_TXD|S2440UART_INT_ERR));
                DWORD dwMask = (GetIntrruptMask() & (S2440UART_INT_RXD|S2440UART_INT_TXD|S2440UART_INT_ERR));
                 DEBUGMSG(ZONE_THREAD,
                      (TEXT(" CPdd2440Uart::ThreadRun INT=%x, MASK =%x/r/n"),dwData,dwMask));
                dwMask &= dwData;
                if (dwMask) {
                    DEBUGMSG(ZONE_THREAD,
                      (TEXT(" CPdd2440Uart::ThreadRun Active INT=%x/r/n"),dwMask));
                    DWORD interrupts=INTR_MODEM; // Always check Modem when we have change. It may work at polling mode.
                    if ((dwMask & S2440UART_INT_RXD)!=0)
                        interrupts |= INTR_RX;
                    if ((dwMask & S2440UART_INT_TXD)!=0)
                        interrupts |= INTR_TX;
                    if ((dwMask & S2440UART_INT_ERR)!=0)
                        interrupts |= INTR_LINE|INTR_RX;
                    NotifyPDDInterrupt((INTERRUPT_TYPE)interrupts);
                    ClearInterrupt(dwData);
                }
                else
                    break;
            }
            m_HardwareLock.Unlock();  
            InterruptDone(m_dwSysIntr);
        }
        else { // Polling Modem.
            NotifyPDDInterrupt(INTR_MODEM);
            DEBUGMSG(ZONE_THREAD,(TEXT(" CPdd2440Uart::ThreadRun timeout INT=%x,MASK=%d/r/n"),m_pINTregs->SUBSRCPND,m_pINTregs->INTSUBMSK));
#ifdef DEBUG
            if ( ZONE_THREAD )
                m_pReg2440Uart->DumpRegister();
#endif
        }
    }
    return 1;
}
——————————————————————————————————————————

这个有点奇怪,这个是硬件中断相关的东西,但是在MMD层有个中断实在让人费解

InterruptInitialize(pSerialHead->pHWObj->dwIntID,//这个中断和PDD的中断以及相关线程到底有什么不同?
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

在中间层的SerInit函数有

//为什么会这样做呢,中断和
  //DeviceArrayIndex()注册表扯上关系?
        DWORD dwIndex= pHWObj->dwIntID;
        pHWObj->dwIntID = 0;

 ——————————————————————

找到了

// GetSerialObj : The purpose of this function is to allow multiple PDDs to be
// linked with a single MDD creating a multiport driver.  In such a driver, the
// MDD must be able to determine the correct vtbl and associated parameters for
// each PDD.  Immediately prior to calling HWInit, the MDD calls GetSerialObject
// to get the correct function pointers and parameters.
//
extern "C" PHWOBJ
GetSerialObject(
               DWORD DeviceArrayIndex
               )
{
    PHWOBJ pSerObj;

    // Unlike many other serial samples, we do not have a statically allocated
    // array of HWObjs.  Instead, we allocate a new HWObj for each instance
    // of the driver.  The MDD will always call GetSerialObj/HWInit/HWDeinit in
    // that order, so we can do the alloc here and do any subsequent free in
    // HWDeInit.
    // Allocate space for the HWOBJ.
    pSerObj=(PHWOBJ)LocalAlloc( LPTR ,sizeof(HWOBJ) );
    if ( !pSerObj )
        return (NULL);

    // Fill in the HWObj structure that we just allocated.

    pSerObj->BindFlags = THREAD_IN_PDD;     // PDD create thread when device is first attached.
    pSerObj->dwIntID = DeviceArrayIndex;   // Only it is useful when set set THREAD_AT_MDD. We use this to transfer DeviceArrayIndex
    pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl; // Return pointer to appropriate functions

    // Now return this structure to the MDD.
    return (pSerObj);
}

果然是注册表中的DeviceArrayIndex值就是dwIntID ,不过我始终对前面的线程绑定dwIntID 有点不解

//中断绑定串口线程SerialEventHandler,这个dwIntID并不是什么硬件中断,这样调用怎么能成功?
//而是注册表中的DeviceArrayIndex的值why?
    if ( !InterruptInitialize(pSerialHead->pHWObj->dwIntID,
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

——————————————————————————————

按照道理,上面是这个所谓的“中断”(dwIntID )绑定了句柄为hSerialEvent的线程。只要dwIntID 有硬件动作

才会启动句柄为hSerialEvent的线程。但是这个中断是假的,不知道微软是怎么弄的,搞定这个,串口的架构基本是算完成了。

——————引用何宗健老师的回答

InterruptInitialize函数要调用成功,必须要求目前的event没有对象正在等待。因此,如果你先创建了线程,并且已经执行了WaitForSingleObject一句的话,肯定就不能满足这个要求了。


  其实InterruptInitialize函数在WinCE里面是有完整源代码的,在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/KERNEL/intrapi.c中,你可以顺藤摸瓜,看看它是如何把中断跟event连上的。

 


     中断是硬件与软件打交道的重要方法,因此,大多数驱动程序都涉及到对中断的处理,本文就驱动程序的开发人员以及BSP的开发人员的角度,来谈谈Windows CE中中断的处理过程。

如果一个驱动程序要处理一个中断,那么驱动程序需要首先建立一个事件,可以使用CreateEvent函数,然后调用InterruptInitialize将该事件与中断号绑定,这一步就会使能该中断,OAL中的OEMInerrupteEnable就会被调用,如果该函数不返回true的话,InterruptInitialize就会失败。然后驱动程序中的IST就可以使用WaitForSingleObject函数来等待中断的发生。

      当一个硬件中断发生之后,操作系统陷入异常,中断向量指示进入CE的异常处理程序,该异常处理程序然后调用OAL的OEMInterruptHandler函数,该函数检测硬件之后,将硬件中断转换为软件的中断号,返回给系统。该中断号就是上面提到的InterruptInitialize中使用的那个中断号。系统得到该中断号之后,就会找到该中断号对应的事件,并唤醒等待相应事件的线程(IST),然后IST就可以在用户态进行中断处理。处理完成之后,IST需要调用InterruptDone来告诉操作系统中断处理结束,操作系统再次调用OAL中的OEMInterruptDone函数,最后完成中断的处理。

在上面的描述中,驱动程序需要使用的是InterruptInitialize,WaitForSingleObject和InterruptDone三个函数,而BSP开发者需要主要考虑的是OEMInerrupteEnable,OEMInterruptHandler,OEMInterruptDone三个函数。当然,这是上面提到的一些函数,实际上BSP开发者在中断部分还需要考虑Init以及Disable等等函数,这里就不再讨论了。

——对于这个问题我还是无法解释,怎么想也不会使用这种注册表的一些参数来啊,要彻底明白,只能看看InterruptInitialize源码到底是怎么回事了。

————————————————————

wogoyixikexie@gliet 说:
WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

        SerialEventHandler(pSerialHead);//开始执行串口线程
CSDN-广州天河wince6.0 说:
???

wogoyixikexie@gliet 说:
要有信号来才会执行这个线程,可是我发现这个
SetEvent(pSerialHead->hSerialEvent);
在StopDispatchThread函数里面,而StopDispatchThread是被COM_Deinit函数调用的
wogoyixikexie@gliet 说:
那么这样导致了关闭驱动的时候才执行上面那个串口发送接收相关的线程SerialEventHandler
wogoyixikexie@gliet 说:
我看后实在不解,不过它是能正常工作的
wogoyixikexie@gliet 说:
我想求个让人信服的理由
加密助手 说:
--- 系统提示: 以下会话未被加密 ---
CSDN-广州天河wince6.0 说:
两个事件是不一样的吧
wogoyixikexie@gliet 说:
一样的
wogoyixikexie@gliet 说:
这个线程句柄就是用来控制线程是否执行的
wogoyixikexie@gliet 说:
如果没有信号,就等待
CSDN-广州天河wince6.0 说:
WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);
是在那个函数中
CSDN-广州天河wince6.0 说:
threadrun(_)?
wogoyixikexie@gliet 说:
SerialDispatchThread
wogoyixikexie@gliet 说:
这个是MDD的线程
wogoyixikexie@gliet 说:
PDD的线程是和硬件中断相关的
wogoyixikexie@gliet 说:
真是神了,这样使用注册表一个配置产生了一个线程
加密助手 说:
--- 系统提示: 以下会话未被加密 ---
CSDN-广州天河wince6.0 说:
不太清楚它为什么要这样子做
wogoyixikexie@gliet 说:
呵呵,先放一下了,以后再补充了。
CSDN-广州天河wince6.0 说:
好像这个线程一直都是阻塞的

——————————————————————————————————————————————————

对于这个MDD中断的线程问题,真搞不懂为什么会这样,先放下了,以后有机会再补充。了解的朋友们请在

博客后面指点一下啊。感激不尽。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gooogleman/archive/2009/01/14/3774536.aspx