【GD32F303红枫派使用手册】第十一节 ADC-电源电压单通道ADC检测实验

11.1 实验内容

通过本实验主要学习以下内容:

  • ADC的简介
  • GD32F303 ADC工作原理
  • 查询方式实现ADC单通道采样

11.2 实验原理

11.2.1 ADC原理

我们知道,自然界中有非常多的模拟信号,比如上一节提到的光照强度,还有其他的例如温度、声音等等,那么人们是怎么来衡量一个模拟信号的呢?

我们通常会说今天光照度达到了3万Lux(照度单位),现在测量到的体温是36.5℃,我们所处的环境是40分贝,没错,人们就是通过将这些模拟信号数字化,从而达到衡量这些模拟信号的目的。那对于MCU来说,如果要测量一个模拟量,可以通过自带的ADC(Analog-to-Digital converters)模块,即模-数转换器将模拟量转化为可以被MCU读取到的数字量。

11.2.2 GD32F303 ADC工作原理

GD32F303有3个12位逐次逼近型ADC(SAR ADC),这三个ADC可以独立工作,也可以让ADC0和ADC1工作在同步模式下。有最多21个外部ADC引脚可用于将连接到这些引脚的电压值转换为数字量,这些引脚号可以通过Datasheet获得。

表中ADC012_INx的意思是:该IO口可以作为通道x用于ADC0、ADC1和ADC2。如ADC012_IN0,表示PA0可以用于ADC0的通道0使用,也可以作为ADC1和ADC2的通道0使用。但要注意:不能在同一个时刻让不同的ADC去转换同一个通道,否则会有无法预料的结果

 

以下总结了GD32F303 ADC的特性:

  • 高性能:
     – 可配置12位、 10位、 8位、或者6位分辨率;
     – 自校准;
     – 可编程采样时间;
     – 数据寄存器可配置数据对齐方式;
     – 支持规则数据转换的DMA请求。
  • 模拟输入通道:
     – 16个外部模拟输入通道;
     – 1个内部温度传感器输入通道(VSENSE);
     – 1个内部参考电压输入通道(VREFINT)。
  •  转换开始的发起:
     – 软件;
     – 硬件触发。
  • 转换模式:
     – 转换单个通道,或者扫描一组通道;
     – 单次模式,每次触发转换一次选择的输入通道;
     – 连续模式,连续转换所选择的输入通道;
     – 间断模式;
     – 同步模式(适用于具有两个或多个ADC的设备)。
  •  模拟看门狗。
  •  中断的产生:
     – 规则组或注入组转换结束;
     – 模拟看门狗事件。
  • 过采样:
     – 16位的数据寄存器;
     – 可调整的过采样率,从2x到256x;
     – 高达8位的可编程数据移位。
  •  ADC供电要求:
     – 2.4V到3.6V,一般供电电压为3.3V。
  •  ADC输入范围: VREFN ≤VIN ≤VREFP  。

下面介绍下GD32F303的ADC框图:

标注1:输入电压和参考电压

输入电压引脚定义如下表:

大于等于100pin的GD32F303,ADC参考电压等于VREFP,100pin以下的GD32F303,ADC参考电压等于VDDA

 GD32F303的ADC是12bit有效位的,满量程对应的转换值是4095,即当采样引脚上的电压等于ADC参考电压时,得到的转换值即为4095。故理论采样是指可通过以下公式得到:

采样数值=实际电压/参考电压*4095

标注2:输入通道

前面提到,ADC有最多16个外部模拟通道和2个内部通道,外部通道号从IN0~IN15,由IO口号来决定,两个内部通道是IN16(温度传感器)和IN17(内部Vrefint,典型值1.2V),下表给出了IO口号对应的ADC通道:

标注3:规则组和注入组

每个ADC有两个组——规则组和注入组。

规则组有两个重要的参数,其一为转换的个数,其二为转换的序列,规定好这两个参数后,一旦开始规则组的转换,则ADC就按照转换序列一个一个的进行模-数转换,直到达到要求的转换个数。

规则组的转换个数由ADC_RSQ0寄存器的RL[3:0]位规定,转换的总数目为RL[3:0]+1,转换总数目最大为16个;转换序列由ADC_RSQ0~ADC_RSQ2共同决定,我们来看下这几个寄存器。

ADC_RSQ0寄存器:

ADC_RSQ1寄存器: ADC_RSQ2寄存器:

举个例子,现需要按照CH3->CH2->CH1的顺序进行规则组转换,则设定RL[3:0] = 2,然后设定RSQ0为CH3,RSQ1为CH2,RSQ2为CH1,则当开始规则组转换时,ADC首先进行RSQ0规定的通道即CH3的转换,再进行RSQ1规定的通道即CH2的转换,最后进行RSQ2规定的通道即CH1转换,当这三个通道转换完后,规则组转换结束。

