4.2 ipu_init_channel函数的详细分析

本文深入剖析了IPU中Channel的定义与初始化过程,详细介绍了不同Channel如何与IPU内部模块如SMFC、IC等关联,并针对特定Channel的初始化函数进行了逐行分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在看这个函数之前,先对ipu内部的channel有所了解,先来看看ipu内部的flow的定义:


对于每个flow,代表数据从摄像头采集到显示(或者保存在内存中)的过程中的一个分步骤,每个flow都对应一个或几个channel,而每个channel的数据流通过程中,是通过DMA传输的,所以每个channel里面又包含一个或几个dma_channel。在这不太好理解,看看手册中的介绍(captureflow):


其中上面和程序中所说的channel对应图中的Taskschain,对于这个channel的理解需要根据ipu内部架构图来理解,如下所示:

以上面CSI0-->SMFC-->MEM这个channel为例,它代表数据从CSI经过SMFC到达内存MEM中,因为数据是从CSI中获得的,所以physicalDMA channel中的videoinput为空,而数据从这个channel输出的话,就需要通过DMAchannel了,从图上可以看出来,physicalDMA channel中的videooutput可以为IDMAC_CH_0~ IDMAC_CH_3

同样对于captureflow,还可以通过CSI0-->SMFC--->MEMCSI0-->VDIC-->MEM这两种方式将摄像头采集到的数据存放到内存中。


再来看手册中的Processingflows的图:

MEM-->IC-->MEM这个channel为例,根据IPU内部框架图可以看到,从内存中取出数据,经过IC处理以后再放入内存中,那么取出数据的时候,就使用到DMAchannel了,从这个图中可以看出来,使用的是IDMAC_CH12,再次放入内存中的时候就使用到IDMAC_CH20


对于其他channel就暂时不一一分析了。再来看看程序中是怎样定义channel的。

内核中使用ipu_channel_t枚举来表示一个channel

typedef enum { 
	CHAN_NONE = -1, 
	MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48), 
	MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49), 
	MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50), 

	MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20), 
	MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21), 
	MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22), 

	MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), 
	MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), 
	MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), 
	MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), 

	MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), 
	MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), 
	MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), 
	MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), 

	DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 
	DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 

	CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0), 
	CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1), 
	CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2), 
	CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3), 

	CSI_MEM = CSI_MEM0, 

	CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20), 
	CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21), 

	/* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/ 
	MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21), 
	MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21), 
	MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21), 

	/* for vdi mem->vdi->mem */ 
	MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5), 
	MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5), 
	MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5), 

	/* fake channel for vdoa to link with IPU */ 
	MEM_VDOA_MEM =  _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 

	MEM_PP_ADC = CHAN_NONE, 
	ADC_SYS2 = CHAN_NONE, 
} ipu_channel_t;
#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ 
	((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)

这个枚举就包含了所有的channel,如果对于上面讲解的过程理解的话,很容易根据channel的过程在这个枚举中找到对应的channel名字。同时可以看到在channel通过_MAKE_CHAN宏的构造过程中,每个channel里面都包含了输入输出dmachannel号。


分析完对channel的理解过程以后,再来看看具体的函数实现:

ipu_init_channel函数

int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel,
 ipu_channel_params_t *params) 

/*这个函数有3个参数,第一个参数ipu代表正在使用的ipu。第二个参数是想要初始化的channel,它其实就是一个数字,可以理解为ID,第三个参数params是想要将这个channel初始化成什么样子,它里面包含channel的一些信息,会根据params参数来初始化这个channel。关于这个结构体的详细讲解可以看《ipu_channel_params_t结构体详解》*/

{ 
	int ret = 0; 
	bool bad_pixfmt; 
	uint32_t ipu_conf, reg, in_g_pixel_fmt, sec_dma; 

	dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel)); 

	ret = pm_runtime_get_sync(ipu->dev); 
	if (ret < 0) { 
		dev_err(ipu->dev, "ch = %d, pm_runtime_get failed:%d!\n", 
				IPU_CHAN_ID(channel), ret); 
		dump_stack(); 
		return ret; 
	} 
	/* 
	 * Here, ret could be 1 if the device's runtime PM status was 
	 * already 'active', so clear it to be 0. 
	 */ 

/*看注释,可以看出来,上面一个函数如果执行成功的话,它的返回值是1,所以在下面需要将这个ret清零。跟踪源码,确实是这样的。*/

	ret = 0; 
 	
	_ipu_get(ipu); 

	mutex_lock(&ipu->mutex_lock); 

	/* Re-enable error interrupts every time a channel is initialized */ 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10)); 

