标题:
NXP的ARM7带ucos中硬中断与软中断响应详细分析
[打印本页]
作者:
yuyang911220
时间:
2017-2-20 16:50
标题:
NXP的ARM7带ucos中硬中断与软中断响应详细分析
一.带UCOS系统的软中断响应过程 1
1.第一步: 2
2.第二步: 2
二.带UCOS系统的硬中断响应过程 6
下面的主要分析LPC系列ARM7的中断响应,以周立公老师带ucos移植程序为分析对象,对其他的ARM中带UCOS的项目也有参考价值。
一.带UCOS系统的软中断响应过程
UCOS操作系统是以任务为单元的执行块,可以理解为linux中线程,任务需要进行切换,轮巡的执行,这种任务切换是通过ARM的软中断来实现的。ARM的软中断是在向量表中就存在,写在startup.s文件中,如:
;interrupt vectors
;中断向量表
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
这个向量表是ARM具备的,放在程序的开始位置,具体怎么放置和定位,要在编译时候设置分散定位表(这里不仔细讲),ARM复位启动的时候就会自动跑到开始行PC, ResetAddr。第三行LDR PC, SWI_Addr为软中断响应行。附带说明,这个向量表程序在编译后的地址(程序在运行时可以看到汇编程序显示的每行地址,也就是常说的中断向量表中所有数据32位累加和为0)累加和为0,所以可以看到程序中为什么有DCD 0xb9205f80这行,数字是手动可以调整的。
软中断和硬中断如果从程序角度分析,其实是一样的,都是响应的一种中断,响应开始要求环境和其他变量入桟保护起来,然后调用到中断响应的C程序中(就是自己写中断响应程序),执行完中断响应程序后,再进行堆栈的出桟,恢复之前保护的环境与变量,再掉转程序到开始进入中断前的位置,这样程序接着运行。其实这个过程,跟51一样,软中断与硬中断也一样。那软中断和硬中断到底有什么不同,软中断是可以人为控制的,硬中断是ARM芯片里面控制的,自动反应,如串口中断,是串口收到数据后,串口自动产生中断。软中断是通过手动调用程序后产生的中断。以我的理解,不知道是否还有其他区别。
现在我们具体可以分析软中断响应过程了:
1.第一步:
在UCOS中,当要进行任务切换,直接手动调用OS_TASK_SW()函数的调用,调用之后,相当执行了os_cpu.h文件中的
__swi(0x00) void OS_TASK_SW(void); /* 任务级任务切换函数 */
这是ADS编译器可以认识的软中断执行程序。这样系统会响应软中断,程序会跳到上面讲过的startup.s文件中断向量表中LDR PC, SWI_Addr,
2.第二步:
在startup.s文件中有SWI_Addr DCD SoftwareInterrupt,相当再跳到SoftwareInterrupt函数,这个函数在OS_cpu_a.s文件中:
;软件中断
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向参数存储位置
MRS R3, SPSR
TST R3, #T_bit ; 中断前是否是Thumb状态
LDRNEH R0, [LR,#-2] ; 是: 取得Thumb状态SWI号
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 取得arm状态SWI号
BICEQ R0, R0, #0xFF000000
; r0 = SWI号,R1指向参数存储位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw
LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换
BL SWI_Exception
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
这个函数,是自己写的,可以进行改动。这其中就是上面说过的环境的一些入桟,出桟,还有查询是否有更高优先级任务的响应。软中断响应时因为有不同的中断,如关OS_ENTER_CRITICAL,开OS_EXIT_CRITICAL等很多软中断,所以程序执行上面中的SWI_Exception执行自己的外部中断C响应程序,里面实现具体的软中断:
void SWI_Exception(int SWI_Num, int *Regs)
{
OS_TCB *ptcb;
switch(SWI_Num)
{
//case 0x00: /* 任务切换函数OS_TASK_SW,参考os_cpu_s.s文件 */
// break;
//case 0x01: /* 启动任务函数OSStartHighRdy,参考os_cpu_s.s文件 */
// break;
case 0x02: /* 关中断函数OS_ENTER_CRITICAL(),参考os_cpu.h文件 */
__asm
{
MRS R0, SPSR
ORR R0, R0, #NoInt
MSR SPSR_c, R0
}
OsEnterSum++;
break;
case 0x03: /* 开中断函数OS_EXIT_CRITICAL(),参考os_cpu.h文件 */
if (--OsEnterSum == 0)
{
__asm
{
MRS R0, SPSR
BIC R0, R0, #NoInt
MSR SPSR_c, R0
}
}
break;
#if OS_SELF_EN > 0
case 0x40:
/* 返回指定系统服务函数的地址 */
/* 函数地址存于数组_OSFunctionAddr中*/
/* 数组_OSFunctionAddr需要另外定义 */
/* Regs[0] 为第一个参数,也是返回值 */
/* Regs[1] 为第二个参数 */
/* Regs[2] 为第三个参数 */
/* Regs[3] 为第四个参数 */
/* 仅有一个参数为系统服务函数的索引 */
Regs[0] = _OSFunctionAddr[Regs[0]];
break;
case 0x41:
/* 返回指定用户的服务函数的地址 */
/* 函数地址存于数组_UsrFunctionAddr中*/
/* 数组_UsrFunctionAddr需要另外定义 */
/* Regs[0] 为第一个参数,也是返回值 */
/* Regs[1] 为第二个参数 */
/* Regs[2] 为第三个参数 */
/* Regs[3] 为第四个参数 */
/* 仅有一个参数为用户服务函数的索引 */
Regs[0] = _UsrFunctionAddr[Regs[0]];
break;
case 0x42: /* 中断开始处理 */
OSIntNesting++;
break;
case 0x43: /* 判断中断是否需要切换 */
if (OSTCBHighRdy == OSTCBCur)
{
Regs[0] = 0;
}
else
{
Regs[0] = 1;
}
break;
#endif
case 0x80: /* 任务切换到系统模式 */
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #SYS32Mode
MSR SPSR_c, R0
}
break;
case 0x81: /* 任务切换到用户模式 */
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #USR32Mode
MSR SPSR_c, R0
}
break;
case 0x82: /* 任务是ARM代码 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);
}
}
break;
case 0x83: /* 任务是THUMB代码 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] |= (1 << 5);
}
}
break;
default:
break;
}
}
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0