基于TI-RTOS的CC2650DK开发(6)---硬件中断

3.4  硬件中断
硬件中断(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)---硬件中断 - 阿巴睇 - 阿巴睇的博客
 请参考3.5.3节了解软件中断所使用的系统栈,3.6.3节获取task栈尺寸相关信息。

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);
当定义多个Hook集时,公共类型的单个hook函数将以hook ID的顺序调用。

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)---硬件中断 - 阿巴睇 - 阿巴睇的博客
 将其中所其中所有代码删除,并粘贴入如下代码:
/* 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所属回调函数在何时会被调用,调用顺序如何。下面用一张图来展示这个顺序。

基于TI-RTOS的CC2650DK开发(6)---硬件中断 - 阿巴睇 - 阿巴睇的博客

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值