/*在初始化每一个channel的时候都需要重新使能错误中断。*/

	if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) { 
		dev_warn(ipu->dev, "Warning: channel already initialized %d\n", 
			IPU_CHAN_ID(channel)); 
	} 

/*这个ipu_soc结构体里面的channel_init_mask项是uint32_t类型的,每个channel对应这个数字里面的某一位,如果这个channel进行过初始化操作的话,就将它的那一位置1.通过这个来判断其中的某一个channel是否进行过初始化。所以在这个初始化函数中肯定有将它置位的操作,我们搜索源码可以发现,在这个函数的后面确实有这样的操作。先在这粘贴一下:

ipu->channel_init_mask|= 1L << IPU_CHAN_ID(channel);

*/

	ipu_conf = ipu_cm_read(ipu, IPU_CONF); 

/*下面的switch判断语句就是根据channel的值来初始化不同的channel,所以这么多case就包括了所有的channel*/

	switch (channel) { 
	case CSI_MEM0: 
	case CSI_MEM1: 
	case CSI_MEM2: 
	case CSI_MEM3: 
		if (params->csi_mem.csi > 1) { //csi有2个,取值为0和1.
			ret = -EINVAL; 
			goto err; 
		} 

		if (params->csi_mem.interlaced) 
			ipu->chan_is_interlaced[channel_2_dma(channel, 
				IPU_OUTPUT_BUFFER)] = true; 
		else 
			ipu->chan_is_interlaced[channel_2_dma(channel, 
				IPU_OUTPUT_BUFFER)] = false; 

/*ipu->chan_is_interlaced是一个bool型的数组,根据params里面的csi_mem.interlaced参数,来设置ipu->chan_is_interlaced数组的某一项。怎么确定是数组的第几项?根据channel的值通过channel_2_dma函数为它在数组中挑选一个位置,然后将这个位置设置为truefalse*/

		ipu->smfc_use_count++; //增加smfc使用计数,这几个channel都会使用到smfc。
		ipu->csi_channel[params->csi_mem.csi] = channel;

/*根据params->csi_mem.csi来确定是ipu->csi_channel数组的第几项,然后将channel赋给它。这个ipu->csi_channel数组包含两项,意思就是每个ipu有两个csi接口,每个接口选择哪个channel,都在这里进行保存。*/

		/*SMFC setting*/ 
		if (params->csi_mem.mipi_en) { 
			ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_mem.csi)); 
			_ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc, 
				params->csi_mem.csi); 
			_ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc, 
				params->csi_mem.mipi_id, params->csi_mem.csi); 
		} else { 
			ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_mem.csi)); 
			_ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi); 
		} 

/*paramscsi_mem.mipi_en是个bool类型的值,如果它为1的话,表示使用的是mipi引脚。

下面来看看ipu_conf寄存器:


这里设置的是ipu_conf寄存器的28或者29位,看看这两位的介绍:




这两位的含义是选择CSI的数据来源,我们知道,CSI接口可以接并行接口或者MIPI接口,驱动会根据mipi_en的值来选择将这两位的数据来源设置为并口或者MIPI接口。如果是MIPI接口的话,需要通过_ipu_csi_set_mipi_di函数来设置MIPI相关的寄存器。

同时,不论是哪一种接口,都需要使用SMFC,就需要通过_ipu_smfc_init函数来初始化smfcSMFC_MAP寄存器,在这个_ipu_smfc_init函数中根据不同的case选择不同的SMFC_MAP_CH;然后再调用_ipu_csi_set_mipi_di函数来初始化CSI_MIPI_DI寄存器。这个寄存器是mipidata identifier的意思。

*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_mem.csi); 
		break; 

/*最后通过_ipu_csi_init函数初始化csi*/

