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