对GD32F450的ADC同步模式的理解

本文详细介绍了GD32F450微控制器中ADC同步模式的工作原理,特别是使用DMA模式0进行数据传输的过程。通过配置ADC同步模式,可以同时采集多个通道的数据,提高效率。代码示例展示了如何配置ADC、DMA和TIMER,以实现同步转换和数据读取。在实践中,作者强调了配置的细节和故障诊断的重要性。
摘要由CSDN通过智能技术生成

对GD32F450的ADC同步模式的理解


前言

本文的参考资料主要为《GD32F4xx_User_Manual_CN_V2.2》,《GD32F450xx_Datasheet_Rev1.1》,《STM32库开发实战指南——基于野火挑战者开发板》以及《GD32F4xx_Firmware_Library_V2.1.3》固件库中的示例。

本文的主要内容为我对GD32F450的ADC同步模式的一点理解,以及代码实现。

作为学习的过程产物,错误之处在所难免,希望各位积极指出,多多交流。


一、对ADC同步模式的理解

该图片引自GD32F4xx_User_Manual_CN_V2.2
该图片引自GD32F4xx_User_Manual_CN_V2.2

ADC的同步模式中,通过ADC0的触发器来同步ADC1和ADC2的转换。

该文字引自GD32F4xx_User_Manual_CN_V2.2

我的理解是:在对多路模拟信号进行采样时,同时使用ADC0,1,2三个模块,能够提高程序效率。
如图所示,规则并行模式下,对通道的采集是同时的,因此三个ADC模块不能同时对同一个通道进行采集,并且ADC采集的通道数量是对其的(各ADC模块的采集序列中的通道个数相同)。

二、ADC同步模式中使用DMA模式 0

在 ADC 同步 DMA 模式 0 中,DMA 传输的位宽为 16。一次 DMA 请求传输一个数据,这个数据轮流的从各 ADC的规则转换结果中取出。对于每次 DMA 请求,DMA 通道的源地址固定为ADC_SYNCDATA 寄存器,而这个寄存器的内容会变成 DMA要被传输的数值。
当所有的ADC都工作在同步模式时,DMA的传输序列为:
ADC0_RDATA[15:0] -> ADC1_RDATA[15:0] -> ADC2_RDATA[15:0] -> ADC0_RDATA[15:0] -> ADC1_RDATA[15:0] -> ADC2_RDATA[15:0]。

该文字引自GD32F4xx_User_Manual_CN_V2.2

DMA的数据传输,是通过读取同步规则数据寄存器 (ADC_SYNCDATA)中的数据来的,由上文可知,DMA的传输序列是ADC0~2的采集值轮流传输,因此DMA传输后的数组变量中的值按该顺序排列。

我的理解是:因为是ADC0~2轮流传输,所以最好将各ADC模块的采集序列中的通道个数设置的相同,这样不会出错,可以对同一个通道多次采样进行补齐。

三、代码

1.配置思路

嵌入式系统外设驱动的配置思路是通用的:
1使能外设时钟;
2配置对应引脚;
3配置外设;
4启动外设(我认为外设的启动最好与配置分开,配置可以放在系统初始化中,启动可以放在任务程序的初始化中);

note:启动顺序最好是 timer/dma->adc.

2.ADC配置

下面代码主要参考了《GD32F4xx_Firmware_Library_V2.1.3》固件库中的示例。

一些宏定义:

