cpufreq: cpufreq_online 的大致流程

本文详细介绍了Linux内核中cpufreq在线过程,包括调用cpu驱动的初始化回调函数,执行cpufreq_driver->init()进行policy初始化,以及注册thermal控制相关的回调函数,如of_cpufreq_cooling_register()用于策略设置。

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

call cpu driver的init callback
ret = cpufreq_driver->init(policy)

//初始化policy并调用set_policy
ret = cpufreq_init_policy(policy);

//注册thermal 相关callback
if (cpufreq_thermal_control_enabled(cpufreq_driver))
policy->cdev = of_cpufreq_cooling_register(policy);

static int cpufreq_online(unsigned int cpu)
{
	struct cpufreq_policy *policy;
	bool new_policy;
	unsigned long flags;
	unsigned int j;
	int ret;

	pr_debug("%s: bringing CPU%u online\n", __func__, cpu);

	/* Check if this CPU already has a policy to manage it */
	policy = per_cpu(cpufreq_cpu_data, cpu);
	if (policy) {
		WARN_ON(!cpumask_test_cpu(cpu, policy->related_cpus));
		if (!policy_is_inactive(policy))
			return cpufreq_add_policy_cpu(policy, cpu);

		/* This is the only online CPU for the policy.  Start over. */
		new_policy = false;
		down_write(&policy->rwsem);
		policy->cpu = cpu;
		policy->governor = NULL;
		up_write(&policy->rwsem);
	} else {
		new_policy = true;
		policy = cpufreq_policy_alloc(cpu);
		if (!policy)
			return -ENOMEM;
	}

	if (!new_policy && cpufreq_driver->online) {
		ret = cpufreq_driver->online(policy);
		if (ret) {
			pr_debug("%s: %d: initialization failed\n", __func__,
				 __LINE__);
			goto out_exit_policy;
		}

		/* Recover policy->cpus using related_cpus */
		cpumask_copy(policy->cpus, policy->related_cpus);
	} else {
		cpumask_copy(policy->cpus, cpumask_of(cpu));

		/*
		 * Call driver. From then on the cpufreq must be able
		 * to accept all calls to ->verify and ->setpolicy for this CPU.
		 */
		ret = cpufreq_driver->init(policy);
		if (ret) {
			pr_debug("%s: %d: initialization failed\n", __func__,
				 __LINE__);
			goto out_free_policy;
		}

		/*
		 * The initialization has succeeded and the policy is online.
		 * If there is a problem with its frequency table, take it
		 * offline and drop it.
		 */
		ret = cpufreq_table_validate_and_sort(policy);
		if (ret)
			goto out_offline_policy;

		/* related_cpus should at least include policy->cpus. */
		cpumask_copy(policy->related_cpus, policy->cpus);
	}

	down_write(&policy->rwsem);
	/*
	 * affected cpus must always be the one, which are online. We aren't
	 * managing offline cpus here.
	 */
	cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);

	if (new_policy) {
		for_each_cpu(j, policy->related_cpus) {
			per_cpu(cpufreq_cpu_data, j) = policy;
			add_cpu_dev_symlink(policy, j);
		}

		policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
					       GFP_KERNEL);
		if (!policy->min_freq_req) {
			ret = -ENOMEM;
			goto out_destroy_policy;
		}

		ret = freq_qos_add_request(&policy->constraints,
					   policy->min_freq_req, FREQ_QOS_MIN,
					   policy->min);
		if (ret < 0) {
			/*
			 * So we don't call freq_qos_remove_request() for an
			 * uninitialized request.
			 */
			kfree(policy->min_freq_req);
			policy->min_freq_req = NULL;
			goto out_destroy_policy;
		}

		/*
		 * This must be initialized right here to avoid calling
		 * freq_qos_remove_request() on uninitialized request in case
		 * of errors.
		 */
		policy->max_freq_req = policy->min_freq_req + 1;

		ret = freq_qos_add_request(&policy->constraints,
					   policy->max_freq_req, FREQ_QOS_MAX,
					   policy->max);
		if (ret < 0) {
			policy->max_freq_req = NULL;
			goto out_destroy_policy;
		}

		blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
				CPUFREQ_CREATE_POLICY, policy);
	}

	if (cpufreq_driver->get && has_target()) {
		policy->cur = cpufreq_driver->get(policy->cpu);
		if (!policy->cur) {
			ret = -EIO;
			pr_err("%s: ->get() failed\n", __func__);
			goto out_destroy_policy;
		}
	}

	/*
	 * Sometimes boot loaders set CPU frequency to a value outside of
	 * frequency table present with cpufreq core. In such cases CPU might be
	 * unstable if it has to run on that frequency for long duration of time
	 * and so its better to set it to a frequency which is specified in
	 * freq-table. This also makes cpufreq stats inconsistent as
	 * cpufreq-stats would fail to register because current frequency of CPU
	 * isn't found in freq-table.
	 *
	 * Because we don't want this change to effect boot process badly, we go
	 * for the next freq which is >= policy->cur ('cur' must be set by now,
	 * otherwise we will end up setting freq to lowest of the table as 'cur'
	 * is initialized to zero).
	 *
	 * We are passing target-freq as "policy->cur - 1" otherwise
	 * __cpufreq_driver_target() would simply fail, as policy->cur will be
	 * equal to target-freq.
	 */
	if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK)
	    && has_target()) {
		unsigned int old_freq = policy->cur;

		/* Are we running at unknown frequency ? */
		ret = cpufreq_frequency_table_get_index(policy, old_freq);
		if (ret == -EINVAL) {
			ret = __cpufreq_driver_target(policy, old_freq - 1,
						      CPUFREQ_RELATION_L);

			/*
			 * Reaching here after boot in a few seconds may not
			 * mean that system will remain stable at "unknown"
			 * frequency for longer duration. Hence, a BUG_ON().
			 */
			BUG_ON(ret);
			pr_info("%s: CPU%d: Running at unlisted initial frequency: %u KHz, changing to: %u KHz\n",
				__func__, policy->cpu, old_freq, policy->cur);
		}
	}

	if (new_policy) {
		ret = cpufreq_add_dev_interface(policy);
		if (ret)
			goto out_destroy_policy;

		cpufreq_stats_create_table(policy);

		write_lock_irqsave(&cpufreq_driver_lock, flags);
		list_add(&policy->policy_list, &cpufreq_policy_list);
		write_unlock_irqrestore(&cpufreq_driver_lock, flags);

		/*
		 * Register with the energy model before
		 * sched_cpufreq_governor_change() is called, which will result
		 * in rebuilding of the sched domains, which should only be done
		 * once the energy model is properly initialized for the policy
		 * first.
		 *
		 * Also, this should be called before the policy is registered
		 * with cooling framework.
		 */
		if (cpufreq_driver->register_em)
			cpufreq_driver->register_em(policy);
	}

	ret = cpufreq_init_policy(policy);
	if (ret) {
		pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n",
		       __func__, cpu, ret);
		goto out_destroy_policy;
	}

	up_write(&policy->rwsem);

	kobject_uevent(&policy->kobj, KOBJ_ADD);

	if (cpufreq_thermal_control_enabled(cpufreq_driver))
		policy->cdev = of_cpufreq_cooling_register(policy);

	pr_debug("initialization complete\n");

	return 0;