/*上面的channel流程就是下图这种模式:数据从CSI中通过SMFC直接到达IDMAC,然后通过IDMAC来进行数据的存放等操作。



*/

	case CSI_PRP_ENC_MEM: 
		if (params->csi_prp_enc_mem.csi > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 
		if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM; 

/*这个ipu->using_ic_dirct_ch是个ipu_channel_t类型的变量,usingic direct channel的意思,只能使用ICchannel而不能使用VDIchannel,所以是MEM_VDI_PRP_VF_MEMMEM_VDI_MEM的话就会报错。最后再把这一位置位成CSI_PRP_ENC_MEM*/

		ipu->ic_use_count++; //增加ic的引用计数。
		ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; 

/*这个ipu_soc结构体中有一项ipu_channel_tcsi_channel[2];因为每个ipu有两个csi,所以这个数组里面保存的是每个csi选用的channel通道。在每一个需要用到csi接口的channel中都需要设置这一项,可以看到在这个switch中前几个用到csicase,里面都设置了这一项。*/

/*同时在params这个参数中,它是ipu_channel_params_t类型的联合,其中包含的两个关于csi的结构体csi_memcsi_prp_enc_mem,这两个结构体里面都含有一个uint32_tcsi,就是通过它来确定想要使用的是哪一个csi设备。 */

		if (params->csi_prp_enc_mem.mipi_en) { 
			ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_enc_mem.csi)); 
			_ipu_csi_set_mipi_di(ipu, 
				params->csi_prp_enc_mem.mipi_vc, 
				params->csi_prp_enc_mem.mipi_id, 
				params->csi_prp_enc_mem.csi); 
		} else 
			ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_enc_mem.csi)); 

/*这个channel没有用到smfc,所以不用初始化它,其他跟上面那个channel一样。设置IPU_CONF寄存器中的28,29位。如果是MIPI接口的话,还需要通过_ipu_csi_set_mipi_di函数来初始化MIPI相关的寄存器。*/

		/*CSI0/1 feed into IC*/ 
		ipu_conf &= ~IPU_CONF_IC_INPUT; 
		if (params->csi_prp_enc_mem.csi) 
			ipu_conf |= IPU_CONF_CSI_SEL; 
		else 
			ipu_conf &= ~IPU_CONF_CSI_SEL; 

/*设置ipu_conf中的IPU_CONF_IC_INPUT位和CSI_SEL位。这两位的含义如下:



在初始化这个channel的时候,我们应该首先想到这个channel使用到了IPU内部的哪些模块,在上面一个channel中,使用到了CSISMFC这两个模块,所以需要设置ipu_conf寄存器中的28,29数据来源位,选择数据是从并口来的还是从MIPI接口来的,然后分别初始化CSISMFC即可。

对于本次这个channelCSI-->IC(prpenc)-->MEM,同样的思想,首先想想这个channel使用了哪几个模块,使用了CSIIC模块,然后同样设置ipu_conf寄存器中的28,29数据来源位,然后就是设置3031位了,30位设置IC的输入数据是从哪里来的,从IPU内部框架中可以看出来,输入IC的数据只能是VDICSI,所以这里设置30位为0。但是CSI的话还有两个呢,这就需要设置31位来确定是哪一个CSI设备了。

*/

		/*PRP skip buffer in memory, only valid when RWS_EN is true*/ 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); 

/*IPU_FS_PROC_FLOW1寄存器里面的FS_ENC_IN_VALID位清零。



这里设置的就是IPU_FS_PROC_FLOW1寄存器的30位,这一位的含义如上所示,它表示是否应该跳过内存中的buffer。这一位是根据RWS_EN来选择的。那么RWS_EN又是什么?在IC_CONF寄存器中找到了它,如下所示:





其中RWSrawsensor的意思,一般sensor的输出数据可以为YUVJPEGRGBRAW几种格式,其中这个RAW格式是未经处理、也未经压缩的格式记录了数码相机传感器的原始信息可以把RAW概念化为“原始图像编码数据”或更形象的称为“数字底片”。所以RWS_EN表示摄像头输出的数据是否是原始的RAW数据。

RWS_EN0时,数据直接从CSI输入到IC中,所以IPU_FS_PROC_FLOW1这个寄存器就需要设置跳过从内存中到达IC的数据IPU_FS_PROC_FLOW1寄存器的ENC_IN_VALID位清零

*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi); 
		_ipu_ic_init_prpenc(ipu, params, true); 
		break; 

/*最后通过_ipu_csi_init函数初始化csi,同时通过_ipu_ic_init_prpenc函数预处理编码,这个函数在ipu_ic.c中。*/

