基于TI-RTOS的CC2650DK开发(7)---软件中断

3.5 软件中断
软件中断仿照I硬件ISRs。SYS/BIOS的Swi模块提供了软件中断的能力。软件中断通过调用SYS/BIOS的API(诸如Swi_post())来以程序的方式触发软件中断。软件中断的优先级高于tasks,但低于硬件中断。请参考 video introducing Swis进行一个概览。

注意:不应将Swi模块跟现存于很多处理器的SWI指令混淆。SYS/BIOS Swi 模块独立于任意指定目标/设备的软件中断功能。

软件中断适合于处理那些相对于Hwis触发速率更慢或者实时期限不太严格的应用任务。
可通过以下SYS/BIOS APIs来触发或提交一个Swi:
  • Swi_andn()
  • Swi_dec()
  • Swi_inc()
  • Swi_or()
  • Swi_post()
Swi管理器控制着所有Swi函数的执行。当程序调用以上某个APIs时,Swi管理器安排指定Swi所对应的函数运行。Swi管理器通过使用Swi对象来处理Swi函数。

如果提交了一个Swi,它只会在所有等待中的Hwis都完成之后才会运行。Swi函数在运行过程中可在任意时间被Hwi所抢占;Hwi会在Swi函数重新执行之前完成。另一方面,Swi函数总是抢占tasks。所有等待中的Swi都会在甚至是最高优先级的task被允许执行前运行。实际上,Swi更象比于所有普通tasks拥有更高优先权的task。

注意:谨记有关Swi函数的两件事:
一个Swi会运行至结束,除非它被Hwi中断或更高优先级的Swi抢占。
任何触发或提交Swi的硬件ISR都必须已经被Hwi调度器调用(对于不支持Hwi调度器的平台,由中断存根产生,如MSP430)。这意味着,Swi必须由Hwi对象所调用的函数触发。

3.5.1 创建Swi对象
和很多其它SYS/BIOS对象一样,你即可以通过调用Swi_create()动态地创建Swi对象,也可以在配置文件中静态创建Swi对象。动态创建的Swis在程序运行期间也可以被删除。

为在配置中添加一个新的Swi,可在配置脚本中创建一个新的Swi对象。为每个Swi配置function属性,当应用程序触发对象时,即可运行对应的函数。也可以为每个Swi函数配置两个参数。

对于所有带实例的模块,你可以决定Swi对象在哪个内存段分配。Swis被提交或安排执行时,由Swi管理器访问Swi对象。
关于完整的Swi API、配置和对象参考信息,请参照在线文档“ti.sysbios.knl”包的Swi模块。(运行在线帮助的相关信息,请参考1.6.1节,23页---Using the API Reference Help System)

动态创建一个Swi对象,使用如下语法进行调用:
Swi_Handle swi0;
Swi_Params swiParams;
Error_Block eb;
Error_init(&eb);
Swi_Params_init(&swiParams);
swi0 = Swi_create(swiFunc, &swiParams, &eb);
if (swi0 ==  NULL)
{
    System_abort( "Swi create failed");
}
其中,swi0是所创建的Swi对象句柄,swiFunc是Swi所关联函数的名称,swiParams是Swi_Params结构体,它包含了Swi实例参数(priority,arg0,arg1等等)。如果传递的是NULL而不是实际的Swi_Params结构体指针,则使用默认的参数集。“eb”是一个错误块,你可以通过它处理Swi对象创建期间所发生的错误。

注意:Swi_create()不能在Hwi或其它Swi线程上下文中调用。应用程序要动态创建Swi线程,必须从main()函数或Task线程上下文中创建。

在配置文件中创建Swi对象,使用如下语法:
var Swi = xdc.useModule( 'ti.sysbios.knl.Swi');
var swiParams =  new Swi.Params();
program.global.swi0 = Swi.create(swiParams);

3.5.2 设置软件中断优先权
Swi的优先级分为不同级别。你可在内存限制所允许的范围内为每个优先级创建尽可能多的Swis。你可以为拥有更短实时期限线程的Swi选择一个更高的优先级(更大的优先级序号),也可以为Swi分配一个更低的优先级,用于处理那些拥有不太关键执行期限的线程。

应用程序中Swi优先级分为32个等级。MSP430和C28x的优先级分为16个等级。默认优先级为16。最低优先级为0,因此最高优先级为15。
你无法对同一优先级的Swis进行排序,它们根据提交顺序依次运行。