需要注意的是,每转换一个规则组通道,转换结果都会放在寄存器ADC_RDATA中,所以CPU一定要在下一个通道转换完成前将上一个通道转换结果读走,否则会导致上一个通道数据被新的数据覆盖。所以在多通道规则组转换时,为了保证能读到所有通道的数据,一定要使用DMA(直接存储器访问控制器),每个通道转换结束后,都会给DMA发送请求,DMA就会将最新的ADC_RDATA中的数据搬走。关于ADC配合DMA的使用,后面章节会详细介绍。

说完规则组,我们再说下注入组。注入组,可以按照特定的序列组织成最多 4 个转换的序列。 ADC_ISQ 寄存器规定了注入组的通道选择。 ADC_ISQ 寄存器的 IL[1:0]位规定了整个注入组转换序列的长度。  

ADC_ISQ寄存器:

 

和规则组转换序列不同的是,如果 IL[1:0]长度不足 4,注入通道转换从(4-IL[1:0]-1) 开始:

当IL = 3,注入组转换顺序为ISQ0 >> ISQ1 >> ISQ2 >> ISQ3,转换结果分别放在ADC_IDATA0~ADC_IDATA3;

当IL = 2,注入组转换顺序为ISQ1 >> ISQ2 >> ISQ3,转换结果分别放在ADC_IDATA0~ADC_IDATA2;

当IL = 1,注入组转换顺序为ISQ2 >> ISQ3,转换结果分别放在ADC_IDATA0~ADC_IDATA1;

当IL = 0,注入组转换ISQ3,转换结果放在ADC_IDATA0

举个例子,现需要按照CH3->CH2->CH1的顺序进行注入组转换,则设定IL[3:0] = 2,然后设定ISQ1为CH3,ISQ2为CH2,ISQ3为CH1,则当开始注入组转换时,ADC首先进行ISQ1规定的通道即CH3的转换,再进行ISQ2规定的通道即CH2的转换,最后进行ISQ3规定的通道即CH1转换,当这三个通道转换完后,注入组转换结束。

因为4个通道转换的结果分别放在4个不同的注入组数据寄存器ADC_IDATAx中,所以注入组不需要用到DMA,只需要在注入组转换完成后分别去不同注入组数据寄存器中取数即可。

标注4:触发源

ADC的规则组和注入组需要选特定的触发源用于触发ADC转换,注意,ADC的Enable(即ADC_CTL1寄存器的ADC_ON位置“1”)不会触发ADC转换,而是当选定的触发源来临后ADC才开始转换。

触发源分为内部触发和外部触发,内部触发是指当ADC_ON已经为“1”的情况下,不改变其他ADC寄存器,再往ADC_ON位写“1”,将触发一次ADC转换;外部触发源是除了内部触发源以外的触发源,外部触发源可以通过ADC_CTL1寄存器查看:

ADC_CTL1寄存器:

标注5:规则组和注入组的数据寄存器

如标注3规则组和注入组中的表述,每个ADC的规则组只有一个数据寄存器ADC_RDATA,每转换一个通道,转换结果放在这个寄存器中,在下一通道转换结束前必须要将上一个通道的转换结果取走;每个ADC的注入组有4个数据寄存器ADC_IDATAx(x = 0,1,2,3),分别保存4个通道的ADC注入组的转换数据。

标注6:ADC中断及标志位

ADC的中断总共有三种:规则组转换结束中断、注入组转换结束中断以及模拟看门狗,可以通过将ADC_CTL0中的EOCIE、EOICIE和WDEIE置“1”来开启相应中断。

ADC_STAT寄存器中的EOC、EOIC和WDE表示相应事件发生,EOC置“1”表示规则组的转换已经结束;EOIC置“1”表示注入组的转换已经结束,注意:注入组转换结束时,EOC标志位也会置起。

GD32F303的ADC原理部分就介绍到这里,下面我们通过电源电压单通道采样实验来详细介绍下ADC的用法。

11.3 硬件设计

电源电压检测的原理图如下:

ADC_IN4连接到MCU的PF6管脚通过ADC转换可以得到PF6脚上具体的电压值,再通过该电压值可反推电源电压值。

11.4 代码解析

本实验只用到一个ADC通道:PF6——ADC2_CH4,故可以选择使用ADC2的规则组进行转换,并通过查询EOC标志位来判断通道转换完成。

11.4.1 ADC初始化

在driver_adc.c中定义了ADC初始化函数driver_adc_config:

C
void driver_adc_config(typdef_adc_ch_general *ADC,typdef_adc_ch_parameter *ADC_CH)
{         uint8_t i;/*配置ADC时钟频率*/rcu_adc_clock_config(ADC->adc_psc);/*使能ADC时钟*/rcu_periph_clock_enable(ADC->rcu_adc);/*配置ADC相关IO口,先配置时钟,再将IO口设置为模拟输入*/for(i=0 ;i<ADC->ch_count; i++){if(ADC_CH[i].adc_channel < ADC_CHANNEL_16){rcu_periph_clock_enable(ADC_CH[i].rcu_port);gpio_init(ADC_CH[i].port, GPIO_MODE_AIN, ADC_CH[i].gpio_speed, ADC_CH[i].pin);                                        }else{adc_tempsensor_vrefint_enable();}}/*配置ADC工作模式,如独立模式,规则并行模式等*/adc_mode_config(ADC->adc_mode);/*配置规则组的扫描模式和连续转换模式*/adc_special_function_config(ADC->adc_port, ADC_SCAN_MODE, ADC->adc_scan_function);if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL){adc_special_function_config(ADC->adc_port, ADC_CONTINUOUS_MODE, ADC->adc_continuous_function);        }/*选择数据右对齐*/adc_data_alignment_config(ADC->adc_port, ADC_DATAALIGN_RIGHT);/*配置转换通道数*/adc_channel_length_config(ADC->adc_port, ADC->adc_channel_group, ADC->ch_count);        /*配置转换顺序*/if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL){for(i = 0;i< ADC->ch_count;i++){adc_regular_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);}}else if(ADC->adc_channel_group == ADC_INSERTED_CHANNEL){for(i = 0;i< ADC->ch_count;i++){adc_inserted_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);}        }/*选择触发源及使能外部触发模式*/adc_external_trigger_source_config(ADC->adc_port, ADC->adc_channel_group, ADC->trigger_source);        adc_external_trigger_config(ADC->adc_port, ADC->adc_channel_group, ENABLE);/*选择是否需要使用DMA*/if(ADC->DMA_mode == ENABLE){adc_dma_mode_enable(ADC->adc_port);}/*ADC的使能和自校准,ADC使能后需要经过一定的ADC_CLK后才能校准,本示例中直接使用1ms延时*/adc_enable(ADC->adc_port);delay_ms(1);adc_calibration_enable(ADC->adc_port);
}

 

C
void driver_adc_config(typdef_adc_ch_general *ADC,typdef_adc_ch_parameter *ADC_CH)
{         
    uint8_t i;
    /*配置ADC时钟频率*/
    rcu_adc_clock_config(ADC->adc_psc);
    /*使能ADC时钟*/
    rcu_periph_clock_enable(ADC->rcu_adc);
    /*配置ADC相关IO口,先配置时钟,再将IO口设置为模拟输入*/
    for(i=0 ;i<ADC->ch_count; i++)
    {
        if(ADC_CH[i].adc_channel < ADC_CHANNEL_16)
        {
            rcu_periph_clock_enable(ADC_CH[i].rcu_port);
            gpio_init(ADC_CH[i].port, GPIO_MODE_AIN, ADC_CH[i].gpio_speed, ADC_CH[i].pin);                                        
        }
        else
        {
            adc_tempsensor_vrefint_enable();
        }
    }
    /*配置ADC工作模式,如独立模式,规则并行模式等*/
    adc_mode_config(ADC->adc_mode);
    /*配置规则组的扫描模式和连续转换模式*/
    adc_special_function_config(ADC->adc_port, ADC_SCAN_MODE, ADC->adc_scan_function);
    if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
    {
        adc_special_function_config(ADC->adc_port, ADC_CONTINUOUS_MODE, ADC->adc_continuous_function);        
    }
    /*选择数据右对齐*/
    adc_data_alignment_config(ADC->adc_port, ADC_DATAALIGN_RIGHT);
    /*配置转换通道数*/
    adc_channel_length_config(ADC->adc_port, ADC->adc_channel_group, ADC->ch_count);        
    /*配置转换顺序*/
    if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
    {
        for(i = 0;i< ADC->ch_count;i++)
        {
            adc_regular_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
        }
    }
    else if(ADC->adc_channel_group == ADC_INSERTED_CHANNEL)
    {
        for(i = 0;i< ADC->ch_count;i++)
        {
            adc_inserted_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
        }        
    }
    /*选择触发源及使能外部触发模式*/
    adc_external_trigger_source_config(ADC->adc_port, ADC->adc_channel_group, ADC->trigger_source);        
    adc_external_trigger_config(ADC->adc_port, ADC->adc_channel_group, ENABLE);
    /*选择是否需要使用DMA*/
    if(ADC->DMA_mode == ENABLE)
    {
        adc_dma_mode_enable(ADC->adc_port);
    }
    /*ADC的使能和自校准,ADC使能后需要经过一定的ADC_CLK后才能校准,本示例中直接使用1ms延时*/
    adc_enable(ADC->adc_port);
    delay_ms(1);
    adc_calibration_enable(ADC->adc_port);
}

 在解析上述代码前,我们先看driver_adc.h两个结构体声明。