/*至此,这个CSI_PRP_ENC_MEMchannel简单分析完毕,它的流程如下图所示:



*/

	case CSI_PRP_VF_MEM: 
		if (params->csi_prp_vf_mem.csi > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 
		if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM; 

/*与上面那个CSI_PRP_ENC_MEMchannel一样,这个ipu->using_ic_dirct_ch只能置位成当前这个CSI_PRP_VF_MEMchannel*/

		ipu->ic_use_count++; //增加ic的引用计数。
		ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel; 

		if (params->csi_prp_vf_mem.mipi_en) { 
			ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_vf_mem.csi)); 
			_ipu_csi_set_mipi_di(ipu, 
				params->csi_prp_vf_mem.mipi_vc, 
				params->csi_prp_vf_mem.mipi_id, 
				params->csi_prp_vf_mem.csi); 
		} else 
			ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_vf_mem.csi)); 

		/*CSI0/1 feed into IC*/ 
		ipu_conf &= ~IPU_CONF_IC_INPUT; 
		if (params->csi_prp_vf_mem.csi) 
			ipu_conf |= IPU_CONF_CSI_SEL; 
		else 
			ipu_conf &= ~IPU_CONF_CSI_SEL; 

		/*PRP skip buffer in memory, only valid when RWS_EN is true*/ 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); 

/*与上一个函数不同的是,设置的IPU_FS_PROC_FLOW1寄存器的VF_IN_VALID位,原理是相同的。*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi); 
		_ipu_ic_init_prpvf(ipu, params, true); 
		break; 

/*除了最后一个函数,其他都与上一个case一样,最后一个函数_ipu_ic_init_prpvf同样在ipu_ic.c中。*/

/*这个CSI_PRP_VF_MEMchannel的流程如下所示:


*/

	case MEM_PRP_VF_MEM: 
		if (params->mem_prp_vf_mem.graphics_combine_en) { 
			sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); 
			in_g_pixel_fmt = params->mem_prp_vf_mem.in_g_pixel_fmt; 
			bad_pixfmt = 
				_ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); 
			if (params->mem_prp_vf_mem.alpha_chan_en) { 
				if (bad_pixfmt) { 
					dev_err(ipu->dev, "bad pixel format " 
						"for graphics plane from " 
						"ch%d\n", sec_dma); 
					ret = -EINVAL; 
					goto err; 
				} 
				ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 
			} 
			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		} 

/*bad_pixfmt的意思可以在查看_ipu_ch_param_bad_alpha_pos函数的时候大致了解,在IPUv3IDMAC中对于32bpp的格式有个bug,所以对于32bpp的都返回true,对于其他返回false*/


/*下面这个涉及到alpha通道的一些知识,

http://baike.baidu.com/link?url=8p8Z8B2o_qOdahK5nfH-9ZiGfcubcPFWVxVCxCWn3XEItoG2KDmTLUpObjQMIp59VvTLBpJNzHYs4-9N4O_KOq

RGBA》里面有简单介绍。*/

/*上面的代码,会根据ipu_channel_params_t里面的mem_prp_vf_mem这一项的graphics_combine_en来决定是否使能secondchannel,根据mem_prp_vf_mem这一项的alpha_chan_en来决定是否使能thirdchannel。如果使能的话,就会设置ipu_soc中的sec_chan_en[]thrd_chan_en[]这两项,根据channelID号来置位。*/

		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); 

		_ipu_ic_init_prpvf(ipu, params, false); 
		ipu->ic_use_count++; 
		break; 

/*其他函数大致都相似,就是细节不太相同,比如将IPU_FS_PROC_FLOW1寄存器的FS_VF_IN_VALID位设置为1_ipu_ic_init_prpvf函数的第三个参数是false等等,_ipu_ic_init_prpvf函数的第三个参数是bool类型的变量,true代表IC的数据来源是CSIfalse代表IC的数据来源不是CSI*/

/*注意这个函数中设置了ipu_soc结构体中的sec_chan_enthrd_chan_en,是两个bool型的数组,从名字上来看就是secondchannel enablethirdchannelenable的意思,我认为MEM_PRP_VF_MEM这个channel是可以和其他某些channel一起启用的(在手册的CM模块对于IPUmainflows进行了详细的讲解,在后面具体分析)。根据params参数中mem_prp_vf_mem.graphics_combine_en置位的话,就会启用第二个channel,如果params参数中mem_prp_vf_mem.alpha_chan_en继续置位的话,就继续启用第三个channel。将启用了几个channel这些信息都保存在ipu_soc结构体中。*/

	case MEM_VDI_PRP_VF_MEM: 
		if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM) || 
		     (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM; 
		ipu->ic_use_count++; 
		ipu->vdi_use_count++; 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		reg &= ~FS_VDI_SRC_SEL_MASK; 
		ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1); 

		if (params->mem_prp_vf_mem.graphics_combine_en) 
			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		_ipu_ic_init_prpvf(ipu, params, false); 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*大部分设置都是相似的,在这个channel中使用到了VDIC,所以需要设置IPU_FS_PROC_FLOW1寄存器中有关VDIC的位:VDI_SRC_SEL,手册中的解释如下:




