3.4 硬件中断
请参考3.5.3节了解软件中断所使用的系统栈,3.6.3节获取task栈尺寸相关信息。
当定义多个Hook集时,公共类型的单个hook函数将以hook ID的顺序调用。
将其中所其中所有代码删除,并粘贴入如下代码:
硬件中断(Hwis)是应用为了回应外部异步事件所必须处理的关键进程。SYS/BIOS中特定target/device的Hwi模块用于管理硬件中断。请阅读概述,请参考
document introducing Hwis。
在典型的嵌入式系统中,中断由装置周边设备或由外部设备传给处理器。两种情况都使中断由处理器导向ISR地址。任何影响Swi和Task调度的SYS/BIOS APIs中断处理都必须使用C或C++编写。早期SYS/BIOS版本所为调用汇编语言所使用的HWI_enter()/HWI_exit宏都不再提供。
跟SYS/BIOS没有互动的汇编ISRs可由Hwi_plug()指定。这类ISRs必须做自己上下文的相关内容。它们可以使用中断关键字、C函数或汇编语言函数。
所有硬件中断都会运行至结束,如果Hwi在它的ISR有机会运行前提交了多次,那么ISR只会运行一次。为此,应当将执行Hwi中断的函数代码最小化。
如果要让中断为全局可用,可以调用Hwi_enable(),ISR可被任意eanbled过的中断抢占。
Hwis不能为指定target使用芯片支持库(CSL)。作为替代,可参考第8章硬件抽象层APIs的相关内容。
可创建一个Hwi对象来联合使用ISR函数和指定中断。
3.4.1 创建Hwi对象
Hwi模块维护了一张指向Hwi对象的指针表,它包含了调度器产生的每个Hwi的管理信息
(或调度器不提供的,由平台产生的中断存根)
。使用以下方式动态创建一个Hwi对象:
Hwi_Handle hwi0; Hwi_Params hwiParams; Error_Block eb; Error_init(&eb); Hwi_Params_init(&hwiParams); hwiParams.arg = 5; hwi0 = Hwi_create(id, hwiFunc, &hwiParams, &eb); if (hwi0 == NULL) { System_abort("Hwi create failed"); } |
这里,hwi0是创建Hwi object的句柄,id是定义的中断号,hwiFunc是和Hwi关联的函数名称,hwiParams是包含Hwi实例参数的一个结构体(enable/restore masks,Hwi函数参数等等)。这里,hwiParams.arg设置为5。如果传递的是Null,而不是指向实际的Hwi_Params结构体指针,由会使用默认参数集合。“eb”是一个错误块,它可以让你在创建Hwi对象期间处理错误。
相应的静态创建Hwi对象的语法如下:
var Hwi = xdc.useModule('ti.sysbios.hal.Hwi'); var hwiParams = new Hwi.Params; hwiParams.arg = 5; Program.global.hwi0 = Hwi.create(id, '&hwiFunc', hwiParams); |
"hwiParams = new Hwi.Params" 声明相当于创建并初始化了一个使用默认值的hwiParams结构体。在静态配置的情况下,“create”函数无需错误块。对于静态创建的Hwi对象来说,“Program.global.hwi0”成为运行时访问句柄(符号name=“hwi0”)。
3.4.2 硬件中断嵌套和系统栈尺寸
当Hwi运行时,它的函数调用使用到了系统栈。最坏情况下,每个Hwi都获得一个嵌套的调度函数(也就是最低优先级Hwi被下个最高优先级Hwi抢占,转而又被下一个最高优先级抢占……)。这导致了每个Hwi优先级实际使用的栈尺寸增加。
默认的系统栈尺寸是4096字节。你可以在配置脚本中增加以下语句来设置系统栈尺寸:
Program.stack = yourStackSize;
|
下表显示了Hwi中断最坏情况下,系统栈所需要的尺寸。第一个数字是某target在首个优先级中所需的系统栈空间大小。第二个数字显示的是应用中每个之后的优先级所需的栈空间大小。
![基于TI-RTOS的CC2650DK开发(6)---硬件中断 - 阿巴睇 - 阿巴睇的博客](http://img2.ph.126.net/wdNGalxLOSptEsFxTHOkBw==/6631720875609160892.jpg)
3.4.3 Hwi Hooks
Hwi模块支持以下Hook函数:
- Register:在运行时,任何Hwis静态创建前需要调用的初始化函数。register hook在main()和中断使能前的启动时间调用 。
- Create:Hwi创建时调用的函数。包括Hwis静态创建时以及使用Hwi_create()进行动态创建时。
- Begin:在运行Hwi ISR函数之前调用的函数。
- End:在Hwi ISR函数结束后调用的函数。
- Delete:在运行时,使用Hwi_delete()删除Hwi时调用的函数。
以下HookSet结构体类型定义是Hwi模块所支持的hook函数封装:
typedef struct Hwi_HookSet { Void (*registerFxn)(Int); /* Register Hook */ Void (*createFxn)(Handle, Error.Block *); /* Create Hook */ Void (*beginFxn)(Handle); /* Begin Hook */ Void (*endFxn)(Handle); /* End Hook */ Void (*deleteFxn)(Handle); /* Delete Hook */ }; |
Hwi Hook函数仅可以静态配置。
3.4.3.1 Register函数
register函数用于允许一个hook集合存放它们对应的hook ID。此ID可传递给Hwi_setHookContext() 和Hwi_getHookContext()用来设置或获取特定hook的Context。如果hook的执行需要使用到Hwi_setHookContext()或Hwi_getHookContext()
register函数,则必须指定
register函数。
在中断使能之前的系统初始化期间,会调用registerFxn hook函数。
register函数语法如下:
Void registerFxn(Int id);
|
3.4.3.2 Create和Delete函数
无论何时,创建和删除Hwi都需要调用Create和Delete函数。Create函数需要传递一个Error_Block,它需要传递给应用程序的Memory_alloc(),这会增加额外的上下文存储空间。
createFxn和deleteFxn函数在中断使能时调用(除了在启动或main()时)。
函数语法如下:
Void createFxn(Hwi_Handle hwi, Error_Block *eb); Void deleteFxn(Hwi_Handle hwi); |
3.4.3.4 Hwi Hooks 示例
以下例子使用了两个Hwi hook集。Hwi关联了一个静态创建的计时器,用于Hwi hook函数。此例演示了如何读写每个hook集所关联的Hook Context Pointer。
配置脚要和程序输出在C代码后面列出。
这是未例的C代码:
(书上给的代码是有错误的,害我又浪费了几天时间,真是一路坎坷。还好,这个过程其实也是一个学习的过程。下面就用自己的语言写出创建项目的详细过程吧。)
1、首先按照前面日志所述方法新创建一个CCS的Empty项目。
2、打开empty.c文件,删除里面所有代码,把下面代码粘贴进去
/* ======== HwiHookExample.c ======== * This example demonstrates basic Hwi hook usage. */ #include <xdc/std.h> #include <xdc/runtime/Error.h> #include <xdc/runtime/System.h> #include <xdc/runtime/Timestamp.h> #include <ti/sysbios/BIOS.h> #include <ti/sysbios/knl/Task.h> #include <ti/sysbios/hal/Timer.h> #include <ti/sysbios/hal/Hwi.h> extern Timer_Handle myTimer; volatile Bool myEnd2Flag = FALSE; Int myHookSetId1, myHookSetId2; // HookSet 1 注册函数, 在Hwi模块启动时,先于main()调用*/ Void myRegister1(Int hookSetId) { System_printf( "myRegister1: assigned hookSet Id = %d\n", hookSetId); myHookSetId1 = hookSetId; } // 在Hwi模块启动,静态创建Hwis时调用,先于Main() Void myCreate1(Hwi_Handle hwi, Error_Block *eb) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); /* pEnv此处应为0,否则就是一个bug. */ System_printf( "myCreate1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xdead1); } //先于Hwi计时器函数前调用 Void myBegin1(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); System_printf( "myBegin1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xbeef1); } //Hwi计时器函数结束后调用 Void myEnd1(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); System_printf( "myEnd1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xc0de1); } // HookSet 2 注册函数, 在Hwi模块启动时,先于main()调用*/ Void myRegister2(Int hookSetId) { System_printf( "myRegister2: assigned hookSet Id = %d\n", hookSetId); myHookSetId2 = hookSetId; } // 在Hwi模块启动,静态创建Hwis时调用,先于Main() Void myCreate2(Hwi_Handle hwi, Error_Block *eb) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); /* pEnv此处应为0,否则就是一个bug. */ System_printf( "myCreate2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xdead2); } //先于Hwi计时器函数前调用 Void myBegin2(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); System_printf( "myBegin2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xbeef2); } //Hwi计时器函数结束后调用 Void myEnd2(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); System_printf( "myEnd2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xc0de2); myEnd2Flag = TRUE; } //中断计时器回调函数 Void myTimerFunc(UArg arg) { System_printf( "Entering myTimerHwi\n"); } //task回调函数 Void myTaskFunc(UArg arg0, UArg arg1) { System_printf( "Entering myTask.\n"); Timer_start(myTimer); /* wait for timer interrupt and myEnd2 to complete */ while (!myEnd2Flag) { ; } System_printf( "myTask exiting ...\n"); } //Idle回调函数 Void myIdleFunc() { System_printf( "Entering myIdleFunc().\n"); System_exit( 0); } //主函数 Int main(Int argc, Char* argv[]) { System_printf( "Starting HwiHookExample...\n"); BIOS_start(); return ( 0); } |
3、打开empty.cfg文件,单击窗体下方【cfg Script】标签进入到配置脚本文件(如下图所示红框处):
![基于TI-RTOS的CC2650DK开发(6)---硬件中断 - 阿巴睇 - 阿巴睇的博客](http://img2.ph.126.net/62lINlbhmhCKyBQO9Zk4HQ==/6631957270606121660.jpg)
/* pull in Timestamp to print time in hook functions */
xdc.useModule( 'xdc.runtime.Timestamp'); /* Disable Clock so that ours is the only Timer allocated */ var BIOS = xdc.useModule( 'ti.sysbios.BIOS'); BIOS.clockEnabled = false; var Idle = xdc.useModule( 'ti.sysbios.knl.Idle'); Idle.addFunc( '&myIdleFunc'); /* Create myTask with default task params */ var Task = xdc.useModule( 'ti.sysbios.knl.Task'); var taskParams = new Task.Params(); Program.global.myTask = Task.create( '&myTaskFunc', taskParams); /* Create myTimer as source of Hwi */ var Timer = xdc.useModule( 'ti.sysbios.hal.Timer'); var timerParams = new Timer.Params(); timerParams.startMode = Timer.StartMode_USER; timerParams.runMode = Timer.RunMode_ONESHOT; timerParams.period = 1000; // 1ms Program.global.myTimer = Timer.create(Timer.ANY, "&myTimerFunc", timerParams); /* Define and add two Hwi HookSets * Notice, no deleteFxn is provided. */ var Hwi = xdc.useModule( 'ti.sysbios.hal.Hwi'); /* Hook Set 1 */ Hwi.addHookSet({ registerFxn: '&myRegister1', createFxn: '&myCreate1', beginFxn: '&myBegin1', endFxn: '&myEnd1', }); /* Hook Set 2 */ Hwi.addHookSet({ registerFxn: '&myRegister2', createFxn: '&myCreate2', beginFxn: '&myBegin2', endFxn: '&myEnd2', }); |
4、编译并将程序烧进开发板,完成后单击工具栏的绿色三角按钮(Resume(F8))。在控制台显示下列运行结果:
[Cortex_M3_0] myRegister1: assigned hookSet Id =
0
myRegister2: assigned hookSet Id = 1 myCreate1: pEnv = 0x0, time = 0 myCreate2: pEnv = 0x0, time = 0 Starting HwiHookExample... Entering myTask. myBegin1: pEnv = 0xdead1, time = 74 myBegin2: pEnv = 0xdead2, time = 84 Entering myTimerHwi myEnd1: pEnv = 0xbeef1, time = 98 myEnd2: pEnv = 0xbeef2, time = 108 myTask exiting ... Entering myIdleFunc(). |
下面来解释这个程序:
Hook的主要作用可以认为是用于监测某一事件的发生,比如为某Hwi注册一个Hook,当此Hwi发生中断前,会触发Hook的begin回调函数;当中断执行完毕后,会触发Hook的end回调函数。这时你就可以在begin和end回调函数内加入你想要监测的代码了。
这个程序演示了如何为一个Hwi注册Hook,更为重要的是演示了Hwi,Task,Idle和Hook所属回调函数在何时会被调用,调用顺序如何。下面用一张图来展示这个顺序。