1、ADC设置参数结构体,该结构体中规定了ADC设置所需要的参数:

C
/*ADC设置参数*/
typedef struct __typdef_adc_general
{rcu_periph_enum rcu_adc;//ADC时钟口uint32_t adc_psc;//ADC时钟源分频系数uint32_t adc_port;//ADC号uint32_t adc_mode;//ADC工作模式:ADC_MODE_FREE,ADC_DAUL_REGULAL_PARALLELuint8_t adc_channel_group;//ADC工作组:规则组或注入组EventStatus adc_scan_function;//设置扫描模式EventStatus adc_continuous_function;//设置循环模式uint8_t ch_count;//设置转换通道个数typdef_adc_dma_parameter dma_parameter;//若使用DMA,则需要设置dmauint32_t trigger_source;//ADC触发源EventStatus DMA_mode;//是否使用DMA
}typdef_adc_ch_general;

 2、ADC IO口及通道参数结构体,该结构体中规定了用于ADC转换的IO口及通道参数:

C
/*ADC IO口及通道参数*/
typedef struct __typdef_adc_ch_parameter
{rcu_periph_enum rcu_port;//IO口时钟uint32_t port;//IO portuint32_t pin;//IO pinuint32_t gpio_speed;//IO 速率uint8_t adc_channel;//IO对应的ADC通道uint32_t sample_time;//IO的采样周期
}typdef_adc_ch_parameter;

好,现在我们来对ADC配置进行分段解析。

1、ADC的时钟频率配置和ADC时钟使能:

C
/*配置ADC时钟频率*/
rcu_adc_clock_config(ADC->adc_psc);
/*使能ADC时钟*/
rcu_periph_clock_enable(ADC->rcu_adc);

 GD32F303的ADC的工作时钟频率不能超过40MHz,而ADC是挂载在APB2总线上的,APB2总线最高频率可以达到120M,所以需要进行分频处理。

2、配置IO口

C
/*配置ADC相关IO口,先使能时钟,再将IO口设置为模拟输入*/
for(i=0 ;i<ADC->ch_count; i++)
{if(ADC_CH[i].adc_channel < ADC_CHANNEL_16){rcu_periph_clock_enable(ADC_CH[i].rcu_port);gpio_init(ADC_CH[i].port, GPIO_MODE_AIN, ADC_CH[i].gpio_speed, ADC_CH[i].pin);                                        }else{adc_tempsensor_vrefint_enable();}
}

被用作ADC采样的IO口需要被设置为Analog模式,因为ADC_CH0~ADC_CH15是和外部IO关联的,而ADC_CH16和ADC_CH17是内部通道,所以只有在通道号小于ADC_CHANNEL_16时才需要配置IO口,而大于等于ADC_CHANNEL_16时需要使能ADC内部通道。

3、配置ADC规则组工作模式

C
/*配置ADC工作模式,如独立模式,规则并行模式等*/
adc_mode_config(ADC->adc_mode);

ADC的模式有独立模式、规则并行、注入并行、快速交叉等9种,其中用到比较多的是独立模式、规则并行和注入并行,现对这三种做简单介绍。

独立模式:三个ADC相互之间无影响,每个ADC单独工作;

规则并行:ADC0和ADC1可工作规则并行模式下,当ADC0规则组被触发开始转换时,ADC1注入组也会自动开始转换(此时ADC1的触发源一定要选择软件触发),转换结果会分别放在ADC0_RDATA和ADC1_RDATA中,其中ADC0_RDATA的上半字也会保存ADC1的转换结果,这样设计是为了方便DMA去进行两个同步ADC结果的同时搬运。

基于16个通道的规则并行模式如下图所示:

注意:
1.不要在两路 ADC 上转换相同的通道(两路 ADC 在同一通道转换时采样时间不可重叠)。
2.在并行模式下, ADC0 和 ADC1 并行采样的两个通道的需要设置为准确的相同采样时间。  

 注入并行:ADC0和ADC1可工作注入并行模式下,当ADC0注入组被触发开始转换时,ADC1注入组也会自动开始转换(此时ADC1的触发源一定要选择软件触发),和规则组不同的是,通道转换结果会放在各自的 ADC_IDATAx 寄存器中 。