#define ADC_CHANNEL_LENGTH		3U
#define ADCT_EN_TIMEOUT   ((uint32_t)0x1000)
#define ADC_MODULE_NUM		3U
    /* configure the ADC sync mode */
    adc_sync_mode_config(ADC_ALL_REGULAL_PARALLEL);
    adc_sync_dma_config(ADC_SYNC_DMA_MODE0);
    adc_sync_dma_request_after_last_enable();// notations of this func are fault
	
    /* ADC SCAN function enable */
    adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE);
    adc_special_function_config(ADC1,ADC_SCAN_MODE,ENABLE);
    adc_special_function_config(ADC2,ADC_SCAN_MODE,ENABLE);
	
	  /* ADC data alignment config */
    adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    adc_data_alignment_config(ADC1,ADC_DATAALIGN_RIGHT);
    adc_data_alignment_config(ADC2,ADC_DATAALIGN_RIGHT);
    
    /* ADC channel length config */
    adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL,ADC_CHANNEL_LENGTH);
    adc_channel_length_config(ADC1,ADC_REGULAR_CHANNEL,ADC_CHANNEL_LENGTH);
    adc_channel_length_config(ADC2,ADC_REGULAR_CHANNEL,ADC_CHANNEL_LENGTH);
	
    /* ADC regular channel config */
    adc_regular_channel_config(ADC0,0,ADC_CHANNEL_8, ADC_SAMPLETIME_15);//[AI9 ]PB0(ADC01_IN8)
    adc_regular_channel_config(ADC1,0,ADC_CHANNEL_10,ADC_SAMPLETIME_15);//[AI11]PC0(ADC012_IN10)
    adc_regular_channel_config(ADC2,0,ADC_CHANNEL_12,ADC_SAMPLETIME_15);//[AI13]PC2(ADC012_IN12)

	adc_regular_channel_config(ADC0,1,ADC_CHANNEL_9, ADC_SAMPLETIME_15);//[AI10]PB1(ADC01_IN9)
	adc_regular_channel_config(ADC1,1,ADC_CHANNEL_11,ADC_SAMPLETIME_15);//[AI12]PC1(ADC012_IN11)
	adc_regular_channel_config(ADC2,1,ADC_CHANNEL_5, ADC_SAMPLETIME_15);//[PTIN5]PF7(ADC2_IN5)
		
	adc_regular_channel_config(ADC0,2,ADC_CHANNEL_0, ADC_SAMPLETIME_15);//[AI1]PA0(ADC012_IN0)
	adc_regular_channel_config(ADC1,2,ADC_CHANNEL_1, ADC_SAMPLETIME_15);//[AI2]PA1(ADC012_IN1)
	adc_regular_channel_config(ADC2,2,ADC_CHANNEL_9, ADC_SAMPLETIME_15);//[PTIN1]PF3(ADC2_IN9)		
	//sample sequence:B0/C0/C2->B1/C1/F7->PA0/PA1/PF3

    /* ADC external trigger enable */
    adc_external_trigger_config(ADC0,ADC_REGULAR_CHANNEL,EXTERNAL_TRIGGER_RISING);
    adc_external_trigger_config(ADC1,ADC_REGULAR_CHANNEL,EXTERNAL_TRIGGER_DISABLE);
    adc_external_trigger_config(ADC2,ADC_REGULAR_CHANNEL,EXTERNAL_TRIGGER_DISABLE);
    adc_external_trigger_source_config(ADC0,ADC_REGULAR_CHANNEL,ADC_EXTTRIG_REGULAR_T1_CH1);

3.ADC使能

    /* enable ADC interface */
    adc_enable(ADC0);
    /* wait for ADC stability */
		ADCTimeout = ADCT_EN_TIMEOUT;
		while((RESET == (ADC_CTL1(ADC0) & ADC_CTL1_ADCON)))
		{
		if ((ADCTimeout--) == 0) return ADC_TIMEOUT_UserCallback(0);
		};
	
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);
		
    /* enable ADC interface */
    adc_enable(ADC1);
    /* wait for ADC stability */
		ADCTimeout = ADCT_EN_TIMEOUT;
		while((RESET == (ADC_CTL1(ADC1) & ADC_CTL1_ADCON)))
		{
		if ((ADCTimeout--) == 0) return ADC_TIMEOUT_UserCallback(0);
		};
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC1);
		
    /* enable ADC interface */
    adc_enable(ADC2);
    /* wait for ADC stability */
		ADCTimeout = ADCT_EN_TIMEOUT;
		while((RESET == (ADC_CTL1(ADC2) & ADC_CTL1_ADCON)))
		{
		if ((ADCTimeout--) == 0) return ADC_TIMEOUT_UserCallback(0);
		};
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC2);
		return 1;

