二十二、 ADC 光敏电阻实验
22.1.配置流程
一般使用 ADC 功能,都需要有以下几个步骤。
- 配置时钟
- 配置引脚
- 配置 ADC 模式
- 配置数据对齐方式
- 配置分辨率
- 配置采集通道
- 配置触发方式
- 开启校准
- 使能 ADC
22.1.1 配置时钟
本案例使用 ADC0 进行操作,需要开启 ADC0 的时钟。 不过需要注意的是 GD32F4 的用户手册写出,ADC 的最大时钟频率为 40MHz。因此我们使用 ADC 时,需要将挂载 ADC 的时钟总线频率分频到 40MHz 以下。
ADC 的时钟来源有 AHB(240MHz)和 APB2(120MHz),我们需要选择一个时钟源,并将其分频到 40MHz 以下。
本案例使用的是 APB2 作为 ADC 时钟来源,其最大时钟频率为 120MHz,我们需要将其分频到 40MHz 以下。而它分频的参数有 2 分频、4 分频、6 分频、8 分频等等。如选择 2 分频,则最终的 ADC 时钟频率为 120 / 2 = 60 MHz,这明显没有达到我们的 40MHz 以下的要求。选择 4 分频,最终的 ADC 时钟频率为:
120 / 4 = 30 MHz
4 分频满足了我们的要求。
22.1.2 配置引脚
GD32F470ZGT6 一共有 16 个外部 ADC 通道,我们将光敏电阻模块的 AO 引脚连接在 PC1 引脚上,查找数据手册的引脚定义可知,PC1 的附加功能有 ADC0 的通道 11,ADC1 的通道 11,ADC2 的通道 11。
因为我们之前配置时钟的章节开启的是 ADC0 的时钟,所以这里选择 PC1 的附加功能,ADC0 的通道 11 进行操作。要操作 GPIO 引脚,必不可少的就是对 GPIO 进行配置,包括开启时钟、配置模式、配置输出、设置功能等,还是这一系列的操作。
PC1 引脚和 ADC0 通道 11 的宏定义如下:
/* PC1 ADC0_IN11 */
#define BSP_ADC_GPIO_RCU RCU_GPIOC
#define BSP_ADC_GPIO_PORT GPIOC
#define BSP_ADC_GPIO_PIN GPIO_PIN_1
/* PC1 ADC0_IN11 */
#define BSP_ADC_RCU RCU_ADC0
#define BSP_ADC ADC0
#define BSP_ADC_CHANNEL ADC_CHANNEL_11
2
3
4
5
6
7
8
9
初始化 GPIO 引脚的配置如下:
//使能引脚时钟
rcu_periph_clock_enable(BSP_ADC_GPIO_RCU);
//使能ADC时钟
rcu_periph_clock_enable(BSP_ADC_RCU);
//配置ADC时钟
adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
//配置引脚为模拟浮空输入模式
gpio_mode_set(BSP_ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_ADC_GPIO_PIN);
2
3
4
5
6
7
8
以上代码需要注意的是使用 ADC 功能时,引脚必须设置为模拟浮空模式,如设置为上拉或下拉模式,则采集的电压是不准确的。
22.1.3 配置 ADC 模式
ADC 的模式有 4 种,分别是单次、连续、扫描和间断转换模式。具体的讲解见章节【22.1.2. GD32 的 ADC 介绍】,这里我们配置为扫描模式,这样就可以根据自己的需要,配置采集通道进行依次扫描采集对应引脚的模拟信号。也可以根据需要配置为连续模式和间断模式。
扫描模式配置如下:
//使能扫描模式
adc_special_function_config(BSP_ADC, ADC_SCAN_MODE, ENABLE);
2
GD32 还有一个同步模式。只有在具有两个或三个 ADC 的设备上,可以使用 ADC 同步模式。 ADC 的同步模式是一种使用相同时钟或者外部同步信号的多路采样模式,以实现多个 ADC 采样时序的严格同步,从而提高 AD 转换的精度。比如 ADC0 和 ADC1 同时采样。在使用 ADC 的同步模式时,需要按照正确的顺序配置各种参数和模块进行初始化设置。
而我们使用的是独立模式,独立模式是指 ADC 使用单独的触发信号进行采样转换,可以被用于单次转换或者轮寻方式下的连续转换。在独立模式下,ADC 模块中的各个转换通道之间是独立的,每个 ADC 都独立工作。
我们通常只需要使用独立模式即可。
独立模式配置如下:
//配置ADC为独立模式
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
2
22.1.4 配置数据对齐方式
GD32F470ZGT6 微控制器的 ADC 支持各种数据对齐方式以适应不同的应用场景。常见的数据对齐方式包括左对齐和右对齐。在右对齐模式下,ADC 的数据在转换结束后被右对齐到最低位,不足的位数在高位填充 0。右对齐模式允许实现在没有精度的损失下更好的动态范围。
在左对齐模式下,ADC 的数据被左对齐到最高位,不足的位数在低位填充 0。左对齐模式可以提高分辨率,但会导致动态范围降低。
我们配置为右对齐方式,这样我们可以直接将转换完成的数据进行运算。
右对齐方式的配置如下:
//数据右对齐
adc_data_alignment_config(BSP_ADC, ADC_DATAALIGN_RIGHT);
2
22.1.5 配置分辨率
GD32F470ZGT6 的分辨率可以配置为 6、8、10、12 位。我们可以通过降低 ADC 的分辨率,获得较快的转换时间(tADC)。对于那些不需要高精度数据的应用,可以使用较低的分辨率来实现更快速地转换。
这里我们配置为 12 位分辨率。分辨率的配置如下:
//ADC0设置为12位分辨率
adc_resolution_config(BSP_ADC, ADC_RESOLUTION_12B);
2
22.1.6 配置采集通道
我们需要配置的是使用的通道和采集通道个数。这里我们使用的是常规通道组,并且只需要转换一个通道。后续需要采集多个通道时,只需要修改采集的通道即可。
采集通道的配置如下:
//ADC0设置为规则组 一共使用 1 个通道
adc_channel_length_config(BSP_ADC,ADC_REGULAR_CHANNEL, 1);
2
22.1.7 配置触发方式
触发的方式有两种,分别是外部触发方式和软件触发方式。
其中外部触发输入的上升沿、下降沿可以触发规则组或注入组的转换。具体见 GD32F4 用户手册的第 271 页。需要注意的是可以实时修改外部触发选择,在修改期间不会出现触发事件。
软件触发方式是由软件触发启动,通常用在单次采样转换中。在软件触发模式下,ADC 转换工作被触发后,由软件控制以固定的时间间隔进行转换,因此通常用于采集时间不太关键,但要求精度较高的应用。
这里选择的是软件触发方式,软件触发方式的配置如下:
//ADC外部触发禁用, 即只能使用软件触发
adc_external_trigger_config(BSP_ADC,ADC_REGULAR_CHANNEL,EXTERNAL_TRIGGER_DISABLE);
//使能软件触发
adc_software_trigger_enable(BSP_ADC, ADC_REGULAR_CHANNEL);
2
3
4
22.1.8 开启校准
ADC 带有一个前置校准功能。在校准期间,ADC 会计算一个出厂校准系数,这个系数是应用于 ADC 内部的,它直到 ADC 下次掉电才无效。在 A/D 转换前应执行校准操作。在校准期间,应用不能使用 ADC,它必须等到校准完成。通过软件设置 CLB=1 来对校准进行初始化,在校准期间 CLB 位会一直保持 1,直到校准完成,该位由硬件清 0。可以直接使用库函数调用校准,完成操作。
开启校准的配置如下:
//开启ADC自校准
adc_calibration_enable(BSP_ADC);
2
22.1.9 使能 ADC
ADC 配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。需要注意的是要先使能了 ADC 之后,才能开始进行自校准。
//ADC0使能
adc_enable(BSP_ADC);
2
这个函数使能 ADC,有一个参数,就是要使能的 ADC。
最终的 ADC 全部配置参数如下:
void adc_config(void)
{
//使能引脚时钟
rcu_periph_clock_enable(BSP_ADC_GPIO_RCU);
//使能ADC时钟
rcu_periph_clock_enable(BSP_ADC_RCU);
//配置ADC时钟
adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
//配置引脚为模拟输入模式
gpio_mode_set(BSP_ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_ADC_GPIO_PIN);
//配置ADC为独立模式
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
//使能扫描模式
adc_special_function_config(BSP_ADC, ADC_SCAN_MODE, ENABLE);
//数据右对齐
adc_data_alignment_config(BSP_ADC, ADC_DATAALIGN_RIGHT);
//ADC0设置为12位分辨率
adc_resolution_config(BSP_ADC, ADC_RESOLUTION_12B);
//ADC0设置为规则组 一共使用 1 个通道
adc_channel_length_config(BSP_ADC,ADC_REGULAR_CHANNEL, 1);
//ADC外部触发禁用, 即只能使用软件触发
adc_external_trigger_config(BSP_ADC, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
//使能软件触发
adc_software_trigger_enable(BSP_ADC, ADC_REGULAR_CHANNEL);
//ADC0使能
adc_enable(BSP_ADC);
//开启ADC自校准
adc_calibration_enable(BSP_ADC);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
22.2. 采集转换
我们之前使用的是扫描模式并使用的是软件触发方式,因此当我们要采集信号时,需要配置好采集通道并开启软件转换。开启转换后,等待 EOC 标志位置 1。其中 EOC 为转换完成标志位,我们可以通过判断其是否置 1,确定是否转换完成。转换完成后将数据从 16 位寄存器中取出。
/**********************************************************
* 函 数 名 称:Get_ADC_Value
* 函 数 功 能:读取ADC值
* 传 入 参 数:ADC_CHANNEL_x=要采集的通道
* 函 数 返 回:测量到的值
* 作 者:LC
* 备 注:默认采样周期为15个ADC采样时间
**********************************************************/
unsigned int Get_ADC_Value(uint8_t ADC_CHANNEL_x)
{
unsigned int adc_value = 0;
//设置采集通道
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_x, ADC_SAMPLETIME_15);
//开始软件转换
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
//等待 ADC0 采样完成
while ( adc_flag_get(ADC0, ADC_FLAG_EOC) == RESET )
{
;
}
//读取采样值
adc_value = adc_regular_data_read(ADC0);
//返回采样值
return adc_value;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
22.3. 实验现象
在 main.c 中调用 unsigned int Get_ADC_Value(uint8_t ADC_CHANNEL_x);函数,采集 PC1 引脚的电压,并通过串口输出。
int main(void)
{
uint16_t value = 0;
systick_config();
usart_gpio_config(9600U);
adc_config();
while(1)
{
value = Get_ADC_Value(ADC_CHANNEL_11);
printf("value = %d\r\n", value );
printf("voltage = %f V\r\n", value / 4095.0 * 3.3 );
delay_1ms(500);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
在灯光充足的情况下,ADC 转换的值大约为 920 左右,转换为实际电压接近 0.5V。
当用手当住光敏电阻后,采集到的值大约在 3790 左右,转换为实际电压接近 3.0V。
如果没有光敏电阻,则可以连接到 3.3V 或者 GND 进行测试。当接入 3.3V 时采集到的值接近 4095;当接入 GND 时,采集到的值接近 0。
关于这一章节的代码,在资源包/04软件资料/代码例程/里面的ADC。