ecos的中断ISR是怎么与硬件中断向量联系起来的,是怎么被调用的?

本文整理自:http://keendawn.blog.163.com/blog/static/8888074320116205833478/

要想知道ecos的中断ISR是怎么与硬件中断向量联系起来的,是怎么被调用的?

那就要看下面这两个关键的函数:

  1. cyg_drv_interrupt_create()
  2. cyg_drv_interrupt_attach()
复制代码
这两个函数都声明在cyg/kernel/kapi.h中,其形式如下:
  1. void cyg_interrupt_create(
  2.     cyg_vector_t        vector,         /* Vector to attach to               */
  3.     cyg_priority_t      priority,       /* Queue priority                    */
  4.     cyg_addrword_t      data,           /* Data pointer                      */
  5.     cyg_ISR_t           *isr,           /* Interrupt Service Routine         */
  6.     cyg_DSR_t           *dsr,           /* Deferred Service Routine          */
  7.     cyg_handle_t        *handle,        /* returned handle                   */
  8.     cyg_interrupt       *intr           /* put interrupt here                */
  9. ) __THROW;

  10. void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW;
复制代码
(注: __THROW是在C++中用的,是用来抛出异常的,详见: eCos中的externC和__THROW 这篇帖子的说明)。

其中文意义对照如下:
cyg_interrupt_create(
        中断号,
        中断优先级,
        传递的中断参数,
        ISR函数,
        DSR函数,
        被返回的中断句柄,
        存放与此中断相关的内核数据的变量空间);

cyg_interrupt_attach(中断句柄);

这样实际上去研究一下cyg_interrupt_create函数的定义内容,应该就能搞明白我们的问题了!
由于其函数声明在kapi.h中,很自然的就想到其定义应在kapi.c文件中。
找到 文件: ....\ecos\ecos-current\packages\kernel\current\src\common\kapi.cxx

找到这两个函数的定义如下:
  1. /*---------------------------------------------------------------------------*/
  2. /* Interrupt handling                                                        */

  3. externC void cyg_interrupt_create(
  4.     cyg_vector_t        vector,         /* Vector to attach to               */
  5.     cyg_priority_t      priority,       /* Queue priority                    */
  6.     cyg_addrword_t      data,           /* Data pointer                      */
  7.     cyg_ISR_t           *isr,           /* Interrupt Service Routine         */
  8.     cyg_DSR_t           *dsr,           /* Deferred Service Routine          */
  9.     cyg_handle_t        *handle,        /* returned handle                   */
  10.     cyg_interrupt       *intr           /* put interrupt here                */
  11. ) __THROW
  12. {
  13.     CYG_ASSERT_SIZES( cyg_interrupt, Cyg_Interrupt );

  14.     Cyg_Interrupt *t = new((void *)intr) Cyg_Interrupt (
  15.         (cyg_vector)vector,
  16.         (cyg_priority)priority,
  17.         (CYG_ADDRWORD)data,
  18.         (cyg_ISR *)isr,
  19.         (cyg_DSR *)dsr );
  20.     t=t;

  21.     CYG_CHECK_DATA_PTR( handle, "Bad handle pointer" );
  22.     *handle = (cyg_handle_t)intr;
  23. }

  24. void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW
  25. {
  26.     ((Cyg_Interrupt *)interrupt)->attach();
  27. }
复制代码
函数内容比想象中的简单,所有的操作又都传给了Cyg_Interrupt这个类来完成,那就来对Cyg_Interrupt探个究竟吧。

注意:Cyg_Interrupt是个C++类,,可不要找成了struct cyg_interrupt,注意哟,cyg_interrupt_create函数的最后一个参数就是这个cyg_interrupt struct类型的,在cyg/kernel/kapidata.h中有个struct cyg_interrupt定义,虽然名字和内容都很相似,但实际上不是。

