精华内容
下载资源
问答
  • 中断函数怎么调用

    2012-10-15 16:57:12
    有点想不通
  • 怎么函数中判断中断执行并且把调用指向另一个函数,根据函数名的判断?怎么传递给新的函数参数并且关闭自身函数的执行?
  • 中断中C函数调用C++

    2016-04-22 10:48:00
    我在用C++进行程序开发的时候就发现了一个需要解决了问题:在断函数中怎么调用C++的成员函数? 我的中断函数定义在文件 IRQHander.c 文件中,我想在串口中断函数调用 gPrinter.Putchar(ch) 函数。用于向 gPrinter ....
      之前,我们在单片机程序开发时都会面对中断函数。众所周知的,这个中断函数肯定是要用C函数来定义的。我在用C++进行程序开发的时候就发现了一个需要解决了问题:在断函数中怎么调用C++的成员函数?

        我的中断函数定义在文件 IRQHander.c 文件中,我想在串口中断函数调用 gPrinter.Putchar(ch) 函数。用于向 gPrinter 发送字符打印消息。

       尝试1:直接将 CDebug.h 文件包含进来。


    <strong>#include "CDebug.h" 
           
    void USART1_IRQHandler()  
    {  
        RxData = USART1->DR;  
        gDebug.Printer(RxData);  
        ...  
    }</strong>

       结果,一大堆错误。原因在于编译器以C的方试进行编译,然而C不认得 CDebug.h 文件中 class 这样的C++中的关键字。


        尝试2:将 IRQHandle.c 文件重命名为 IRQHandle.cpp ,让其以C++的方式进行编译。

        结果,编译没有问题。一旦触发了中断,系统就死在 startup.s 文件中默认的中断函数中了。这个默认的中断函数为找不到用户定议的中断处理函数默认指定的。原因在于 IRQHandle.cpp 中 USART1_IRQHandler() 在用C++进行编译后会根据参数类型对函数名进行修饰,因为C++要实现重载功能。如:

        原定义函数:int  Func ( int , char * )

        C++编译后:int  Func_i_cp ( int, char* ) 

        尝试3:加 extern "C" {} 进行修饰,让编译器不修改函数名。


    <strong>#include "CDebug.h" 
           
    extern "C" {  
           
    void USART1_IRQHandler()  
    {  
        RxData = USART1->DR;  
        gDebug.Printer(RxData);  
        ...  
    }  
    void Other_IRQHandler()  
    {  
      ...    
    }  
        ....  
           
        } </strong>

        结果,OK了。分析如下:
        (1)IRQHandler.c文件重命名为IRQHandle.cpp,让编译器用C++的方式进行编译,从而include C++ 的头文件不会报错。

        (2)加 extern "C" 的功能就是告诉编译器不对根据形参类型重新命令函数名,使之与C函数名一致。


        所以,要在C函数调用C++的成员函数,就像方案3那么做。


    展开全文
  • 我的想法是,通过将一个Python函数注册到侦听器,当有按键中断触发的时候,调用这个Python函数。也就是说,这些函数不是事先写死到代码中的,而是通过动态注册的方式实现。这里面要用到的一个特殊的方法是mp_sched_...

    接下来的任务是实现在C语言中调用Python函数。

    我的想法是,通过将一个Python函数注册到侦听器,当有按键中断触发的时候,调用这个Python函数。

    也就是说,这些函数不是事先写死到代码中的,而是通过动态注册的方式实现。

    这里面要用到的一个特殊的方法是mp_sched_schedule,提供个C调用Python的方法,但是此方法目前好像只能传一个参数过去,还没来得及传入多个参数用那个方法。

    废话不多说,开始写码!

    第一步,在昨天代码的基础上,对children_obj_t扩展出一个lollipop_arrive的方法,并定义一个active_children作为最后的活动类,让C知道应该触发谁的事件,同时对mars_children_make_new方法进行简单修改,加入一行active_children=self;代码如下:typedef struct _children_obj_t

    {

    mp_obj_base_t base; // 定义的对象结构体要包含该成员

    char* name; // 成员函数

    uint8_t age;

    uint8_t sex;

    **mp_obj_t lollipop_arrive; //当棒棒糖到达的时候的回调函数**

    }children_obj_t;

    **children_obj_t* active_children;**

    STATIC mp_obj_t mars_children_make_new(const mp_obj_type_t *type,

    size_t n_args , size_t n_kw,const mp_obj_t *args)

    {

    mp_arg_check_num(n_args ,n_kw,1,3,true); // 检查参数个数,最少1个参数,最多3个参数

    children_obj_t *self = m_new_obj(children_obj_t); // 创建对象,分配空间

    self->base.type = &mars_children_type; // 定义对象类型

    if(n_args >=1 )

    { self->name = mp_obj_str_get_str(args[0]); }

    if(n_args >=2 )

    { self->age = mp_obj_get_int(args[1]); }

    if(n_args ==3 )

    { self->sex = mp_obj_get_int(args[2]); }

    printf("Create a new children , name:%s , age:%d , sex:%s\n"

    ,self->name,self->age,self->sex==0?"girl":"boy");

    **active_children=self; //将最后一个定义的对象设置为活动对象**

    return MP_OBJ_FROM_PTR(self); //返回对象

    }

    只有加粗的部分是修改的,其他和昨天的一样一样的。

    第二步,写中断触发函数和侦听器注册函数,以及定义字典,最后别忘了QDEF(MP_QSTR_set_fun, (const byte*)"x85x07" "set_fun")//中断服务函数

    void Key_Handler(void* args)

    {

    rt_interrupt_enter(); //通知操作系统此时进入中断状态

    int16_t key;

    memcpy(&key, (void *)&args, sizeof(int16_t));

    mp_sched_schedule(active_children->lollipop_arrive, MP_OBJ_FROM_PTR(mp_obj_new_int(key)));

    rt_kprintf("KeyOn %d!\n",key);

    //调用Python

    rt_interrupt_leave(); //通知操作系统此时离开中断状态

    }

    //侦听器设置函数

    STATIC mp_obj_t mars_children_set_fun(mp_obj_t self_in , mp_obj_t fun)

    {

    children_obj_t *self = MP_OBJ_TO_PTR(self_in);

    self->lollipop_arrive = fun;

    return mp_const_none;

    }

    STATIC MP_DEFINE_CONST_FUN_OBJ_2(mars_children_set_fun_obj,mars_children_set_fun);

    STATIC const mp_rom_map_elem_t children_locals_dict_table[] = {

    { MP_ROM_QSTR(MP_QSTR_sayhello),MP_ROM_PTR(&mars_children_sayhello_obj) },

    **{ MP_ROM_QSTR(MP_QSTR_set_fun),MP_ROM_PTR(&mars_children_set_fun_obj) },**

    };

    在上一段代码中,主要是mp_sched_schedule函数的使用,函数有两个参数,第一个是调用的方法,第二个是传入的一个参数,目前只研究出了单参的传递,多参的回头试试用数组能否解决,下面一行的打印是用来做测试的,不会在Python中输出。这里需要提醒一下大家,这种调用尽量还是用线程间通讯的方式去做,尽可能的不要在中断中处理任何事务,所以我们这种方法,仅作为例子参考。

    第三步,安装中断,这个在main函数中写就行了// 安装中断

    rt_pin_mode(KEY_1,PIN_MODE_INPUT_PULLDOWN); //上拉输入

    rt_pin_attach_irq(KEY_1,PIN_IRQ_MODE_RISING,Key_Handler,(void*)1); //上升沿导通

    rt_pin_irq_enable(KEY_1,PIN_IRQ_ENABLE); //使能中断

    rt_pin_mode(KEY_2,PIN_MODE_INPUT_PULLDOWN); //上拉输入

    rt_pin_attach_irq(KEY_2,PIN_IRQ_MODE_RISING,Key_Handler,(void*)2); //上升沿导通

    rt_pin_irq_enable(KEY_2,PIN_IRQ_ENABLE); //使能中断

    这里注册了两个按键,分别是PA0和PC13(这个根据你板子而定)

    好了,整体代码就完成了,烧入板子,我们开始写python部分。

    这里需要提醒一下,最好在进入Python之前测试一下你的中断是否被触发了。第一次写的时候发现引脚搞错了,死活没有中断,我以为是Python写的有问题呢,最后测试是GPIO的问题……单元测试还是很有必要的。

    开机启动后进入Python,我是在main中用mpy_main进入的,如果用文件方式启动,执行完就退出了,无法测试中断。

    Python代码中,主要流程是定义一个children实例,然后给这个实例注入一个触发函数,最后,通过实体按键实现函数的触发。import mars

    e = mars.children("Claire",8,0)

    def lollipop(num):

    print("Thank you for the lollipop:%d"%(num))

    e.set_fun(lollipop)

    这段代码通过串口调试助手发送。定义方法后不要手动输入空格,直接输入内容就行了,然后下面的三个空行是跳出方法定义继续执行下面的,这个函数只有一个参数,多了会报错。

    set_fun把这个函数注入到侦听器中,然后就可以出发中断了。>>> import mars

    >>> e = mars.children("Claire",8,0)

    Create a new children , name:Claire , age:8 , sex:girl

    >>> def lollipop(num):

    ... print("Thank you for the lollipop:%d"%(num))

    ...

    ...

    ...

    >>> e.set_fun(lollipop)

    >>> Thank you for the lollipop:1

    Thank you for the lollipop:2

    展开全文
  • 在定时器中断函数中写一个事件函数,触发中断后调用上层C++类对象设置的回调函数,但是这个上层对象类型不确定,传给定时器的只有一个回调函数地址和该对象本身this的地址。 现在要怎么写?我想到的结构应该就像...
  • 函数调用中断产生时程序的跳转、OS中的Multiple Task的切换等等这些均属于分支范畴,说的直白点就是根据需求合理的控制执行流。执行这些跳转共有一个特征:还需要再返回到跳转前的Point。怎么记录需要返回到哪里?...

    在计算机程序中,分支[Branch]具有很重要的意义。函数调用、中断产生时程序的跳转、OS中的Multiple Task的切换等等这些均属于分支范畴,说的直白点就是根据需求合理的控制执行流。执行这些跳转共有一个特征:还需要再返回到跳转前的Point。怎么记录需要返回到哪里?多层级的调用和返回都是有顺序的,怎么保证这些顺序?跳转前如有执行的环境怎么原封不动地保存?这样返回时才能继续之前的工作。程序执行的环境或者说执行流从CPU层次来看主要包括哪些核心内容?本篇文章重点介绍这些内容。

    何为Stack?

    Stack是这样一种结构:本身是一段连续的内存空间,那怎么使用这样一种内存空间才算是起到了栈的实际作用呐?首先要规定这一段连续空间的基地址,然后就从这个基地址开始依次放东西。取东西时也是从最上面的开始取。Stack这个英文含义就是把东西堆叠起来的意思。就像水桶盛水一样,进水的时候是从低往上,放水的时候是从上往低。存取有一定的先后顺序. 按照上面的方案管理这一段存储空间,就是发挥了栈的作用。因为栈使用的频率实在是太高了,所以在计算机汇编层次就有专门操作栈的指令。包括Push(入栈)、Pop(出栈)等。

    其实栈又有一些逻辑上的分类:

    根据先腾出空间再用还是先用再腾空间分为:

    1. 满堆栈:即入栈后堆栈指针sp指向最后一个入栈的元素。也就是sp先减一(加一)再入栈。

    2. 空堆栈:即入栈后堆栈指针指向最后一个入栈元素的下一个元素。也就是先入栈sp再减一(或加一)。

    根据从高地址开始用还是从低地址开始用分为:

    1. 递增堆栈:即堆栈一开始的地址是低地址,向高地址开始递增。就如同一个水杯(假设上面地址大)开口的是大地址,从杯底开始装水。自己画一画图就清楚了。我就偷懒一下不画了。

    2. 递减堆栈:即堆栈一开始的地址是高地址,向低地址开始递增。就如同还是刚才说的那个水杯,现在开口的是小地址,从大地址开始用,往下走,相当于杯子口朝下。我们用的时候是把水往上一点点压上去。呵呵呵,不过这样的杯子就失去了用途。但在内存上还是可以的。

    那么根据这上面分类方法,我们就可以得到四种栈的类型。

    Cortex-M3中使用的是递减满堆栈(full descending stack)。

    Stack在函数调用时的应用

    我们使用高级语言设计程序时,为了程序的简介性和易读性,使用合理的函数抽象和函数调用是比较常用的设计方法。在函数调用时,有两个核心的问题需要关注:指令流(PC)的控制、临时数据(FP)的保存和恢复。这个也好理解,数据结构里提到的程序=数据结构+算法也是一个意思,一个程序就有两个重要的内容:指令、数据。通俗地描述一下函数调用时需要考虑的问题,大家想一想,我们写c语言时用到函数调用,有时候还嵌套调用很多函数。还有有些函数还需要参数和返回值。怎么处理各个函数的参数和返回值,以及当每一个函数完成工作时该返回到那个地方。这些都是要解决的问题。当然最容易想到的也是必须做的是在进行调用跳转之前,把我这个函数现有的状态保存起来,保存什么那,调用函数返回后的下一条指令,还有我这个函数需要的哪些数据。还有就是保存这些信息到哪些地方哪?这些都是我们要解决的问题。还有就是你不光要保存这些信息,还要保存这些信息的顺序。因为函数调用本身有顺序,你像a调用b,b又接着调用c。在c执行完后要返回到b,b执行完再返回a。呵呵,有顺序。

    我们一一想办法来解决,当然别人已经用栈的策略解决的很完美了,我们只是想一些更简洁的最容易想起来的但是不完善的方法,也正说明了人家的策略是多么的优秀。

    关于调用函数的问题,我们可以把返回地址保存到一些地方,当然程序员知道在那?还知道顺序,再根据顺序返回就好了,但做这样的工作太累了,除了写程序还要记这些东西。这样太累了,聪明的程序员肯定也不这样做。关于传参,有这样可以考虑的,用专门规定好的寄存器来做传参。行,但有缺陷,如果传的参数很多或者是变化的,就不好用寄存器传参了。而且我们有操作系统时往往要求编译器产生的代码具有可重入性,也就是保证代码和数据的相对独立性。一个函数被调用两次,都有两次的参数环境。到底现在我们是怎么做的呐。答案是用栈。

    其他废话不多说了,直接上个Cortex-M3的Demo。

    function_stack.c :

    int  add(int a,int b);
    void test(void);
    
    
    void  main_frame(void)
    {
            int result = 11;
            result = add(1,2);
    
    }
    
    
    
    int  add(int a,int b)
    {
            int result_temp = 22;
    
            result_temp = a + b;
            test();
            return result_temp;
    }
    
    void test(void)
    {
            int a = 31,b = 32,c = 33;
    
    
    }

    function_stack.s :

            .cpu arm7tdmi
            .eabi_attribute 20, 1
            .eabi_attribute 21, 1
            .eabi_attribute 23, 3
            .eabi_attribute 24, 1
            .eabi_attribute 25, 1
            .eabi_attribute 26, 1
            .eabi_attribute 30, 6
            .eabi_attribute 34, 0
            .eabi_attribute 18, 4
            .file   "function_stack.c"
            .text
            .align  2
            .global main_frame
            .syntax unified
            .arm
            .fpu softvfp
            .type   main_frame, %function
    main_frame:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 8
            @ frame_needed = 1, uses_anonymous_args = 0
            push    {fp, lr}
            add     fp, sp, #4
            sub     sp, sp, #8
            mov     r3, #11
            str     r3, [fp, #-8]
            mov     r1, #2
            mov     r0, #1
            bl      add
            str     r0, [fp, #-8]
            nop
            sub     sp, fp, #4
            @ sp needed
            pop     {fp, lr}
            bx      lr
            .size   main_frame, .-main_frame
            .align  2
            .global add
            .syntax unified
            .arm
            .fpu softvfp
            .type   add, %function
    add:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 16
            @ frame_needed = 1, uses_anonymous_args = 0
            push    {fp, lr}
            add     fp, sp, #4
            sub     sp, sp, #16
            str     r0, [fp, #-16]
            str     r1, [fp, #-20]
            mov     r3, #22
            str     r3, [fp, #-8]
            ldr     r2, [fp, #-16]
            ldr     r3, [fp, #-20]
            add     r3, r2, r3
            str     r3, [fp, #-8]
            bl      test
            ldr     r3, [fp, #-8]
            mov     r0, r3
            sub     sp, fp, #4
            @ sp needed
            pop     {fp, lr}
            bx      lr
            .size   add, .-add
            .align  2
            .global test
            .syntax unified
            .arm
            .fpu softvfp
            .type   test, %function
    test:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 16
            @ frame_needed = 1, uses_anonymous_args = 0
            @ link register save eliminated.
            str     fp, [sp, #-4]!
            add     fp, sp, #0
            sub     sp, sp, #20
            mov     r3, #31
            str     r3, [fp, #-8]
            mov     r3, #32
            str     r3, [fp, #-12]
            mov     r3, #33
            str     r3, [fp, #-16]
            nop
            add     sp, fp, #0
            @ sp needed
            ldr     fp, [sp], #4
            bx      lr
            .size   test, .-test
            .ident  "GCC: (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 7.2.1 20170904 (release) [ARM/embedded-7-branch revision 255204]"

    先用gdb直观的感觉一下function stack。
    在函数test处打个Break。

    (gdb) bt
    #0  0x0800023c in test () at main.c:48
    #1  0x0800022a in add (a=1, b=2) at main.c:42
    #2  0x080001e2 in main () at main.c:29
    (gdb) frame 0
    #0  0x0800023c in test () at main.c:48
    48              int a = 31,b = 32,c = 33;

    再来张图直观感受下,DrawSquare调用DrawLine。

    Call stack](/images/posts/2018-05-22![Call stack Frame

    下面我们好好分析一下函数调用的函数帧。
    整个函数栈帧 main—add—test 。
    下面详细分析下main函数调用add函数,然后add函数调用test的整个过程,分析下Stack在其中的作用。
    main函数在调用add时,有两方面的工作需要做:保存main函数的当前数据状态(核心Registers)、传递的参数、返回参数和test函数执行完毕后的返回地址等相关的执行流。我们看编译器编译出来的汇编代码,控制data相关的Registers是:FP、SP ;控制指令流的Registers为PC、LR。一个基本原则是如果函数调用发生后,子函数需要改变的那些个Registers都需要保存到Stack中。

        push    {fp, lr}
        add fp, sp, #4
        sub sp, sp, #8
        mov r3, #11
        str r3, [fp, #-8]

    我们知道add函数会使用FP来操作add函数内部的data registers,而add函数里面又调用了test函数,从而改变了LR的值,因此需要将FP和LR保存到main函数栈帧中。这样当add函数执行完返回时,弹出原来保存的FP、LR就可以恢复到原来main函数时的状态。下面是调整FP的指针值,使其指向Stack中FP的位置。以后在main函数栈帧里操作数据都使用FP这个指针,以这个为基础操作Stack。接下来就要分配main函数需要的局部变量了,包括main函数里自己申请的局部变量result,以及其他需要传递给add函数的参数。我们看到我们将result的值11放到了R3中,然后存到了main的栈帧中,原因是因为main函数里需要使用这个R3,但子函数add中也有可能使用R3这个Register,因此需要将R3保存到main函数的栈帧中,到时候从add函数返回时可以恢复到未调用add函数之前的main函数状态。

    后面就开始给add函数传递参数了。

        mov r1, #2
        mov r0, #1

    可以看到使用的是R0和R1寄存器来传递1 和 2 这两个参数。这个是要参考AAPCS的,这个规范里就是这么规定的。

    下面直接执行bl 指令用于跳转。

        bl  add

    没事,别担心,我们已经把main函数相关的内容都保存起来了,不怕add函数破坏,破坏了我可以再恢复。^_^。
    bl指令修改了两个Registers的值,PC和LR。

    下面开始到了add的地盘,

        push    {fp, lr}
        add fp, sp, #4
        sub sp, sp, #16
        str r0, [fp, #-16]
        str r1, [fp, #-20]
        mov r3, #22
        str r3, [fp, #-8]
        ldr r2, [fp, #-16]
        ldr r3, [fp, #-20]
        add r3, r2, r3
        str r3, [fp, #-8]

    老套路,因为后面add函数会调用test函数,所以先把核心的FP LR保存起来,调整FP指针。add函数用到了R0,R1这两个Registers,而后面又要调用test函数,因此要把R0,R1这两个Registers保存起来。后面还有一个result_temp的值为22,也需要保存起来。后面执行了具体的加法操作。把结果存到了R3中。然后执行bl指令,跳转到test函数。

    test函数里面和main、add函数栈帧也基本一致,只不过test没有要调用的子函数了,不会修改LR指针,因此不需要保存LR了。其他的倒是没什么特别的地方。

    str fp, [sp, #-4]!
        add fp, sp, #0
        sub sp, sp, #20
        mov r3, #31
        str r3, [fp, #-8]
        mov r3, #32
        str r3, [fp, #-12]
        mov r3, #33
        str r3, [fp, #-16]
        nop
        add sp, fp, #0
        @ sp needed
        ldr fp, [sp], #4
        bx  lr

    调用正向过程分析完了,我们接着分析一下Return过程。重点分析add返回到main的情况。

        ldr r3, [fp, #-8]
        mov r0, r3
        sub sp, fp, #4
        @ sp needed
        pop {fp, lr}
        bx  lr

    add函数需要把结果返回到main函数中,使用当前栈帧的FP将add的返回值load到R3中,然后放到R0中,最后根据FP调整SP,最后把弹出刚进到add函数时保存到FP,LR的值从Stack中弹出来。这样LR的值就是add函数执行结束后,需要返回的值,FP就是main函数的栈帧FP指针。R0中保存的是add函数的返回值,当然也是AAPCS规定的喽。执行bx之后,一切后回到了main函数栈帧。

    分析完了,这里面有两个链表结构:一个PC和LR组成的指令流链表;一个SP和FP组成的数据流链表。以FP作为例子,FP始终指向当前Stack存放上一个FP的栈地址上,操作这个FP可以读取到当前栈帧的Data,当前函数返回后,将FP指向的Stack里存的上一个FP的值放到FP中,就保证了FP永远指向当前函数的栈帧。

    Call Stack 最好是自己动手画画动态的栈帧变化,你就理解什么叫Call Frame了,也顺便理解了什么是Local Variable了.

    同时,从上面的分析我们也可以看到,从CPU角度来看,一个执行流的核心资源就是一些Registers再加上一些逻辑运算和Mem的操作

    对计算机描述比较清楚的一本书是Structured Computer Organization(Andrew S.Tanenbaum) 欢迎阅读这本书。

    要想系统的了解汇编语言级别的函数调用规范和二进制规范还请参考以下规范:
    * AADWARF DWARF for the ARM Architecture
    * AAELF ELF for the ARM Architecture
    * AAPCS Procedure Call Standard for the ARM Architecture
    * ADDENDA Addenda to, and errata in, the ABI for the ARM Architecture
    * BPABI Base Platform ABI for the ARM Architecture
    * BSABI This document ABI for the ARM Architecture (Base Standard)
    * CLIBABI C Library ABI for the ARM Architecture
    * CPPABI C++ ABI for the ARM Architecture
    * EHABI Exception Handling ABI for the ARM Architecture
    * EHEGI Exception handling components, example implementations
    * RTABI Run-time ABI for the ARM Architecture
    * GC++ABI Generic C++ ABI
    * GDWARF DWARF 3.0, the generic debug format.
    * GABI Generic ELF, 17th December 2003 draft.
    * GLSB gLSB v1.2 Linux Standard Base
    * Open BSD Open BSD standard

    Stack在中断或者异常发生时的应用

    我们以STM32F103(Cortex-M3)为例,具体说明下中断或者异常发生时Stack的作用。
    前面说了,只要有Branch的产生,就需要保存跳转之前的一些运行状态。正常情况下,Cortex-M3会在Thread Mode运行一个执行流。当有外部中断或者CPU异常发生时,会切换到Handle Mode,且需要使用Stack保存一些当前状态,以保证中断/异常服务程序返回时,能继续被打断的工作。

    具体这些内容请参见
    STM32 Programming manual 的2.3 章节Exception model 和 Cortex-M3 Devices Generic User Guide 的2.3章节的内容。

    这里仅仅简单介绍一下大致流程,当中断或者异常发生时,硬件会自动保存部分关键Registers到当前使用的Stack中。当然,其他寄存器需要软件保存到Stack。然后将一个特殊的Return Value(0xFFFFFFFx) 放到LR寄存器中,当中断/异常服务程序返回时,CPU通过判断这个特殊的返回值得知这是一个中断或者异常跳转,然后硬件自动将之前保存的关键Registers弹出来以恢复Thread的运行。这里有一些细节,RTOS用到的一些硬件特性会有一个专门的文章介绍。

    Stack在RTOS中的应用

    前面说了,Cortex-M3这个CPU默认的执行流只有两个,一个是Thread Mode下的执行流,一个是当中断或者异常发生时产生的Handle Mode下的执行流。RTOS提供的一个核心服务是抽象出更多的执行流,也叫Task。关于为什么要抽象出更多的执行流,前面文章里也给出了明确的说明。这个核心控制器一般CPU是单core的,只能提供一个执行流,再加上Interrupt,可以说整体就有了两个可管理的执行流,且中断的执行流优先级要大于CPU提供的执行流。

    如果利用软件管理手段能把CPU上的执行流管理起来,势必能加速整体的吞吐量和实时响应能力。因为CPU上的执行流从宏观上来看利用CPU进行运算的时间是间隔的,这个也很好理解,因为CPU运算能力是很强的,但是一个任务一般会涉及到等待外部事件,外部事件发生后才去做逻辑处理或者数值运算。在等待外部相对于CPU运算来说慢的多的多的事件时,如果能把CPU让给其他任务来用,那CPU的整体利用率就很高了。真是不错的想法。一个嵌入式产品不可能不与外部交互,毕竟科技产品是为人服务的。这样一个执行流在等待一些慢的事件时,可以把CPU让出来让其他Task使用,这样整个CPU利用率就提高了,系统整体响应就快了。

    切换Task对CPU来讲,就是保存当前Task的一些核心的Registers状态,然后Load要切换的Task原先保存好的各个寄存器。就实现了Task切换。每个Task都有自己独立的Stack,保存了当前CPU所有的Registers。
    来张图:
    RTOS Stack

    你看,每一个Task都有一个独立的Stack,用于保存自身的运行状态。

    引用资源

    C语言和堆栈

    AAPCS: The ARM-THUMB Procedure Call Standard

    BSABI: ABI for the ARM Architecture (Base Standard)

    STM32 Programming manual

    Cortex-M3 Devices Generic User Guide

    展开全文
  • 本文整理自:... 要想知道ecos的中断ISR是怎么与硬件中断向量联系起来的,是怎么调用的? 那就要看下面这两个关键的函数: cyg_drv_interrupt_create() cyg_drv_interrupt_attach() 复制代码

    本文整理自: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层的宏,那就继续追踪这个宏吧,它定义在<cyg/hal/hal_intr.h>里,如下:
    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 宏定义在<cyg/infra/cyg_type.h>中:
    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相关的代码。
    展开全文
  • Linux在众多进程中是怎么进行调度的,这个牵涉到Linux进程调度时机的概念,由Linux内核中Schedule()的函数来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等等。Linux进程调度时机主要有:1、进程状态...
  • 永远阻塞的系统调用是指调用永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就会一直阻塞。 慢系统调用可以被永久阻塞,包括以下几个类别: (1)读写‘慢’...
  • 怎么拦截系统函数

    千次阅读 2006-01-10 02:42:00
    ) 在Dos下,我们可以拦截中断,用自己的处理替换系统中断,然后调用原来的中断,在Windows中,没有了中断,只有函数,那么我们如何来拦截系统的函数,然后我们的函数调用原来的系统函数呢?比如说系统有一个...
  • 正如生活之难一样,难的是适应环境而不是改变环境。编程之难难的不是编程。而是调试。调试之难难在排除各种复杂条件下的错误。软件硬件。。 像单片机,这类软件硬件结合的产品...他怎么也不会碰到这些问题。而这也是差
  • Linux在众多进程中是怎么进行调度的,这个牵涉到Linux进程调度时机的概念,由Linux内核中Schedule()的函数来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等等。 Linux进程调度时机主要有: 1、进程...
  • 同时,这也有助于我们理解系统调用的过程,了解3环API是怎么进0环的。 二、分析逆向 ReadProcessMemory 和 WriteProcessMemory ReadProcessMemory 和 WriteProcessMemory 是kernel32.dll的函数。 首先来看 ...
  • 其实基于的思想是一样的,就是查表,每一种系统函数都对应一种中断服务号,然后通过0x80系统调用进入内核,然后查表,这里就可以找到对应的内核系统函数,回顾一下linus是怎么做到的。 就以系统函数open为例子int ...
  •  高内聚:一个C文件里面的函数,只有相互之间的调用,而没有调用其它文件里面的函数,这样可以视为高内聚。尽量减小不同文件里函数的交叉引用。  低耦合:一个完整的系统,模块与模块之间,尽可能的使其独立存在...
  • 但是,最开始想当然的使用HAL库中的HAL_UART_Receive_IT() 和HAL_UART_Receive(),调用函数发现怎么也不对,总是有bug。网上查了一堆,博客很多,解决不了我的问题啊,按照他们的方法我还是不能接收到数据。 后来...
  • 中断分析

    2014-08-22 14:33:00
    MINIX3 中断机制源码分析 下面我们来探讨下 MINIX 中断处理函数的具体处理流程: 由前面的章节知道,MINIX 的架构...我们会在系统任务里 设置一个中断注册调用,通过那个调用,我们能够将用户需要的中断处理函数挂 ...
  • 系统调用是内核和应用程序间的接口,应用程序要访问硬件设备和其他操作系统资源,可以通过...C库中的函数可以不调用系统调用,也可以只是简单封装一个系统调用,可以通过调用多个系统调用来实现一个功能。 从程序员的
  • (使用锁是无法解决可重入问题的,因为当出现信号中断的,中断函数里再次调用此函数就会出现死锁!) 而线程安全函数,如果使用了静态数据或者全局数据的话,可以用锁来将其变为线程安全的。(但无论怎么样都无法...
  • 解析asm_do_IRQ,了解中断处理,怎么进行的。     单片机下的中断处理: 1、分辨是哪一个中断 2、调用处理函数 3、清中断     Linux内核: asm_do_IRQ(C函数入口)最终 调用到 事先初始化的handle_irq...
  • * 时间中断函数 */ void PASCAL OneMilliSecondProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dwl, DWORD dw2) { printf("Timout!\n"); longjmp(j,1); } int longTimeFunction(){ while (1) { printf(...
  • 不可重入怎么改改成可重入函数。一个可重入的函数简单来说就是可以被中断函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数...
  • desc结构体来描述这些中断,里面记录了中断名称、中断状态、中断标记、并提供了中断的底层硬件访问函数(如:清除、屏蔽、使能中断),提供了这个中断的处理函数入口,通过它还可以调用用户注册的的中断处理函数。...
  • 系统调用

    2011-11-09 22:20:26
    而一般的系统调用的过程是:我们的程序调用C库包装好的一些函数,这些函数去执行系统调用,然后再把返回的结果给我们看。现在有一个问题就出现了:用户空间是不能去访问内存空间的,那么怎么去运行内核空间中的代码...
  • 中断的内容真的好多,课是听完...确定了之后又是怎么调用Key的中断处理函数的?中断处理函数在内核中是怎么运行的?怎么退出中断等等。 自己想的不全面,网上有文章写的很好:https://www.cnblogs.com/arnoldlu/...
  • 从前面的介绍,我们知道了裸机中断处理的流程、WINCE驱动的中断处理,但是,WINCE底层是...一、在OAL层的初始化函数,在系统启动的过程中被调用,如下所示: BOOL OALIntrInit() { BOOL rc = FALSE; // Initializ
  • 一、STM32的启动过程:1、复位第一条指令:Reset_Handler PROC,这里指定为 LDR R0, =__main...二、如何跑到中断入口地址我们也知道怎么跳到main函数了,那么,中断发生后,又是怎么跑到中断入口地址的呢?从stm32f...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 186
精华内容 74
关键字:

怎么调用中断函数