3.5.3 软件中断优先权和系统栈尺寸
当提交了一个Swi,将会使用系统栈调用它相关的Swi函数。当你为某些目标设置了所有32个级别的Swi优先级,请记住在最坏的情况下,每个Swi优先级都可能变成一个嵌套的Swi调度函数(也就是说,最低优先级Swi被下一优先级抢占,转而下一优先级又被更高一级优先级抢占……)。这将增加每个在用的Swi优先级增加其栈尺寸需求。因此,就栈使用尺寸而言,给Swi分配相同优先级比分配不同优先级更具效率。

默认系统栈尺寸是4096字节。你可以在配置脚本中使用如下代码来设置系统栈尺寸:
Program.stack = yourStackSize;

注意:Clock模块创建并使用最大优先级的Swi(也就是说,如果有16个Swi优先级,Clock Swi使用序号为15的优先级)。

下表显示了在最坏Swi中断嵌套情况下所需要的系统栈尺寸。第一列数字是某目标的首个优先级系统栈尺寸。第二列数字显示的是每个之后的优先级所使用的系统栈的尺寸。
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 查看3.4.2节获知Hwis的系统栈使用信息,查看3.6.3节获知task栈尺寸信息。

3.5.4 软件中断的运行
Swis可通过调用Swi_andn()、Swi_dec()、Swi_inc()、Swi_or()和Swi_post()来安排运行。这些调用可在程序的决大部分地方调用---Hwi函数、Clock函数、Idle函数或其他Swi函数。

当Swi被提交,Swi管理器将其添加进Swis posted列表等待执行。Swi管理器会检查当前是否可以运行Swis。如果不能,多半是因为在执行Hwi函数,Swi管理器将把控制权返回给当前线程。

如果Swis可用,Swi管理器会对比提交的Swi对象的优先权和当前运行线程的优先权。如果当前运行线程是后台Idle Loop、一个Task、或低优先级Swi,Swi管理器将从Swis posted列表删除Swi,将CPU控制权从当前线程切换至提交的Swi函数,并开始运行。

如果当前运行的线程是相同或更高优先级的Swi,Swi管理器会返回控制当前线程,所提交的线程直到之前提交的所有相同或更高优先级线程都执行完毕后才会执行。

当多个相同优先级的Swis被提交,它们会按照提交顺序依次执行。
关于Swi有两个重要的事情:
  • 当Swi开始执行,它必须无阻塞地执行完毕。
  • 当从硬件ISR中被调用,任何触发或提交Swi的函数的代码必须已经被Hwi调度器调用(对于不支持Hwi调度器的平台,由中断存根产生,如MSP430)。这意味着,Swi必须由Hwi对象所调用的函数触发。
Swi函数可被拥有更高优先级的线程所抢占(如Hwi或更高优先级的Swi)。然后Swi函数又不能被阻塞。你无法挂起一个Swi让它象一个设备一样处于准备状态。

如果Swi在Swi管理器将其从posted列表中删除之前提交过多次,它的Swi函数仅会执行一次。这很像Hwi,在CPU清除中断标志寄存器中相应的中断标志位前,如果Hwi被触发多次,最终它只会执行一次。(参考3.5.5节,获取更多关于如何处理Swi在执行前多次被提交的问题)

应用程序对相同优先权的Swi函数的执行顺序不应做任何假设,尽管Swi函数可安全地自行提交(或由其它中断提交)。如果超过一个Swi函数处于等待状态,所有的Swi函数都会在所有task运行前被调用。

3.5.5 使用Swi对象的触发器变量
每个Swi对象都拥有相关联的触发器变量,C6x目标为32bit触发器变量,C5x、C28x和MSP430目标为16bit触发器变量。它即可用于决定是否提交Swi,也可用于在Swi函数中提供可评估的值。

Swi_post()、Swi_or()、and Swi_inc() 用于无条件地提交Swi对象:
  • 当Swi_post()用于提交Swi时,不会改变Swi对象触发器的值。
  • Swi_or()设置由作为参数传递过来的掩码决定的触发器的标志位,然后提交Swi。
  • Swi_inc()在提交Swi对象前将Swi的触发器值加1。
  • Swi_andn()和Swidec()仅在Swi对象的触发器值变为0时提交它。
  • Swi_andn()将作为参数传递过来的掩码决定的触发器的标志位清0。
  • Swi_dec()将触发器值减1。

表3-6展示了这些函数之间的区别:
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
Swi触发器允许你可以对导致Swi函数被提交的条件进行更严格地控制,或者当Swi被提交并安排执行时,Swi函数被执行的次数。

