天天看點

win32 timeSetEvent 使用記錄(win32移植ucos ii)

在win32上面使用網上提供的ucos移植代碼,使用過程中經常會出現要麼無法啟動,要麼會在0-5小時内假死(ucos ii的線程都在等待,任務排程停止),而且中間不會報錯,先來看看這個win32上面移植ucos的原理;

win32上面移植ucos ii是靠的1個timeSetEvent多媒體定時器(貌似是精度最高的了),加上一個任務排程線程,首先初始化一個timeSetEvent定時器,一個OSTickEventHandle事件,一個OSTickW32 win32線程,這3個組成了單片機裡面的定時器中斷(滴答時鐘),也就是任務排程,多媒體定時器精度1ms(實際任務排程用的是2ms排程一次,windows 1ms排程一次有點吃力,畢竟不是實時作業系統),多媒體定時器2ms将OSTickEventHandle事件置為有效,然後OSTickW32等待事件有效了,就跳過等待,開始執行OSTickISR(),也就是任務排程了;

DWORD WINAPI OSTickW32( LPVOID lpParameter )
{
    OS_INIT_CRITICAL();

    while(!OSTerminateTickW32)
    {
        OSTickISR();
        
#ifdef WIN_MM_TICK
        
        if( WaitForSingleObject(OSTickEventHandle, 5000) == WAIT_TIMEOUT)
        {
            #ifdef OS_CPU_TRACE
                OS_Printf("Error: MM OSTick Timeout!\n");
            #endif
        }

        ResetEvent(OSTickEventHandle);
#else
        Sleep(1000/OS_TICKS_PER_SEC);
#endif
    }

    return 0;
}
           

我在仿真的時候,當ucos停止後,發現OSTickEventHandle線程永遠不會再有效了,這個時候我就懷疑是定時器問題,開始檢視定時器初始化代碼,如下:

OSTickTimer       = timeSetEvent((1000/OS_TICKS_PER_SEC),OSTimeCap.wPeriodMin,(LPTIMECALLBACK)OSTickEventHandle, dwID,TIME_PERIODIC/|TIME_CALLBACK_EVENT_SET);
           

上面 OS_TICKS_PER_SEC 定義的是500,也就是定時周期2ms,然後定時器周期執行,将事件OSTickEventHandle置為有效;

由于是定時器直接将事件置為有效,我無法獲知是否在周期性執行,是以我專門寫了個回調函數,然後修改了初始化代碼,如下:

void OSTickTimerHandle_Callback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
    if (OSIntNesting == 0)  //
    {
        SetEvent(OSTickEventHandle);

    }
}

OSTickTimer       = timeSetEvent((1000/OS_TICKS_PER_SEC),OSTimeCap.wPeriodMin,/*(LPTIMECALLBACK)OSTickEventHandle*/(LPTIMECALLBACK)OSTickTimerHandle_Callback, dwID,TIME_PERIODIC/*|TIME_CALLBACK_EVENT_SET*/);
           

測試發現能夠正常周期性執行回調函數,OSIntNesting如果不為0,意味着已經關閉了中斷,這個時候就不能再進行排程了,測試過程中依舊會出現幾分鐘到幾小時程式不再排程了,而且這個時候vs會直接報錯,但是程式可以繼續,報錯位置在回調函數中,起初以為是事件出問題了,後來将回調函數清空,依舊會報錯:0x00000000 處有未經處理的異常: 0xC0000005: Access violation

最後我開始懷疑是timeSetEvent的問題了,由于之前沒有用過,開始在網上搜尋timeSetEvent學習,最後在網上找到,timeSetEvent的回調函數必須加 CALLBACK 修飾,否則會出錯,這個出錯如果是百分百立即出錯當場就能發現問題,麻煩點在于他會運作幾分鐘到幾小時之後再出錯,就有點坑爹了,修改的代碼如下,隻需要修改回調函數,增加 CALLBACK修飾;

//注意CALLBACK部分在函數中一定不能丢掉,否則會引起程式的崩潰出現: 0x00000000 處有未經處理的異常: 0xC0000005: Access violation
void CALLBACK OSTickTimerHandle_Callback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
    if (OSIntNesting == 0)  //
    {
        SetEvent(OSTickEventHandle);

    }
}
           

是以現在也懷疑之前的那個事件也是這個原因,但是沒有再去測試了,太耗時間了,這個回調我也好控制是否開啟中斷,出問題了也好調試,經過幾天連續測試,沒有再出現不排程的情況了。