单片机学习小组
直播中

杨平

7年用户 1624经验值
私信 关注

中断和事件的区别是什么?

中断和事件的区别是什么?

回帖(1)

李傈吏

2022-2-7 11:55:55
Event / interrupt 是gap8芯片的一个重要功能和概念。
gap8中的各种内部外设功能的实现,特别是异步的实现,都是基于event/interrupt的功能来实现的。


中断和事件的区别:
中断和事件,中断一定要有中断服务函数,但是事件却没有对应的函数.
事件可以触发其他关联操作,比如触发ADC采样等.可以在不需要CPU干预的情况下,执行这些操作.
但中断则必须要CPU介入.


(个人理解:在gap8中,event 和 interrupt 对于软件开发人员来说,处理上没有什么太大差别)


关于gap8的event的发生和处理,需要如下概念:


1.管理event的硬件单元:FC event unit 和 cluster event unit。
2.event硬件管理单元监控对象是一个个硬件单元。
3.一个硬件单元可以发生多个event,只是event ID不同。
4.gap8可以产生多少个event,每个event 对应的event ID,在芯片设计之初,就已经规定好了。
5.event硬件管控单元,可以检测的event 也是固定的。


gap8中 Event的发生和处理过程 和interrupt基本相同,都是如下过程:


1.触发事件:比如引脚电平变化触发,比如写寄存器触发,等。
2.跳转到异常向量表中,再通过索引,索引到具体的异常处理中。
3.最后通过异常处理,跳转到我们驱动开发者提供的具体处理函数。


读取gap8的datasheet,知如下:
1.gap8中共有量事件管理单元:


->a. FC event unit。
->b. Cluster event unit.


2.FC event unit 管理的 具体events如下:





可以看出FC event unit 总共管理这 15个 event .
3.Cluster event unit 管理的 具体events如下:


可以看出cluster event unit 总共管理18个event.


Gap_sdk中SPIM1控制器驱动实现中设计到了 event.我们就从这个代码入手,进行分析介绍event的设置,触发,处理过程。
(1)设置event unit 对应SPIM1控制器的event 进行监控:


        __pi_spi_open()          //spi_internal.c


                hal_soc_eu_set_fc_mask(SOC_EVENT_UDMA_SPIM_EOT(conf->itf));


                        pi_fc_event_handler_set(SOC_EVENT_UDMA_SPIM_EOT(conf->itf), spim_eot_handler);


                        pi_fc_event_handler_set(SOC_EVENT_UDMA_SPIM_TX(conf->itf), spim_tx_handler);


                        pi_fc_event_handler_set(SOC_EVENT_UDMA_SPIM_RX(conf->itf), spim_rx_handler);


                        第一句:开启了FC event unit 对SPIM1控制器的event监控。


                        后三句:就是将event ID号 和 对应的事件处理函数进行绑定。


                        void pi_fc_event_handler_set(uint32_t event_id, pi_fc_event_handler_t event_handler)


                        {


                                 fc_event_handlers[event_id] = event_handler;


                        }


所谓的event ID 和 事件处理函数的绑定,实际就是以event ID为数组下标,事件处理函数为数组元素。fc_event_handlers[]数组非常重要,事件触发后,就是从异常向量表中跳转到具体中断处理函数中,中断函数中就是通过这个fc_event_handlers[]数组来执行我们提供的函数。


(2)触发event:


