gap8-sdk-gap8芯片event事件触发-接受-处理过程分析

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.chal_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.cspim_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:%d\n",__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控制器的。

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

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

(但gap8的event/interrupt相应时间较长,大约3.3ms)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值