从程序中可以看到,只是将这些位清空了,所以默认的VDIC的数据来源是ARM平台。

同样,设置IC初始化为prpvf模式,然后通过_ipu_vdi_init初始化VDIC_ipu_vdi_initipu_ic.c中定义。*/

/*这个模式的流程是下面这张图:


*/

	case MEM_VDI_PRP_VF_MEM_P: 
	case MEM_VDI_PRP_VF_MEM_N: 
	case MEM_VDI_MEM_P: 
	case MEM_VDI_MEM_N: 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*这几个channel不再分析了。*/

	case MEM_VDI_MEM: 
		if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
		     (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = MEM_VDI_MEM; 
		ipu->ic_use_count++; 
		ipu->vdi_use_count++; 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*设置ipu_soc中的using_ic_dirct_ch,增加ic_use_countvdi_use_count的引用计数,最终也是调用这个_ipu_vdi_init函数。这个函数中增加了vdi_use_count引用计数,上面几个与这个channel相似的channel都没有增加这个引用计数。这个channel只使用了VDIC*/

	case MEM_ROT_VF_MEM: 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		_ipu_ic_init_rotate_vf(ipu, params); 
		break; 
	case MEM_PRP_ENC_MEM: 
		ipu->ic_use_count++; 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); 
		_ipu_ic_init_prpenc(ipu, params, false); 
		break; 
	case MEM_ROT_ENC_MEM: 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		_ipu_ic_init_rotate_enc(ipu, params); 
		break; 

/*上面几个channel程序比较简单,就暂时不分析。*/

	case MEM_PP_MEM: 
		if (params->mem_pp_mem.graphics_combine_en) { 
			sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); 
			in_g_pixel_fmt = params->mem_pp_mem.in_g_pixel_fmt; 
			bad_pixfmt = 
				_ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); 

			if (params->mem_pp_mem.alpha_chan_en) { 
				if (bad_pixfmt) { 
					dev_err(ipu->dev, "bad pixel format " 
						"for graphics plane from " 
						"ch%d\n", sec_dma); 
					ret = -EINVAL; 
					goto err; 
				} 
				ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 
			} 

			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		} 

		_ipu_ic_init_pp(ipu, params); 
		ipu->ic_use_count++; 
		break; 

/*这个channelMEM_PRP_VF_MEMchannel类似,它也是根据params参数启用了sec_chan_enthrd_chan_en*/

/*


*/

	case MEM_ROT_PP_MEM: 
		_ipu_ic_init_rotate_pp(ipu, params); 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		break; 
	case MEM_DC_SYNC: 
		if (params->mem_dc_sync.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[1] = params->mem_dc_sync.di; 
		_ipu_dc_init(ipu, 1, params->mem_dc_sync.di, 
			     params->mem_dc_sync.interlaced, 
			     params->mem_dc_sync.out_pixel_fmt); 
		ipu->di_use_count[params->mem_dc_sync.di]++; 
		ipu->dc_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case MEM_BG_SYNC: 
		if (params->mem_dp_bg_sync.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		if (params->mem_dp_bg_sync.alpha_chan_en) 
			ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 

		ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di; 
		_ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt, 
			     params->mem_dp_bg_sync.out_pixel_fmt); 
		_ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di, 
			     params->mem_dp_bg_sync.interlaced, 
			     params->mem_dp_bg_sync.out_pixel_fmt); 
		ipu->di_use_count[params->mem_dp_bg_sync.di]++; 
		ipu->dc_use_count++; 
		ipu->dp_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case MEM_FG_SYNC: 
		_ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt, 
			     params->mem_dp_fg_sync.out_pixel_fmt); 

		if (params->mem_dp_fg_sync.alpha_chan_en) 
			ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 

		ipu->dc_use_count++; 
		ipu->dp_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case DIRECT_ASYNC0: 
		if (params->direct_async.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[8] = params->direct_async.di; 
		_ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); 
		ipu->di_use_count[params->direct_async.di]++; 
		ipu->dc_use_count++; 
		break; 
	case DIRECT_ASYNC1: 
		if (params->direct_async.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[9] = params->direct_async.di; 
		_ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); 
		ipu->di_use_count[params->direct_async.di]++; 
		ipu->dc_use_count++; 
		break; 
	default: 
		dev_err(ipu->dev, "Missing channel initialization\n"); 
		break; 
	} 

	ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel); 

	ipu_cm_write(ipu, ipu_conf, IPU_CONF); 