可通过调用Swi_getTrigger()来获取Swi函数的触发器值。Swi_getTrigger()仅能在Swi对象函数中调用。Swi_getTrigger()返回的是Swi函数被安排执行并从Swi posted队列删除前的Swi对象触发器的值。

当Swi管理器从posted队列移除一个等待中的Swi对象时,它的触发器会重设为初始值。触发器初始值在应用程序的配置脚本设置。如果在Swi函数运行期间,Swi再次提交,触发器会相应地更新。尽管如此,当Swi函数运行时,这不会影响Swi_getTrigger()的返回值。也就是说,当Swi从等待列表中被删除时,Swi_getTrigger()所返回的触发器值是锁存触发器的值,而当Swi从等待列表中被删除并被安排执行时,Swi的触发器会马上被重置。这使得应用程序拥有在新提交发生时保持Swi触发器更新值的能力,甚至在Swi函数还未完成运行时也是如此。

例如,一个Swi对象在其从posted队列中被删除前多次提交,Swi管理器仅安排其执行一次。然而,当Swi对象提交多次,如果希望Swi函数必须也要运行多次时,则应当使用Swi_inc()进行提交。一旦Swi管理器调用相应的Swi函数执行,Swi函数可以访问Swi对象触发器以知道在它被安排运行时已经提交了多少次,并按照触发器所指示的次数继续运行。
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
如果给定Swi必须要触发超过一个事件,应当使用Swi_andn()来提交相应的Swi对象,如图3-4所示。例如,一个Swi在其可以继续运行前必须等待两个不同设备的中断,那么在配置Swi对象时,应当为其触发器配置两个标志位。当所有提供输入数据的函数都完成了它们的任务之后,都应当通过调用Swi_andn()提供互补掩码以清除Swi触发器默认值中相应的标志位。因此,Swi仅在两个进程数据都已准备就绪的情况下提交。
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
如果程序的运行需要相同事件多次发生才能提交Swi,则应当如图5所示,使用Swi_dec()来提交Swi。首先将Swi触发器配置为Swi提交前所需发生事件的次数,然后每次在事件发生时调用Swi_dec(),仅当触发器被减为0时才提交Swi,也就是在事件触发的次数等于触发器值时才提交Swi。
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
在某些情境下,Swi可根据提交它的事件来调用不同的函数。此时当事件发生时,程序可使用Swi_or()来无条件地提交Swi对象,其过程如图3-6所示。Swi_or()将触发器提交操作时的事件类型进行编码,而Swi函数则根据不同的事件选择相应的函数执行。
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
3.5.6 利弊权衡
使用Swis而不是Hwis有几点利益:
  • 如果使用Swi函数而不是Hwi来更改共享数据结构体,你可通过在Task访问共享数据结构体时禁用Swis获得互斥(见73页)。这使得系统可以使用Hwis实时响应事件。相比之下,如果Hwi函数直接更改共享数据结构体,Tasks就需要禁止Hwis访问数据结构体以获得互斥。显而易见,禁用Hwis会降低实时系统的性能。
  • 将长的ISRs分成两块通常是有意义的。Hwi关心那些对时间要求极其严格的关键操作,而把不太关键的处理在Hwi内提交到Swi函数进行推迟处理。
 记住,任何阻塞的Task被允许运行前,都必须先完成Swi函数。

3.5.7 Swi函数 同步
在Idle、Task或Swi函数中,可以通过调用Swi_disable()来临时阻止更高优先级的Swi的抢占,这会禁止所有的Swi抢占。重新使能Swi抢占,调用Swi_restore()。Swis可作为一个组整体使能或禁用,单独的Swi无法自已使能或禁用。

当SYS/BIOS在第一个task被调用前完成初始化时,Swis已经被使能。如果应用程序希望禁用Swis,可调用Swi_disable():
key = Swi_disable();
相应地,使能函数为Swi_restore(),其中key值是由Swi模块使用,用于决定是否Swi_disable()已经被调用多次。
Swi_restore(key);
允许嵌套调用Swi_disable() / Swi_restore() ,因为仅有最外层Swi_restore()才实际使能Swis。就是说,task可在不知道Swi_disable()是否已在别处被调用的情况下直接禁止或使能Swis。

当Swis被禁用,一个提交的Swi函数无法立即运行。当Swis可用,并且它的最高优先级线程正准备运行,则中断是锁定在软件中运行的。

使用Swi_delete()来删除一个动态创建的Swi,此时分配给Swi将会释放。Swi_delete()仅能在task中被调用。

