ecos中断机制分析(2)

 mcf52xx系列ISR向量表实际是一个数组cyg_hal_interrupt_handlers[],那么数组里保存的ISR入口地址是什么时候存进去的呢?,原来用户在添加ISR的时候会通过cyg_drv_interrupt_attach()函数,该函数将该ISR的入口地址按中断向量号顺序存入ISR向量表数组。具体实现如下:

#define HAL_INTERRUPT_ATTACH( _vector_, _isr_, _data_, _object_ )    \
    CYG_MACRO_START                                                  \
    cyg_uint32 _index_;                                              \
    HAL_TRANSLATE_VECTOR((_vector_), _index_);                       \
    if (cyg_hal_interrupt_handlers[_index_]                          \
        ==(CYG_ADDRESS)&hal_arch_default_isr)                        \
    {                                                                \
        cyg_hal_interrupt_handlers[_index_] = (CYG_ADDRESS)(_isr_);  \
        cyg_hal_interrupt_data[_index_] = (CYG_ADDRWORD)(_data_);    \
        cyg_hal_interrupt_objects[_index_] = (CYG_ADDRESS)(_object_);\
    }                                                                   \
    CYG_MACRO_END

其中HAL_TRANSLATE_VECTOR宏又定义为:

#define HAL_TRANSLATE_VECTOR(_vector_,_index_)                          \
    CYG_MACRO_START                                                   
 
 \
    switch ((_vector_))                                                 \
    {                                                                   \
    case CYGNUM_HAL_VECTOR_AUTOVEC1 ... CYGNUM_HAL_VECTOR_AUTOVEC7:     \
        (_index_) = ((_vector_) - CYGNUM_HAL_VECTOR_AUTOVEC1);          \
        break;                                                          \
    case CYGNUM_HAL_VECTOR_INTRFIRST ... CYGNUM_HAL_VECTOR_INTRLAST:    \
        (_index_) = ((_vector_)                                         \
                     - CYGNUM_HAL_VECTOR_INTRFIRST                      \
                     + (CYGNUM_HAL_VECTOR_AUTOVEC7                      \
                        - CYGNUM_HAL_VECTOR_AUTOVEC1                    \
                        + 1));                                          \
        break;                                                          \
    default:                                                            \
        CYG_FAIL("Unknown Interrupt!!!");                               \
        (_index_) = (typeof(_index_))-1;                                \
    }                                                                   \
    CYG_MACRO_END

   上面两段宏显示了ISR向量表数组的构成,cyg_hal_interrupt_handlers[0]到cyg_hal_interrupt_handlers[6]为25-31号Autovector interrupts1-7的ISR程序入口地址,cyg_hal_interrupt_handlers[7]到cyg_hal_interrupt_handlers[198]为64-255号User interrupt的ISR程序入口地址。   

   例如产生的是第70号中断,那么是刚才那段汇编程序获得ISR程序入口地址在cyg_hal_interrupt_handlers[] 数组中存放位置?

   当70号中断产生时,MCU将自动压栈32位状态字和PC,因为状态字里VECTOR[7:0]保存了中断向量号,所以根据这个向量号就可以换算出ISR放在cyg_hal_interrupt_handlers[]数组的位置了。  

hw_vsr_interrupt:

        int_pres_regs        

        move.l #(-64+7)*4,%d0     

hw_vsr_int_common:                                 

        move.w  int_pres_regs_sz(%sp),%d1    //出栈32位状态字

        and.l   #0x000003fc,%d1      //获取VECTOR[7:0]      

        add.l   %d1,%d0      //d0为ISR程序入口地址在

                             //cyg_hal_interrupt_handlers[]数组的相对位置

        asr.l   #2,%d1       //d1为中断向量号

   获取了ISR入口地址,接下来该调用ISR了。              

#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT

        .extern cyg_scheduler_sched_lock        

        addq.l  #1,cyg_scheduler_sched_lock    //将调度器上锁次数增加1

#endif

        pea     (%sp)                      

        .extern cyg_hal_interrupt_objects     

        lea     cyg_hal_interrupt_objects,%a0   

        move.l  (%a0,%d0.l),-(%sp)

        .extern cyg_hal_interrupt_data         

        lea     cyg_hal_interrupt_data,%a0      

        move.l  (%a0,%d0.l),-(%sp)

        .extern cyg_hal_interrupt_handlers      

        lea     cyg_hal_interrupt_handlers,%a0  

        move.l  (%a0,%d0.l),%a0

        move.l  %d1,-(%sp)                 

  这一段主要为调用static cyg_uint32 isr(CYG_ADDRWORD vector, CYG_ADDRWORD data)以及void interrupt_end(cyg_uint32 isr_ret,
Cyg_Interrupt *intr,HAL_SavedRegisters  *regs)
作准备,
 将函数需要的参数*intr,vector和data压栈,此时的堆栈如下图所示:

                    ecos中断机制分析(2)               

        jbsr   (%a0)       //这里调用ISR,返回值作为传入interrupt_end();

                           //的参数isr_ret。          

        addq.l  #4*1,%sp                   

        move.l  %d0,(%sp)  //保存isr_ret,此时堆栈如图所示:

                    ecos中断机制分析(2)

interrupt_end()函数将根据ISR返回值isr_ret和调度器上锁次数cyg_scheduler_sched_lock来判断是否执行DSR。如果isr_ret等于2(CYG_ISR_CALL_DSR),且cyg_scheduler_sched_lock等于1,在interrupt_end()函数中将执行DSR.         

#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT

        move.w  (4*3)+int_pres_regs_sz+2(%sp),%d2
        move.w  %d2,%sr
      
//恢复本次中断前SR[10:8]的值

        and.l   #0x0700,%d2
        lsr.l   #8,%d2
        add.l   %d2,cyg_scheduler_sched_lock
 //处于中断嵌套则使上锁次数大于1

  在调用interrupt_end()之前,首先恢复了本次中断前SR[10:8]的值,如果这个值不为0,说明处于中断嵌套中,因为初始化SR[10:8]为0,只有退到嵌套的最后一层,其中断前SR[10:8]才为0,如果不是0,通过增加 cyg_scheduler_sched_lock的值使interrupt_end()不执行DSR.也即保证DSR不能在中断嵌套的情况下执行,从这里可以看出ecos中断策略是,允许中断嵌套,每个中断分为ISR,DSR两部分,ISR立即执行,DSR在ISR之后进行,如果此时处于中断嵌套,则将推迟DSR处理,将其放在DSR队列里(在interrupt_end()中放置),直到退出中断嵌套,所有积累的DSR才能执行。

        .extern interrupt_end              

        jbsr    interrupt_end     //这里调用interrupt_end()

http://blog.sina.com.cn/s/blog_48803ff301000ai6.html

原文地址:https://www.cnblogs.com/kuainiao/p/2883332.html