当前位置: 首页 > 科技观察

鸿蒙LightKernelM-Core源码分析系列之四中断Hwi

时间:2023-03-23 11:13:39 科技观察

更多内容请访问:鸿蒙LightKernelM-Core源码分析系列五-interruptHwi在鸿蒙LightKernel源码分析系列前几篇文章中分析了重要的数据结构。在这篇文章中,我们将谈谈中断,并向读者介绍中断的概念以及鸿蒙LightKernel中断模块的源码。一、中断概念介绍中断是指CPU暂停执行当前程序,在需要时执行新程序的过程。当外围硬件需要CPU时,会响应中断请求,产生中断信号,使CPU立即中断当前任务。在分析中断源码之前,下面先介绍一些中断相关的硬件和中断相关的概念。1.1中断相关硬件介绍中断相关硬件可以分为三类:设备、中断控制器和CPU本身。设备发起的中断源。当设备需要请求CPU时,产生一个中断信号,该信号连接到中断控制器。中断控制器中断控制器是CPU众多外设之一。一方面,它接收其他外设中断引脚的输入。另一方面,它向CPU发送中断信号。中断源可以打开和关闭,中断源的优先级和触发方式可以通过对中断控制器的编程来设置。CPUCPU会响应中断源的请求,中断当前正在执行的任务,然后执行中断处理程序。1.2中断相关概念中断号每个中断请求信号都会有一个特定的标志,以便计算机判断是哪个设备发出了中断请求,这个标志就是中断号。中断优先级为了使系统能够及时响应和处理所有的中断,系统根据中断时间的重要性和紧急程度将中断源分为几个等级,称为中断优先级。中断处理程序当外设产生中断请求时,CPU暂停当前任务,然后响应中断请求,即执行中断处理程序。每个产生中断的设备都有一个相应的中断处理程序。中断向量中断服务程序的入口地址。中断向量表存放中断向量的存储区,中断向量对应中断号,中断向量在中断向量表中按照中断号的顺序存放。中断共享当外设较少时,一个外设可以对应一个中断号,但为了支持更多的硬件设备,多个设备可以共享一个中断号,共享相同中断号的中断处理程序形成一个链表。当外部设备产生中断请求时,系统会遍历中断号对应的中断处理程序列表,直到找到对应设备的中断处理程序。在遍历执行过程中,每个中断处理程序都可以通过检测设备ID来判断中断是否是由中断处理程序对应的设备产生的。接下来我们看一下鸿蒙之光内核中断的源码。2.鸿蒙之光内核中断源码2.1中断相关的声明和定义在文件kernel\arch\arm\cortex-m7\gcc\los_interrupt.c中定义了一些结构体、全局变量和内联函数。在分析源码之前,我们先看看这些定义和声明。所有变量g_intCount表示正在处理的中断数。每次进入中断处理程序,该变量的值都会加1,当中断处理完成退出时,该变量的值会减1。对应的内联函数HalIsIntActive()用于获取是否中断正在处理中断,返回值大于0,表示正在处理中断。UINT32g_intCount=0;inlineUINT32HalIsIntActive(VOID){return(g_intCount>0);}我们再看一下中断向量表定义。(1)此处的代码为系统支持的中断定义了数组g_hwiForm[OS_VECTOR_CNT]。对于每个中断号hwiNum,对应的数组元素g_hwiForm[hwiNum]表示每个中断对应的中断处理执行入口程序。⑵宏OS_HWI_WITH_ARG表示中断处理程序是否支持参数输入,默认关闭。如果支持传参,在⑶处定义结构体HWI_HANDLER_FUNC来维护中断处理函数及其参数,另外还需要在⑷处定义g_hwiHandlerForm数组。如果不支持参数传递,请使用(6)中定义的g_hwiHandlerForm数组。对于每个中断号hwiNum,其对应的数组元素g_hwiHandlerForm[hwiNum]表示每个中断对应的中断处理程序。(5)和(7)定义一个函数OsSetVector()来设置指定中断号对应的中断处理执行入口程序和中断处理程序。中断处理执行入口程序与中断处理程序的关系是,当有中断发生时,会执行中断处理执行入口程序,该函数会进一步调用中断处理程序。⑴STATICHWI_PROC_FUNC__attribute__((aligned(0x100)))g_hwiForm[OS_VECTOR_CNT]={0};⑵#if(OS_HWI_WITH_ARG==1)⑶typedefstruct{HWI_PROC_FUNCpfnHandler;VOID*pParm;}HWI_HANDLER_FUNC;⑷STATICHWI_HANDLER_FUNCg_hwiHandlerForm[OS_VECTOR_CNT]={{(HWI_PROC_FUNC)0,(HWI_ARG_T)0}};⑸VOIDOsSetVector(UINT32num,HWI_PROC_FUNCvector,VOID*arg){if((num+OS_SYS_VECTOR_CNT)VTOR。对于Cortex-M3及以上的CPU内核,还需要执行(5)设置优先级组。⑵代码启用指定的异常。LITE_OS_SEC_TEXT_INITVOIDHalHwiInit(){#if(LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT==1)UINT32index;⑴g_hwiForm[0]=0;/*[0]TopofStack*/g_hwiForm[1]=Reset_Handler;/*[1]reset*/⑵for(index=2;indexVTOR=(UINT32)(UINTPTR)g_hwiForm;#endif#if(__CORTEX_M>=0x03U)/*onlyforCortex-M3andabove*/⑷NVIC_SetPriorityGrouping(OS_NVIC_AIRCR_PRIGROUP);#endif/*EnableUSGFAULT,BUSFAULT,MEMFAULT*/⑩*(volatileUINT32*)OS_NVIC_SHCSR|=(USGFAULT|BUSFAULT|MEMFAULT);/*EnableDIV0andunalignedexception*/*(volatileUINT32*)OS_NVIC_CCR|=DIV0FA创建中断UINT32HalHwiCreate()开发者可以调用函数UINT32HalHwiCreate()创建中断并注册中断处理函数先来看这个函数的参数,HWI_HANDLE_ThwiNum为硬件中断号,HWI_PRIOR_ThwiPrio中断优先级,HWI_MODE_T模式中断模式,暂保留没用过。HWI_PROC_FUNChandler是一个需要注册的中断处理函数,这个函数会在中断触发后被调用。HWI_ARG_Targ是中断处理程序的参数。下面我们一起来分析一下这个函数的源码。代码从(1)开始并检查输入参数。中断处理程序不能为空,中断号不能大于支持的最大中断号,中断优先级不能超过指定的优先级。如果要创建的中断号对应的中断执行入口程序不等于HalHwiDefaultHandler,则表示已经创建,返回错误码。关闭中断,然后执行(2)处的OsSetVector()函数,为指定的中断号设置中断处理程序。(3)调用CMSIS函数使能中断,设置中断的优先级,使能中断,完成中断的创建。LITE_OS_SEC_TEXT_INITUINT32HalHwiCreate(HWI_HANDLE_ThwiNum,HWI_PRIOR_ThwiPrio,HWI_MODE_Tmode,HWI_PROC_FUNChandler,HWI_ARG_Targ){UINTPTRintSave;⑴if(handler==NULL){returnOS_ERRNO_HWI_PROC_FUNC_NULL;}if(hwiNum>=OS_HWI_MAX_NUM){returnOS_ERRNO_HWI_NUM_INVALID;}if(g_hwiForm[hwiNum+OS_SYS_VECTOR_CNT]!=(HWI_PROC_FUNC)HalHwiDefaultHandler){returnOS_ERRNO_HWI_ALREADY_CREATED;}if(hwiPrio>OS_HWI_PRIO_LOWEST){returnOS_ERRNO_HWI_PRIO_INVALID;}intSave=LOS_IntLock();#if(OS_HWI_WITH_ARG==1)OsSetVector(hwiNum,handler,arg);#else⑵OsSetVector(hwiNum,handler);#endif⑶NVIC_EnableIRQ((IRQn_Type)hwiNum);NVIC_SetPriority((IRQn_Type)hwiNum,hwiPrio);LOS_IntRestore(intSave);returnLOS_OK;}2.4删除中断UINT32HalHwiDelete()中断删除操作是create操作的逆操作,简单易懂。开发者可以调用函数UINT32HalHwiDelete(HWI_HANDLE_ThwiNum)来删除中断。该函数需要指定中断号参数HWI_HANDLE_ThwiNum。下面我们一起来分析一下这个函数的源码。(1)处的代码检查输入参数,不能大于支持的最大中断数。(2)调用CMSIS函数禁用中断,然后锁定中断,执行(3)将中断向量表中指定中断号的中断执行入口程序设置为默认程序HalHwiDefaultHandler。LITE_OS_SEC_TEXT_INITUINT32HalHwiDelete(HWI_HANDLE_ThwiNum){UINT32intSave;⑴if(hwiNum>=OS_HWI_MAX_NUM){returnOS_ERRNO_HWI_NUM_INVALID;}⑵NVIC_DisableIRQ((IRQn_Type)hwiNum);intSave=LOS_IntLock();⑶g_hwiForm[hwiNum+OS_SYS_VECTOR_CNT]=(HWI_PROC_FUNC)HalHwiDefaultHandler;LOS_IntRestore(intSave);returnLOS_OK;}2.5中断处理执行入口程序我们再来看一下中断处理执行入口程序。默认函数HalHwiDefaultHandler()如下,调用函数HalIntNumGet()获取中断号,打印出来,然后执行死循环。其中,函数HalIntNumGet()读取寄存器ipsr,获取触发中断的中断号。LITE_OS_SEC_TEXT_MINORVOIDHalHwiDefaultHandler(VOID){UINT32irqNum=HalIntNumGet();PRINT_ERR("%sirqNum:%d\n",__FUNCTION__,irqNum);while(1){}}继续看中断处理执行入口程序HalInterrupt(),源代码如下。⑴将全局变量g_intCount表示的正在处理的中断数加1。中断执行完成后,在⑩处将正在处理的中断个数减1。(2)调用函数HalIntNumGet()获取中断号,(3)和(5)调用函数HalPreInterruptHandler(),HalAftInterruptHandler()可以处理中断处理程序执行前后的一些其他操作,目前默认为一个空函数。(4)根据中断号从中断处理程序数组中获取中断处理程序,不为空则调用执行。LITE_OS_SEC_TEXTVOIDHalInterrupt(VOID){UINT32hwiIndex;UINT32intSave;#if(LOSCFG_KERNEL_RUNSTOP==1)SCB->SCR&=(UINT32)~((UINT32)SCB_SCR_SLEEPDEEP_Msk);#endifintSave=LOS_IntLock();⑴g_intHetN++;;al_InthIntRestore;();OsHookCall(LOS_HOOK_TYPE_ISR_ENTER,hwiIndex);⑶HalPreInterruptHandler(hwiIndex);#if(OS_HWI_WITH_ARG==1)if(g_hwiHandlerForm[hwiIndex].pfnHandler!=0){g_hwiHandlerForm[hwiIndex].pfn_Form_Index([VOID*)].pParm);}#elseif(g_hwiHandlerForm[hwiIndex]!=0){⑷g_hwiHandlerForm[hwiIndex]();}#endif⑸HalAftInterruptHandler(hwiIndex);OsHookCall(LOS_HOOK_TYPE_ISR_EXIT,hwiIndex);intSave=LOS_IntLock();--g_intCountOS(intSave);}3.开关中断最后分享一下开启和关闭中断的相关知识。启用和禁用中断分别是指:启用中断以执行特定的短命程序,启用中断以响应中断。关闭中断为了保护执行的程序不被中断,关闭相应的外部中断。打开和关闭中断对应的函数定义在文件kernel\arch\include\los_context.h中,代码如下。⑴UINT32LOS_IntLock(VOID)将关闭中断并暂停响应中断。(2)函数VOIDLOS_IntRestore(UINT32intSave)可以用来恢复被UINT32LOS_IntLock(VOID)函数禁用的中断,UINT32LOS_IntLock(VOID)的返回值作为VOIDLOS_IntRestore(UINT32intSave)的参数恢复中断。⑶处函数UINT32LOS_IntUnLock(VOID)使能中断,可以响应中断。UINTPTRHalIntLock(VOID);defineLOS_IntLockHalIntLockVOIDHalIntRestore(UINTPTRintSave);defineLOS_IntRestoreHalIntRestoreUINTPTRHalIntUnLock(VOID);defineLOS_IntUnLockHalIntUnLock可以看出,LOS_IntLock、LOS_IntRestore和LOS_IntUnLock是定义的宏,他们对应定义在文件kernel\arch\arm\cortex-m7\gcc\los_dispatch.S中的汇编函数源码如下。我们来分析一下这些汇编函数。寄存器PRIMASK是一个单位寄存器。设置为1时,关闭所有可屏蔽异常,只能响应NMI和HardFault异常。默认值为0,表示不关闭中断。汇编指令CPSIDI将设置PRIMASK=1以禁用中断,指令CPSIEI将设置PRIMASK=0以启用中断。(1)HalIntLock函数将寄存器PRIMASK的值写入寄存器R0并返回,执行CPSIDI禁止中断。(2)HalIntUnLock函数将寄存器PRIMASK的值写入寄存器R0并返回,执行指令CPSIEI使能中断。两个函数的返回结果可以传递给⑶处的HalIntRestore函数,将寄存器状态值写入寄存器PRIMASK,用于恢复之前的中断状态。HalIntLock和HalIntUnLock都可以与ArchIntRestore配对。.typeHalIntLock,%function.globalHalIntLockHalIntLock:.fnstart.cantunwind⑴MRSR0,PRIMASKCPSIDIBXLR.fnend.typeHalIntUnLock,%function.globalHalIntUnLockHalIntUnLock:.fnstart.cantunwind⑵MRSR0,PRIMASKCPSIEIBXLR.fnend.typeHalIntRestore,%function.globalHalIntRestoreHalIntRestore:.fnstart.cantunwind⑶MSRPRIMASK,R0BXLR.fnend小结本文他带领大家分析了鸿蒙LightKernel的中断模块源码,掌握了中断的概念、中断初始化操作、中断创建、删除、切换中断操作等。更多内容请访问:Harmonyos.51cto.com,与华为官方合作搭建的鸿蒙技术社区

最新推荐
猜你喜欢