真正的class Cyg_Interrupt定义在cyg/kernel/intr.hxx中,这个头文件没干别的,就是声明这个class了,可见这是一个很大的class,如下:
  1. // -------------------------------------------------------------------------
  2. // Interrupt class. This both represents each interrupt and provides a static
  3. // interface for controlling the interrupt hardware.

  4. class Cyg_Interrupt
  5. {

  6.     friend class Cyg_Scheduler;
  7.     friend void interrupt_end( cyg_uint32,
  8.                                Cyg_Interrupt *,
  9.                                HAL_SavedRegisters *);
  10.     friend void cyg_interrupt_post_dsr( CYG_ADDRWORD intr_obj );
  11.     friend void cyg_interrupt_call_pending_DSRs( void );
  12.    
  13.     cyg_vector          vector;         // Interrupt vector

  14.     cyg_priority        priority;       // Queuing priority
  15.    
  16.     cyg_ISR             *isr;           // Pointer to ISR

  17.     cyg_DSR             *dsr;           // Pointer to DSR

  18.     CYG_ADDRWORD        data;           // Data pointer


  19.    
  20.     // DSR handling interface called by the scheduler

  21.                                         // Check for pending DSRs
  22.     static cyg_bool     DSRs_pending();

  23.                                         // Call any pending DSRs
  24.     static void         call_pending_DSRs();
  25.     static void         call_pending_DSRs_inner();

  26.     // DSR handling interface called by the scheduler and HAL
  27.     // interrupt arbiters.

  28.     void                post_dsr();     // Post the DSR for this interrupt


  29.    
  30.     // Data structures for handling DSR calls.  We implement two DSR
  31.     // handling mechanisms, a list based one and a table based
  32.     // one. The list based mechanism is safe with respect to temporary
  33.     // overloads and will not run out of resource. However it requires
  34.     // extra data per interrupt object, and interrupts must be turned
  35.     // off briefly when delivering the DSR. The table based mechanism
  36.     // does not need unnecessary interrupt switching, but may be prone
  37.     // to overflow on overload. However, since a correctly programmed
  38.     // real time application should not experience such a condition,
  39.     // the table based mechanism is more efficient for real use. The
  40.     // list based mechainsm is enabled by default since it is safer to
  41.     // use during development.

  42. #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE
  43.    
  44.     static Cyg_Interrupt *dsr_table[CYGNUM_KERNEL_CPU_MAX]
  45.                                    [CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE]
  46.                                    CYGBLD_ANNOTATE_VARIABLE_INTR;

  47.     static cyg_ucount32 dsr_table_head[CYGNUM_KERNEL_CPU_MAX]
  48.                                       CYGBLD_ANNOTATE_VARIABLE_INTR;

  49.     static volatile cyg_ucount32 dsr_table_tail[CYGNUM_KERNEL_CPU_MAX]
  50.                                                CYGBLD_ANNOTATE_VARIABLE_INTR;

  51. #endif
  52. #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST

  53.     // Number of DSR posts made
  54.     volatile cyg_ucount32 dsr_count CYGBLD_ANNOTATE_VARIABLE_INTR;

  55.     // next DSR in list
  56.     Cyg_Interrupt* volatile next_dsr CYGBLD_ANNOTATE_VARIABLE_INTR;

  57.     // head of static list of pending DSRs
  58.     static Cyg_Interrupt* volatile dsr_list[CYGNUM_KERNEL_CPU_MAX]
  59.                                            CYGBLD_ANNOTATE_VARIABLE_INTR;

  60. #  ifdef CYGSEM_KERNEL_INTERRUPTS_DSRS_LIST_FIFO
  61.     // tail of static list of pending DSRs
  62.     static Cyg_Interrupt* volatile dsr_list_tail[CYGNUM_KERNEL_CPU_MAX]
  63.                                            CYGBLD_ANNOTATE_VARIABLE_INTR;
  64. #  endif

  65. #endif  // defined  CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST

  66. #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN

  67.     // The default mechanism for handling interrupts is to attach just
  68.     // one Interrupt object to each vector. In some cases, and on some
  69.     // hardware, this is not possible, and each vector must carry a chain
  70.     // of interrupts.

  71.     Cyg_Interrupt       *next;          // Next Interrupt in list

  72.     // Chaining ISR inserted in HAL vector
  73.     static cyg_uint32 chain_isr(cyg_vector vector, CYG_ADDRWORD data);   

  74.     // Table of interrupt chains
  75.     static Cyg_Interrupt *chain_list[CYGNUM_HAL_ISR_TABLE_SIZE];
  76.    
  77. #endif

  78.     // Interrupt disable data. Interrupt disable can be nested. On
  79.     // each CPU this is controlled by disable_counter[cpu]. When the
  80.     // counter is first incremented from zero to one, the
  81.     // interrupt_disable_spinlock is claimed using spin_intsave(), the
  82.     // original interrupt enable state being saved in
  83.     // interrupt_disable_state[cpu].  When the counter is decremented
  84.     // back to zero the spinlock is cleared using clear_intsave().

  85.     // The spinlock is necessary in SMP systems since a thread
  86.     // accessing data shared with an ISR may be scheduled on a
  87.     // different CPU to the one that handles the interrupt. So, merely
  88.     // blocking local interrupts would be ineffective. SMP aware
  89.     // device drivers should either use their own spinlocks to protect
  90.     // data, or use the API supported by this class, via
  91.     // cyg_drv_isr_lock()/_unlock(). Note that it now becomes
  92.     // essential that ISRs do this if they are to be SMP-compatible.

  93.     // In a single CPU system, this mechanism reduces to just
  94.     // disabling/enabling interrupts.

  95.     // Disable level counter. This counts the number of times
  96.     // interrupts have been disabled.
  97.     static volatile cyg_int32 disable_counter[CYGNUM_KERNEL_CPU_MAX]
  98.                                               CYGBLD_ANNOTATE_VARIABLE_INTR;

  99.     // Interrupt disable spinlock. This is claimed by any CPU that has
  100.     // disabled interrupts via the Cyg_Interrupt API.
  101.     static Cyg_SpinLock interrupt_disable_spinlock CYGBLD_ANNOTATE_VARIABLE_INTR;

  102.     // Saved interrupt state. When each CPU first disables interrupts
  103.     // the original state of the interrupts are saved here to be
  104.     // restored later.
  105.     static CYG_INTERRUPT_STATE interrupt_disable_state[CYGNUM_KERNEL_CPU_MAX]
  106.                                                        CYGBLD_ANNOTATE_VARIABLE_INTR;

  107.    
  108. public:

  109.     Cyg_Interrupt                       // Initialize interrupt
  110.     (
  111.         cyg_vector      vector,         // Vector to attach to
  112.         cyg_priority    priority,       // Queue priority
  113.         CYG_ADDRWORD    data,           // Data pointer
  114.         cyg_ISR         *isr,           // Interrupt Service Routine
  115.         cyg_DSR         *dsr            // Deferred Service Routine
  116.         );

  117.     ~Cyg_Interrupt();
  118.         
  119.     // ISR return values
  120.     enum {
  121.         HANDLED  = 1,                   // Interrupt was handled
  122.         CALL_DSR = 2                    // Schedule DSR
  123.     };

  124.     // Interrupt management
  125.         
  126.     void        attach();               // Attach to vector


  127.     void        detach();               // Detach from vector
  128.         
  129.    
  130.     // Static Interrupt management functions

  131.     // Get the current service routine
  132.     static void get_vsr(cyg_vector vector, cyg_VSR **vsr);

  133.     // Install a vector service routine
  134.     static void set_vsr(
  135.         cyg_vector vector,              // hardware vector to replace
  136.         cyg_VSR *vsr,                   // my new service routine
  137.         cyg_VSR **old = NULL            // pointer to old vsr, if required
  138.         );


  139.     // Static interrupt masking functions

  140.     // Disable interrupts at the CPU
  141.     static void disable_interrupts();

  142.     // Re-enable CPU interrupts
  143.     static void enable_interrupts();

  144.     // Are interrupts enabled at the CPU?
  145.     static inline cyg_bool interrupts_enabled()
  146.     {
  147.         return (0 == disable_counter[CYG_KERNEL_CPU_THIS()]);
  148.     }
  149.    
  150.     // Get the vector for the following calls
  151.     inline cyg_vector get_vector()
  152.     {
  153.         return vector;
  154.     }
  155.    
  156.     // Static PIC control functions
  157.    
  158.     // Mask a specific interrupt in a PIC
  159.     static void mask_interrupt(cyg_vector vector);
  160.     // The same but not interrupt safe
  161.     static void mask_interrupt_intunsafe(cyg_vector vector);

  162.     // Clear PIC mask
  163.     static void unmask_interrupt(cyg_vector vector);
  164.     // The same but not interrupt safe
  165.     static void unmask_interrupt_intunsafe(cyg_vector vector);

  166.     // Acknowledge interrupt at PIC
  167.     static void acknowledge_interrupt(cyg_vector vector);

  168.     // Change interrupt detection at PIC
  169.     static void configure_interrupt(
  170.         cyg_vector vector,              // vector to control
  171.         cyg_bool level,                 // level or edge triggered
  172.         cyg_bool up                     // hi/lo level, rising/falling edge
  173.         );

  174. #ifdef CYGPKG_KERNEL_SMP_SUPPORT

  175.     // SMP support for associating an interrupt with a specific CPU.
  176.    
  177.     static void set_cpu( cyg_vector, HAL_SMP_CPU_TYPE cpu );
  178.     static HAL_SMP_CPU_TYPE get_cpu( cyg_vector );
  179.    
  180. #endif   
  181. };
