6.1.ADC 基础知识 12 位逐次逼近式模数转换器模块(ADC),可以采样来自于外部输入通道、内部输入通道的模拟信号,采样转换后,转换结果可以按照最低有效位对齐或最高有效位对齐的方式保存在相应的数据寄存器中。 6.2.GD32 ADC 外设原理GD32 ADC 主要特性 ◼ 高性能: – ADC采样分辨率: 12位、 10位、 8位、或者6位分辨率; – 前置校准功能; – 可编程采样时间; – 数据存储模式:最高有效位对齐和最低有效位对齐; – 支持规则数据转换的DMA请求。 ◼ 模拟输入通道: – 16个外部模拟输入通道; – 1个内部温度传感器输入通道(VSENSE); – 1个内部参考电压输入通道(VREFINT)。 ◼ 转换开始的发起: – 软件触发; – 硬件触发。 ◼ 转换模式: – 转换单个通道,或者扫描一序列的通道; – 单次运行模式,每次触发转换一次选择的输入通道; – 连续运行模式,连续转换所选择的输入通道; – 间断运行模式; – 同步模式(适用于具有两个或多个ADC的设备)。 ◼ 转换结果阈值监测器功能: 模拟看门狗。 ◼ 中断的产生: – 常规序列转换结束; – 模拟看门狗事件。 ◼ 过采样: – 16位的数据寄存器; – 可调整的过采样率,从2x到256x; – 高达8位的可编程数据移位。 ◼ ADC供电要求: – 2.4V到3.6V,一般供电电压为3.3V。 ◼ ADC输入范围: VREF- ≤VIN ≤VREF+。 – VREF- : ADC 负参考电压, VREF- = VSSA – VREF+ : ADC 正参考电压, 2.6 V ≤ VREF+ ≤ VDDA ADC 结构框图介绍 6.3.硬件连接说明ADC的检测需要将输入阻抗和采样周期保持一定的关系,下表列出了以GD32E103为例子 6.4.软件配置说明查询法 查询法采用的是转换单个通道,或者扫描一序列的通道,通过不断切换通道的方式去读取对应通道的ADC数值。我们介绍使用库函数来设置 ADC0 的通道来进行 AD 转换的步骤,下面讲解其详细设置步骤: 开启 GPIO口时钟和 ADC0 时钟,设置GPIO口为模拟输入。 rcu_periph_clock_enable(RCU_GPIOA);
/* enable GPIOB clock */
rcu_periph_clock_enable(RCU_GPIOB);
/* enable ADC0 clock */
rcu_periph_clock_enable(RCU_ADC0);
/* config ADC clock */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4);
/* enable alternate function clock */
rcu_periph_clock_enable(RCU_AF); //使能 GPIO 口复用
这里需要说明一下, ADC 的通道与引脚的对应关系在数据手册可以查到 (1)配置 ADC 输入时钟分频,模式为独立模式,转换长度以及触发方式等 /* reset ADC */
adc_deinit(ADC0);
/* ADC mode config */
adc_mode_config(ADC_MODE_FREE); //独立模式
/* ADC continous function enable */
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE)
/* ADC data alignment config */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); //ADC 数据右对齐
adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
/* ADC channel length config */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);//设置 ADC 转换的通道数量
/* ADC trigger config */
adc_external_trigger_source_config(ADC0,ADC_REGULAR_CHANNEL,ADC0_1_EXTTRIG_REGULAR_NONE);
//通过软件触发 ADC 转换
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
(2)开启 AD 转换器 在设置完了以上信息后,我们就开启 AD 转换器了 /* enable ADC interface */
adc_enable(ADC0);
delay_1ms(1);//延迟 等待稳定
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC0);//使能 ADC 校准
(3)读取 ADC 值 在上面的步骤完成后, ADC 就算准备好了。所谓的查询法就是不断地进行通道切换 unsigned short adc0_transfer(unsigned char adc_channel)
{
uint16_t ADC_temp;
/* ADC regular channel config */
adc_regular_channel_config(ADC0, 0, adc_channel, ADC_SAMPLETIME_55POINT5);
adc_software_trigger_enable(ADC0,ADC_REGULAR_CHANNEL);
while(!adc_flag_get(ADC0, ADC_FLAG_EOC));//等待转换完成
/* clear the end of conversion flag */
adc_flag_clear(ADC0, ADC_FLAG_EOC);
ADC_temp=adc_regular_data_read(ADC0);
return ADC_temp;
}
这里还需要说明一下ADC的参考电压,是以Vref作为参考电压。 常规通道转换 DMA 搬运 通过ADC转换,在ADC 转换完成后 触发DMA 进行数据搬运。 DMA 请求,可以通过设置 ADC_CTL1 寄存器的 DMA 位来使能,它用于规则组多个通道的转换结果。 ADC 在规则组一个通道转换结束后产生一个 DMA 请求, DMA 接受到请求后可以将转换的数据从 ADC_RDATA 寄存器传输到用户指定的目的地址。 在和查询法一节的差别,主要在于ADC 的配置方面和DMA的配置 (1)ADC 的配置 /* ADC channel length config */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 4);//常规通道长度配置
/* ADC regular channel config */
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_7, ADC_SAMPLETIME_55POINT5);
/* ADC trigger config */
adc_external_trigger_source_config(ADC0,ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
/* ADC DMA function enable */
adc_dma_mode_enable(ADC0);//使能 DMA 模式
/* enable ADC interface */
adc_enable(ADC0);
delay_1ms(1);
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC0);
(2)DMA的配置 /* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter;
/* ADC DMA_channel configuration */
dma_deinit(DMA0, DMA_CH0);
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); //外设地址
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_data_parameter.memory_addr = (uint32_t)(&adc_value); //搬运目标地址
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_data_parameter.number = 4;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
/* enable DMA circulation mode */
dma_circulation_enable(DMA0, DMA_CH0);
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
(3)触发ADC 转换 adc_software_trigger_enable(ADC0,ADC_REGULAR_CHANNEL);
注入通道-中断 在和查询法一节的差别,主要在于ADC 的配置方面和注入通道会产生中断 (1)ADC 的配置 /* reset ADC */
adc_deinit(ADC0);
/* ADC mode config */
adc_mode_config(ADC_MODE_FREE);
/* ADC continous function enable */
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
/* ADC data alignment config */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* ADC channel length config *//注入通道配置
adc_channel_length_config(ADC0, ADC_INSERTED_CHANNEL, 4);//注入通道长度配置
/* ADC inserted channel config */
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_2, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
/* ADC trigger config */ //通过 EXTI 15 进行触发
adc_external_trigger_source_config(ADC0,ADC_INSERTED_CHANNEL,ADC0_1_EXTTRIG_INSERTED_EXTI_15);
/* ADC external trigger enable */
adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
/* clear the ADC flag */
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOC);
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC);
/* enable ADC interrupt */
adc_interrupt_enable(ADC0, ADC_INT_EOIC);//使能注入通道的中断
/* enable ADC */
adc_enable(ADC0);
delay_1ms(1);
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC0);
(2)ADC 注入通道转换完成服务函数 void ADC0_1_IRQHandler(void)
{
/* clear the ADC flag */
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC);//清除注入通道转换完成标志位
/* read ADC inserted group data register */
inserted_data[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0);
inserted_data[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1);
inserted_data[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2);
inserted_data[3] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3);
}
6.5.ADC 使用注意事项ADC通道的采集引脚未配置为模拟输入,GD32要求通道IO口必须配置为模拟输入; ADC时钟过高,ADC采样时钟高于ADC最高采样时钟获取到的数据不具有参考意义,需要手动分频到合适的ADC 要求的频率; ADC采样值偏小或不稳定,应该适当的降低ADC时钟,加大采样周期的值; ADC的查询法在获取ADC数值的时候,不允许重入,即不允许在同一时间调用ADC的数据读取函数; ADC在使能之后需要进行短暂的延时,时间周期约为2毫秒左右,以保证ADC能正常启动。
教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462
|