基于4个通道的注入并行模式如下图所示:

4、配置扫描和连续模式

C
/*配置规则组的扫描模式和连续转换模式*/
adc_special_function_config(ADC->adc_port, ADC_SCAN_MODE, ADC->adc_scan_function);
if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
{
    adc_special_function_config(ADC->adc_port, ADC_CONTINUOUS_MODE, ADC->adc_continuous_function);        
}                

扫描模式:当一个规则组或注入组规定了1个以上的通道转换时,需要开启扫描模式,此时规则组或注入组会根据设定好的转换顺序对通道一个一个进行转换;如果关闭扫描模式,那么规则组和注入组只会转换RSQ0和ISQ0规定的通道。

连续模式:该模式只适用于规则组,开启该模式时,当规则组转换完成后,会自动开始新一轮的转换。

下图为扫描转换模式,且连续转换模式失能的转换情况:

下图为扫描转换模式,连续转换模式使能的转换情况:

 5、设置数据对齐

C
/*选择数据右对齐*/
adc_data_alignment_config(ADC->adc_port, ADC_DATAALIGN_RIGHT);

ADC的转换结果可以选择左对齐或右对齐,以12位结果为例,左右对齐如下:

6、配置转换个数、转换顺序及通道采样周期

C
/*配置转换通道数*/
    adc_channel_length_config(ADC->adc_port, ADC->adc_channel_group, ADC->ch_count);        
    /*配置转换顺序*/
    if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL)
    {
        for(i = 0;i< ADC->ch_count;i++)
        {
            adc_regular_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
        }
    }
    else if(ADC->adc_channel_group == ADC_INSERTED_CHANNEL)
    {
        for(i = 0;i< ADC->ch_count;i++)
        {
            adc_inserted_channel_config(ADC->adc_port, i, ADC_CH[i].adc_channel,ADC_CH[i].sample_time);
        }        
    }

 这段程序是配置规则组和注入组的转换长度、转换顺序及通道采样周期。转换长度和顺序前面讲过,不再赘述,这里简单介绍下采样周期。

采样周期通过adc_regular_channel_config和adc_inserted_channel_config函数的最后一个形参设置,相应的寄存器是ADC_SAMPT0和ADC_SAMPT1。

GD32F303是SAR ADC(逐次逼近型ADC),内部有采样电容,采样过程为先将内部采样电容和外部采样引脚相连,通过对内部采样电容的充放电让采样电容和外部采样引脚电压相等,然后关闭内外部电路通道,内部再通过逐次逼近比较得到电压的数字值。逐次比较的过程需要固定的1.5个采样周期,而采样电容充放电过程是可以设置的,即这里介绍的采样周期。

采样周期分8个档位可选,如一个通道选择12.5周期,则转换过程需要的总周期数为12.5+1.5 = 14,如果设置ADC的时钟(前面提到,ADC时钟是通过APB2总线经过分频所得)为30M,那么该通道转换所需要的时间为14/30M = 0.467us。

那么如何选择合适的采样周期,这个和内部采样电容的充放电时间有关,如果充放电时间不够,采样电容电压不能和外部采样引脚电压一致,则得不到准确的采样值,如充放电时间过长,则浪费CPU资源。我们可以根据采样引脚对内输入阻抗值大小来选择合适的采样周期,阻抗值和采样周期对应表可以通过GD32F303 Datasheet ADC电气参数章节获得:

举个例子,实际采样引脚对内阻抗为20kΩ,根据上表需要选择的采样周期为55.5。

7、选择ADC规则组和注入组的触发源以及使能外部触发

C
/*选择触发源及使能外部触发模式*/
adc_external_trigger_source_config(ADC->adc_port, ADC->adc_channel_group, ADC->trigger_source);        
adc_external_trigger_config(ADC->adc_port, ADC->adc_channel_group, ENABLE);

8、选择是否需要DMA

C
/*选择是否需要使用DMA*/
if(ADC->DMA_mode == ENABLE)
{adc_dma_mode_enable(ADC->adc_port);
}

 只有规则组才能使用DMA,在规则组设置了多通道采样即扫描模式打开时,是一定要用DMA的,否则没办法及时将转换完成的通道数据取走,其他的情况下DMA是否使用根据实际应用决定。

9、ADC的使能和自校准

C/*ADC的使能和自校准,ADC使能后需要经过一定的ADC_CLK后才能校准,本示例中直接使用1ms延时*/
adc_enable(ADC->adc_port);
delay_ms(1);
adc_calibration_enable(ADC->adc_port);

