ADC(模数转换器)是用于将模拟电压信号转换成数字量的电路单元,是模拟信号数字化的必要器件。独立的ADC芯片和MCU连接构成信号数字化电路
1、主要特性
(1)高性能:可配置为12位、10位、8位或6位分辨率;具前置校准功能;可编程采样时间;数据存储模式分最高有效位对齐和最低有效位对齐;可处理DMA请求
(2)模拟输入通道:16个外部模拟输入通道,还有一个内部温度传感通道(VSENSE)和一个内部参考电压通道(VREFINT)
(3)运行模式分为软件触发和硬件触发
(4)转换模式:转换单次通道(扫描一序列的通道);单次运行模式(每次触发转换一次选择的输入通道);连续运行模式(连续转换所选择的输入通道);间断运行模式;同步模式(适用于两个或多个ADC的设备)
2、工作原理
ADC模块框图如图所示
2.1 模拟部分供电
模拟部分的供电有四个引脚
(1)VDDA是模拟部分工作电源,可以选择使用独立的模拟电源输入,来保证ADC的精度。VAAD最高不能超过3.6V,最低不低于2.6V,可以选择将VDDA与数字电源VDD连接。
(2)VSSA是模拟电源地,与数字电源共地。
(3)VREF+是ADC转换的正参考电压,最高不能超过VDDA,最低不能低于2.6V,一般选择将VREF+与VDDA连接。
(4)VREF-是ADC转换的负参考电压,必须与VSSA连接。
2.2 输入通道
每个ADC单元都有16个外部输入通道,对应16个ADC输入复用引脚,即结构框图中的ADC_IN0至ADC_IN15。除此之外还额外包括两个内部输入使用通道,温度传感器通道(ADC0_CH16)和VREFINT通道(ADC0_CH17)
使用温度传感器:
1. 配置温度传感器通道(ADC_IN16)的转换序列和采样时间为 ts_temp µs
2. 置位 ADC_CTL1 寄存器中的 TSVREN 位,使能温度传感器
3. 置位 ADC_CTL1 寄存器的ADCON 位,或者由外部触发启动ADC 转换
4. 读取内部温度传感器输出电压 Vtemperature,
并由此公式计算出实际温度:温度 (°C) = {( – V temperature ) / Avg_Slope} + 25其中是内部温度传感器在25℃下的电压,为1.405V;Avg_Slope是温度与内部温度传感器输出电压曲线的均值斜率,为4.5mV/℃;ts_temp µs是读取温度时的ADC采样时间,为17.1us。
2.3 规则通道和注入通道
选择的多个模拟输入通道可以分为两组:规则通道组和注入通道组,每个组的通道构成一个转换序列。
规则转换序列最多可以设置16个通道,一个规则转换序列规定了多路复用转换时的顺序。例如,选择了IN0、IN1、IN2共3个通道作为规则通道,定义的规则转换序列可以是IN0、IN1、IN2,也可以是IN0、IN2、IN1,也可以是IN1、IN2、IN0。
注入通道就是可以在规则通道转换过程中插入进行转换的通道,类似于中断的现象。注入转换序列最多可以设置4个注入通道,也可以像规则转换序列那样设置转换序列。
每个注入通道还可以设置一个数据偏移量,每次转换结果自动减去这个偏移量,所以转换结果可以是负数。例如,设置偏移量为信号的直流分量,每次转换自动减去直流分量。
2.4 启动或触发转换
规则通道和注入通道有单独的触发源,有3类启动或触发转换的方式:
(1)软件启动:直接将控制寄存器ADC_CR2的ADON的位置1启动ADC转换,写入0时停止ADC转换,这种方式常用于轮询方式的ADC转换。
(2)内部定时器触发:可以选择某个定时器的触发输出信号(TRGO)或输入捕获信号作为触发源。例如,选择TIM2_TRGO信号作为启动触发信号,而TIM2的TRGO设置为UEV事件信号,这样定时器TIM2每次定时溢出时就启动一次转换。这种方式可用于周期性ADC转换。
(3)外部IO触发:可以选择外部中断线EXTI_11和EXTI_15作为规则组或注入组的外部中断触发源。
2.5 ADC时钟与转换时间
ADC转换需要时钟信号ADCCLK驱动,ADCCLK有PCLK2经过分频产生,最少2分频,最多8分频。PCLK2的最高频率为84MHz,所以ADCCLK的最高频率为42MHz。
我们可以设置在N个ADCCLK周期内对信号进行采样,N取值最小为3,最大为480。在ADC上电后,在开始精确转换之前,需要一段稳定时间,对于之后接下来的转换就不需要稳定延迟,而且ADON位只需要被置位一次。一次ADC转换需要14个时钟周期,在转换完成后EOC标志被置位,同时转换结果保存在10位ADC数据寄存器里面,ADC转换完成需要N+12个时钟周期。
例如,PCLK2的频率位84MHz,ADC使用2分频,则ADCCLK的频率位42MHz,若将采样设置为15次,则一次转换的时间为:
可以设置在转换完一个通道后就产生EOC信号,也可以设置在转换完一个序列后产生EOC信号。
2.6 转换结果数据寄存器
ADC完成转换后将结果数据存入数据寄存器,规则通道和注入通道有不同的数据寄存器。
规则通道只有一个数据寄存器ADC_DR,只有低16位有效。在多通道转换时,如果前一通道转换结束后,ADC_DR的数据未被及时读出,下一个通道的转换结果就会覆盖上一次结果的数据。所以,在多通道转换时,一般在EOC中断里及时读取或者通过DMA将数据传输到内存里。
注入通道有4个数据寄存器,分别对应四个注入通道的转换结果。
规则数据寄存器和注入数据寄存器都是低16位有效,因为转换结果数据最多12有效,可以设置数据左对齐或右对齐,一般使用右对齐。
2.7 模拟看门狗
可以使用模拟看门狗对某一个通道的模拟电压进行监测,设置一个阈值上限和下限(12位数表示的数值,0-4095),当监测的模拟电压ADC结果超出范围时,就产生模拟看门狗中断。
2.8 转换结果电压计算
ADC的转换结果是一个数字量,与实际的模拟电压之间的计算关系由VREF+和转换精度位数确定。
2.9 多重ADC模式
GD32F30x系列有3个ADC,这3个ADC可以独立工作,也可以组成双重或三重工作模式。
多重模式就是使用主器件ADC0的触发信号去交替触发或同步触发其他ADC启动转换。例如,对于三分量模输出的振动传感器,需要对X、Y、Z这三个方向的振动信号同步采集,以合成一个三维空间中的振动矢量,这是就需要使用3个ADC对3路信号同步采集,而不能使用一个ADC对3路信号通过多路复用进行采集。
多重ADC对多种工作模式,可以交替出发,也可以同步触发。设置ADC0和ADC1双重同步工作模式时,为ADC0设置的触发源同时也ADC1,以实现两个ADC同步转换。在多重模式下,有一个专门的32位数据寄存器ADC_CDR,用于存储多重模式下的转换结果数据。在双重模式下,ADC_CDR的高16位存储ADC1的规则转换结果数据,ADC_CDR的低16位存储ADC0的规则转换结果数据。
在多重模式下,使用DMA进行数据传输有3种模式,其中DMA模式2适用于双重ADC的数据传输。双重ADC时,DMA模式2的工作特点是:每发送一个DMA请求,就以字的形式传输表示ADC1和ADC0转换结果的32位数据,其中高16位是ADC1的转换结果;低16位是ADC0的转换结果,相当于将ADC_CDR的数据在一个DMA请求时传输出去。
在双重ADC同步模式下,两个ADC不能转换同一个通道。两个ADC的规则转换序列的通道个数应该相同,每个通道的采样点数也应该相同,以使得两个ADC能保持同步。
3、ADC采样代码示例
3.1 软件触发采样内部温度和参考电压
adc.c
void rcu_config(void)
{
rcu_periph_clock_enable(RCU_ADC0); //使能ADC时钟
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6); //配置ADC预分频因子,预分频器选择CK_APB2/6
}
void adc_config(void)
{
adc_mode_config(ADC_MODE_FREE); //ADC模式配置,选择所有ADC独立工作
adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT); //ADC数据右对齐
adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE); //使能ADC_SCAN功能
adc_channel_length_config(ADC0,ADC_INSERTED_CHANNEL,2);//配置常规通道组长度为 2
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_16, ADC_SAMPLETIME_239POINT5); //温度传感器通道配置,采样值配置为239.5cycles,序列号为0
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_17, ADC_SAMPLETIME_239POINT5); //内部参考电压通道配置,采样值配置为239.5cycles,序列号为1
adc_external_trigger_config(ADC0,ADC_INSERTED_CHANNEL,ENABLE); //启动外部触发
adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL, ADC0_1_2_EXTTRIG_INSERTED_NONE); //配置ADC外部触发源,选择为软件触发
adc_tempsensor_vrefint_enable(); //使能温度触发器和Vrefint通道
adc_enable(ADC0); //ADC0接口使能
delay_1ms(1);
adc_calibration_enable(ADC0); //启用ADC0的校准,并重置校准
}
main.c
int main(void)
{
rcu_config();
systick_config();
/* ADC configuration */
adc_config();
uart0_init(115200);
while(1)
{
adc_software_trigger_enable(ADC0, ADC_INSERTED_CHANNEL); //使能软件触发
delay_1ms(2000);
/* value convert */
temperature = (1.43 - ADC_IDATA0(ADC0)*3.3/4096) * 1000 / 4.3 + 25;//计算方法useer_menu中有
vref_value = (ADC_IDATA1(ADC0) * 3.3 / 4096);
/* value print */
u1_printf(" the temperature data is %2.0f degrees Celsius,the reference voltage data is %5.3fV \r\n", temperature,vref_value);//用的是自定义打印函数,在uart.c中定义
}
}
功能验证
可以通过串口调试助手查看打印结果
3.2 定时器触发DMA传输ADC转换
adc.c
#include "gd32f30x.h"
#include "adc.h"
#include "systick.h"
uint32_t adc_value[2]; //DMA传输缓冲区
void rcu_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_DMA0);
rcu_periph_clock_enable(RCU_TIMER0);
rcu_periph_clock_enable(RCU_ADC0); //使能ADC时钟
rcu_periph_clock_enable(RCU_ADC0); //使能ADC时钟
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6); //配置ADC预分频因子,预分频器选择CK_APB2/6
}
void gpio_config(void)
{
gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_3|GPIO_PIN_5); //GPIOC_PIN3:ADC0_IN13 GPIOC_PIN5:ADC0_IN15
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); //GPIOA_PIN8:TIMER0_CH0
}
void adc_config(void)
{
adc_mode_config(ADC_DAUL_REGULAL_FOLLOWUP_FAST); //ADC模式配置,选择所有ADC独立工作
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); //ADC数据右对齐
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); //使能ADC_SCAN功能
adc_special_function_config(ADC1, ADC_SCAN_MODE, ENABLE);
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL,2); //配置规则通道组,长度为2
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL,2);
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_13, ADC_SAMPLETIME_239POINT5); //配置ADC0、1的规则转换序列
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_15, ADC_SAMPLETIME_239POINT5);
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_15, ADC_SAMPLETIME_239POINT5);
adc_regular_channel_config(ADC1, 1, ADC_CHANNEL_13, ADC_SAMPLETIME_239POINT5);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); //规则通道使能
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T0_CH0); //定时器T0触发
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); //软件触发
adc_enable(ADC0);
delay_1ms(1);
adc_calibration_enable(ADC0);
adc_enable(ADC1);
delay_1ms(1);
adc_calibration_enable(ADC1);
adc_dma_mode_enable(ADC0); //使能DMA传输
}
void dma_config(void)
{
dma_parameter_struct dma_data_parameter; //DMA配置结构体
dma_deinit(DMA0, DMA_CH0);
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_32BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; //外设到存储器
dma_data_parameter.number = 2;
dma_data_parameter.priority = DMA_PRIORITY_HIGH; //优先级等级高
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
dma_circulation_enable(DMA0, DMA_CH0);
dma_channel_enable(DMA0, DMA_CH0);
}
void timer_config(void)
{
timer_oc_parameter_struct timer_ocintpara;
timer_parameter_struct timer_initpara;
timer_initpara.prescaler = 8399;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 9999;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER0, &timer_initpara);
timer_channel_output_struct_para_init(&timer_ocintpara);
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 3999);
timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);
timer_primary_output_config(TIMER0, ENABLE);
timer_auto_reload_shadow_enable(TIMER0);
timer_enable(TIMER0);
}
main.c
#include <string.h>
#include "uart.h" //调用u1_u1_printf作为打印函数
#include "gd32f30x.h"
#include "systick.h"
#include <stdio.h>
#include "stdarg.h"
#include "adc.h"
int main(void)
{
/* system clocks configuration */
rcu_config();
/* configure systick */
systick_config();
/* GPIO configuration */
gpio_config();
/* TIMER configuration */
timer_config();
/* DMA configuration */
dma_config();
/* ADC configuration */
adc_config();
/* configure COM port */
uart0_init(115200);
while(1){
delay_1ms(2000);
u1_printf(" the data adc_value[0] is %08X \r\n",adc_value[0]);
u1_printf(" the data adc_value[1] is %08X \r\n",adc_value[1]);
u1_printf("\r\n");
}
}
功能验证
总结
ADC的配置基本流程当中都有,关键步骤都有注释,不同模式的差别便是触发源,规则转换序列等。
标签:采样,GD32F303RCT6,转换,ADC,ADC0,通道,config,adc From: https://blog.csdn.net/weixin_72587039/article/details/140682566