【GD32F303红枫派开发板使用手册】第十一讲 ADC-电源电压单通道ADC检测实验
2024-12-13 来源:elecfans
通过本实验主要学习以下内容:
ADC的简介
GD32F303 ADC工作原理
查询方式实现ADC单通道采样
11.2实验原理
11.2.1ADC原理
我们知道,自然界中有非常多的模拟信号,比如上一节提到的光照强度,还有其他的例如温度、声音等等,那么人们是怎么来衡量一个模拟信号的呢?
我们通常会说今天光照度达到了3万Lux(照度单位),现在测量到的体温是36.5℃,我们所处的环境是40分贝,没错,人们就是通过将这些模拟信号数字化,从而达到衡量这些模拟信号的目的。那对于MCU来说,如果要测量一个模拟量,可以通过自带的ADC(Analog-to-Digital converters)模块,即模-数转换器将模拟量转化为可以被MCU读取到的数字量。
11.2.2GD32F303 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.1ADC初始化
在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 ;ich_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_PARALLEL
uint8_t adc_channel_group;//ADC工作组:规则组或注入组
EventStatus adc_scan_function;//设置扫描模式
EventStatus adc_continuous_function;//设置循环模式
uint8_t ch_count;//设置转换通道个数
typdef_adc_dma_parameter dma_parameter;//若使用DMA,则需要设置dma
uint32_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 port
uint32_t pin;//IO pin
uint32_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 ;ich_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结果的同时搬运。
- GD32F303学习笔记(1)——搭建环境、编译烧写
- 【GD32F303红枫派开发板使用手册】第二十四讲 DHT11温湿度传感器检测实验
- 【GD32F303红枫派开发板使用手册】第二十一讲 I2C-EEPROM读写实验
- 【GD32F303红枫派开发板使用手册】第二十讲八 USB-虚拟串口实验
- 【GD32F303红枫派开发板使用手册】第十七讲 USART-中断串口收发实验
- 【GD32F303红枫派开发板使用手册】第十五讲 USART-printf打印实验
- 【GD32F303红枫派开发板使用手册】第十二讲 ADC-双轴按键摇杆多通道循环采样实验
- 【GD32F303红枫派开发板使用手册】第四讲 GEXTI-按键中断检测实验
- 【GD32F303红枫派开发板使用手册】第十四讲 DAC-输出电压实验
- 【GD32F303红枫派开发板使用手册】第十九讲 SPI-SPI NOR FLASH读写实验
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析
- 蓝牙信道探测技术原理与开发套件实践
- 意法半导体中国本地造STM32微控制器启动规模量产




