二十二、ADC光敏电阻实现
模块来源
规格参数
工作电压:3.3-5V
工作电流:1MA
模块尺寸:31.1475 x 14.097mm
输出方式: DO接口为数字量输出 AO接口为模拟量输出
读取方式:ADC
管脚数量:4 Pin(2.54mm间距排针)
配置流程
接线图:
配置时钟
本案例使用ADC0进行操作,需要开启ADC0的时钟。 不过需要注意的是STM32F4的用户手册写出,ADC的最大时钟频率为40MHz。V因此我们使用ADC时,需要将挂载ADC的时钟总线频率分频到40MHz以下。
本案例使用的是APB2作为ADC时钟来源,其最大时钟频率为84MHz,我们需要将其分频到40MHz以下。而它分频的参数有2分频、4分频、6分频、8分频等等。如选择2分频,则最终的ADC时钟频率为 84 / 2 = 42MHz,这明显没有达到我们的40MHz以下的要求。选择4分频,最终的ADC时钟频率为:
4分频满足了我们的要求。
配置引脚
STM32F407VET6一共有16个外部ADC通道,我们将光敏电阻模块的AO引脚连接在PA5引脚上,查找数据手册的引脚定义可知,PA5的附加功能有ADC1的通道5,ADC2的通道5。
因为我们之前配置时钟的章节开启的是ADC0的时钟,所以这里选择PA5的附加功能,ADC1的通道5进行操作。要操作 GPIO 引脚,必不可少的就是对 GPIO 进行配置,包括开启时钟、配置模式、配置输出、设置功能等,还是这一系列的操作。
GPIO配置:
GPIO_InitTypeDef GPIO_InitStructure; // 配置结构体
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE); // 使能时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIO2
3
4
5
6
7
8
9
10
11
使能GPIOA的时钟(
RCC_AHB1Periph_GPIOA)。设置引脚模式为模拟输入模式(
GPIO_Mode_AN)。设置引脚速度为100MHz(
GPIO_Speed_100MHz)。设置引脚为无上拉下拉(
GPIO_PuPd_NOPULL)。
以上代码需要注意的是使用ADC功能时,引脚必须设置为模拟输入模式,如设置为上拉或下拉模式,则采集的电压是不准确的。
配置ADC
ADC的模式有4种,分别是单次、连续、扫描和间断转换模式,这里我们配置为扫描模式,这样就可以根据自己的需要,配置采集通道进行依次扫描采集对应引脚的模拟信号。也可以根据需要配置为连续模式和间断模式。
ADC公共配置:
ADC_CommonInitTypeDef ADC_CommonInitStruct;
ADC_DeInit();//ADC复位
ADC_CommonInitStruct.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled;
ADC_CommonInitStruct.ADC_Mode=ADC_Mode_Independent;
ADC_CommonInitStruct.ADC_Prescaler=ADC_Prescaler_Div4;
ADC_CommonInitStruct.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStruct);2
3
4
5
6
7
8
9
10
11
禁用DMA访问模式(
ADC_DMAAccessMode_Disabled)。设置ADC为独立模式(
ADC_Mode_Independent)。设置ADC的预分频器,这里是4分频(
ADC_Prescaler_Div4),用于控制ADC的时钟。设置两次采样之间的延迟为5个ADC时钟周期(
ADC_TwoSamplingDelay_5Cycles)。
ADC特定配置:
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_None;
ADC_InitStruct.ADC_NbrOfConversion=1;
ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStruct);2
3
4
5
6
7
8
9
10
11
12
13
禁用连续转换模式(
ADC_ContinuousConvMode),即ADC完成一次转换后停止。设置数据对齐方式为右对齐(
ADC_DataAlign_Right)。禁用外部触发转换(
ADC_ExternalTrigConvEdge_None),即使用软件触发ADC转换。设置转换数量为1(
ADC_NbrOfConversion),因为不使用扫描模式。设置ADC分辨率为12位(
ADC_Resolution_12b)。禁用扫描模式(
ADC_ScanConvMode),即一次只转换一个通道。
使能ADC
ADC配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。需要注意的是要先使能了ADC之后,才能开始进行自校准。
//ADC1使能
ADC_Cmd(ADC1, ENABLE);2
采集转换
采集数据函数:
/**********************************************************
* 函 数 名 称: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_RegularChannelConfig( PORT_ADC, ADC_CHANNEL_x, 1, ADC_SampleTime_480Cycles );
//开始软件转换
ADC_SoftwareStartConv(PORT_ADC);
// 等待转换结束
while(!ADC_GetFlagStatus(PORT_ADC, ADC_FLAG_EOC ));
//读取采样值
adc_value = ADC_GetConversionValue(BSP_ADC);
//返回采样值
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
使用
ADC_RegularChannelConfig函数配置ADC1的规则通道,包括指定的通道CHx,排队的顺序1(因为这里不使用扫描模式,所以顺序为1),以及采样时间ADC_SampleTime_480Cycles。这个采样时间较长,有助于提高采样的准确性,特别是在输入阻抗较高的情况下。通过
ADC_SoftwareStartConv函数启动ADC1的软件转换。使用
while循环等待转换结束,这通过检查ADC_FLAG_EOC(转换结束标志)实现。最后,通过
ADC_GetConversionValue函数读取转换结果并返回。
数据转换函数:
/**********************************************************
* 函 数 名 称:Get_Adc_Average
* 函 数 功 能:计算得出某个通道给定次数采样的平均值
* 传 入 参 数:CHx :通道数字
* times :采集次数
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
uint16_t Get_Adc_Average(uint8_t CHx,uint8_t times)//
{
uint32_t value = 0;
uint8_t t;
for(t=0;t<times;t++)
{
value += Get_Adc(CHx);
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
21
22
23
定义一个
uint32_t类型的变量temp_val用于累加每次采样的值。使用
for循环,调用Get_Adc函数times次,每次采样后等待5毫秒(delay_ms(5)),以避免采样速度过快导致的数据不准确。将累加的总值除以采样次数
times,得到平均值,并返回该值。
实验对象
在main.c中调用uint16_t Get_Adc_Average(uint8_t CHx,uint8_t times)函数,采集PA5引脚的电压,并通过串口输出。
int main(void)
{
board_init();
uart1_init(115200U);
Adc_Init();
while(1)
{
uint16_t value = Get_Adc_Average(ADC_Channel_5,20);
printf("value = %d\r\n", value);
delay_ms(1000);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在灯光充足的情况下,ADC转换的值大约为4000左右,转换为实际电压接近3.3V。
当用手挡住光敏电阻后,采集到的值大约在60左右,转换为实际电压接近0V。
如果没有光敏电阻,则可以连接到3.3V或者GND进行测试。当接入3.3V时采集到的值接近4095;当接入GND时,采集到的值接近0。
这一章节的代码
在开发板介绍百度网盘链接中:立创·梁山派·天空星STM32F407VET6开发板资料/第03章软件资料/代码例程/011 ADC采集。