复制代码
这只是声明了这个class,这个class的构造/析构函数和成员函数的实现,还要找cyg/kernel/intr.hxx头文件相对应的C文件,
在这里....\packages\kernel\current\src\intr\intr.cxx找到了intr.cxx文件,因为cyg_interrupt_create函数实际上调用了class Cyg_Interrupt的构造函数,我们就来看看这个构造函数:
  1. Cyg_Interrupt::Cyg_Interrupt(
  2.     cyg_vector      vec,                // Vector to attach to
  3.     cyg_priority    pri,                // Queue priority
  4.     CYG_ADDRWORD    d,                  // Data pointer
  5.     cyg_ISR         *ir,                // Interrupt Service Routine
  6.     cyg_DSR         *dr                 // Deferred Service Routine
  7.     )
  8. {
  9.     CYG_REPORT_FUNCTION();
  10.     CYG_REPORT_FUNCARG5("vector=%d, priority=%d, data=%08x, isr=%08x, "
  11.                         "dsr=%08x", vec, pri, d, ir, dr);
  12.    
  13.     vector      = vec;
  14.     priority    = pri;
  15.     isr         = ir;
  16.     dsr         = dr;
  17.     data        = d;

  18. #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST

  19.     dsr_count   = 0;
  20.     next_dsr    = NULL;

  21. #endif

  22. #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN

  23.     next        = NULL;
  24.    
  25. #endif

  26.     CYG_REPORT_RETURN();
  27.    
  28. };