3.5.8 Swi Hooks
Swi模块支持以下Hook函数集:
  • Register:在所静态创建Swis前被调用,用于在运行时进行初始化。register hook在main()前的中断使能前被调用。
  • Create:在创建一个Swi时被调用。包括静态创建和使用Swi_create()进行动态创建。
  • Ready:在任何Swi准备运行前调用。
  • Begin:在运行一个Swi函数前调用。
  • End:Swi函数返回前被调用。
  • Delete:在运行时使用Swi_delete()删除一个Swi时调用。

下面的Swi_HookSet结构体类型囊括了Swi模块所支持的hook函数:
typedef  struct Swi_HookSet
{
    Void (*registerFxn)(Int);  /* Register Hook */
    Void (*createFxn)(Handle, Error.Block *);  /* Create Hook */
    Void (*readyFxn)(Handle);  /* Ready Hook */
    Void (*beginFxn)(Handle);  /* Begin Hook */
    Void (*endFxn)(Handle);  /* End Hook */
    Void (*deleteFxn)(Handle);  /* Delete Hook */
};
Swi Hook函数仅能静态配置。
当定义了超过一个Hook集,公共类型中单独的hook函数根据hook ID顺序调用。

3.5.8.1 Register函数
Register函数用于提供hook集存储相应hook ID的允许。此ID可传递给Swi_setHookContext()和Swi_getHookContext()以设置或获取指定hook的上下文。如果hook实现需要使用Swi_setHookContext()或Swi_getHookContext(),则必须指定Register函数。

registerFxn函数在系统初始化期间先于中断使能前被调用。
Register函数语法如下:
Void registerFxn(Int id);

3.5.8.2 Create和Delete函数
无论何时创建和删除一个Swi都需调用Create和Delete函数。Create函数需要一个Error_Block参数用于传给Memory_alloc()以使应用程序获取额外的上下文存储空间。

createFxn和deleteFxn函数在中断使能时调用(除了在启动时间或从main()调用)
这些函数语法如下:
Void createFxn(Swi_Handle swi, Error_Block *eb);
Void deleteFxn(Swi_Handle swi);

3.5.8.3 Ready、Begin和End函数
Ready、Begin和End hook函数在中断可用时调用。readyFxn函数在Swi已经提交,并准备运行时调用。beginFxn函数在给定Swi所关联的函数运行前调用。endFxn函数在Swi函数返回后调用。

提供readyFxn和beginFxn hooks是因为Swi有可能被提交并准备,但此时还得等待更高优先级的线程结束。
这些函数的语法如下:
Void readyFxn(Swi_Handle swi);
Void beginFxn(Swi_Handle swi);
Void endFxn(Swi_Handle swi);

3.5.8.4 Swi Hooks示例
下例使用了两个Swi hook集,并演示了如何读写每个hook集的Hook上下文指针。
下面列出配置脚本和C代码。
C文件代码:
#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/knl/Swi.h>
Swi_Handle mySwi;
Int myHookSetId1, myHookSetId2;
/* HookSet 1 functions */
/* ======== myRegister1 ========
* invoked during Swi module startup before main
* for each HookSet */

Void myRegister1(Int hookSetId)
{
    System_printf( "myRegister1: assigned hookSet Id = %d\n", hookSetId);
    myHookSetId1 = hookSetId;
}
/* ======== myCreate1 ========
* invoked during Swi_create for dynamically created Swis */

Void myCreate1(Swi_Handle swi, Error_Block *eb)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId1);
     /* pEnv should be 0 at this point. If not, there's a bug. */
    System_printf( "myCreate1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId1, (Ptr)0xdead1);
}
//* ======== myReady1 ========
/* invoked when Swi is posted */

Void myReady1(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId1);
    System_printf( "myReady1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId1, (Ptr)0xbeef1);
}
/* ======== myBegin1 ========
* invoked just before Swi func is run */

Void myBegin1(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId1);
    System_printf( "myBegin1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId1, (Ptr)0xfeeb1);
}
/* ======== myEnd1 ========
* invoked after Swi func returns */

Void myEnd1(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId1);
    System_printf( "myEnd1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId1, (Ptr)0xc0de1);
}
/* ======== myDelete1 ========
* invoked upon Swi deletion */

Void myDelete1(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId1);
    System_printf( "myDelete1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
}
/* HookSet 2 functions */
/* ======== myRegister2 ========
* invoked during Swi module startup before main
* for each HookSet */

Void myRegister2(Int hookSetId)
{
    System_printf( "myRegister2: assigned hookSet Id = %d\n", hookSetId);
    myHookSetId2 = hookSetId;
}
/* ======== myCreate2 ========
* invoked during Swi_create for dynamically created Swis */

