中断控制器
1.
路径:\OpenharmonyFor6ull\kernel\liteos_a\platform\main.c
LITE_OS_SEC_TEXT_INIT INT32 main(VOID)
{
UINT32 uwRet = LOS_OK;
OsSetMainTask();
OsCurrTaskSet(OsGetMainTask());
/* set smp system counter freq */
#if (LOSCFG_KERNEL_SMP == YES)
#ifndef LOSCFG_TEE_ENABLE
HalClockFreqWrite(OS_SYS_CLOCK);
#endif
#endif
/* system and chip info */
OsSystemInfo();//涉及中断控制器
PRINT_RELEASE("\nmain core booting up...\n");
其中,进入'HalIrqVersion'
CHAR *HalIrqVersion(VOID)
{
//#define GICD_PIDR2V2 (GICD_OFFSET + 0xfe8)
//#define GIC_REG_32(reg) (*(volatile UINT32 *)((UINTPTR)(GIC_BASE_ADDR + (reg))))
//#define GIC_BASE_ADDR IO_DEVICE_ADDR(0x10300000) 物理地址转换为虚拟地址,需要把这个物理地址与目前芯片进行对应
//#define GICD_OFFSET 0x1000 /* interrupt distributor offset */
UINT32 pidr = GIC_REG_32(GICD_PIDR2V2);
CHAR *irqVerString = NULL;
switch (pidr >> GIC_REV_OFFSET) {
case GICV1:
irqVerString = "GICv1";
break;
case GICV2:
irqVerString = "GICv2";
break;
default:
irqVerString = "unknown";
}
return irqVerString;
}
那么宏定义原型为:《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\include\gic_common.h》
#define GIC_REG_32(reg) (*(volatile UINT32 *)((UINTPTR)(GIC_BASE_ADDR + (reg))))
这里GIC的基地址需要修改:《OpenharmonyFor6ull\vendor\nxp\imx6ull\board\include\asm\platform.h》
#define GIC_BASE_ADDR IO_DEVICE_ADDR(0x00a00000) // IO_DEVICE_ADDR(0x10300000)
2.异常向量表和中断初始化/调用过程
如何指定中断处理函数?
《OpenharmonyFor6ull\drivers\hdf\lite\adapter\osal\src\osal_irq.c》
int32_t OsalRegisterIrq(uint32_t irqId, uint32_t config, OsalIRQHandle handle, const char *name, void *dev)
分析中断处理过程:以触摸屏为例,《OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\touch\touch_gt9xx.c》
(1)中断初始化函数:注册中断号和处理函数
void gt9xx_irq_init(TouchCoreData *cd)
{
...
/* IrqHandle */
//中断号:cd->intGpioNum
//配置:0
//中断处理函数:IrqHandle
//中断名称:"gt9xx_irq"
//驱动:NULL
ret = OsalRegisterIrq(cd->intGpioNum, 0, IrqHandle, "gt9xx_irq", NULL);
...
}
(2)中断号的确定:
//note:0#中断对应数字32
// GPIO1的pin0~pin15即66#中断对应数字(66+32)
#define GT9XX_INT_NUM (66 + 32)
static void TouchConfigInit(TouchCoreData *cd)
{
/* init waitqueue for poll */
__init_waitqueue_head(&cd->pollWait);
/* config i2c and input */
cd->i2cClient.i2cCfg.addr = DRIVER_CHIP_I2C_ADDR;
cd->i2cClient.i2cCfg.busNum = I2C_BUS_NUM;
cd->inputCfg.solutionX = TOUCH_SOLUTION_X;
cd->inputCfg.solutionY = TOUCH_SOLUTION_Y;
/* config device type and module info */
cd->inputDevType = INDEV_TYPE_TOUCH;
(void)strncpy_s(cd->chipInfo, CHIP_INFO_LEN, "HZ1145130", strlen("HZ1145130"));
(void)strncpy_s(cd->vendorName, VENDOR_NAME_LEN, "ZG1695", strlen("TG1695"));
(void)strncpy_s(cd->chipName, CHIP_NAME_LEN, "GT9xx", strlen("GT9xx"));
/* pin num and irq trigger config info */
//cd->rstGpioNum = RST_GPIO_GROUP * GPIO_GROUP_SIZE + RST_GPIO_OFFSET;
//中断号的确定
cd->intGpioNum = GT9XX_INT_NUM;
cd->irqFlag = OSAL_IRQF_TRIGGER_FALLING;
cd->shouldStop = false;
}
(3)中断处理函数的确定:
《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_hwi.c》
STATIC UINT32 OsHwiCreateShared(HWI_HANDLE_T hwiNum, HWI_MODE_T hwiMode,
HWI_PROC_FUNC hwiHandler, const HwiIrqParam *irqParam)
{
UINT32 intSave;
HwiHandleForm *hwiFormNode = NULL;
HwiHandleForm *hwiForm = NULL;
HwiIrqParam *hwiParam = NULL;
HWI_MODE_T modeResult = hwiMode & IRQF_SHARED;
//g_hwiForm数组中序号为hwiNum的变量取出来
hwiForm = &g_hwiForm[hwiNum];
...
hwiFormNode->pfnHook = hwiHandler;//把中断处理函数放入对应变量
hwiFormNode->pstNext = (struct tagHwiHandleForm *)NULL;
hwiForm->pstNext = hwiFormNode;
if ((irqParam != NULL) && (irqParam->pName != NULL)) {
g_hwiFormName[hwiNum] = (CHAR *)irqParam->pName;
}
g_hwiForm[hwiNum].uwParam = modeResult;
HWI_UNLOCK(intSave);
return LOS_OK;
}
其中,HwiHandleForm结构体原型如下:
typedef struct tagHwiHandleForm {
HWI_PROC_FUNC pfnHook;//中断处理函数
HWI_ARG_T uwParam;
struct tagHwiHandleForm *pstNext;//下一个元素的位置
} HwiHandleForm;
(4)中断执行流程:
step1:中断向量表
b OsIrqHandler
step2:《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_dispatch.S》
OsIrqHandler:
SUB LR, LR, #4
/* push r0-r3 to irq stack */
STMFD SP, {R0-R3}
SUB R0, SP, #(4 * 4)
MRS R1, SPSR
MOV R2, LR
/* disable irq, switch to svc mode */
CPSID i, #0x13
/* push spsr and pc in svc stack */
STMFD SP!, {R1, R2}
STMFD SP, {LR}
AND R3, R1, #CPSR_MASK_MODE
CMP R3, #CPSR_USER_MODE
BNE OsIrqFromKernel
/* push user sp, lr in svc stack */
STMFD SP, {R13, R14}^
OsIrqFromKernel:
/* from svc not need save sp and lr */
SUB SP, SP, #(2 * 4)
/* pop r0-r3 form irq stack*/
LDMFD R0, {R0-R3}
/* push caller saved regs as trashed regs in svc stack */
STMFD SP!, {R0-R3, R12}
/* 8 bytes stack align */
SUB SP, SP, #4
/* 保存FPU寄存器的值
* save fpu regs in case in case those been
* altered in interrupt handlers.
*/
PUSH_FPU_REGS R0
#ifdef LOSCFG_IRQ_USE_STANDALONE_STACK
PUSH {R4}
MOV R4, SP
EXC_SP_SET __svc_stack_top, OS_EXC_SVC_STACK_SIZE, R1, R2
#endif
//
//调用函数
//
BLX HalIrqHandler
step3:《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\interrupt\gic\gic_v2.c》
VOID HalIrqHandler(VOID)
{
//读取GIC控制器寄存器,判断中断向量编号
UINT32 iar = GIC_REG_32(GICC_IAR);
UINT32 vector = iar & 0x3FFU;
/*
* invalid irq number, mainly the spurious interrupts 0x3ff,
* gicv2 valid irq ranges from 0~1019, we use OS_HWI_MAX_NUM
* to do the checking.
*/
if (vector >= OS_HWI_MAX_NUM) {
return;
}
g_curIrqNum = vector;
//处理向量号对应的中断
OsInterrupt(vector);
/* use orignal iar to do the EOI */
GIC_REG_32(GICC_EOIR) = iar;
}
《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_hwi.c》
VOID OsInterrupt(UINT32 intNum)
{
HwiHandleForm *hwiForm = NULL;
UINT32 *intCnt = NULL;
...
hwiForm = (&g_hwiForm[intNum]);
...
//通过调用对应的结构体函数指针执行中断服务函数
HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->pfnHook;
...
}
这里就执行了中断处理函数
3.tick中断
CPU core内部自带了timer,可以用来实现tick中断(10ms周期)
(1)
《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c》
LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
UINT32 ret;
//读取时钟频率->
//return READ_TIMER_REG32(TIMER_REG_CNTFRQ); ---------调用-------->
//#define READ_TIMER_REG32(reg) ARM_SYSREG_READ(reg) --调用->
//__asm__ volatile("mrc " REG : "=r" (_val)); \直接读取协处理器的寄存器值即可得到
g_sysClock = HalClockFreqRead();
//OS的系统滴答的中断向量号:OS_TICK_INT_NUM
//OS的系统滴答的中断优先级:MIN_INTERRUPT_PRIORITY
//OS的系统滴答的中断处理函数:OsTickEntry
ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickEntry, 0);
if (ret != LOS_OK) {
PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
}
}
(2)tick中断向量号OS_TICK_INT_NUM的值
《OpenharmonyFor6ull\vendor\nxp\imx6ull\board\include\asm\hal_platform_ints.h》
#define NUM_HAL_INTERRUPT_CNTPSIRQ 29
#define NUM_HAL_INTERRUPT_CNTPNSIRQ 30
#define OS_TICK_INT_NUM NUM_HAL_INTERRUPT_CNTPSIRQ // use secure physical timer for now
(3)滴答中断的处理函数
《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c》
LITE_OS_SEC_TEXT VOID OsTickEntry(VOID)
{
//timer暂停计数
TimerCtlWrite(0);
//中断处理函数
OsTickHandler();
/*
* use last cval to generate the next tick's timing is
* absolute and accurate. DO NOT use tval to drive the
* generic time in which case tick will be slower.
*/
TimerCvalWrite(TimerCvalRead() + OS_CYCLE_PER_TICK);
//timer继续计数
TimerCtlWrite(1);
}
(4)继续追踪中断函数
《OpenharmonyFor6ull\kernel\liteos_m\kernel\base\core\los_tick.c》
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
...
//操作系统任务扫描,检查当前进程的剩余时间是否用完,若未用完则继续,用完则切换出去
OsTaskScan(); // task timeout scan
...
}
《OpenharmonyFor6ull\kernel\liteos_a\kernel\base\core\los_task.c》
LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
{
SortLinkList *sortList = NULL;
LosTaskCB *taskCB = NULL;
BOOL needSchedule = FALSE;
UINT16 tempStatus;
LOS_DL_LIST *listObject = NULL;
SortLinkAttribute *taskSortLink = NULL;
taskSortLink = &OsPercpuGet()->taskSortLink;
taskSortLink->cursor = (taskSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
listObject = taskSortLink->sortLink + taskSortLink->cursor;
/* 任务超时之后,任务块....看不懂,韦老师也没讲...
* When task is pended with timeout, the task block is on the timeout sortlink
* (per cpu) and ipc(mutex,sem and etc.)'s block at the same time, it can be waken
* up by either timeout or corresponding ipc it's waiting.
*
* Now synchronize sortlink preocedure is used, therefore the whole task scan needs
* to be protected, preventing another core from doing sortlink deletion at same time.
*/
LOS_SpinLock(&g_taskSpin);
...