对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
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的初学者,深知驱动的配置和使用只是嵌入式开发的入门和皮毛,希望和各位多交流,共同进步。