err: 
	mutex_unlock(&ipu->mutex_lock); 
	return ret; 
} 
EXPORT_SYMBOL(ipu_init_channel);

/*后面几个channel是关于显示的channel,他们与前面几个case的步骤是相似的,暂时不分析,用到的时候再分析。*/

小总结:

可以看出来,在ipu_init_channel函数中,会根据对应的channel来决定使用哪个模块。

比如对于CSI_MEM0CSI_MEM3,就会使用SMFC_ipu_smfc_init函数),然后通过_ipu_csi_init函数来指定数据的目的地址。


对于CSI_PRP_ENC_MEMchannel,就会使用IC,使用的是ICprpenc功能,就需要使用_ipu_ic_init_prpenc函数,同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。


对于CSI_PRP_VF_MEMchannel,需要使用到IC,但是使用的是ICprpvf功能,就需要使用_ipu_ic_init_prpvf函数。同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。


上面几个函数都使用到了CSI设备,所以都使用了_ipu_csi_init函数来指定数据的目的地址。对于有些channel没有使用CSI设备,肯定就不用使用_ipu_csi_init这个函数了。


比如对于MEM_VDI_MEMchannel,它使用到VDI模块,只需要通过_ipu_vdi_init函数来设置即可。

比如MEM_ROT_VF_MEMchannel,它需要使用到ICrotateviewfinder功能,就需要通过_ipu_ic_init_rotate_vf函数来设置了。


