用户操作
[留言]  [发消息]  [加为好友] 
订阅我的博客
XML聚合    FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
yefanqiu的公告
本博客原创文章,作者保留一切权利,需经作者同意后方可转载,转载时请注明[叶帆工作室]及文章链接。
文章分类
【叶帆软件】
[01]VB源码之友(V2.1.548)
[02]API浏览器.net(V5.0)
[03]叶帆成语词典(V2.0.8)
[04]叶帆密码库(V1.2.8)
【叶帆资源】
DAO 2.0引擎
叶帆快速通道
Windows Embedded 专题
中文MSDN
叶帆工作室(博客园)
叶帆工作室(嵌入式)
叶帆工作室(工控)
微软中文技术论坛
瑞康社区论坛
叶帆友情链接
张欣
枕善居
莫依
葛涵涛
郑建
陈辉
马宁
马骐
魏涛序
黎波
存档

原创  【.Net MF深入研究】中断处理机制 收藏

.Net Micro Framework的中断处理机制相对比较简单,不支持中断嵌套,中断优先级功能的实现由相关硬件提供支持,软件层面仅仅进行相关优先级的设定即可。

下面以TI DM335开发板为例简单介绍一下相关技术细节(这里仅介绍普通中断IRQ)。

1 中断向量表

我们知道中断向量表一般默认的存放在内存0起始地址处。

先让我们在NativeSampleTinyCLR目录中找到Scatterfile_tools_xxx.xml文件,我们会发现在其中都会有如下这么一段文字:

<ExecRegion Name="ER_RAM_RO" Base="0x00000000" Options="ABSOLUTE" Size="0x1A000">

    <FileMapping Name="VectorsTrampolines.obj" Options="(+RO, +FIRST)" />

</ExecRegion>

这是一个编译选项,告诉ARM编译器,连接时把VectorsTrampolines.obj的相关内容放在位置0处(我们可以用WinHex查看编译后的Bin文件,我们会发现Bin文件中最开始的内容就是中断向量表的内容)。

下面是VectorsTrampolines.s文件的汇编代码:

;**************************************************************************

    IMPORT  UNDEF_SubHandler

    IMPORT  ABORTP_SubHandler

    IMPORT  ABORTD_SubHandler 

    IMPORT  IRQ_Handler      ; from stubs.cpp

    EXPORT  ARM_Vectors

;**************************************************************************

    AREA |.text|, CODE, READONLY

    ; ARM directive is only valid for ARM/THUMB processor, but not CORTEX

    IF :DEF:COMPILE_ARM :LOR: :DEF:COMPILE_THUMB

    ARM

    ENDIF   

ARM_Vectors

    ; RESET

RESET_VECTOR

    b       UNDEF_VECTOR

    ; UNDEF INSTR

UNDEF_VECTOR

    ldr     pc, UNDEF_SubHandler_Trampoline

    ; SWI

SWI_VECTOR

    DCD     0xbaadf00d

    ; PREFETCH ABORT

PREFETCH_VECTOR

    ldr     pc, ABORTP_SubHandler_Trampoline

    ; DATA ABORT

DATA_VECTOR

    ldr     pc, ABORTD_SubHandler_Trampoline

    ; unused

USED_VECTOR

    DCD     0xbaadf00d

    ; IRQ

IRQ_VECTOR

    ldr     pc, IRQ_SubHandler_Trampoline

    ; FIQ

    ; we place the FIQ handler where it was designed to go, immediately at the end of the vector table

    ; this saves an additional 3+ clock cycle branch to the handler

FIQ_Handler

    IF :DEF: FIQ_SAMPLING_PROFILER   

    ldr     pc,FIQ_SubHandler_Trampoline        

FIQ_SubHandler_Trampoline   

    DCD     FIQ_SubHandler

    ENDIF   

UNDEF_SubHandler_Trampoline

    DCD     UNDEF_SubHandler

ABORTP_SubHandler_Trampoline

    DCD     ABORTP_SubHandler

ABORTD_SubHandler_Trampoline

    DCD     ABORTD_SubHandler

; route the normal interupt handler to the proper lowest level driver

IRQ_SubHandler_Trampoline

    DCD       IRQ_Handler

;**************************************************************************

从中我们可以看出,当硬件触发IRQ中断时,跳转的最终位置为IRQ_Handler

 

