二十三、ADC光敏电阻实验
1. 模块来源
2. 规格参数
工作电压:3.3-5V
工作电流:1MA
模块尺寸:31.1475 x 14.097mm
输出方式: DO接口为数字量输出 AO接口为模拟量输出
读取方式:ADC
管脚数量:4 Pin(2.54mm间距排针)
3. 模块与开发板接线
4. ADC配置流程
4.1 配置时钟
本案例使用ADC0进行操作,需要开启ADC1的时钟。 不过需要注意的是STM32F1的中文参考手册中的ADC章节有进行说明,ADC的最大时钟频率为14MHz。V因此我们使用ADC时,需要将挂载ADC的时钟总线频率分频到14MHz以下。
本案例使用的是APB2作为ADC时钟来源,其最大时钟频率为72MHz,我们需要将其分频到14MHz以下。而它分频的参数有2分频、4分频、6分频、8分频等等。选择6分频,则最终的ADC时钟频率为:
6分频满足了我们的要求。
4.2 配置引脚
每个ADC都可以共享多达16个外部通道,我们将光敏电阻模块的AO引脚连接在PA5引脚上,查找数据手册的引脚定义可知,PA5的附加功能有ADC1的通道5,ADC2的通道5。
因为我们之前配置时钟的章节开启的是ADC0的时钟,所以这里选择PA5的附加功能,ADC1的通道5进行操作。要操作 GPIO 引脚,必不可少的就是对 GPIO 进行配置,包括开启时钟、配置模式、配置输出、设置功能等,还是这一系列的操作。
GPIO配置:
GPIO_InitTypeDef GPIO_InitStructure; // 配置结构体
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE); // 使能时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIO
2
3
4
5
6
7
参数讲解
GPIO_InitTypeDef GPIO_InitStructure;
:这里声明了一个名为GPIO_InitStructure的结构体变量,用于配置GPIO模块的参数。RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE);
:通过调用RCC_APB2PeriphClockCmd函数来启用GPIOA端口的时钟。在STM32系列中,要使用某个GPIO端口之前,需要确保该端口的时钟已经被使能。GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
:将GPIO_Pin_5赋值给GPIO_InitStructure结构体的GPIO_Pin成员。这表示我们将要配置的GPIO引脚是GPIOA的第5号引脚。GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
:指定了GPIO引脚的工作模式。在这种情况下,将该引脚配置为模拟输入模式,以便连接到模数转换器(ADC)等模拟电路。GPIO_Init(GPIOA, &GPIO_InitStructure);
:通过调用GPIO_Init函数,将上述配置应用到GPIOA的第5号引脚上,从而完成了对该引脚的初始化配置。
以上代码需要注意的是使用ADC功能时,引脚必须设置为模拟输入模式,如设置为上拉或下拉模式,则采集的电压是不准确的。
4.3 配置ADC
ADC_InitTypeDef ADC_InitStructure;
// 打开ADC时钟
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_ADC1, ENABLE );
// ADC 模式配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;// 只使用一个ADC,独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE ;// 禁止扫描模式,单通道不需要扫描
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;// 连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 软件触发转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;// 数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;// 转换通道1个
// 初始化ADC
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC时钟PCLK2的6分频,即12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
讲解
通过
ADC_InitTypeDef ADC_InitStructure;
声明了一个名为ADC_InitStructure
的结构体变量,用于配置 ADC 模块的参数。通过
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_ADC1, ENABLE );
启用了 ADC1 的时钟。在使用 ADC 之前,需要确保相应的时钟已经被使能。对 ADC 进行了一系列配置:
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
:配置 ADC 为独立模式,表示只使用一个 ADC。ADC_InitStructure.ADC_ScanConvMode = DISABLE;
:禁止扫描模式,因为只使用单通道,不需要扫描多个通道。ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
:启用连续转换模式,允许 ADC 连续地进行模拟信号转换。ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
:设置外部触发转换为软件触发,即由软件启动转换。ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
:指定数据右对齐,表示转换后的数据在数据寄存器中右对齐存放。ADC_InitStructure.ADC_NbrOfChannel = 1;
:指定转换通道数量为 1,表明只有一个转换通道。
通过
ADC_Init(ADC1, &ADC_InitStructure);
将上述配置应用到 ADC1 模块上,完成了对 ADC 的初始化配置。通过
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
配置了 ADC 时钟 PCLK2 的分频系数为 6,将 PCLK2 分频为 6,即获得 12MHz 的时钟频率。这个步骤是为了给 ADC 提供合适的时钟频率。通过
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);
配置了 ADC 的常规通道转换顺序为 1,第一个转换,使用 ADC 的第 5 号通道,并设置了采样时间为 55.5 个时钟周期。
4.4 使能ADC
ADC配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。需要注意的是要先使能了ADC之后,才能开始进行自校准。
//ADC1使能
ADC_Cmd(ADC1, ENABLE);
2
4.5 ADC转换
// 初始化ADC 校准寄存器
ADC_ResetCalibration(ADC1);
// 等待校准寄存器初始化完成
while (ADC_GetResetCalibrationStatus(ADC1));
// ADC开始校准
ADC_StartCalibration(ADC1);
// 等待校准完成
while (ADC_GetCalibrationStatus(ADC1));
// 使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
2
3
4
5
6
7
8
9
10
11
12
讲解
ADC_ResetCalibration(ADC1);
:通过调用 ADC_ResetCalibration 函数,初始化了ADC的校准寄存器,为后续的校准做准备。while (ADC_GetResetCalibrationStatus(ADC1));
:在这里使用了一个循环来等待校准寄存器初始化完成。这行代码会一直循环执行,直到校准寄存器初始化完成。ADC_StartCalibration(ADC1);
:启动了ADC的校准过程,通过调用 ADC_StartCalibration 函数开始对ADC进行校准。while (ADC_GetCalibrationStatus(ADC1));
:类似于第二步,这里也使用了一个循环来等待校准过程完成。代码会一直循环执行,直到校准完成。ADC_SoftwareStartConvCmd(ADC1, ENABLE);
:最后,通过调用 ADC_SoftwareStartConvCmd 函数,使用软件触发来启动ADC的转换过程。
5. 数据采集函数编写
/**********************************************************
* 函 数 名 称:Get_ADC_Value
* 函 数 功 能:读取ADC值
* 传 入 参 数:无
* 函 数 返 回:测量到的值
* 作 者:LC
* 备 注:
**********************************************************/
uint32_t Get_ADC_Value(void)
{
uint32_t adc_value = 0;
// 等待转换结束
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
//读取采样值
adc_value = ADC_GetConversionValue(ADC1);
//返回采样值
return adc_value;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Get_ADC_Value函数讲解
- 首先,声明了一个名为 adc_value 的变量,并将其初始化为 0。
- 进入一个循环,等待转换结束。在这里使用了 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )); 来等待ADC转换结束的标志位。
- 转换结束后,通过 ADC_GetConversionValue(ADC1); 读取采样值,并将其存储在 adc_value 中。
- 最后,返回读取到的采样值 adc_value。
/**********************************************************
* 函 数 名 称:Get_Adc_Average
* 函 数 功 能:计算得出某个通道给定次数采样的平均值
* 传 入 参 数:times:采集次数
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
uint16_t Get_Adc_Average(uint8_t times)
{
uint32_t value = 0;
uint8_t t;
for(t=0;t<times;t++)
{
value += Get_ADC_Value();
delay_ms(5);
}
return value/times;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Get_Adc_Average函数讲解
- 定义一个uint32_t类型的变量temp_val用于累加每次采样的值。
- 使用for循环,调用Get_Adc函数times次,每次采样后等待5毫秒(delay_ms(5)),以避免采样速度过快导致的数据不准确。
- 将累加的总值除以采样次数times,得到平均值,并返回该值。
6. 实验现象
在main.c中调用**uint16_t Get_Adc_Average(uint8_t times)**函数,采集PA5引脚的电压,并通过串口输出。
int main(void)
{
board_init();
uart1_init(115200);
ADC_Config(); // ADC1通道5初始化
while(1)
{
printf("Value = %d\r\n\n",Get_Adc_Average(30));
delay_ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
现象:
- 在灯光充足的情况下,ADC转换的值大约为4000左右,转换为实际电压接近3.3V。
- 当用手挡住光敏电阻后,采集到的值大约在1000左右,转换为实际电压接近0.8V。
如果没有光敏电阻,则可以连接到3.3V或者GND进行测试。当接入3.3V时采集到的值接近4095;当接入GND时,采集到的值接近0。
⭐提示
关于这一章节的代码百度网盘下载,在立创·STM32F103C8T6开发板资料/第03章软件资料/代码例程/011ADC采集。