out_destroy_policy:
	for_each_cpu(j, policy->real_cpus)
		remove_cpu_dev_symlink(policy, get_cpu_device(j));

	up_write(&policy->rwsem);

out_offline_policy:
	if (cpufreq_driver->offline)
		cpufreq_driver->offline(policy);

out_exit_policy:
	if (cpufreq_driver->exit)
		cpufreq_driver->exit(policy);

out_free_policy:
	cpufreq_policy_free(policy);
	return ret;
}

/****************************************************************************/ /* */ /* 音频测试:MIC_IN读取音频数据,从LINE_OUT播出 */ /* */ /* 2014年7月1日 */ /* */ /****************************************************************************/ #include "TL6748.h" // 创龙 DSP6748 开发板相关声明 #include "edma_event.h" #include "interrupt.h" #include "soc_OMAPL138.h" #include "hw_syscfg0_OMAPL138.h" #include "codecif.h" #include "mcasp.h" #include "aic31.h" #include "edma.h" #include "psc.h" #include "uartStdio.h" #include <string.h> /****************************************************************************** ** 宏定义 *******************************************************************************/ /* ** Values which are configurable */ /* Slot size to send/receive data */ #define SLOT_SIZE (16u) /* Word size to send/receive data. Word size <= Slot size */ #define WORD_SIZE (16u) /* Sampling Rate which will be used by both transmit and receive sections */ #define SAMPLING_RATE (48000u) /* Number of channels, L & R */ #define NUM_I2S_CHANNELS (2u) /* Number of samples to be used per audio buffer */ #define NUM_SAMPLES_PER_AUDIO_BUF (2000u) /* Number of buffers used per tx/rx */ #define NUM_BUF (3u) /* Number of linked parameter set used per tx/rx */ #define NUM_PAR (2u) /* Specify where the parameter set starting is */ #define PAR_ID_START (40u) /* Number of samples in loop buffer */ #define NUM_SAMPLES_LOOP_BUF (10u) /* AIC3106 codec address */ #define I2C_SLAVE_CODEC_AIC31 (0x18u) /* Interrupt channels to map in AINTC */ #define INT_CHANNEL_I2C (2u) #define INT_CHANNEL_MCASP (2u) #define INT_CHANNEL_EDMACC (2u) /* McASP Serializer for Receive */ #define MCASP_XSER_RX (12u) /* McASP Serializer for Transmit */ #define MCASP_XSER_TX (11u) /* ** Below Macros are calculated based on the above inputs */ #define NUM_TX_SERIALIZERS ((NUM_I2S_CHANNELS >> 1) \ + (NUM_I2S_CHANNELS & 0x01)) #define NUM_RX_SERIALIZERS ((NUM_I2S_CHANNELS >> 1) \ + (NUM_I2S_CHANNELS & 0x01)) #define I2S_SLOTS ((1 << NUM_I2S_CHANNELS) - 1) #define BYTES_PER_SAMPLE ((WORD_SIZE >> 3) \ * NUM_I2S_CHANNELS) #define AUDIO_BUF_SIZE (NUM_SAMPLES_PER_AUDIO_BUF \ * BYTES_PER_SAMPLE) #define TX_DMA_INT_ENABLE (EDMA3CC_OPT_TCC_SET(1) | (1 \ << EDMA3CC_OPT_TCINTEN_SHIFT)) #define RX_DMA_INT_ENABLE (EDMA3CC_OPT_TCC_SET(0) | (1 \ << EDMA3CC_OPT_TCINTEN_SHIFT)) #define PAR_RX_START (PAR_ID_START) #define PAR_TX_START (PAR_RX_START + NUM_PAR) /* ** Definitions which are not configurable */ #define SIZE_PARAMSET (32u) #define OPT_FIFO_WIDTH (0x02 << 8u) /****************************************************************************** ** 函数原型声明 *******************************************************************************/ static void McASPErrorIsr(void); static void McASPErrorIntSetup(void); static void AIC31I2SConfigure(void); static void McASPI2SConfigure(void); static void McASPTxDMAComplHandler(void); static void McASPRxDMAComplHandler(void); static void EDMA3CCComplIsr(void); static void I2SDataTxRxActivate(void); static void I2SDMAParamInit(void); static void ParamTxLoopJobSet(unsigned short parId); static void BufferTxDMAActivate(unsigned int txBuf, unsigned short numSamples, unsigned short parToUpdate, unsigned short linkAddr); static void BufferRxDMAActivate(unsigned int rxBuf, unsigned short parId, unsigned short parLink); /****************************************************************************/ /* 全局变量 */ /****************************************************************************/ static unsigned char loopBuf[NUM_SAMPLES_LOOP_BUF * BYTES_PER_SAMPLE] = {0}; /* ** Transmit buffers. If any new buffer is to be added, define it here and ** update the NUM_BUF. */ static unsigned char txBuf0[AUDIO_BUF_SIZE]; static unsigned char txBuf1[AUDIO_BUF_SIZE]; static unsigned char txBuf2[AUDIO_BUF_SIZE]; /* ** Receive buffers. If any new buffer is to be added, define it here and ** update the NUM_BUF. */ static unsigned char rxBuf0[AUDIO_BUF_SIZE]; static unsigned char rxBuf1[AUDIO_BUF_SIZE]; static unsigned char rxBuf2[AUDIO_BUF_SIZE]; /* ** Next buffer to receive data. The data will be received in this buffer. */ static volatile unsigned int nxtBufToRcv = 0; /* ** The RX buffer which filled latest. */ static volatile unsigned int lastFullRxBuf = 0; /* ** The offset of the paRAM ID, from the starting of the paRAM set. */ static volatile unsigned short parOffRcvd = 0; /* ** The offset of the paRAM ID sent, from starting of the paRAM set. */ static volatile unsigned short parOffSent = 0; /* ** The offset of the paRAM ID to be sent next, from starting of the paRAM set. */ static volatile unsigned short parOffTxToSend = 0; /* ** The transmit buffer which was sent last. */ static volatile unsigned int lastSentTxBuf = NUM_BUF - 1; /* Array of receive buffer pointers */ static unsigned int const rxBufPtr[NUM_BUF] = { (unsigned int) rxBuf0, (unsigned int) rxBuf1, (unsigned int) rxBuf2 }; /* Array of transmit buffer pointers */ static unsigned int const txBufPtr[NUM_BUF] = { (unsigned int) txBuf0, (unsigned int) txBuf1, (unsigned int) txBuf2 }; /* ** Default paRAM for Transmit section. This will be transmitting from ** a loop buffer. */ static struct EDMA3CCPaRAMEntry const txDefaultPar = { (unsigned int)(EDMA3CC_OPT_DAM | (0x02 << 8u)), /* Opt field */ (unsigned int)loopBuf, /* source address */ (unsigned short)(BYTES_PER_SAMPLE), /* aCnt */ (unsigned short)(NUM_SAMPLES_LOOP_BUF), /* bCnt */ (unsigned int) SOC_MCASP_0_DATA_REGS, /* dest address */ (short) (BYTES_PER_SAMPLE), /* source bIdx */ (short)(0), /* dest bIdx */ (unsigned short)(PAR_TX_START * SIZE_PARAMSET), /* link address */ (unsigned short)(0), /* bCnt reload value */ (short)(0), /* source cIdx */ (short)(0), /* dest cIdx */ (unsigned short)1 /* cCnt */ }; /* ** Default paRAM for Receive section. */ static struct EDMA3CCPaRAMEntry const rxDefaultPar = { (unsigned int)(EDMA3CC_OPT_SAM | (0x02 << 8u)), /* Opt field */ (unsigned int)SOC_MCASP_0_DATA_REGS, /* source address */ (unsigned short)(BYTES_PER_SAMPLE), /* aCnt */ (unsigned short)(1), /* bCnt */ (unsigned int)rxBuf0, /* dest address */ (short) (0), /* source bIdx */ (short)(BYTES_PER_SAMPLE), /* dest bIdx */ (unsigned short)(PAR_RX_START * SIZE_PARAMSET), /* link address */ (unsigned short)(0), /* bCnt reload value */ (short)(0), /* source cIdx */ (short)(0), /* dest cIdx */ (unsigned short)1 /* cCnt */ }; /****************************************************************************/ /* 函数声明 */ /****************************************************************************/ static void ParamTxLoopJobSet(unsigned short parId); static void I2SDMAParamInit(void); static void AIC31I2SConfigure(void); static void McASPI2SConfigure(void); static void EDMA3IntSetup(void); static void McASPErrorIntSetup(void); static void I2SDataTxRxActivate(void); void BufferTxDMAActivate(unsigned int txBuf, unsigned short numSamples, unsigned short parId, unsigned short linkPar); static void BufferRxDMAActivate(unsigned int rxBuf, unsigned short parId, unsigned short parLink); static void McASPRxDMAComplHandler(void); static void McASPTxDMAComplHandler(void); static void EDMA3CCComplIsr(void); static void McASPErrorIsr(void); /****************************************************************************/ /* 主函数 */ /****************************************************************************/ int main(void) { unsigned short parToSend; unsigned short parToLink; UARTStdioInit(); UARTPuts("\r\n ============Test Start===========.\r\n", -1); UARTPuts("Welcome to StarterWare Audio_MIC_In Demo application.\r\n\r\n", -1); UARTPuts("This application loops back the input at MIC_IN of the EVM to the LINE_OUT of the EVM\r\n\r\n", -1); /* Set up pin mux for I2C module 0 */ I2CPinMuxSetup(0); McASPPinMuxSetup(); /* Power up the McASP module */ PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_MCASP0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); /* Power up EDMA3CC_0 and EDMA3TC_0 */ PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_CC0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_TC0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); #ifdef _TMS320C6X // Initialize the DSP interrupt controller IntDSPINTCInit(); #else /* Initialize the ARM Interrupt Controller.*/ IntAINTCInit(); #endif /* Initialize the I2C 0 interface for the codec AIC31 */ I2CCodecIfInit(SOC_I2C_0_REGS, INT_CHANNEL_I2C, I2C_SLAVE_CODEC_AIC31); EDMA3Init(SOC_EDMA30CC_0_REGS, 0); EDMA3IntSetup(); McASPErrorIntSetup(); #ifdef _TMS320C6X IntGlobalEnable(); #else /* Enable the interrupts generation at global level */ IntMasterIRQEnable(); IntGlobalEnable(); IntIRQEnable(); #endif /* ** Request EDMA channels. Channel 0 is used for reception and ** Channel 1 is used for transmission */ EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, EDMA3_CHA_MCASP0_TX, EDMA3_CHA_MCASP0_TX, 0); EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, EDMA3_CHA_MCASP0_RX, EDMA3_CHA_MCASP0_RX, 0); /* Initialize the DMA parameters */ I2SDMAParamInit(); /* Configure the Codec for I2S mode */ AIC31I2SConfigure(); /* Configure the McASP for I2S */ McASPI2SConfigure(); /* Activate the audio transmission and reception */ I2SDataTxRxActivate(); /* ** Looop forever. if a new buffer is received, the lastFullRxBuf will be ** updated in the rx completion ISR. if it is not the lastSentTxBuf, ** buffer is to be sent. This has to be mapped to proper paRAM set. */ while(1) { if(lastFullRxBuf != lastSentTxBuf) { /* ** Start the transmission from the link paramset. The param set ** 1 will be linked to param set at PAR_TX_START. So do not ** update paRAM set1. */ parToSend = PAR_TX_START + (parOffTxToSend % NUM_PAR); parOffTxToSend = (parOffTxToSend + 1) % NUM_PAR; parToLink = PAR_TX_START + parOffTxToSend; lastSentTxBuf = (lastSentTxBuf + 1) % NUM_BUF; /* Copy the buffer */ memcpy((void *)txBufPtr[lastSentTxBuf], (void *)rxBufPtr[lastFullRxBuf], AUDIO_BUF_SIZE); /* ** Send the buffer by setting the DMA params accordingly. ** Here the buffer to send and number of samples are passed as ** parameters. This is important, if only transmit section ** is to be used. */ BufferTxDMAActivate(lastSentTxBuf, NUM_SAMPLES_PER_AUDIO_BUF, (unsigned short)parToSend, (unsigned short)parToLink); } } } /* ** Assigns loop job for a parameter set */ static void ParamTxLoopJobSet(unsigned short parId) { EDMA3CCPaRAMEntry paramSet; memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET - 2); /* link the paRAM to itself */ paramSet.linkAddr = parId * SIZE_PARAMSET; EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, parId, &paramSet); } /* ** Initializes the DMA parameters. ** The RX basic paRAM set(channel) is 0 and TX basic paRAM set (channel) is 1. ** ** The RX paRAM set 0 will be initialized to receive data in the rx buffer 0. ** The transfer completion interrupt will not be enabled for paRAM set 0; ** paRAM set 0 will be linked to linked paRAM set starting (PAR_RX_START) of RX. ** and further reception only happens via linked paRAM set. ** For example, if the PAR_RX_START value is 40, and the number of paRAMS is 2, ** reception paRAM set linking will be initialized as 0-->40-->41-->40 ** ** The TX paRAM sets will be initialized to transmit from the loop buffer. ** The size of the loop buffer can be configured. ** The transfer completion interrupt will not be enabled for paRAM set 1; ** paRAM set 1 will be linked to linked paRAM set starting (PAR_TX_START) of TX. ** All other paRAM sets will be linked to itself. ** and further transmission only happens via linked paRAM set. ** For example, if the PAR_RX_START value is 42, and the number of paRAMS is 2, ** So transmission paRAM set linking will be initialized as 1-->42-->42, 43->43. */ static void I2SDMAParamInit(void) { EDMA3CCPaRAMEntry paramSet; int idx; /* Initialize the 0th paRAM set for receive */ memcpy(&paramSet, &rxDefaultPar, SIZE_PARAMSET - 2); EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_RX, &paramSet); /* further paramsets, enable interrupt */ paramSet.opt |= RX_DMA_INT_ENABLE; for(idx = 0 ; idx < NUM_PAR; idx++) { paramSet.destAddr = rxBufPtr[idx]; paramSet.linkAddr = (PAR_RX_START + ((idx + 1) % NUM_PAR)) * (SIZE_PARAMSET); paramSet.bCnt = NUM_SAMPLES_PER_AUDIO_BUF; /* ** for the first linked paRAM set, start receiving the second ** sample only since the first sample is already received in ** rx buffer 0 itself. */ if( 0 == idx) { paramSet.destAddr += BYTES_PER_SAMPLE; paramSet.bCnt -= BYTES_PER_SAMPLE; } EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, (PAR_RX_START + idx), &paramSet); } /* Initialize the required variables for reception */ nxtBufToRcv = idx % NUM_BUF; lastFullRxBuf = NUM_BUF - 1; parOffRcvd = 0; /* Initialize the 1st paRAM set for transmit */ memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET); EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_TX, &paramSet); /* rest of the params, enable loop job */ for(idx = 0 ; idx < NUM_PAR; idx++) { ParamTxLoopJobSet(PAR_TX_START + idx); } /* Initialize the variables for transmit */ parOffSent = 0; lastSentTxBuf = NUM_BUF - 1; } /* ** Function to configure the codec for I2S mode */ static void AIC31I2SConfigure(void) { volatile unsigned int delay = 0xFFF; AIC31Reset(SOC_I2C_0_REGS); while(delay--); /* Configure the data format and sampling rate */ AIC31DataConfig(SOC_I2C_0_REGS, AIC31_DATATYPE_I2S, SLOT_SIZE, 0); AIC31SampleRateConfig(SOC_I2C_0_REGS, AIC31_MODE_BOTH, SAMPLING_RATE); /* Initialize both ADC and DAC */ AIC31ADCInit(SOC_I2C_0_REGS); AIC31DACInit(SOC_I2C_0_REGS); } /* ** Configures the McASP Transmit Section in I2S mode. */ static void McASPI2SConfigure(void) { McASPRxReset(SOC_MCASP_0_CTRL_REGS); McASPTxReset(SOC_MCASP_0_CTRL_REGS); /* Enable the FIFOs for DMA transfer */ McASPReadFifoEnable(SOC_MCASP_0_FIFO_REGS, 1, 1); McASPWriteFifoEnable(SOC_MCASP_0_FIFO_REGS, 1, 1); /* Set I2S format in the transmitter/receiver format units */ McASPRxFmtI2SSet(SOC_MCASP_0_CTRL_REGS, WORD_SIZE, SLOT_SIZE, MCASP_RX_MODE_DMA); McASPTxFmtI2SSet(SOC_MCASP_0_CTRL_REGS, WORD_SIZE, SLOT_SIZE, MCASP_TX_MODE_DMA); /* Configure the frame sync. I2S shall work in TDM format with 2 slots */ McASPRxFrameSyncCfg(SOC_MCASP_0_CTRL_REGS, 2, MCASP_RX_FS_WIDTH_WORD, MCASP_RX_FS_EXT_BEGIN_ON_FALL_EDGE); McASPTxFrameSyncCfg(SOC_MCASP_0_CTRL_REGS, 2, MCASP_TX_FS_WIDTH_WORD, MCASP_TX_FS_EXT_BEGIN_ON_RIS_EDGE); /* configure the clock for receiver */ McASPRxClkCfg(SOC_MCASP_0_CTRL_REGS, MCASP_RX_CLK_EXTERNAL, 0, 0); McASPRxClkPolaritySet(SOC_MCASP_0_CTRL_REGS, MCASP_RX_CLK_POL_RIS_EDGE); McASPRxClkCheckConfig(SOC_MCASP_0_CTRL_REGS, MCASP_RX_CLKCHCK_DIV32, 0x00, 0xFF); /* configure the clock for transmitter */ McASPTxClkCfg(SOC_MCASP_0_CTRL_REGS, MCASP_TX_CLK_EXTERNAL, 0, 0); McASPTxClkPolaritySet(SOC_MCASP_0_CTRL_REGS, MCASP_TX_CLK_POL_FALL_EDGE); McASPTxClkCheckConfig(SOC_MCASP_0_CTRL_REGS, MCASP_TX_CLKCHCK_DIV32, 0x00, 0xFF); /* Enable synchronization of RX and TX sections */ McASPTxRxClkSyncEnable(SOC_MCASP_0_CTRL_REGS); /* Enable the transmitter/receiver slots. I2S uses 2 slots */ McASPRxTimeSlotSet(SOC_MCASP_0_CTRL_REGS, I2S_SLOTS); McASPTxTimeSlotSet(SOC_MCASP_0_CTRL_REGS, I2S_SLOTS); /* ** Set the serializers, Currently only one serializer is set as ** transmitter and one serializer as receiver. */ McASPSerializerRxSet(SOC_MCASP_0_CTRL_REGS, MCASP_XSER_RX); McASPSerializerTxSet(SOC_MCASP_0_CTRL_REGS, MCASP_XSER_TX); /* ** Configure the McASP pins ** Input - Frame Sync, Clock and Serializer Rx ** Output - Serializer Tx is connected to the input of the codec */ McASPPinMcASPSet(SOC_MCASP_0_CTRL_REGS, 0xFFFFFFFF); McASPPinDirOutputSet(SOC_MCASP_0_CTRL_REGS,MCASP_PIN_AXR(MCASP_XSER_TX)); McASPPinDirInputSet(SOC_MCASP_0_CTRL_REGS, MCASP_PIN_AFSX | MCASP_PIN_ACLKX | MCASP_PIN_AHCLKX | MCASP_PIN_AXR(MCASP_XSER_RX)); /* Enable error interrupts for McASP */ McASPTxIntEnable(SOC_MCASP_0_CTRL_REGS, MCASP_TX_DMAERROR | MCASP_TX_CLKFAIL | MCASP_TX_SYNCERROR | MCASP_TX_UNDERRUN); McASPRxIntEnable(SOC_MCASP_0_CTRL_REGS, MCASP_RX_DMAERROR | MCASP_RX_CLKFAIL | MCASP_RX_SYNCERROR | MCASP_RX_OVERRUN); } /* ** Sets up the interrupts for EDMA in AINTC */ static void EDMA3IntSetup(void) { #ifdef _TMS320C6X IntRegister(C674X_MASK_INT5, EDMA3CCComplIsr); IntEventMap(C674X_MASK_INT5, SYS_INT_EDMA3_0_CC0_INT1); IntEnable(C674X_MASK_INT5); #else IntRegister(SYS_INT_CCINT0, EDMA3CCComplIsr); IntChannelSet(SYS_INT_CCINT0, INT_CHANNEL_EDMACC); IntSystemEnable(SYS_INT_CCINT0); #endif } /* ** Sets up the error interrupts for McASP in AINTC */ static void McASPErrorIntSetup(void) { #ifdef _TMS320C6X IntRegister(C674X_MASK_INT6, McASPErrorIsr); IntEventMap(C674X_MASK_INT6, SYS_INT_MCASP0_INT); IntEnable(C674X_MASK_INT6); #else /* Register the error ISR for McASP */ IntRegister(SYS_INT_MCASPINT, McASPErrorIsr); IntChannelSet(SYS_INT_MCASPINT, INT_CHANNEL_MCASP); IntSystemEnable(SYS_INT_MCASPINT); #endif } /* ** Activates the data transmission/reception ** The DMA parameters shall be ready before calling this function. */ static void I2SDataTxRxActivate(void) { /* Start the clocks */ McASPRxClkStart(SOC_MCASP_0_CTRL_REGS, MCASP_RX_CLK_EXTERNAL); McASPTxClkStart(SOC_MCASP_0_CTRL_REGS, MCASP_TX_CLK_EXTERNAL); /* Enable EDMA for the transfer */ EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_RX, EDMA3_TRIG_MODE_EVENT); EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_TX, EDMA3_TRIG_MODE_EVENT); /* Activate the serializers */ McASPRxSerActivate(SOC_MCASP_0_CTRL_REGS); McASPTxSerActivate(SOC_MCASP_0_CTRL_REGS); /* make sure that the XDATA bit is cleared to zero */ while(McASPTxStatusGet(SOC_MCASP_0_CTRL_REGS) & MCASP_TX_STAT_DATAREADY); /* Activate the state machines */ McASPRxEnable(SOC_MCASP_0_CTRL_REGS); McASPTxEnable(SOC_MCASP_0_CTRL_REGS); } /* ** Activates the DMA transfer for a parameterset from the given buffer. */ void BufferTxDMAActivate(unsigned int txBuf, unsigned short numSamples, unsigned short parId, unsigned short linkPar) { EDMA3CCPaRAMEntry paramSet; /* Copy the default paramset */ memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET - 2); /* Enable completion interrupt */ paramSet.opt |= TX_DMA_INT_ENABLE; paramSet.srcAddr = txBufPtr[txBuf]; paramSet.linkAddr = linkPar * SIZE_PARAMSET; paramSet.bCnt = numSamples; EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, parId, &paramSet); } /* ** Activates the DMA transfer for a parameter set from the given buffer. */ static void BufferRxDMAActivate(unsigned int rxBuf, unsigned short parId, unsigned short parLink) { EDMA3CCPaRAMEntry paramSet; /* Copy the default paramset */ memcpy(&paramSet, &rxDefaultPar, SIZE_PARAMSET - 2); /* Enable completion interrupt */ paramSet.opt |= RX_DMA_INT_ENABLE; paramSet.destAddr = rxBufPtr[rxBuf]; paramSet.bCnt = NUM_SAMPLES_PER_AUDIO_BUF; paramSet.linkAddr = parLink * SIZE_PARAMSET ; EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, parId, &paramSet); } /* ** This function will be called once receive DMA is completed */ static void McASPRxDMAComplHandler(void) { unsigned short nxtParToUpdate; /* ** Update lastFullRxBuf to indicate a new buffer reception ** is completed. */ lastFullRxBuf = (lastFullRxBuf + 1) % NUM_BUF; nxtParToUpdate = PAR_RX_START + parOffRcvd; parOffRcvd = (parOffRcvd + 1) % NUM_PAR; /* ** Update the DMA parameters for the received buffer to receive ** further data in proper buffer */ BufferRxDMAActivate(nxtBufToRcv, nxtParToUpdate, PAR_RX_START + parOffRcvd); /* update the next buffer to receive data */ nxtBufToRcv = (nxtBufToRcv + 1) % NUM_BUF; } /* ** This function will be called once transmit DMA is completed */ static void McASPTxDMAComplHandler(void) { ParamTxLoopJobSet((unsigned short)(PAR_TX_START + parOffSent)); parOffSent = (parOffSent + 1) % NUM_PAR; } /* ** EDMA transfer completion ISR */ static void EDMA3CCComplIsr(void) { #ifdef _TMS320C6X IntEventClear(SYS_INT_EDMA3_0_CC0_INT1); #else IntSystemStatusClear(SYS_INT_CCINT0); #endif /* Check if receive DMA completed */ if(EDMA3GetIntrStatus(SOC_EDMA30CC_0_REGS) & (1 << EDMA3_CHA_MCASP0_RX)) { /* Clear the interrupt status for the 0th channel */ EDMA3ClrIntr(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_RX); McASPRxDMAComplHandler(); } /* Check if transmit DMA completed */ if(EDMA3GetIntrStatus(SOC_EDMA30CC_0_REGS) & (1 << EDMA3_CHA_MCASP0_TX)) { /* Clear the interrupt status for the first channel */ EDMA3ClrIntr(SOC_EDMA30CC_0_REGS, EDMA3_CHA_MCASP0_TX); McASPTxDMAComplHandler(); } } /* ** Error ISR for McASP */ static void McASPErrorIsr(void) { #ifdef _TMS320C6X IntEventClear(SYS_INT_MCASP0_INT); #else IntSystemStatusClear(SYS_INT_MCASPINT); #endif ; /* Perform any error handling here.*/ } /***************************** End Of File ***********************************/ 将以上代码和以下代码合在一起:#include "math.h" #include "mathlib.h" #include "dsplib.h" #define PI 3.1415926535 #define F_TOL (1e-06) #define Tn 1024 #define Fs 48000.0 #define N 132 // 滤波器阶数(偶数) #define FilterCount 5 const float F1s[FilterCount] = {20.0, 400.0, 1200.0, 4000.0, 13000.0}; const float F2s[FilterCount] = {400.0, 1200.0, 4000.0, 13000.0, 20000.0}; #pragma DATA_ALIGN(FIR_In, 8); float FIR_In[Tn]; #pragma DATA_ALIGN(FIR_Outs, 8); float FIR_Outs[FilterCount][Tn]; #pragma DATA_ALIGN(FIR_CombinedOut, 8); float FIR_CombinedOut[Tn]; #pragma DATA_ALIGN(Bs, 8); float Bs[FilterCount][N]; void FIRTest(void); void design_blackman_bandpass_fir(float *h, int n, float f1, float f2, float fs); void blackman_window(float *w, int n); void normalize_filter_response(float *h, int n, float f1, float f2, float fs); int main(void) { int i; // 声明循环变量 for (i = 0; i < FilterCount; i++) { design_blackman_bandpass_fir(Bs[i], N, F1s[i], F2s[i], Fs); } FIRTest(); return 0; } void blackman_window(float *w, int n) { int i; // 声明循环变量 for (i = 0; i < n; i++) { w[i] = 0.42f - 0.5f * cosf(2.0f * PI * i / (n - 1)) + 0.08f * cosf(4.0f * PI * i / (n - 1)); } } void normalize_filter_response(float *h, int n, float f1, float f2, float fs) { float center_freq = (f1 + f2) / 2.0f; float omega = 2.0f * PI * center_freq / fs; float real_gain = 0.0f; int i; // 声明循环变量 for (i = 0; i < n; i++) { real_gain += h[i] * cosf(omega * (i - (n-1)/2.0f)); } if (fabsf(real_gain) > F_TOL) { for (i = 0; i < n; i++) { h[i] /= real_gain; } } } void design_blackman_bandpass_fir(float *h, int n, float f1, float f2, float fs) { float w[N]; float fc1 = f1 / fs; float fc2 = f2 / fs; int i; // 声明循环变量 blackman_window(w, n); for (i = 0; i < n; i++) { float m = i - (n - 1)/2.0f; h[i] = (fabsf(m) < F_TOL) ? 2.0f * (fc2 - fc1) : (sinf(2.0f * PI * fc2 * m) - sinf(2.0f * PI * fc1 * m)) / (PI * m); h[i] *= w[i]; } normalize_filter_response(h, n, f1, f2, fs); } void FIRTest(void) { int i, j; // 声明循环变量 // 生成测试信号 - 每个频段一个测试频率 for (i = 0; i < Tn; i++) { float t = (float)i / Fs; FIR_In[i] = 5.0f * sinf(2.0f * PI * 10.0f * t) + 5.0f * sinf(2.0f * PI * 15000.0f * t)+ 5.0f * sinf(2.0f * PI * 25000.0f * t); } // 初始化并处理滤波器输出 for (i = 0; i < Tn; i++) { FIR_CombinedOut[i] = 0.0f; } for (j = 0; j < FilterCount; j++) { DSPF_sp_fir_r2(FIR_In, Bs[j], FIR_Outs[j], N, Tn); // 累加各滤波器输出 for (i = 0; i < Tn; i++) { FIR_CombinedOut[i] += FIR_Outs[j][i]; } } } 将测试信号去掉,合成后的代码输入就是音频输入,输出就是音频输出,滤波器来处理数据基于TMS320C6748在CCS环境中实现
最新发布
05-13
<think>好的,我现在要帮助用户解决如何在TMS320C6748的CCS开发环境中将音频采集与播放代码和滤波器设计代码合并的问题。首先,我需要理解用户的需求。用户希望将两段现有的代码整合,使得音频输入经过滤波器处理后输出。这意味着需要将音频采集、处理和输出三个部分连接起来。 首先,我应该回顾TMS320C6748的硬件架构和相关外设。根据引用[1],音频信号的处理流程涉及ADC、McASP、DSP和DAC。所以,用户现有的音频采集与播放代码可能已经处理了ADC和DAC的配置,以及通过McASP进行数据传输。而滤波器代码可能是在DSP中处理数字信号的部分。 接下来,我需要考虑代码整合的步骤。通常,这样的整合需要确保数据流的连贯性。采集到的音频数据需要传递给滤波器处理,然后再发送到输出。因此,首先需要确认两段代码的接口部分,比如数据缓冲区的位置和格式。例如,采集代码可能将数据存入一个缓冲区,而滤波器需要从该缓冲区读取数据进行处理,然后将结果存入另一个输出缓冲区。 然后,需要检查硬件初始化部分。McASP、ADC、DAC的配置是否正确,是否在整合过程中有冲突。比如,两段代码可能都配置了McASP,这时需要确保配置参数一致,或者只保留一份正确的配置代码。 另外,中断服务程序(ISR)的处理也很重要。音频采集和播放通常依赖中断来触发数据传输。需要确保中断处理函数中正确调用滤波处理函数,并且处理后的数据能及时发送出去。这里可能需要调整中断服务程序的流程,添加滤波处理步骤。 关于实时性,滤波器算法的复杂度可能会影响系统的实时性能。如果滤波器是FIR或IIR,需要确保在采样间隔内完成计算,否则会导致数据丢失或延迟。可能需要优化滤波器代码,比如使用汇编指令或利用DSP的硬件加速功能,如引用[2]中提到的程序优化部分。 还需要考虑数据的格式转换。ADC输出的数据可能是整数格式,而滤波器可能需要浮点数进行计算。在整合时,可能需要添加数据格式转换的步骤,处理后再转换回整数发送到DAC。 测试和调试也是关键。整合后,需要通过CCS的调试工具查看数据流是否正确,是否有溢出或数据错位的情况。可以利用CCS的断点和内存查看功能,检查各个缓冲区的数据是否正确处理。 可能遇到的问题包括:数据缓冲区的大小不匹配,导致数据覆盖或丢失;中断优先级设置不当,影响实时性;滤波器处理时间过长,导致无法及时输出。需要逐一排查这些问题,可能需要调整缓冲区大小,优化代码结构,或者简化滤波器算法。 最后,参考引用[2]中的内容,可能需要查阅相关外设开发章节,确认McASP、DMA等配置是否正确,确保数据传输的高效性。同时,程序优化部分的知识可以帮助提升滤波器的处理效率,确保实时性要求得到满足。 总结步骤大致如下:分析现有代码结构,确定数据流接口;整合初始化配置,确保外设正确工作;修改中断服务程序,加入滤波处理;处理数据格式转换;优化算法保证实时性;测试调试确保功能正常。</think>在TMS320C6748的CCS开发环境中实现音频采集-滤波-输出功能,需要按照以下步骤进行系统整合: ### 一、硬件接口配置 1. **McASP初始化** 根据引用[1]的音频传输流程,需配置McASP接口参数: ```c void McASP_Init() { // 设置工作模式、时钟分频、帧同步 MCASP_PinMux(MCASP0AXR0_PINMUX); MCASP_ConfigData(MCASP0_BASE, 16, 2, MCASP_WORD_16); // 使能DMA事件 MCASP_DMAEnable(MCASP0_BASE, MCASP_DMA_RX | MCASP_DMA_TX); } ``` ### 二、数据流整合 2. **双缓冲机制实现** 采用乒乓缓冲区保证实时处理: ```c #define BUFFER_SIZE 256 #pragma DATA_SECTION(rxBuffer, ".edma"); int16_t rxBuffer[2][BUFFER_SIZE]; // ADC输入缓冲区 #pragma DATA_SECTION(txBuffer, ".edma"); int16_t txBuffer[2][BUFFER_SIZE]; // DAC输出缓冲区 volatile int bufIndex = 0; // 当前活动缓冲区索引 ``` ### 三、滤波器集成 3. **实时滤波处理函数** 在EDMA中断中调用滤波器(以FIR为例): ```c interrupt void EDMA_ISR() { EDMA_ClrEvent(EDMA_CHA_RX); // 清除中断标志 // 获取当前缓冲区指针 int16_t *input = rxBuffer[bufIndex]; int16_t *output = txBuffer[bufIndex]; // 执行滤波处理 FIR_Filter(input, output, BUFFER_SIZE); // 切换缓冲区索引 bufIndex ^= 0x01; } ``` ### 四、关键优化技术 4. **DSP加速实现** 使用C674x特有指令优化滤波器(引用[2]程序优化篇): ```c void FIR_Filter(int16_t *in, int16_t *out, int len) { #pragma MUST_ITERATE(256,,256) for(int i=0; i<len; i++) { _amem4(&out[i]) = _dotp2(_amem4(&in[i]), _amem4(&coeffs[0])); // 使用硬件乘法累加 } } ``` ### 五、系统调试要点 1. **实时性验证** 使用CCS的CPU负载分析工具(Tools → Load Meter),确保滤波处理时间小于采样周期: $$ T_{process} < \frac{1}{f_s} $$ 例如采样率48kHz时需满足: $$ T_{process} < 20.8μs $$ 2. **数据完整性检查** 通过Memory Browser查看EDMA传输的缓冲区数据,确认滤波前后的波形变化符合预期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值