【疑问】中断向量表的内容最开始的时候一定是存放在Flash(或其它存储介质)中,它是如何被转移到内存0处的呢(当然调试时也可以通过调试器把中断向量表直接Download到内存中去)?

【解答】在FirstEntry.s汇编代码中有这么一句代码 bl  BootstrapCode”,BootstrapCode是一个子函数,该函数优先于“b   BootEntry”执行,在BootstrapCode函数中又调用了一个子函数PrepareImageRegions,在PrepareImageRegions函数中我们就可以看到拷贝中断向量表的相关代码:

UINT32* src = (UINT32*)&LOAD_RAM_RO_BASE;

UINT32* dst = (UINT32*)&IMAGE_RAM_RO_BASE;

UINT32  len = (UINT32 )&IMAGE_RAM_RO_LENGTH + (UINT32)&Image$$ER_RAM_RW$$Length;

Prepare_Copy( src, dst, len );

 

注:FIQ_SAMPLING_PROFILER选项是没有定义的,所以在DM335开发板上支持FIQ是有问题的。

2 中断函数(IRQ_Handler

中断向量表一经设置,则触发相关中断时,CPU会把PC指针指到相关的入口地址处,对IRQ中断来说,其入口地址就是IRQ_Handler

DM335平台,IRQ_Handler函数的内容如下:

void __irq IRQ_Handler(void)

{

    DM335_INTC_Driver::IRQ_Handler();

}

void /*__irq*/DM335_INTC_Driver::IRQ_Handler(void)

{

    DM335_INTC &pAic = DM335::INTC();

    unsigned int index;

    // set before jumping elsewhere or allowing other interrupts

    SystemState_SetNoLock(SYSTEM_STATE_ISR);

    SystemState_SetNoLock(SYSTEM_STATE_NO_CONTINUATIONS);   

         while(pAic.IRQENTRY != 0x0)

    {

        index = (pAic.IRQENTRY>>2) - 1;

             if(index < 32)

                pAic.IRQ0 |= 1 << index;

        else

                pAic.IRQ1 |= 1 << (index % 32);

        s_IsrTable[index].Handler.Execute();  //执行对应的中断处理函数

    }

    SystemState_ClearNoLock(SYSTEM_STATE_NO_CONTINUATIONS); // nestable

    SystemState_ClearNoLock(SYSTEM_STATE_ISR); // nestable

}

在中断函数执行之前,其实已经执行了中断寄存器的初始工作,比如根据需要打开或关闭(Enable/Disable)相关的中断,对我们的应用来说串口和USB的中断是要打开的,而DMA中断就是关闭的。此外根据你应用的需求,你也可以设置不同中断的优先级(0~7,其中0~1对应快速中断,对我们来说由于快速中断无效,所以中断的优先级最高可设为2)。

相同等级的中断同时来到,是谁来排序呢?

DM335来说,是由硬件完成的,DM335提供一个寄存器IRQENTRYIRQENTRY寄存器中存放的值,就是下一个要执行的中断号(要经过一定的换算),该寄存器就像一个队列的出队口,排好队的中断依次出列,直到IRQENTRY的值为0为止(表示当前队列中没有任何中断被触发)。

(当然我们也可以直接根据IRQ0IRQ1中的值触发二级中断函数,但是这样来做,设定的中断优先级就没有任何意义了。)

这时候我们就可以根据不同的中断号,执行相对应的二级中断函数了。

那中断来了,程序是如何知道该执行那个二级中断函数呢?请看下一节内容就明白了。

3 中断连接函数

CPU_INTC_ActivateInterrupt函数是把硬件的中断号和要执行的中断函数联系起来,其原型是:

BOOL CPU_INTC_ActivateInterrupt(UINT32 Irq_Index, HAL_CALLBACK_FPN ISR, void *ISR_Param)

Irq_Index – 中断号(可根据相关硬件手册进行设定)

ISR – 该中断触发时所要执行的中断函数

ISR_Param –中断函数的入口参数(可以为空)

 

当然根据需要也可以断开这种连接,或关掉相应的中断,这些函数由于比较好理解,所以这里就不过多介绍了。    

     我们以USB驱动为例,简单介绍一下CPU_INTC_ActivateInterrupt函数的使用,在USB的初始化函数中,我们添加了如下代码:

      CPU_INTC_ActivateInterrupt(DM335_INTC::c_IRQ_Index_USBINT, Global_ISR, NULL);

      其中断函数Global_ISR的部分代码如下:

      void DM335_USB_Driver::Global_ISR(void *Param)

{

          DM335_USB &usb = DM335_USB::Type();

UINT32 endpoint = 0;

          UINT32 tmp = usb.Base.INTSRCR;

          usb.Base.INTCLRR = usb.Base.INTSRCR;

 

          //在这里面就可以根据USB相关寄存器的值,确定那一个中断被触发了

          // … …

       }

这样一层一层,由根到枝,按图索骥,获知实际的中断,并最终执行相关的处理代码(如接收数据或发送数据等等)。

4 中断控制

     Porting Kit提供的样例代码中,我们会在中断函数中看到不少GLOBAL_LOCK(irq)代码,由于MF目前不支持中断嵌套,所以从某种意义上说,在中端函数中添加代码GLOBAL_LOCK(irq)是没有意义的(对IRQ中断来说,CPU在触发该中断时,就已经关闭了IRQ中断位,除非你在中断函数中又打开了,当然你这样做的目的一般就是支持中断嵌套了,这部分内容暂不在讨论之列)。

GLOBAL_LOCK是一个宏,其实现为:

#define GLOBAL_LOCK(x)     SmartPtr_IRQ x

SmartPtr_IRQ类的代码为:

class SmartPtr_IRQ

{

    UINT32 m_state;

    void*  m_context;

public:

    SmartPtr_IRQ(void* context=NULL)  { m_context = context; Disable(); }

    ~SmartPtr_IRQ() { Restore(); }

}

void SmartPtr_IRQ::Disable()

{

    UINT32 Cp;

    UINT32 Cs;

    __asm

    {

        MRS     Cp, CPSR

        ORR     Cs, Cp, #0x80

        MSR     CPSR_c, Cs

    }

    m_state = Cp;

}

void SmartPtr_IRQ::Restore()

{

    UINT32 Cp = m_state;

    if((Cp & DISABLED_MASK) == 0)

    {

        ASSERT_SYSTEM_IRQ_MODE(Cp);

        __asm

        {

            MRS     Cp, CPSR

            BIC     Cp, Cp, #0x80

            MSR     CPSR_c, Cp

        }

    }

}

 

由以上代码可知,定义SmartPtr_IRQ的实例的同时,就执行了构造函数,也就是执行了Disable函数(关闭IRQ中断)。换句话说,GLOBAL_LOCK(irq)代码的作用就是,在irq的生命周期内关闭IRQ中断。

相信,如果MF的后续版本开始支持中断嵌套,该部分的内容才会变的复杂。

5 补充说明

MF应用程序(C#)中的中断(或事件函数)其实和我们这章内容所说的中断不是一会事,由以上内容我们可以了解到,中断函数串序执行,所以每一个中断函数执行时间尽量短,否则整个系统的效率便会大大降低。所以我们上层的事件函数是不可能直接勾连在中端函数内,因为在实际的应用中,我们非常有可能在事件函数中执行很多耗时的操作的。实际上TinyCLR代码对这两部分进行了一定得隔离处理,等我们有时间在一一剖析相关的实现机理。

除了中断函数被执行外,对嵌入式应用来说剩下的也就是一个Main函数(对MF来说就是ApplicationEntryPoint)和一个while循环了。对于我们的TinyCLR来说其实就是一个在相对简陋的操作系统上的一个精彩应用而已:

void ApplicationEntryPoint()

{

    CLR_SETTINGS clrSettings;

    memset(&clrSettings, 0, sizeof(CLR_SETTINGS));

    clrSettings.MaxContextSwitches = 50;

#if !defined(BUILD_RTM)

    clrSettings.PerformGarbageCollection = false;

    clrSettings.PerformHeapCompaction    = false;

#endif

    clrSettings.WaitForDebugger          = false;

    ClrStartup( clrSettings );

#if !defined(BUILD_RTM)

    debug_printf( "Exiting.\r\n" );

#else

    ::CPU_Reset();

#endif

}

发表于 @ 2009年05月04日 17:13:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:红外条码扫描器的另类使用C#版 | 新一篇:【.Net MF新特性】Usb双接口支持

  • 发表评论
  • 评论内容:
  •  
Copyright © yefanqiu
Powered by CSDN Blog