复制代码
也就是分配了一下成员变量,把cyg_interrupt_create函数传进来的 中断号、ISR、DSR等 分配给类的成员变量,好像也没什么特别的。
看来整个cyg_interrupt_create函数也就是在构造这个类对象了。

这样重要的好戏是在cyg_interrupt_attach函数里完成了,看cyg_interrupt_attach的源代码,只一行,再次列出如下:
  1. void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW
  2. {
  3.     ((Cyg_Interrupt *)interrupt)->attach();
  4. }
复制代码
它也就是调用了class Cyg_Interrupt的attach成员函数,那我们到intr.cxx中看看attach这个成员函数都干了些啥?
  1. // -------------------------------------------------------------------------
  2. // Attach an ISR to an interrupt vector.

  3. void
  4. Cyg_Interrupt::attach(void)
  5. {
  6.     CYG_REPORT_FUNCTION();

  7.     CYG_ASSERT( vector >= CYGNUM_HAL_ISR_MIN, "Invalid vector");
  8.     CYG_ASSERT( vector <= CYGNUM_HAL_ISR_MAX, "Invalid vector");

  9.     CYG_INSTRUMENT_INTR(ATTACH, vector, 0);

  10.     HAL_INTERRUPT_SET_LEVEL( vector, priority );
  11.    
  12. #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN

  13.     CYG_ASSERT( next == NULL , "Cyg_Interrupt already on a list");

  14.     cyg_uint32 index;

  15.     HAL_TRANSLATE_VECTOR( vector, index );

  16.     if( chain_list[index] == NULL )
  17.     {
  18.         int in_use CYGBLD_ATTRIB_UNUSED;
  19.         // First Interrupt on this chain, just assign it and register
  20.         // the chain_isr with the HAL.
  21.         
  22.         chain_list[index] = this;

  23.         HAL_INTERRUPT_IN_USE( vector, in_use );
  24.         CYG_ASSERT( 0 == in_use, "Interrupt vector not free.");
  25.         HAL_INTERRUPT_ATTACH( vector, chain_isr, &chain_list[index], NULL );
  26.     }
  27.     else
  28.     {
  29.         // There are already interrupts chained, add this one into the
  30.         // chain in priority order.
  31.         
  32.         Cyg_Interrupt **p = &chain_list[index];

  33.         while( *p != NULL )
  34.         {
  35.             Cyg_Interrupt *n = *p;

  36.             if( n->priority < priority ) break;
  37.             
  38.             p = &n->next;
  39.         }
  40.         next = *p;
  41.         *p = this;
  42.     }
  43.    
  44. #else
  45.    
  46.     {
  47.         int in_use CYGBLD_ATTRIB_UNUSED;

  48.         HAL_INTERRUPT_IN_USE( vector, in_use );
  49.         CYG_ASSERT( 0 == in_use, "Interrupt vector not free.");

  50.         HAL_INTERRUPT_ATTACH( vector, isr, data, this );
  51.     }

  52. #endif   
  53.     CYG_REPORT_RETURN();
  54. }
