02-RT1060 双ADC采样+eDMA传输 RT1060-双ADCeDMA外设的配合使用该项目是基于官方开发板1060一、头文件包含介绍#include pin_mux.h #include clock_config.h #include board.h #include fsl_adc.h #include fsl_adc_etc.h #include fsl_pit.h #include fsl_xbara.h #include fsl_common.h #include fsl_iomuxc.h #include fsl_edma.h #include fsl_dmamux.h上面代码段是需要包含的头文件分别由ADC相关的fsl_adc.h、fsl_adc_etc.h定时触发的周期定时器fsl_pit.h,触发链接的fsl_xbara.h和DMA相关的fsl_edma.h、fsl_dmamux.h其他几个是使用IO可初始化部分系统时钟需要用到的头文件。二、自定义宏介绍//1.ADC配置 #define USER_ADC1_BASE ADC1 #define ADC1_USER_CHANNEL 16U #define ADC1_CHANNEL_GROUP 4U #define USER_ADC2_BASE ADC2 #define ADC2_USER_CHANNEL 16U #define ADC2_CHANNEL_GROUP 5U //2.ADC DMA配置设置 #define ADC_DMA DMA0 #define USER_DMAMUX DMAMUX #define ADC1_EDMA_CHANNEL 4 #define ADC2_EDMA_CHANNEL 5 //3.多通道采样 #define USER_ADC_ETC_BASE ADC_ETC #define ADC1_ETC_CHAIN_LENGTH 2U #define ADC2_ETC_CHAIN_LENGTH 4U //4.触发 #define XBARA_BASE XBARA1 #define ADC1_XBARA_INPUT_PITCH kXBARA1_InputPitTrigger0 #define ADC1_XBARA_OUTPUT_ADC_ETC kXBARA1_OutputAdcEtcXbar0Trig3 #define ADC1_ETC_XBARA_TRIGGER_CHANNELx 3 //每个ADC有4个转换通道组ADC1对应0到3ADC2对应4到7. #define ADC1_ETC_XBARA_CHANNEL_BASE 0x403B00A0 #define ADC2_XBARA_INPUT_PITCH kXBARA1_InputPitTrigger1 #define ADC2_XBARA_OUTPUT_ADC_ETC kXBARA1_OutputAdcEtcXbar1Trig3 #define ADC2_ETC_XBARA_TRIGGER_CHANNELx 7 //每个ADC有4个转换通道组ADC1对应0到3ADC2对应4到7. #define ADC2_ETC_XBARA_CHANNEL_BASE 0x403B0140 //5.ADC采样周期配置设置 #define USER_PIT_BASE PIT #define PIT_SOURCE_CLOCK CLOCK_GetFreq(kCLOCK_OscClk) //得到PIT的时钟频率 #define ADC1_PIT_CHANNEL kPIT_Chnl_0 #define ADC2_PIT_CHANNEL kPIT_Chnl_1 #define ADC1_SAMPLE_NUM 5 //采样滤波次数 #define ADC2_SAMPLE_NUM 5 //采样滤波次数1.ADC配置USER_ADC1_BASE、USER_ADC2_BASE分别定义了ADC1和ADC2ADC1_USER_CHANNEL、ADC2_USER_CHANNEL是设置的转换通道号取值范围为0~31ADC1_CHANNEL_GROUP和ADC2_CHANNEL_GROUP是设置ADC的通道组这里分别取4和5。2.ADC DMA配置设置系统只有DMA0所以ADC的DMA为DMA0ADC1_EDMA_CHANNEL定义的是DMA的通道我这边0~3用于了其他设备所以这里取的是通道4和5.3.多通道采样USER_ADC_ETC_BASE定义ADC外部触发的ADC_ETC,ADC1_ETC_CHAIN_LENGTH取2表示ADC1采集2个ADC通道ADC2_EDMA_CHANNEL取4表示ADC2采集4个ADC通道.4.触发定义了ADC的触发通道使用PIT的通道0触发XBARA1的0-3使用PIT的通道1触发XBARA1的1-3分别对应的ADC_ETC的转换通道3和7查《IMXRT1060RM》手册可知ADC_ETC转换通道3的输出存储地址为0x403B00A0ADC_ETC转换通道7的输出存储地址为0x403B0140。注图片被CSDN给转存没了无语了。5.ADC采样周期ADC的采样周期这里使用的是周期定时器PIT来触发使用了PIT的通道0和通道1.三、使用到的变量定义/* Private typedef----------------------------------------------------------------------------*/ typedef struct _str_adc_gpio /*ADC GPIO*/ { uint8_t adc_channel; // ADC 通道 }str_adc_gpio; /* Private variables--------------------------------------------------------------------------*/ edma_handle_t g_adc1_edma_handle; //EDMA的回调函数句柄 edma_handle_t g_adc2_edma_handle; edma_transfer_config_t adc1_transfer_config; //EDMA传输配置 edma_transfer_config_t adc2_transfer_config; uint8_t g_adc1_count 0; uint8_t g_adc2_count 0; volatile uint32_t adc1_sample_count; volatile uint32_t adc2_sample_count; //传输到内存的缓存区 AT_NONCACHEABLE_SECTION_INIT(uint16_t g_ADC1_buff[ADC1_SAMPLE_NUM][ADC1_ETC_CHAIN_LENGTH]) {0x00}; AT_NONCACHEABLE_SECTION_INIT(uint16_t g_ADC2_buff[ADC2_SAMPLE_NUM][ADC2_ETC_CHAIN_LENGTH]) {0x00}; //定义ADC1的采样通道 static const str_adc_gpio c_str_adc1_gpio_tl[ADC1_ETC_CHAIN_LENGTH] { {.adc_channel 15U,}, {.adc_channel 0U,}, }; //定义ADC2的采样通道 static const str_adc_gpio c_str_adc2_gpio_tl[ADC2_ETC_CHAIN_LENGTH] { {.adc_channel 9U,}, {.adc_channel 10U,}, {.adc_channel 6U,}, {.adc_channel 5U,}, };四、ADC通道GPIO初始化函数/** * adc_gpio_config * * param adc_channel * return none * **/ static void adc_gpio_config(void) { IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_10_GPIO1_IO26, 0U); //ADC_15 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_11_GPIO1_IO27, 0U); //ADC_00 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_04_GPIO1_IO20, 0U); //ADC_09 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_05_GPIO1_IO21, 0U); //ADC_10 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_01_GPIO1_IO17, 0U); //ADC_06 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_00_GPIO1_IO16, 0U); //ADC_05 IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_10_GPIO1_IO26, 0xB0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_11_GPIO1_IO27, 0xB0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_04_GPIO1_IO20, 0xB0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_05_GPIO1_IO21, 0xB0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_01_GPIO1_IO17, 0xB0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_00_GPIO1_IO16, 0xB0U); }基于五、EDMA的ADC采样完成回调函数//ADC1 DMA 发送函数 static void adc1_transfer_create_handle_edma(void) { g_adc1_count adc1_sample_count % ADC1_SAMPLE_NUM; adc1_sample_count; /* Configure and submit transfer structure */ EDMA_PrepareTransferConfig(adc1_transfer_config, //返回的配置信息 (void *)ADC1_ETC_XBARA_CHANNEL_BASE, sizeof(uint16_t), sizeof(uint16_t), g_ADC1_buff[g_adc1_count][0], sizeof(uint16_t), sizeof(uint16_t), sizeof(uint16_t), // minor loop bytes : 4 sizeof(uint16_t) * ADC1_ETC_CHAIN_LENGTH); // major loop counts : 8 EDMA_SubmitTransfer(g_adc1_edma_handle, adc1_transfer_config); //提交发送请求 EDMA_StartTransfer(g_adc1_edma_handle); //开始发送 } //ADC1 DMA回调函数 static void ADC1_EDMACallback(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds) { adc1_transfer_create_handle_edma(); } //ADC2 DMA 发送函数 static void adc2_transfer_create_handle_edma(void) { g_adc2_count adc2_sample_count % ADC2_SAMPLE_NUM; adc2_sample_count; /* Configure and submit transfer structure */ EDMA_PrepareTransferConfig(adc2_transfer_config, //返回的配置信息 (void *)ADC2_ETC_XBARA_CHANNEL_BASE, sizeof(uint16_t), sizeof(uint16_t), g_ADC2_buff[g_adc2_count][0], sizeof(uint16_t), sizeof(uint16_t), sizeof(uint16_t), // minor loop bytes : 4 sizeof(uint16_t) * ADC2_ETC_CHAIN_LENGTH); // major loop counts : 8 EDMA_SubmitTransfer(g_adc2_edma_handle, adc2_transfer_config); //提交发送请求 EDMA_StartTransfer(g_adc2_edma_handle); //开始发送 } //ADC2 DMA回调函数 static void ADC2_EDMACallback(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds) { adc2_transfer_create_handle_edma(); }六、ADC的配置函数/*! * brief 配置ADC与ADC ETC一起工作。 */ void adc_configuration(void) { adc_config_t t_k_adcConfig; adc_channel_config_t t_adcChannelConfigStruct; /* Initialize the ADC module. */ ADC_GetDefaultConfig(t_k_adcConfig); //获取ADC默认配置 t_k_adcConfig.enableAsynchronousClockOutput true; //使能 异步时钟输出 t_k_adcConfig.enableOverWrite false; //使能 重写 t_k_adcConfig.enableContinuousConversion false; //使能 连续转换 t_k_adcConfig.enableHighSpeed false; //使能 高速 t_k_adcConfig.enableLowPower false; //使能 低功耗 t_k_adcConfig.enableLongSample false; //使能 长采样 t_k_adcConfig.referenceVoltageSource kADC_ReferenceVoltageSourceAlt0; //选择基准电压源 t_k_adcConfig.samplePeriodMode kADC_SamplePeriod2or12Clocks; //采样周期模式 t_k_adcConfig.clockSource kADC_ClockSourceAD; //时钟源 t_k_adcConfig.clockDriver kADC_ClockDriver1; //时钟分频器 t_k_adcConfig.resolution kADC_Resolution12Bit; //采样位数 ADC_Init(USER_ADC1_BASE, t_k_adcConfig); //初始化ADC ADC_EnableHardwareTrigger(USER_ADC1_BASE, true); //使能硬件触发 ADC_Init(USER_ADC2_BASE, t_k_adcConfig); //初始化ADC ADC_EnableHardwareTrigger(USER_ADC2_BASE, true); //使能硬件触发 //adc 通道配置 t_adcChannelConfigStruct.channelNumber ADC1_USER_CHANNEL; // 外部通道选择从 ADC_ETC. */ t_adcChannelConfigStruct.enableInterruptOnConversionCompleted false; // 启动转换完成中断 ADC_SetChannelConfig(USER_ADC1_BASE, ADC1_CHANNEL_GROUP, t_adcChannelConfigStruct); t_adcChannelConfigStruct.channelNumber ADC2_USER_CHANNEL; // 外部通道选择从 ADC_ETC. */ t_adcChannelConfigStruct.enableInterruptOnConversionCompleted false; // 启动转换完成中断 ADC_SetChannelConfig(USER_ADC2_BASE, ADC2_CHANNEL_GROUP, t_adcChannelConfigStruct); /* 进行自动硬件校准 */ ADC_DoAutoCalibration(USER_ADC1_BASE); ADC_DoAutoCalibration(USER_ADC2_BASE); }七、ADC的DMA配置函数/** * adc_dma_config * * param * return none * **/ static void adc_dma_config(void) { edma_config_t t_EDMA_Config {0}; //配置ADC 的 EDMA设置 ADC_EnableDMA(USER_ADC1_BASE, true); ADC_EnableDMA(USER_ADC2_BASE, true); DMAMUX_Init(USER_DMAMUX); /* Set channel for ADC1 */ DMAMUX_SetSource(USER_DMAMUX, ADC1_EDMA_CHANNEL, kDmaRequestMuxADC1); //设置通道源 DMAMUX_EnableChannel(USER_DMAMUX, ADC1_EDMA_CHANNEL); DMAMUX_SetSource(USER_DMAMUX, ADC2_EDMA_CHANNEL, kDmaRequestMuxADC2); //设置通道源 DMAMUX_EnableChannel(USER_DMAMUX, ADC2_EDMA_CHANNEL); /* Init the EDMA module */ EDMA_GetDefaultConfig(t_EDMA_Config); t_EDMA_Config.enableRoundRobinArbitration false; //使能轮循仲裁 t_EDMA_Config.enableHaltOnError true; //使能错误中止 t_EDMA_Config.enableContinuousLinkMode false; //使能连续链路模式 t_EDMA_Config.enableDebugMode false; //使能调试模式 EDMA_Init(ADC_DMA, t_EDMA_Config); //初始化 EDMA_CreateHandle(g_adc1_edma_handle, ADC_DMA, ADC1_EDMA_CHANNEL); EDMA_SetCallback(g_adc1_edma_handle, ADC1_EDMACallback, NULL); EDMA_CreateHandle(g_adc2_edma_handle, ADC_DMA, ADC2_EDMA_CHANNEL); EDMA_SetCallback(g_adc2_edma_handle, ADC2_EDMACallback, NULL); adc1_transfer_create_handle_edma(); adc2_transfer_create_handle_edma(); }八、XBARA的配置函数/*! * brief 配置XBARA与ADC等一起工作。 */ static void xbara_configuration(void) { /* Init xbara module. */ XBARA_Init(XBARA_BASE); /* Configure the XBARA signal connections. */ XBARA_SetSignalsConnection(XBARA_BASE, ADC1_XBARA_INPUT_PITCH, ADC1_XBARA_OUTPUT_ADC_ETC); XBARA_SetSignalsConnection(XBARA_BASE, ADC2_XBARA_INPUT_PITCH, ADC2_XBARA_OUTPUT_ADC_ETC); }九、PIT的配置函数static void adc_pit_configuration(void) { /* Structure of initialize PIT */ pit_config_t pitConfig; /* Init pit module */ PIT_GetDefaultConfig(pitConfig); PIT_Init(USER_PIT_BASE, pitConfig); } /*! * brief 配置PIT触发ADC ETC。 */ static void set_adc1_pit_time(uint32_t delay_us) { PIT_SetTimerPeriod(USER_PIT_BASE, ADC1_PIT_CHANNEL, USEC_TO_COUNT(delay_us, PIT_SOURCE_CLOCK)); } static void set_adc2_pit_time(uint32_t delay_us) { PIT_SetTimerPeriod(USER_PIT_BASE, ADC2_PIT_CHANNEL, USEC_TO_COUNT(delay_us, PIT_SOURCE_CLOCK)); }十、ADC整体初始化函数/** * adc_init * * **/ void adc_init(void) { uint32_t t_adc1_etc_Offset 0; uint32_t t_adc2_etc_Offset 0; uint16_t i 0; uint8_t t_pit_init_flag 0; adc_etc_config_t t_adcEtcConfig; //ADC_ETC 配置 adc_etc_trigger_config_t t_adcEtcTriggerConfig; //ADC_ETC 触发配置 adc_etc_trigger_chain_config_t t_adcEtcTriggerChainConfig; //ADC_ETC 触发通道配置 g_adc1_count 0; g_adc2_count 0; adc_gpio_config(); /* Set PERCLK_CLK source to OSC_CLK*/ CLOCK_SetMux(kCLOCK_PerclkMux, 1U); /* Set PERCLK_CLK divider to 1 */ CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U); adc_configuration(); adc_dma_config(); xbara_configuration(); adc_pit_configuration(); set_adc1_pit_time(1000); set_adc2_pit_time(10000); /* ADC_ETC ADC外部触发控制 */ ADC_ETC_GetDefaultConfig(t_adcEtcConfig); t_adcEtcConfig.enableTSCBypass false; t_adc1_etc_Offset 1 ADC1_ETC_XBARA_TRIGGER_CHANNELx; t_adc2_etc_Offset 1 ADC2_ETC_XBARA_TRIGGER_CHANNELx; t_adcEtcConfig.XBARtriggerMask t_adc1_etc_Offset | t_adc2_etc_Offset; ADC_ETC_Init(USER_ADC_ETC_BASE, t_adcEtcConfig); /* 设置外部XBAR trigger0配置 */ t_adcEtcTriggerConfig.enableSyncMode false; //使能同步模式 t_adcEtcTriggerConfig.enableSWTriggerMode false; //使能软件触发模式 t_adcEtcTriggerConfig.triggerChainLength ADC1_ETC_CHAIN_LENGTH - 1; //从0开始 t_adcEtcTriggerConfig.triggerPriority 0U; //外部触发优先级7为最高0为最低 t_adcEtcTriggerConfig.sampleIntervalDelay 0U; t_adcEtcTriggerConfig.initialDelay 0U; ADC_ETC_SetTriggerConfig(USER_ADC_ETC_BASE, ADC1_ETC_XBARA_TRIGGER_CHANNELx, t_adcEtcTriggerConfig); t_adcEtcTriggerConfig.enableSyncMode false; //使能同步模式 t_adcEtcTriggerConfig.enableSWTriggerMode false; //使能软件触发模式 t_adcEtcTriggerConfig.triggerChainLength ADC2_ETC_CHAIN_LENGTH - 1; //从0开始 t_adcEtcTriggerConfig.triggerPriority 0U; //外部触发优先级7为最高0为最低 t_adcEtcTriggerConfig.sampleIntervalDelay 0U; t_adcEtcTriggerConfig.initialDelay 0U; ADC_ETC_SetTriggerConfig(USER_ADC_ETC_BASE, ADC2_ETC_XBARA_TRIGGER_CHANNELx, t_adcEtcTriggerConfig); /* 设置外部XBAR触发器链配置 */ for (i 0; i ADC1_ETC_CHAIN_LENGTH; i) { t_adcEtcTriggerChainConfig.enableB2BMode true; t_adcEtcTriggerChainConfig.ADCHCRegisterSelect 1U ADC1_CHANNEL_GROUP; /* 选择ADC HC0寄存器触发。 */ t_adcEtcTriggerChainConfig.ADCChannelSelect c_str_adc1_gpio_tl[i].adc_channel; /* ADC_HC0将被触发采样相应的通道。 */ t_adcEtcTriggerChainConfig.InterruptEnable kADC_ETC_InterruptDisable; /* 启用Done0中断。 */ #if defined(FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN) FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN t_adcEtcTriggerChainConfig.enableIrq true; /* Enable the IRQ. */ #endif /* FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN */ ADC_ETC_SetTriggerChainConfig(USER_ADC_ETC_BASE, ADC1_ETC_XBARA_TRIGGER_CHANNELx, i, t_adcEtcTriggerChainConfig); /* 配置trigger0 chain0。 */ } /* 设置外部XBAR触发器链配置 */ for (i 0; i ADC2_ETC_CHAIN_LENGTH; i) { t_adcEtcTriggerChainConfig.enableB2BMode true; t_adcEtcTriggerChainConfig.ADCHCRegisterSelect 1U ADC2_CHANNEL_GROUP; /* 选择ADC HC0寄存器触发。 */ t_adcEtcTriggerChainConfig.ADCChannelSelect c_str_adc2_gpio_tl[i].adc_channel; /* ADC_HC0将被触发采样相应的通道。 */ t_adcEtcTriggerChainConfig.InterruptEnable kADC_ETC_InterruptDisable; /* 启用Done0中断。 */ #if defined(FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN) FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN t_adcEtcTriggerChainConfig.enableIrq true; /* Enable the IRQ. */ #endif /* FSL_FEATURE_ADC_ETC_HAS_TRIGm_CHAIN_a_b_IEn_EN */ ADC_ETC_SetTriggerChainConfig(USER_ADC_ETC_BASE, ADC2_ETC_XBARA_TRIGGER_CHANNELx, i, t_adcEtcTriggerChainConfig); /* 配置trigger0 chain0。 */ } /* Start PIT channel0. */ PIT_StartTimer(PIT, kPIT_Chnl_0); PIT_StartTimer(PIT, kPIT_Chnl_1); }十一、使用示例在主函数中调用adc_init()函数即可启动 ADCDMA 的采样流程。采样数据会实时存储到全局缓冲区中int main(void) { // 硬件初始化时钟、引脚等 BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitBootPeripherals(); // 初始化 ADC 系统包含 ADC、DMA、XBARA、PIT 等配置 adc_init(); while (1) { // 主循环中可以读取采样数据 // g_ADC1_buff 和 g_ADC2_buff 是二维数组存储多通道、多次采样的数据 // 例如读取 ADC1 第0次采样的第0通道数据 uint16_t adc1_ch0_value g_ADC1_buff[0][0]; uint16_t adc2_ch0_value g_ADC2_buff[0][0]; // 这里可以添加数据处理、显示或传输逻辑 // ... // 简单延时 SDK_DelayAtLeastUs(1000000, CLOCK_GetFreq(kCLOCK_CpuClk)); } }数据访问说明g_ADC1_buff[ADC1_SAMPLE_NUM][ADC1_ETC_CHAIN_LENGTH]ADC1 的采样缓冲区第一维采样次数索引0 ~ ADC1_SAMPLE_NUM-1第二维通道索引0 ~ ADC1_ETC_CHAIN_LENGTH-1g_ADC2_buff[ADC2_SAMPLE_NUM][ADC2_ETC_CHAIN_LENGTH]ADC2 的采样缓冲区第一维采样次数索引0 ~ ADC2_SAMPLE_NUM-1第二维通道索引0 ~ ADC2_ETC_CHAIN_LENGTH-1注意事项缓冲区使用了AT_NONCACHEABLE_SECTION_INIT属性确保 DMA 能够正确访问采样数据会在 DMA 回调函数中自动更新无需手动触发可以通过修改set_adc1_pit_time()和set_adc2_pit_time()的参数来调整采样频率实际通道映射关系在c_str_adc1_gpio_tl和c_str_adc2_gpio_tl数组中定义