GD32F303RCT6(5)-ADC采样内部温度和参考电压

        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_{25}   – V temperature ) / Avg_Slope} + 25

        其中V_{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上电后,在开始精确转换之前,需要一段稳定时间t_{STAB},对于之后接下来的转换就不需要稳定延迟,而且ADON位只需要被置位一次。一次ADC转换需要14个时钟周期,在转换完成后EOC标志被置位,同时转换结果保存在10位ADC数据寄存器里面,ADC转换完成需要N+12个时钟周期。

        例如,PCLK2的频率位84MHz,ADC使用2分频,则ADCCLK的频率位42MHz,若将采样设置为15次,则一次转换的时间为:

                                                        t_{c}=\frac{15+12}{42×10^{6}}=0.64us

        可以设置在转换完一个通道后就产生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的配置基本流程当中都有,关键步骤都有注释,不同模式的差别便是触发源,规则转换序列等。

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值