复制代码
attach成员函数又调用了 HAL_INTERRUPT_ATTACH 这个HAL层的宏,那就继续追踪这个宏吧, 它定义在里,如下:
  1. #define HAL_INTERRUPT_ATTACH( _vector_, _isr_, _data_, _object_ )           \
  2.     CYG_MACRO_START                                                         \
  3.     cyg_uint32 _index_;                                                     \
  4.     HAL_TRANSLATE_VECTOR( _vector_, _index_ );                              \
  5.                                                                             \
  6.     if( hal_interrupt_handlers[_index_] == (CYG_ADDRESS)HAL_DEFAULT_ISR )   \
  7.     {                                                                       \
  8.         hal_interrupt_handlers[_index_] = (CYG_ADDRESS)_isr_;               \
  9.         hal_interrupt_data[_index_] = (CYG_ADDRWORD)_data_;                 \
  10.         hal_interrupt_objects[_index_] = (CYG_ADDRESS)_object_;             \
  11.     }                                                                       \
  12.     CYG_MACRO_END
复制代码
注:CYG_MACRO_START 和 CYG_MACRO_END 宏定义在中:
  1. #define CYG_MACRO_START do {
  2. #define CYG_MACRO_END   } while (0)
复制代码
这个宏主要干了3件事:以中断号作为索引,将 isr地址、data 和 0bject地址(也就是class Cyg_Interrupt的对象) 3者分别存入hal_interrupt_handlers、hal_interrupt_data and hal_interrupt_objects 3个数组中。这3个数组定义在hal_intr.h头文件相对应的hal_intr.c 文件中,这个C文件也没干其他事,就只定义了这3个全局数组,如下:
  1. // Create the interrupt handler table, with all handlers set to the 'safe'
  2. // default.

  3. volatile CYG_ADDRESS hal_interrupt_handlers[CYGNUM_HAL_ISR_COUNT] =
  4. {(CYG_ADDRESS)HAL_DEFAULT_ISR,
  5. (CYG_ADDRESS)HAL_DEFAULT_ISR,
  6.              .
  7.              .
  8.              .
  9.              .
  10. (CYG_ADDRESS)HAL_DEFAULT_ISR,
  11. (CYG_ADDRESS)HAL_DEFAULT_ISR};

  12. volatile CYG_ADDRWORD   hal_interrupt_data[CYGNUM_HAL_ISR_COUNT];
  13. volatile CYG_ADDRESS    hal_interrupt_objects[CYGNUM_HAL_ISR_COUNT];
复制代码
最关键的就是hal_interrupt_handlers数组,它放的就是所有终端ISR的地址。

但是,追踪到这个数组,好像也就追不下去了, 这样的一个ISR table,又是怎样被索引调用的呢?

我们只顾忙着追踪,应该回想一下,我们知道HAL层是ecos中和硬件结构相关的地址, 我们上面分析的部分HAL层代码实际上是具体于某一CPU架构的。 而我上面看的HAL代码就是NIOS II的HAL层,只是上面的一点HAL代码好像还看不出与CPU结构相关的特殊性。