ADC的使能比较简单,使能后ADC才能工作,这里再强调下,使能ADC不代表就开始了ADC转换,而是ADC等到触发信号才会转换,关于触发条件,请读者看前面触发源介绍。

ADC 带有一个前置校准功能。在校准期间, ADC 计算一个校准系数,这个系数是应用于 ADC内部的,它直到 ADC 下次掉电才无效。在校准期间,应用不能使用 ADC,它必须等到校准完成。在 A/D 转换前应执行校准操作。通过软件设置 CLB=1 来对校准进行初始化,在校准期间CLB 位会一直保持 1,直到校准完成,该位由硬件清 0。

ADC使能后校准前比较要等待至少14个ADC时钟周期,本实验中就直接使用1ms延时了,是完全足够的。

关于校准:一般建议ADC enable之后进行一次校准,但需要保证校准器件,ADC参考电压一定要很稳定,否则可能校准到一个错误的情况,这样后续的ADC转换反而不准确了,所以在一些特定情况下,校准也可以不加。

 11.4.2 轮训方式获取采样值函数

ADC初始化好后,就可以进行采样了,本实验是通过轮训方式进行ADC采样,下面是轮训方式获取采样值函数。

C
uint16_t driver_adc_transform_polling(typdef_adc_ch_general *ADC,typdef_adc_ch_parameter *ADC_CH)
{/*规则组采样*/if(ADC->adc_channel_group == ADC_REGULAR_CHANNEL){/*设置规则组需要采样的通道*/adc_regular_channel_config(ADC->adc_port, 0, ADC_CH->adc_channel, ADC_CH->sample_time);        /*软件触发规则组转换*/adc_software_trigger_enable(ADC->adc_port, ADC_REGULAR_CHANNEL);/*等待EOC置起*/while(RESET == adc_flag_get(ADC->adc_port,ADC_FLAG_EOC));/*清除EOC标志位*/adc_flag_clear(ADC->adc_port,ADC_FLAG_EOC);/*将规则组转换结果作为返回值*/return ADC_RDATA(ADC->adc_port);}/*注入组采样*/else if(ADC->adc_channel_group == ADC_INSERTED_CHANNEL){/*设置注入组需要采样的通道*/adc_inserted_channel_config(ADC->adc_port, 0, ADC_CH->adc_channel, ADC_CH->sample_time);        /*软件触发注入组转换*/adc_software_trigger_enable(ADC->adc_port, ADC_INSERTED_CHANNEL);/*等待EOIC置起 */while(RESET == adc_flag_get(ADC->adc_port,ADC_FLAG_EOIC));/*清除EOIC标志位*/adc_flag_clear(ADC->adc_port,ADC_FLAG_EOIC);/*将注入组转换结果作为返回值*/return ADC_IDATA0(ADC->adc_port);}return 0;
}

读者可以读以上代码的注释来分析这个函数,强调下,判断规则组转换完成用EOC标志位,注入组用EOIC。另外这个函数返回值是16位,但如果使用了规则并行或注入并行的话,该函数还需要稍作调整,读者可以思考下该如何修改?

11.4.3 Power_detect ADC设置所需要的参数及IO口结构体定义

在bsp_adc.c中,对Power_detect_ADC设置所需要的参数及IO扩结构体进行了定义:

C
typdef_adc_ch_general  Power_detect_ADC= {.rcu_adc = RCU_ADC2,//ADC2的时钟.adc_psc = RCU_CKADC_CKAPB2_DIV6,//ADC2设置为APB2 6分频.adc_port = ADC2,//ADC口为ADC2.adc_mode = ADC_MODE_FREE,//ADC模式为独立模式.adc_channel_group = ADC_REGULAR_CHANNEL,//使用规则组.adc_scan_function = DISABLE,//关闭扫描模式.adc_continuous_function = DISABLE,//关闭循环模式.ch_count = 1,//转换长度为1.dma_parameter = {0},//不使用DMA.trigger_source = ADC0_1_2_EXTTRIG_REGULAR_NONE,//ADC触发源选择为软件触发.DMA_mode = DISABLE//不使用DMA
};
//ADC通道参数配置,包括IO口,和对应通道以及采样周期
typdef_adc_ch_parameter Power_detect_ch_parameter= 
{.rcu_port = RCU_GPIOF,//GPIOF时钟.port = GPIOF,//GPIO port.pin = GPIO_PIN_6,//PF6.gpio_speed = GPIO_OSPEED_10MHZ,//PF6速度设置为10MHz.adc_channel = ADC_CHANNEL_4,//PF6是ADC2的通道4.sample_time = ADC_SAMPLETIME_55POINT5//设置采样周期为55.5
};

