最近遇到一個問題,當我在UCOS里調用系統延時"OSTimeDlyHMSM(0, 0, 0, 10)",程序進入硬件錯誤中斷“HardFault_Handler”中。
我開始以為是主堆棧空間嵌套過多導致溢出,於是設置增大了主堆棧,但依然沒有解決問題,和一個朋友聯系后得知,他寫代碼很少在ISR中調用系統延時,我開始有了想法,如果說ISR里不允許,那為什么操作系統端沒做限制呢?查看相關資料得知,是我對操作系統的不了解。
uCOS為了防止主堆棧的用空導致程序跑飛,定義了“OSIntNesting”全局變量。
而通過這個全局變量,操作系統能很容易的監控中斷的嵌套層次。
而想要利用這個樣的機制,你得在你的中斷服務程序中配合使用:
OSIntEnter()//告知操作系統你已經進入中斷 OSIntNesting++
OSIntExit()//告知操作系統你已經進入中斷 OSIntNesting--
正對此次問題的摘要:
摘要1:
1 //文章來源:http://gliethttp.cublog.cn[轉載請聲明出處] 2 3 //---------------------------------------------------------------------- 4 //1.OSTimeDly()函數 5 void OSTimeDly (INT16U ticks) 6 { 7 INT8U y; 8 #if OS_CRITICAL_METHOD == 3 9 OS_CPU_SR cpu_sr = 0; 10 #endif 11 if (OSIntNesting > 0) { 12 return;//在中斷處理函數中調用了OSTimeDly(),那么直接退出 13 } 14 if (ticks > 0) { 15 OS_ENTER_CRITICAL(); 16 //調用OSTimeDly()的進程自己把自己從就緒控制矩陣中拿下來, 17 //即:去掉調度器(x,y)矩形陣列(OSRdyTbl,OSRdyGrp)中該task對應的bit位,使得調度器不考慮 18 //該task的調度 19 y = OSTCBCur->OSTCBY; 20 OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX; 21 if (OSRdyTbl[y] == 0) { 22 OSRdyGrp &= ~OSTCBCur->OSTCBBitY; 23 } 24 //延時ticks值,放入OSTCBDly單元,在os時鍾滴答處理函數OSTimeTick()中,會處理該單元[gliethttp] 25 OSTCBCur->OSTCBDly = ticks; 26 OS_EXIT_CRITICAL(); 27 //因為本task正在運行,所以本task現在的優先級最高,現在本task已經將自己從就緒控制矩陣中--調度器(x,y)矩形陣列 28 //把自己摘掉,所以調度函數OS_Sched()一定會切換到另一個task中執行新task的代碼[gliethttp] 29 OS_Sched();//具體參見《淺析μC/OS-II v2.85內核調度函數》 30 }//ticks==0,那么什么也不做 31 } 32 //---------------------------------------------------------------------- 33 //2.OSTimeTick()--在定時中斷里引用的系統滴答函數 34 void OSTimeTick (void) 35 { 36 OS_TCB *ptcb; 37 #if OS_TICK_STEP_EN > 0 38 BOOLEAN step; 39 #endif 40 #if OS_CRITICAL_METHOD == 3 41 OS_CPU_SR cpu_sr = 0;//該3方式將使中斷狀態寄存器放入堆棧中 42 43 #endif 44 45 #if OS_TIME_TICK_HOOK_EN > 0 46 OSTimeTickHook(); 47 #endif 48 #if OS_TIME_GET_SET_EN > 0 49 OS_ENTER_CRITICAL(); 50 OSTime++; 51 OS_EXIT_CRITICAL(); 52 #endif 53 if (OSRunning == OS_TRUE) { 54 #if OS_TICK_STEP_EN > 0 55 //控制內核的tick 56 switch (OSTickStepState) { 57 case OS_TICK_STEP_DIS: 58 step = OS_TRUE; 59 break; 60 case OS_TICK_STEP_WAIT: 61 step = OS_FALSE; 62 break; 63 case OS_TICK_STEP_ONCE: 64 //本次tick將將影響到task的OSTCBDly域 65 //但以后的tick將一直被屏蔽,不會影響到OSTCBDly域 66 //直到外部將OSTickStepState改變為止[gliethttp] 67 step = OS_TRUE; 68 OSTickStepState = OS_TICK_STEP_WAIT; 69 break; 70 default: 71 step = OS_TRUE;//本次tick將影響到task的OSTCBDly域 72 OSTickStepState = OS_TICK_STEP_DIS; 73 break; 74 } 75 if (step == OS_FALSE) { 76 return; 77 } 78 #endif 79 ptcb = OSTCBList; 80 //2007-09-08 gliethttp 81 //OSTCBList是一個按進程創建的先后順序鏈接成的task單向鏈表,最后創建的task在最前面,最先創建的 82 //task在單向鏈表的尾端, 83 //所以OS_TaskIdle空閑進程在鏈表的最后,因為它最先創建 84 while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { 85 OS_ENTER_CRITICAL(); 86 if (ptcb->OSTCBDly != 0) { 87 if (--ptcb->OSTCBDly == 0) { 88 //該task的延時時間已到,解析此次延時是OSTimeDly()引起的,還是OSQPend()之類超時引起的[gliethttp] 89 if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { 90 //2007-09-08 gliethttp 91 //如:由OSSemPend (pevent,timeout,perr);定義的timeout已經到了,對應task需要運行了 92 //超時時間到,所以不論當前進程是在做什么,只要時間一到 93 //該task就可以運行了,所以清除所有事件標志,之后狀態標示為OS_STAT_PEND_TO(超時) 94 ptcb->OSTCBStat &= ~(INT8U)OS_STAT_PEND_ANY; 95 ptcb->OSTCBStatPend = OS_STAT_PEND_TO;//超時異常 96 } else { 97 //2007-09-08 gliethttp 98 //說明該task調用的是OSTimeDly() 99 ptcb->OSTCBStatPend = OS_STAT_PEND_OK;//正常結束 100 } 101 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { 102 //2007-09-08 gliethttp 103 //如果該task沒有suspend,那么把當前就緒的task加入到運行調度器的就緒控制矩陣中 104 //等待被調度 105 OSRdyGrp |= ptcb->OSTCBBitY; 106 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; 107 } 108 } 109 } 110 ptcb = ptcb->OSTCBNext;//繼續運算下一個task的OSTCBDly時間域 111 OS_EXIT_CRITICAL(); 112 } 113 } 114 }
摘要2:
前些天參照FL2440的bootloader的程序,寫了關於裸機USB設備驅動的測試程序。然后就想把USB的驅動程序移植到uCos系統中運行。
對於USB設備驅動的工作原理,我這里不多說,網上有很多這方面的資料。這里只說明在UCOS系統中移植USB設備驅動所需要注意的細節。
第一步:在uCos系統啟動以后,首先對USB設備進行初始化。 這里的初始化包含對中斷函數的設置和端點功能的設置 。
第二步:創建一個端點0的控制處理任務函數。這個任務的優先級最好設置為最高優先級,以便能夠及時處理USB的枚舉。在任務函數中,我們通過請求信號量的方式來等待中斷服務程序發來的信號,當中斷服務程序檢測到是端點0的中斷,即發送一個信號。端點0的控制任務函數接收到此信號,便開始進入端點0的控制傳輸。如果沒有接收到信號,則此任務函數一直處於堵塞狀態。便於其他任務的調度執行。這里還有一點需要注意的是:在進入中斷函數時,要使用OSIntEnter()和OSIntExit()這兩個配套函數。我開始的時候沒有加入這兩個函數,結果導致一進入枚舉就會導致程序跑飛。
1 //摘要來自http://blog.sina.com.cn/u/1093812390 2 //USB控制傳輸任務函數 3 4 void TaskUsb(void *pdata) 5 { 6 #if OS_CRITICAL_METHOD == 3 7 OS_CPU_SR cpu_sr; 8 #endif 9 INT8U err; 10 while(1){ 11 OSSemPend(pSetup_Event,0,&err); //等待信號 12 if (err == OS_NO_ERR){ 13 OS_ENTER_CRITICAL(); //關中斷 14 Ep0Handler(); //進行控制傳輸處理 15 OS_EXIT_CRITICAL(); //開中斷 16 } 17 } 18 } 19 20 //中斷函數如下: 21 22 void IsrUsbd(void) 23 { 24 #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; 25 #endif 26 27 28 U8 usbdIntpnd,epIntpnd; 29 U8 saveIndexReg = rINDEX_REG; 30 OS_ENTER_CRITICAL(); //關中斷 31 OSIntEnter(); //通知UCOS系統,已進入中斷處理程序 usbdIntpnd = rUSB_INT_REG; //讀取USB中斷寄存器 32 epIntpnd = rEP_INT_REG; //讀取端點中斷寄存器 33 .... 34 if (epIntpnd & EP0_INT) { 35 rEP_INT_REG = EP0_INT; 36 OSSemPost(pSetup_Event); //通知控制端口處理任務 37 } 38 ClearPending(BIT_USBD); rINDEX_REG = saveIndexReg; 39 OS_EXIT_CRITICAL(); //開中斷 40 OSIntExit(); //通知系統,已處理結束中斷服務程序。任務調度 41 }
