SylixOS 共用中断号机制
原理概述
SylixOS開發人員在編寫中斷驅動時經常會遇到多個中斷源共用一個中斷號的情況,但在驅動中為了保證代碼獨立性,各個外設的中斷服務函數應該放在各自的C文件中編寫,用統一的中斷服務函數是不合理的。為了適應這種情況,SylixOS支持隊列類型中斷向量,即SylixOS內核將同一中斷向量號的多個中斷服務函數鏈接成隊列,執行時依次執行。
技術實現
應用舉例
下面以at91sam9x25處理器為例,該處理器的調試串口,tick時鐘等外設共用1號中斷向量。如圖2-1所示。
圖2-1 中斷向量
中斷向量詳見AT91SAM9X25芯片手冊8.2節Peripheral Identifiers部分。
代碼分析
SylixOS開發人員需要將1號中斷向量設置成隊列類型中斷向量,設置方法如程序清單1所示。
程序清單 1 中斷初始化
VOID bspIntInit (VOID) {interruptInit(); /* 中斷控制器初始化 */API_InterVectorSetFlag(ID_SYS, LW_IRQ_FLAG_QUEUE); /* 設置ID_SYS為單向量、多服務 */ }bspIntInit函數位于bspLib.c中,用來初始化中斷控制器。
ID_SYS是1號中斷向量,LW_IRQ_FLAG_QUEUE宏是隊列類型中斷向量的標志,該標志必須在安裝任何一個驅動前設置, 且設置后不能再取消,因此應該在 bspIntInit函數中完成設置。
API_InterVectorSetFlag函數用來設置中斷向量屬性,代碼實現如程序清單2所示。
?
程序清單 2 中斷向量屬性設置函數
LW_API ULONG API_InterVectorSetFlag (ULONG ulVector, ULONG ulFlag) {INTREG iregInterLevel;PLW_CLASS_INTDESC pidesc;if (_Inter_Vector_Invalid(ulVector)) {_ErrorHandle(ERROR_KERNEL_VECTOR_NULL);return (ERROR_KERNEL_VECTOR_NULL);}pidesc = LW_IVEC_GET_IDESC(ulVector);LW_SPIN_LOCK_QUICK(&pidesc->IDESC_slLock, &iregInterLevel); /* 關閉中斷同時鎖住 spinlock */if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) { /* 已經是 QUEUE 類型中斷向量 */LW_IVEC_SET_FLAG(ulVector, ulFlag | LW_IRQ_FLAG_QUEUE);} else {LW_IVEC_SET_FLAG(ulVector, ulFlag); /* 中斷向量選項設置 */}LW_SPIN_UNLOCK_QUICK(&pidesc->IDESC_slLock, iregInterLevel); /* 打開中斷, 同時打開 spinlock */return (ERROR_NONE); }設置完成后,不同外設多次調用API_InterVectorConnect 函數設置1號中斷向量的多個中斷服務函數,以AT91SAM9X25 調試串口中斷服務函數和tick時鐘中斷服務函數為例,如程序清單3、程序清單4所示。
程序清單 3 tick中斷服務函數安裝
API_InterVectorConnect(ulVector,(PINT_SVR_ROUTINE)__tickTimerIsr, /* tick中斷服務函數 */LW_NULL,"tick_timer");? ?程序清單 4 ?串口中斷服務函數安裝
API_InterVectorConnect(sam9x25UartIntNum(iChannelNum),(PINT_SVR_ROUTINE)sam9x25SioIsr, /* 串口中斷服務函數安裝 */(PVOID)psiochanUart,pcIsrName);Tick和串口中斷向量號均為1。
API_InterVectorConnect函數將中斷向量號和中斷服務函數進行掛接時,會判斷中斷向量是否是隊列類型中斷向量,若是隊列類型中斷向量,則將中斷服務函數加入中斷隊列,部分代碼實現如程序清單5所示。
程序清單 5 中斷服務函數掛接
LW_API ULONG API_InterVectorConnectEx (ULONG ulVector,PINT_SVR_ROUTINE pfuncIsr,VOIDFUNCPTR pfuncClear,PVOID pvArg,CPCHAR pcName) {... ...if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) { /* 隊列服務類型向量 */for (plineTemp = pidesc->IDESC_plineAction;plineTemp != LW_NULL;plineTemp = _list_line_get_next(plineTemp)) {piactionOld = _LIST_ENTRY(plineTemp, LW_CLASS_INTACT, IACT_plineManage);if ((piactionOld->IACT_pfuncIsr == pfuncIsr) &&(piactionOld->IACT_pvArg == pvArg)) { /* 中斷處理函數是否被重復安裝 */break;}}if (plineTemp) { /* 此中斷被重復安裝 */bNeedFree = LW_TRUE;} else {/** 將中斷服務函數加入到對應的中斷向量號* 下的中斷服務函數列表*/_List_Line_Add_Ahead(&piaction->IACT_plineManage,&pidesc->IDESC_plineAction);bNeedFree = LW_FALSE;}} else { /* 非隊列服務式中斷向量 */... ... }當產生中斷并執行中斷服務函數時,SylixOS內核總中斷服務函數API_InterVectorIsr會找到對應中斷號的中斷服務函數鏈表,并遍歷鏈表,部分代碼如程序清單6所示。
程序清單 6 總中斷服務函數
LW_API irqreturn_t API_InterVectorIsr (ULONG ulVector) {... ...for (plineTemp = pidesc->IDESC_plineAction;plineTemp != LW_NULL;plineTemp = _list_line_get_next(plineTemp)) {piaction = _LIST_ENTRY(plineTemp, LW_CLASS_INTACT, IACT_plineManage);{irqret = piaction->IACT_pfuncIsr(piaction->IACT_pvArg, ulVector);}/** 根據中斷服務函數返回值判斷,中斷* 是否被處理。返回值為1時表示被處理* 跳出循環;為0時表示未被處理,繼續* 遍歷服務函數鏈表。*/if (LW_IRQ_RETVAL(irqret)) { /* 中斷是否已經被處理 */piaction->IACT_iIntCnt[pcpu->CPU_ulCPUId]++;if (piaction->IACT_pfuncClear) {piaction->IACT_pfuncClear(piaction->IACT_pvArg, ulVector);}break; /* 跳出循環 */}}... ... }由程序清單6可知,當中斷向量號是隊列類型中斷向量時,中斷服務函數返回值顯得尤為重要,為0時表示中斷函數未被處理,返回值為1時表示中斷函數已經被處理,所以串口中斷服務函數編寫如程序清單7所示。
程序清單 7 串口中斷函數
static irqreturn_t sam9x25SioIsr (SIO_CHAN *psiochanChan, ULONG ulVector) {... .../** 得出具體是哪一個串口中斷*/uiStatus = readl(DBGU_BA + REG_US_CSR);uiPending = uiStatus & readl(DBGU_BA + REG_US_IMR);if(uiPending != 0) { /* 串口中斷 */if (sam9x25UartIsTxInt(uiPending)) { /* 發送中斷 */if (psiochanUart->pcbGetTxChar(psiochanUart->pvGetTxArg, &cChar)!= ERROR_NONE) {sam9x25UartTxIntDisable(psiochanUart->iChannelNum); /* 關閉發送中斷 */} else {sam9x25UartTxPut(psiochanUart->iChannelNum, cChar); /* 發送數據 */}return (LW_IRQ_HANDLED);}if (sam9x25UartIsRxInt(uiPending)) { /* 接收中斷 */if (sam9x25UartRxGet(psiochanUart->iChannelNum, &cChar) /* 接收數據 */== ERROR_NONE) {psiochanUart->pcbPutRcvChar(psiochanUart->pvPutRcvArg, cChar);} else {}/** 返回值是1,表示是串口中斷,中斷處理結束*/return (LW_IRQ_HANDLED);}}/** 返回值是0,表示不是串口中斷,繼續遍歷中斷服務函數鏈表*/return (LW_IRQ_NONE); }
Tick中斷服務函數編寫如程序清單8所示。
程序清單 8 tick中斷函數
static irqreturn_t __tickTimerIsr (VOID) {INT uiPending;uiPending = readl(REG_PIT_SR);if (uiPending & 1) { /* tick 中斷 */API_KernelTicksContext(); /* 保存被時鐘中斷的線程控制塊 */API_KernelTicks(); /* 內核 TICKS 通知 */API_TimerHTicks(); /* 高速 TIMER TICKS 通知 */timerIsr();return (LW_IRQ_HANDLED); /* tick中斷,中斷結束 */}/** 不是tick中斷,繼續遍歷中斷服務函數*/return (LW_IRQ_NONE); }
SylixOS開發人員在編寫單向量、多中斷中斷服務函數時,中斷服務函數中應該首先讀取具體外設的狀態寄存器以判斷是否是該外設產生的中斷,否則會進入其他外設中斷,結束遍歷中斷服務函數鏈表,導致錯誤。
總結
以上是生活随笔為你收集整理的SylixOS 共用中断号机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数乘法(C语言实现)
- 下一篇: 2020清华大学计算机复试线,2020清