4.DMA配置

    /* ADC_DMA_channel configuration */
    dma_single_data_parameter_struct dma_single_data_parameter;
    
    /* ADC_DMA_channel configuration */
    dma_deinit(DMA1,DMA_CH0);
    
    /* initialize DMA single data mode */
    dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_SYNCDATA);
    dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_single_data_parameter.memory0_addr = (uint32_t)(adc_value);
    dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
    dma_single_data_parameter.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
    dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY;
    dma_single_data_parameter.number = ADC_MODULE_NUM*ADC_CHANNEL_LENGTH;
    dma_single_data_parameter.priority = DMA_PRIORITY_HIGH;
    dma_single_data_mode_init(DMA1,DMA_CH0,&dma_single_data_parameter);
    dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0);

5.DMA使能

    /* enable DMA channel */
    dma_channel_enable(DMA1,DMA_CH0);

6.TIMER配置

    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;
	
    /* TIMER1 configuration */
    timer_initpara.prescaler         = 199;//200 MHz / (199 + 1) = 1 MHz
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 999;//period = 1 ms
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1,&timer_initpara);

    /* CH1 configuration in PWM mode0 */
    timer_ocintpara.ocpolarity  = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
    timer_channel_output_config(TIMER1,TIMER_CH_1,&timer_ocintpara);

    timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,899);
    timer_channel_output_mode_config(TIMER1,TIMER_CH_1,TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER1,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);

7.TIMER使能

		/* enable TIMER6 */
    timer_enable(TIMER1);	

8.读取测试

			adctest[0] = (uint32_t)adc_value[PB0_AI9] * 3300 / 4095;
			adctest[1] = (uint32_t)adc_value[PC0_AI11] * 3300 / 4095;
			adctest[2] = (uint32_t)adc_value[PC2_AI13] * 3300 / 4095;
			adctest[3] = (uint32_t)adc_value[PB1_AI10] * 3300 / 4095;
			adctest[4] = (uint32_t)adc_value[PC1_AI12] * 3300 / 4095;
			adctest[5] = (uint32_t)adc_value[PF7_PTIN5] * 3300 / 4095;
			adctest[6] = (uint32_t)adc_value[PA0_AI1] * 3300 / 4095;
			adctest[7] = (uint32_t)adc_value[PA1_AI2] * 3300 / 4095;
			adctest[8] = (uint32_t)adc_value[PF3_PTIN1] * 3300 / 4095;

总结

上述代码段可以直接放入基于GD32F450项目的函数中去使用,需要配置GPIO和时钟。一点思考是,在使用DMA的时候一定要考虑故障诊断。
我本人也是GD32的初学者,深知驱动的配置和使用只是嵌入式开发的入门和皮毛,希望和各位多交流,共同进步。

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
嗨!对于使用GD32F450微控制器的ADC DMA,您可以按照以下步骤进行配置: 1. 首先,确保您已经初始化了ADC和DMA外设,并启用了它们的时钟。 2. 配置ADC的通道和采样时间: - 使用`adc_channel_length`函数设置ADC的通道数。 - 使用`adc_regular_channel_config`函数配置每个通道的采样时间、转换模式和触发源。 3. 配置DMA: - 使用`dma_deinit`函数重置DMA外设。 - 使用`dma_init_struct`结构体配置DMA的通道、数据传输方向、数据宽度和传输模式。 - 使用`dma_mode_config`函数配置DMA的传输模式。 - 使用`dma_circulation_disable`函数禁用DMA的循环传输模式(如果不需要循环传输)。 - 使用`dma_memory_address_config`函数配置DMA的内存地址和外设地址。 - 使用`dma_memory_width_config`函数配置DMA的内存和外设数据宽度。 - 使用`dma_memory_increase_enable`函数启用DMA的内存递增模式。 - 使用`dma_channel_enable`函数启用DMA通道。 4. 启动ADC和DMA: - 使用`adc_software_trigger_enable`函数启动ADC转换。 - 使用`dma_channel_enable`函数启动DMA传输。 5. 在DMA传输完成后,您可以使用DMA的中断或轮询方法来处理接收到的数据。 这是一个基本的ADC DMA配置过程的概述。具体的代码实现可能会有所不同,具体取决于您使用的开发环境和库。确保参考GD32F450微控制器的参考手册和相关的库文档以获取更详细的信息和示例代码。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值