11.4.4 Power_detect ADC初始化和读Power_detect的具体实现函数

在bsp_adc.c中定义了Power_detect ADC初始化和读Power_detect的具体实现函数:

C
uint16_t Power_detect_data = 0;
void bsp_Power_detect_ADC_config()
{driver_adc_config(&Power_detect_ADC,&Power_detect_ch_parameter);
}
void bsp_Power_detect_data_get()
{Power_detect_data = driver_adc_transform_polling(&Power_detect_ADC,&Power_detect_ch_parameter);
}

11.4.5 main函数实现

C
int main(void)
{driver_init();//延时函数初始化bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化bsp_Power_detect_ADC_config();//Power_detect ADC配置        while (1){delay_ms(1000);//延时1sbsp_Power_detect_data_get();//获取Power_detect数据printf(" the Power_detect data is %d \r\n", Power_detect_data);//打印Power_detect数据printf(" the Power voltage is %.2f V \r\n", (float)Power_detect_data/4096*3.3f*2);//输出供电电压值}
}

 

本例程main函数首先进行了延时函数初始化,为了演示实验结果,这里初始化了BOARD_UART串口,关于串口的使用,请读者参考串口章节,然后是Power_detect ADC配置。在主循环中实现每秒钟进行一次Power_detect的ADC采样,并将转换结果打印出来。

11.5 实验结果

使用USB-TypeC线,连接电脑和板上USB to UART口后,配置好串口调试助手,即可看到Power_detect打印数据了。

由聚沃科技原创,来源于【红枫派开发板】第十一讲 ADC-电源电压单通道ADC检测实验 - 苏州聚沃电子科技有限公司 (gd32bbs.com)

GD32MCU技术交流群:859440462   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25099.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RTOS笔记--任务通知+软件定时器

任务通知的本质 对于之前使用过的几种互斥操作方式队列&#xff0c;互斥量&#xff0c;信号量&#xff0c;事件组&#xff0c;他们都是黑箱操作&#xff0c;对于写入和读取的任务来说并不知道对方是哪个任务&#xff0c;只是操作环形缓冲区和链表。 而任务通知的方式就是通知方…

【CS.AL】算法复杂度分析 —— 时间复杂度详解

文章目录 1 概述2 时间复杂度的详细分析2.1 常数时间复杂度&#xff08;O(1&#xff09;&#xff09;2.2 对数时间复杂度&#xff08;O(log n)&#xff09;2.3 线性时间复杂度&#xff08;O(n)&#xff09;2.4 线性对数时间复杂度&#xff08;O(n log n)&#xff09;2.5 平方时…

程序的基本结构、cout语句(c++语言)

一、如何下载Dev C 登录网站&#xff1a;ht.51goc.com 二、安装Dev C 一、启动Dev C 双击桌面的图标 二、新建一个程序 三、复制一个程序 请你复制以下代码到“程序编辑区” #include<bits/stdc.h> using namespace std; int main() { cout<<"Hell…

计网仿真综合实验 实验十二

实验十二 综合网络实验 实验过程 IP配置说明参考连线配置OSPF使公司内部联通 路由器R1的OSPF配置路由器R2的OSPF配置路由器R3的OSPF配置R1、R2、R3的相关解释路由器R4的OSPF配置路由器R5的OSPF配置路由器R6的OSPF配置R4、R5、R6解释: 路由器R2的RIP配置路由器R7的RIP配置 总结 …

MicroPython esp32 连接wifi 配网

整体流程&#xff1a; 1&#xff09;开启STA 和 AP 模式 2&#xff09;扫描周围wifi 保存在 变量 wifi_list&#xff08;后面要用到&#xff09; 3) 尝试STA模式连接Wifi&#xff0c;并查寻状态。 4) 如果STA 无法连网&#xff0c;就用AP模式&#xff0c;创建热点。 5&a…

【lesson1】第三方库(jsoncpp,bundle, httplib)的介绍和使用

文章目录 jsoncpp库json 认识jsoncpp 认识jsoncpp 实现序列化jsoncpp 实现反序列化 bundle库bundle库实现文件压缩bundle库实现文件解压缩 httplib 库httplib 库搭建简单服务器httplib库搭建简单客户端 jsoncpp库 json 认识 json 是一种数据交换格式&#xff0c;采用完全独立…

【Vscode配置java环境并配置stringboot】