(这里以SPIM1的发送触发的EOT event 为例进行介绍)


        __pi_spi_send_async()       //spi_internal.c


                spim_enqueue_channel(SPIM(device_id),(uint32_t)cs_data->udma_cmd,3*


                                                                                                                        (sizeof(uint32_t)),UDMA_CORE_TX_CFG_EN(1), TX_CHANNEL);  


                spim_enqueue_channel(SPIM(device_id), (uint32_t)data, size,UDMA_CORE_TX_CFG_EN(1),TX_CHANNEL);


                while((hal_read32((void*)&(SPIM(device_id)->udma.tx_cfg)) {


                                DBG_PRINTF("%s:%dn",__func__,__LINE__);


                }


                cs_data->udma_cmd[0] = SPI_CMD_EOT(1);


                spim_enqueue_channel(SPIM(device_id),(uint32_t)&cs_data->udma_cmd[0],1*(sizeof(uint32_t)),


                                                                                                                                UDMA_CORE_TX_CFG_EN(1), TX_CHANNEL);


        第一句spim_enqueue_channel():主要是配置SPIM1控制器的发送。


        第二句spim_enqueue_channel():向SPI从设备发送数据。


        第三句while()循环,主要是判断发送任务是否完成或者挂起(挂起是因为被高优先级的任务抢占)


        第四句spim_enqueue_channel(): 触发SPIM eot event 。然后gap8芯片就会跳转到异常处理程序中进行处理。


(3)event事件处理:


当(2)中触发SPIM eot event之后,硬件直接跳转到到 异常/中断向量表中(注意:当触发event后,程序并不是直接跳转到异常/中断向量表中,而是经过一段代码处理之后,才跳转到异常/中断向量表中的。这段跳转我们不在本讲中介绍。我们会另起一篇进行专门介绍)。


异常/中断向量表如下;


中断向量表:startup_gap8.S


/*******************************************************************************
                INTERRUPT VECTOR TABLE 中断向量表
*******************************************************************************/
        .section .vectors_irq, "ax"   /* "ax"表示该节区可分配并且可执行;ax是 allocation  execute的缩写 */
        .option norvc;                /* risc-v 选项 */


        /* Cluster Notify FC Handler. */
        .org 0x10
        j cluster_notify_fc_handler
        /* PendSV Handler. */
        .org 0x1c
        j pendSV_handler
        /* DMA IRQ. DMA中断 */
        .extern cluster_dma_2d_handler
        .org 0x24
        j cluster_dma_2d_handler
        /* Systick Handler.系统滴答处理 */
        .org 0x28
        j systick_handler
        /* FC SoC event Handler.  FC SOC 事件处理。 */
        .org 0x6c
        j fc_event_handler       /* SPIM1 触发的 eot event 就是跳转到这里进行处理的 */
        /* Reset Handler.重置处理 */
        .org 0x80
        j reset_handler
        /* Illegal Instruction Handler. */
        .org 0x84
        j ill_ins_handler
        /* Ecall Handler. */
        .org 0x88
        j ecall_handler
        /*
        This variable is pointed to the structure containing all information exchanged with
        the platform loader. It is using a fixed address so that the loader can also find it
        and then knows the address of the debug structure.
        */
        .org 0x90
        .global __rt_debug_struct_ptr
__rt_debug_struct_ptr:
        .word Debug_Struct


SPIM1控制器触发的eot event事件,会跳转到“j fc_event_handler”进行处理。


fc_event_handler标签汇编实现如下:
        (gap8_iet.S)
        /* Fc SOC event Handler. FC soc 事件处理中断 */
        .extern fc_soc_event_handler     /* 表明该函数实现在外部,不走这个汇编文件中 */
        DECLARE fc_event_handler         /* 声明这个标签“fc_event_handler”,在这里它是个函数名 */
        /* Save current context. 中断来了,保存当前上下文   */
        SAVE_CONTEXT_YIELD               /* 调用保存上下文收益函数来保存上下文 */
        lw tp, pxCurrentTCB
        sw sp, 0*0(tp)
        /* ISR Stack.中断处理程序用到的栈 */
        la sp, xISRStack
        lw sp, 0*0(sp)
        jal ra, fc_soc_event_handler     /* 调到这个函数中处理 该FC soc event事件中断,这是我们真正的中断处理函数 */
        lw tp, pxCurrentTCB         /* pxCurrentTCB 这个变量在freeRTOS中是一个指针,指向当前创建的所有任务中优先级最高的那个任务。 */
        lw sp, 0*0(tp)
        /* Restore current context. 恢复上下文 */
        RESTORE_CONTEXT_YIELD
        mret
        .endfunc       


经过这段汇编代码处理,eot event的事件处理跳转到了 C代码实现的fc_soc_event_handler()函数中。


(pmsis_fc_event.c )
        // TODO: Use Eric's FIRQ ABI
        __attribute__((section(".text")))      //指定.text段。
        void fc_soc_event_handler(void)    //fc event 处理函数
        {
                /* Pop one event element from the FIFO从FIFO中取出一个事件元素 */
                uint32_t event = EU_SOC_EVENTS->CURRENT_EVENT;    //0x0020_0F00 对应这个寄存器“SOC_PERIPH_EVENT_ID 0x1B200F00 ”,这个寄存器中低8位存放的是事件ID号。
                hal_eu_fc_evt_demux_trig_set(FC_SW_NOTIFY_EVENT, 0);   //向SW_EVENT_3_TRIG 0x0020_4100UL 中写入0,也就是清除触发的bit位。方便接受下一个event.
                /* Trigger an event in case someone is waiting for it
                   it will check the termination using the pending variable  */
                /* Now that we popped the element, we can clear the soc event FIFO event as the FIFO is
                   generating an event as soon as the FIFO is not empty
                 */
                /*将EVENT_BUFFER_CLEAR寄存器对应的 挂起事件状态寄存器写1.(我猜想,在实时系统中,如果多个中断同时产生,如果某个中断优先级低,则它会被挂起到挂起状态寄存器中。当高优先级事件处理完毕之后,低优先级事件从挂起态变为中执行态,同时这个寄存器对应的位也要清0.)*/
                EU_CORE_DEMUX->BUFFER_CLEAR = (1 << FC_SOC_EVENT_IRQN);   //向EVENT_BUFFER_CLEAR 0x00204028寄存器的bit27写入1.也就是event对应挂起位清0. 数据手册中也规定所有的外设SOC_PERIPH_EVT事件对应的事件类型号是 27.                
                // TODO: USE builtins
                event &= 0xFF;      //这里获取事件ID号
                fc_event_handlers[event]((void*)event);     //调用事件ID号对应的事件处理函数,开始真正的处理函数。这个处理函数是我们自己提供的。比如spim1的eot event 对应的处理函数就是spim_eot_handler。
        }


(至此,SPIM1控制触发的eot event,最终就会跳转到我们当初设置的处理函数spim_eot_handler()中)


注:


1.Gap8的spi event 触发是通过写寄存器,来实现的。


2.gap8中的 SPIM 的操作是指令集的。


3.gap8 SPIM 控制器给我们提供同一个寄存器接口(这种“接口”的叫法是我自己起的,慎用)。


SPIM控制器对应的接口寄存器如下:
        RX_SADDR         0x1A102100
        RX_SIZE         0x1A102104
        RX_CFG         0x1A102108
        TX_SADDR         0x1A102110
        TX_SIZE         0x1A102114
        TX_CFG         0x1A102118


(这套接口很简单,接受对应3个寄存器,发送对应3个寄存器)


使用时,只需要将对应通道的数据,地址和长度写入对应的寄存器 即可。


当然以上两组寄存器除了发送我们要发送的有效数据。其他的,比如,配置spiM的时钟,相位,极性等数据,也是通过以上两组寄存器,进行传输送(当然主要是TX_xx那一组进行发送)。


上面已经提到,gap8的SPIM 的操作是通过指令来控制的。其实配置spiM的时钟,相位,极性等数据,就是按照各种指令的格式来进行配置的。


spim对应的各种指令及格式如下:


                Name                           Command number Size Description


                SPI_CMD_CFG                         0 32         SPIM         configuration command.


                SPI_CMD_SOT                         1 32         SPIM         Start of Transfer command.


                SPI_CMD_SEND_CMD                 2 32         SPIM         send command command.


                SPI_CMD_SEND_ADDR                 3 32         SPIM         send address command.


                SPI_CMD_DUMMY                         4 32         SPIM         dummy RX command.


                SPI_CMD_WAIT                         5 32         SPIM         wait uDMA external event command.


                SPI_CMD_TX_DATA                 6 32         SPIM         send data command (max 64kbits).


                SPI_CMD_RX_DATA                 7 32         SPIM         receive data command (max 64kbits).


                SPI_CMD_RPT                         8 32         SPIM         repeat next transfer command.


                SPI_CMD_EOT                         9 32         SPIM         End of Transfer command.


                SPI_CMD_RPT_END                 10 32         SPIM         end of repeat command.


                SPI_CMD_RX_CHECK                 11 32         SPIM         RX check data command.


                SPI_CMD_FULL_DUPL                 12 32         SPIM         full duplex mode command.
(每种指令都有对应的名字,命令号,及对应的功能。)


4.还要注意到一点,gap8中的 SPIM 其实是和 uDMA紧密结合的。这些指令从某种程序上也可以看做是给uDMA和SPIM控制器的。


这种指令集的方式操作 SPIM的方法和顺序如下;
1.配置指令集的数据:spim 时钟,相位,极性,等等(一般需要多个uint32_t,通常组成数组)
2.配置要发送数据的 地址,大小,等
3.配置spim发送结束后的指令数据,比如是否产生event事件。


gap8的异步机制,基本都依赖于这个event事件。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分