#include "stm32f10x.h" // 初始化编码器1(TIM3, PA6/PA7) void Encoder1_Init(void) { // 启用时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置GPIO(上拉输入模式) GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置定时器基础参数 TIM_TimeBaseInitTypeDef TIM_BaseStruct; TIM_TimeBaseStructInit(&TIM_BaseStruct); TIM_BaseStruct.TIM_Period = 0xFFFF; // 16位计数器最大值 TIM_BaseStruct.TIM_Prescaler = 0; TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_BaseStruct); // 配置编码器接口模式 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 配置输入捕获滤波器(提高抗噪能力) TIM_ICInitTypeDef TIM_ICStruct; TIM_ICStruct.TIM_ICFilter = 0x0F; // 最大滤波值 TIM_ICStruct.TIM_Channel = TIM_Channel_1; TIM_ICInit(TIM3, &TIM_ICStruct); TIM_ICStruct.TIM_Channel = TIM_Channel_2; TIM_ICInit(TIM3, &TIM_ICStruct); TIM_Cmd(TIM3, ENABLE); // 启动定时器 } // 初始化编码器2(TIM4, PB6/PB7) void Encoder2_Init(void) { // 启用时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置GPIO GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置定时器 TIM_TimeBaseInitTypeDef TIM_BaseStruct; TIM_TimeBaseStructInit(&TIM_BaseStruct); TIM_BaseStruct.TIM_Period = 0xFFFF; TIM_BaseStruct.TIM_Prescaler = 0; TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_BaseStruct); // 配置编码器接口 TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 配置输入捕获 TIM_ICInitTypeDef TIM_ICStruct; TIM_ICStruct.TIM_ICFilter = 0x0F; TIM_ICStruct.TIM_Channel = TIM_Channel_1; TIM_ICInit(TIM4, &TIM_ICStruct); TIM_ICStruct.TIM_Channel = TIM_Channel_2; TIM_ICInit(TIM4, &TIM_ICStruct); TIM_Cmd(TIM4, ENABLE); } // 读取编码器1计数值(带溢出保护) int16_t Encoder1_Get(void) { int16_t count = (int16_t)TIM_GetCounter(TIM3); TIM_SetCounter(TIM3, 0); return count; } // 读取编码器2计数值 int16_t Encoder2_Get(void) { int16_t count = (int16_t)TIM_GetCounter(TIM4); TIM_SetCounter(TIM4, 0); return count; } 延续上面的要求,使用端口各不重复
07-21
#include "stm32f10x.h" #include <stdio.h> // 硬件配置 #define PWM_TIM TIM1 #define PWM_PORT GPIOA #define PWM_PIN GPIO_Pin_8 #define PWM_CHANNEL TIM_Channel_1 #define INT_PORT GPIOA #define INT_PIN GPIO_Pin_0 #define CAPTURE_TIM TIM2 #define CAPTURE_TIM_IRQn TIM2_IRQn // 全局变量 volatile uint32_t rise_time = 0; volatile uint32_t fall_time = 0; volatile uint8_t capture_done = 0; volatile uint8_t capture_state = 0; // 0:等待上升沿 1:已捕获上升沿 // 重定向printf到串口 int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return ch; } // 初始化USART1用于printf输出 void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX(PA9)和RX(PA10)引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } // PWM初始化(方波输出) void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // 配置PWM引脚 GPIO_InitStructure.GPIO_Pin = PWM_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(PWM_PORT, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 50%占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInit(PWM_TIM, PWM_CHANNEL, &TIM_OCInitStructure); // 启动定时器 TIM_CtrlPWMOutputs(PWM_TIM, ENABLE); TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(PWM_TIM, ENABLE); TIM_Cmd(PWM_TIM, ENABLE); } // 外部中断初始化 void EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 配置中断引脚 GPIO_InitStructure.GPIO_Pin = INT_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(INT_PORT, &GPIO_InitStructure); // 配置外部中断线 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // 配置NVIC NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 捕获定时器初始化 void Capture_Timer_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // 最大计数值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 72MHz/72 = 1MHz (1us分辨率) TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(CAPTURE_TIM, &TIM_TimeBaseStructure); // 启动定时器 TIM_Cmd(CAPTURE_TIM, ENABLE); } // 外部中断服务函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // 上升沿检测 if(GPIO_ReadInputDataBit(INT_PORT, INT_PIN) == SET) { rise_time = TIM_GetCounter(CAPTURE_TIM); capture_state = 1; // 标记已捕获上升沿 } // 下降沿检测(且已捕获上升沿) else if(capture_state == 1) { fall_time = TIM_GetCounter(CAPTURE_TIM); capture_done = 1; // 标记完成捕获 capture_state = 0; // 重置状态 } EXTI_ClearITPendingBit(EXTI_Line0); } } int main(void) { // 初始化系统时钟 SystemInit(); // 初始化各模块 USART1_Init(); PWM_Init(); EXTI_Init(); Capture_Timer_Init(); printf("系统启动完成\r\n"); printf("方波频率: 1kHz, 占空比: 50%%\r\n"); while(1) { if(capture_done) { // 计算高电平时间(考虑定时器溢出) uint32_t high_time; if(fall_time >= rise_time) { high_time = fall_time - rise_time; } else { // 处理定时器溢出情况 high_time = (0xFFFFFFFF - rise_time) + fall_time; } // 转换为微秒输出 printf("高电平时间: %lu us\r\n", high_time); capture_done = 0; // 重置标志 } } } #include "stm32f10x.h" #include <stdio.h> // 硬件配置 #define PWM_TIM TIM1 #define PWM_PORT GPIOA #define PWM_PIN GPIO_Pin_8 #define PWM_CHANNEL TIM_Channel_1 #define INT_PORT GPIOA #define INT_PIN GPIO_Pin_0 #define CAPTURE_TIM TIM2 #define CAPTURE_TIM_IRQn TIM2_IRQn // 全局变量 volatile uint32_t rise_time = 0; volatile uint32_t fall_time = 0; volatile uint8_t capture_done = 0; volatile uint8_t capture_state = 0; // 0:等待上升沿 1:已捕获上升沿 // 重定向printf到串口 int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return ch; } // 初始化USART1用于printf输出 void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX(PA9)和RX(PA10)引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } // PWM初始化(方波输出) void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // 配置PWM引脚 GPIO_InitStructure.GPIO_Pin = PWM_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(PWM_PORT, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 50%占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInit(PWM_TIM, PWM_CHANNEL, &TIM_OCInitStructure); // 启动定时器 TIM_CtrlPWMOutputs(PWM_TIM, ENABLE); TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(PWM_TIM, ENABLE); TIM_Cmd(PWM_TIM, ENABLE); } // 外部中断初始化 void EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 配置中断引脚 GPIO_InitStructure.GPIO_Pin = INT_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(INT_PORT, &GPIO_InitStructure); // 配置外部中断线 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // 配置NVIC NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 捕获定时器初始化 void Capture_Timer_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // 最大计数值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 72MHz/72 = 1MHz (1us分辨率) TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(CAPTURE_TIM, &TIM_TimeBaseStructure); // 启动定时器 TIM_Cmd(CAPTURE_TIM, ENABLE); } // 外部中断服务函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // 上升沿检测 if(GPIO_ReadInputDataBit(INT_PORT, INT_PIN) == SET) { rise_time = TIM_GetCounter(CAPTURE_TIM); capture_state = 1; // 标记已捕获上升沿 } // 下降沿检测(且已捕获上升沿) else if(capture_state == 1) { fall_time = TIM_GetCounter(CAPTURE_TIM); capture_done = 1; // 标记完成捕获 capture_state = 0; // 重置状态 } EXTI_ClearITPendingBit(EXTI_Line0); } } int main(void) { // 初始化系统时钟 SystemInit(); // 初始化各模块 USART1_Init(); PWM_Init(); EXTI_Init(); Capture_Timer_Init(); printf("系统启动完成\r\n"); printf("方波频率: 1kHz, 占空比: 50%%\r\n"); while(1) { if(capture_done) { // 计算高电平时间(考虑定时器溢出) uint32_t high_time; if(fall_time >= rise_time) { high_time = fall_time - rise_time; } else { // 处理定时器溢出情况 high_time = (0xFFFFFFFF - rise_time) + fall_time; } // 转换为微秒输出 printf("高电平时间: %lu us\r\n", high_time); capture_done = 0; // 重置标志 } } }#include "stm32f10x.h" #include <stdio.h> // 硬件配置 #define PWM_TIM TIM1 #define PWM_PORT GPIOA #define PWM_PIN GPIO_Pin_8 #define PWM_CHANNEL TIM_Channel_1 #define INT_PORT GPIOA #define INT_PIN GPIO_Pin_0 #define CAPTURE_TIM TIM2 #define CAPTURE_TIM_IRQn TIM2_IRQn // 全局变量 volatile uint32_t rise_time = 0; volatile uint32_t fall_time = 0; volatile uint8_t capture_done = 0; volatile uint8_t capture_state = 0; // 0:等待上升沿 1:已捕获上升沿 // 重定向printf到串口 int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return ch; } // 初始化USART1用于printf输出 void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX(PA9)和RX(PA10)引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } // PWM初始化(方波输出) void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // 配置PWM引脚 GPIO_InitStructure.GPIO_Pin = PWM_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(PWM_PORT, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 50%占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInit(PWM_TIM, PWM_CHANNEL, &TIM_OCInitStructure); // 启动定时器 TIM_CtrlPWMOutputs(PWM_TIM, ENABLE); TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(PWM_TIM, ENABLE); TIM_Cmd(PWM_TIM, ENABLE); } // 外部中断初始化 void EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 配置中断引脚 GPIO_InitStructure.GPIO_Pin = INT_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(INT_PORT, &GPIO_InitStructure); // 配置外部中断线 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // 配置NVIC NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 捕获定时器初始化 void Capture_Timer_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; // 最大计数值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 72MHz/72 = 1MHz (1us分辨率) TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(CAPTURE_TIM, &TIM_TimeBaseStructure); // 启动定时器 TIM_Cmd(CAPTURE_TIM, ENABLE); } // 外部中断服务函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // 上升沿检测 if(GPIO_ReadInputDataBit(INT_PORT, INT_PIN) == SET) { rise_time = TIM_GetCounter(CAPTURE_TIM); capture_state = 1; // 标记已捕获上升沿 } // 下降沿检测(且已捕获上升沿) else if(capture_state == 1) { fall_time = TIM_GetCounter(CAPTURE_TIM); capture_done = 1; // 标记完成捕获 capture_state = 0; // 重置状态 } EXTI_ClearITPendingBit(EXTI_Line0); } } int main(void) { // 初始化系统时钟 SystemInit(); // 初始化各模块 USART1_Init(); PWM_Init(); EXTI_Init(); Capture_Timer_Init(); printf("系统启动完成\r\n"); printf("方波频率: 1kHz, 占空比: 50%%\r\n"); while(1) { if(capture_done) { // 计算高电平时间(考虑定时器溢出) uint32_t high_time; if(fall_time >= rise_time) { high_time = fall_time - rise_time; } else { // 处理定时器溢出情况 high_time = (0xFFFFFFFF - rise_time) + fall_time; } // 转换为微秒输出 printf("高电平时间: %lu us\r\n", high_time); capture_done = 0; // 重置标志 } } } 在上述代码中出现main.c(170): error: #165: too few arguments in function call的报错怎么解决
最新发布
07-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值