Void myCreate2(Swi_Handle swi, Error_Block *eb)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId2);
     /* pEnv should be 0 at this point. If not, there's a bug. */
    System_printf( "myCreate2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId2, (Ptr)0xdead2);
}
/* ======== myReady2 ========
* invoked when Swi is posted */

Void myReady2(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId2);
    System_printf( "myReady2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId2, (Ptr)0xbeef2);
}
/* ======== myBegin2 ========
* invoked just before Swi func is run */

Void myBegin2(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId2);
    System_printf( "myBegin2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId2, (Ptr)0xfeeb2);
}
/* ======== myEnd2 ========
* invoked after Swi func returns */

Void myEnd2(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId2);
    System_printf( "myEnd2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
    Swi_setHookContext(swi, myHookSetId2, (Ptr)0xc0de2);
}
/* ======== myDelete2 ========
* invoked upon Swi deletion */

Void myDelete2(Swi_Handle swi)
{
    Ptr pEnv;
    pEnv = Swi_getHookContext(swi, myHookSetId2);
    System_printf( "myDelete2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32());
}
/* ======== mySwiFunc ======== */
Void mySwiFunc(UArg arg0, UArg arg1)
{
    System_printf( "Entering mySwi.\n");
}
/* ======== myTaskFunc ======== */
Void myTaskFunc(UArg arg0, UArg arg1)
{
    System_printf( "Entering myTask.\n");
    System_printf( "Posting mySwi.\n");
    Swi_post(mySwi);
    System_printf( "Deleting mySwi.\n");
    Swi_delete(&mySwi);
    System_printf( "myTask exiting ...\n");
}
/* ======== myIdleFunc ======== */
Void myIdleFunc()
{
    System_printf( "Entering myIdleFunc().\n");
    System_exit( 0);
}
/* ======== main ======== */
Int main(Int argc, Char *argv[])
{
    Error_Block eb;
    Error_init(&eb);
    System_printf( "Starting SwiHookExample...\n");
     /* Create mySwi with default params
    * to exercise Swi Hook Functions */

    mySwi = Swi_create(mySwiFunc,  NULL, &eb);
     if (mySwi ==  NULL)
    {
        System_abort( "Swi create failed");
    }
    BIOS_start();
     return ( 0);
}

配置脚本代码:
/* pull in Timestamp to print time in hook functions */
xdc.useModule( 'xdc.runtime.Timestamp');
/* Disable Clock so that ours is the only Swi in the application */
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);
/* Define and add two Swi Hook Sets */
var Swi = xdc.useModule( "ti.sysbios.knl.Swi");
/* Hook Set 1 */
Swi.addHookSet({
    registerFxn:  '&myRegister1',
    createFxn:  '&myCreate1',
    readyFxn:  '&myReady1',
    beginFxn:  '&myBegin1',
    endFxn:  '&myEnd1',
    deleteFxn:  '&myDelete1'
});
/* Hook Set 2 */
Swi.addHookSet({
    registerFxn:  '&myRegister2',
    createFxn:  '&myCreate2',
    readyFxn:  '&myReady2',
    beginFxn:  '&myBegin2',
    endFxn:  '&myEnd2',
    deleteFxn:  '&myDelete2'
});

运行结果:
[Cortex_M3_0] myRegister1: assigned hookSet Id =  0
myRegister2: assigned hookSet Id =  1
Starting SwiHookExample...
myCreate1: pEnv = 0x0, time =  6
myCreate2: pEnv = 0x0, time =  14
Entering myTask.
Posting mySwi.
myReady1: pEnv = 0xdead1, time =  30
myReady2: pEnv = 0xdead2, time =  38
myBegin1: pEnv = 0xbeef1, time =  48
myBegin2: pEnv = 0xbeef2, time =  58
Entering mySwi.
myEnd1: pEnv = 0xfeeb1, time =  72
myEnd2: pEnv = 0xfeeb2, time =  80
Deleting mySwi.
myDelete1: pEnv = 0xc0de1, time =  92
myDelete2: pEnv = 0xc0de2, time =  102
myTask exiting ...
Entering myIdleFunc().

最后,画张图来表示整个程序的运行过程,及hook的各个函数会在何时执行:
基于TI-RTOS的CC2650DK开发(6)---软件中断 - 阿巴睇 - 阿巴睇的博客
 
这个程序和上篇日志中的程序最显著的差别在于,Swi是由程序手动提交然后触发运行的,而上个程序中的Hwi是由硬件触发运行的。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值