二十二、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); // 初始化GPIO
2
3
4
5
6
7
8
9
10
使能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
禁用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
禁用连续转换模式(
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
使用
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
定义一个
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
在灯光充足的情况下,ADC转换的值大约为4000左右,转换为实际电压接近3.3V。
当用手挡住光敏电阻后,采集到的值大约在60左右,转换为实际电压接近0V。
如果没有光敏电阻,则可以连接到3.3V或者GND进行测试。当接入3.3V时采集到的值接近4095;当接入GND时,采集到的值接近0。
这一章节的代码
在开发板介绍百度网盘链接中:立创·梁山派·天空星STM32F407VET6开发板资料/第03章软件资料/代码例程/011 ADC采集。