1.VSCODE配置JAVA环境 参考这篇文章配置JAVA环境&#xff1a;连接 java版本&#xff0c;我是win11系统,我下载的JAVA安装版本是下面&#xff0c;是最新版的&#xff1a; 配置环境&#xff1a;步骤很简单&#xff0c;就是向系统环境变量中添加路径&#xff0c;参考上面文章中的…

基于学习模型的可学习小波变换方法(Pytorch)

首先以图像编码为例进行说明。 图像编码是一个复杂的系统&#xff0c;通常包含多个模块&#xff0c;其中变换模块具有重要作用。小波变换在图像编码领域得到了广泛的应用&#xff0c;例如著名的JPEG 2000就是一种小波图像编码方法。然而&#xff0c;现阶段的小波图像编码方法与…

htb-window-1-legacy-smb

nmap smb-vuln-ms08-067 py文件测试失败 msf 漏洞定位 反弹 获取flag

【Oracle篇】rman全库异机恢复:从单机环境到RAC测试环境的转移(第五篇,总共八篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

一文学会Spring 实现事务,事务的隔离级别以及事务的传播机制

目录 一.Spring (Spring Boot) 实现事务 1.通过代码的方式手动实现事务 (手动档的车) 2.通过注解的方式实现声明式事务 (自动挡的车) 二.事务的4大特性(ACID) 三.事务的隔离级别 ①Mysql的事务隔离级别: ②Spring的事务隔离级别: 四.事务的传播机制 ①事务传播机制的概…

验证码案例

目录 前言 一、Hutool工具介绍 1.1 Maven 1.2 介绍 1.3 实现类 二、验证码案例 2.1 需求 2.2 约定前后端交互接口 2.2.1 需求分析 2.2.2 接口定义 2.3 后端生成验证码 2.4 前端接收验证码图片 2.5 后端校验验证码 2.6 前端校验验证码 2.7 后端完整代码 前言…

基于可解释性深度学习的马铃薯叶病害检测

数据集来自kaggle文章&#xff0c;代码较为简单。 import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)# Input data files are available in the read-only "../input/" directory # For example, runni…

快团团供货大团长如何查看帮卖团长的订单?

一、功能说明 可以看到团购中每个帮卖团长帮卖产生的订单 二、具体设置方法 1、小程序端如何操作&#xff1f; 在团购页面中&#xff0c;点击订单管理&#xff0c;在这里可以选择全部团长订单&#xff0c;我的团订单&#xff0c;和帮卖团长的帮卖订单。 2、PC端如何操作&am…

ssm616基于vue.js的购物商场的设计与实现+vue【已测试】

前言&#xff1a;&#x1f469;‍&#x1f4bb; 计算机行业的同仁们&#xff0c;大家好&#xff01;作为专注于Java领域多年的开发者&#xff0c;我非常理解实践案例的重要性。以下是一些我认为有助于提升你们技能的资源&#xff1a; &#x1f469;‍&#x1f4bb; SpringBoot…

【基于C++与OpenCV实现魔方图像识别和还原算法】施工总览图

文章目录 主要效果展示思维导图魔方还原算法 本系列博客长期更新&#xff0c;分为两大部分 OpenCV实现魔方六面识别 C编写科先巴二阶段还原算法实现三阶魔方的还原 主要效果展示 摄像头识别六面 3D图像构建&#xff0c;提供还原公式 动画演示还原过程 思维导图 魔方还原算法 参…

达梦8 开启物理逻辑日志对系统的影响

物理逻辑日志&#xff0c;是按照特定的格式存储的服务器的逻辑操作&#xff0c;专门用于 DBMS_LOGMNR 包挖掘获取数据库系统的历史执行语句。当开启记录物理逻辑日志的功能时&#xff0c;这部分日志内 容会被存储在重做日志文件中。 要开启物理逻辑日志的功能&#xff0c;需要…

社区物资交易互助平台的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;公告信息管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;求助留言板&#xff0c;公…

SQL Chat:从SQL到SPEAKL的数据库操作新纪元

引言 SQL Chat是一款创新的、对话式的SQL客户端工具。 它采用自然语言处理技术&#xff0c;让你能够像与人交流一样&#xff0c;通过日常对话的形式对数据库执行查询、修改、创建及删除操作 极大地简化了数据库管理流程&#xff0c;提升了数据交互的直观性和效率。 在这个框…

反AI浪潮中的新机遇:Cara艺术社区异军突起

近日,一个名为Cara的艺术社区在网络上迅速走红,其独特的反AI定位吸引了大量创意人士。在AI技术日益普及的今天,Cara社区反其道而行之,致力于打造一个无AI侵害的创作和交流环境。这一创新模式不仅赢得了艺术家的青睐,也为国内创业者提供了新的思考角度。 一、精准定位,守…