那如果我们还要继续trace, 看来就必须要和CPU结构密切相关了,也能感觉的到是和硬件中断向量有关的东东, 那我们就以NIOS II为例继续追踪, 你会发现,和上面hal_intr.c文件同一目录下, ....\packages\hal\nios2\arch\current\src\ 有一个vector.S汇编文件,一看到它的名字,就有感觉,打开它,你会发现, 它分了4大块,分别定义了4种不同情况下,CPU发生异常or接收到中断时,要执行的代码,
  1. /*
  2.      * ========================================================================
  3.      * _hardware_reset
  4.      *
  5.      * This is the reset entry point for Nios II.
  6.      *
  7.      * At reset, only the cache line which contain the reset vector is
  8.      * initialized. Therefore the code within the first cache line is required
  9.      * to initialize the instruction cache for the remainder of the code.
  10.      *
  11.      * Note that the automatically generated linker script requires that
  12.      * the .init section is less than 0x20 bytes long.
  13.      */
  14. L106  _hardware_reset:


  15.     /*
  16.      * ========================================================================
  17.      * _exception_vector
  18.      *
  19.      * This is the exception entry point. It is responsible for determing if the
  20.      * exception was caused by an hardware interrupt, or a software exception.
  21.      * It then vectors accordingly using the VSR table to handle the exception.
  22.      */
  23. L283  _exception_vector:


  24.     /*
  25.      * ========================================================================
  26.      * _interrupt_handler
  27.      *
  28.      * This is the default handler for hardware interrupts.
  29.      */
  30. L342  _interrupt_handler::

  31.     /*
  32.      * ========================================================================
  33.      * _software_exception_handler
  34.      *
  35.      * This is the default handler for software exceptions.
  36.      */

  37.     .globl _software_exception_handler
  38.     .type _software_exception_handler, @function

  39. L613  _software_exception_handler:

  40. 顾名思义,我们要找的应该在_interrupt_handler中,果不其然,其中有如下一段代码:

  41. L465    /*
  42.          * Having located the interrupt source, r4 contains the index of the
  43.          * interrupt to be handled.
  44.          *
  45.          * This is converted into an offset into the handler table,
  46.          * and stored in r15.
  47.          */
  48.    
  49.         slli r15, r4, 2
  50.         
  51. L475    /*
  52.          * Load into r1 the address of the handler for this interrupt. This is
  53.          * obtained from the interrupt handler table: hal_interrupt_handlers.
  54.          */
  55.    
  56.         movhi r1, %hiadj(hal_interrupt_handlers)
  57.         addi r1, r1, %lo(hal_interrupt_handlers)
  58.         add r1, r1, r15
  59.         ldw r1, (r1)
  60.    
  61.         /*
  62.          * Load into r5 the data associated with the interrupt handler. This is
  63.          * obtained from the table: hal_interrupt_data.
  64.          */
  65.    
  66.         movhi r5, %hiadj(hal_interrupt_data)
  67.         addi r5, r5, %lo(hal_interrupt_data)
  68.         add r5, r5, r15
  69.         ldw r5, (r5)
  70.    
  71.         /*
  72.          * Call the interrupt handler  The input arguments are the interrupt number
  73.          * and the associated data obtained above. Save off r15 explicitly since
  74.          * its a caller-saved register and is used below.
  75.          */
  76.         addi sp, sp, -4
  77.         stw r15, (sp)
  78. L502    callr r1
  79.         ldw r15, (sp)
  80. L504    addi sp, sp, 4
复制代码
你看,r4中存有要相应的中断号,它又存入r15中,根据r15的值, 在hal_interrupt_handlers数组中得到相应的ISR地址存入r1中, 在L502行callr r1,就调用了中断ISR。

至此,层层追踪结束,大功告成。 终于搞明白了ecos中ISR是怎么响应到硬件中断事件的。 其实,剥茧抽丝,层层追踪,兜兜转转,你会发现class Cyg_Interrupt这个C++类,只是做了一个高层的包装,
真正起作用的还是底层HAL相关的代码。

你可能感兴趣的:(eCos)