1. 光敏电阻与ADC采集的基础原理
光敏电阻是一种特殊的光电元件,它的电阻值会随着光照强度的变化而改变。当光线照射时,内部电子获得能量,导电性增强,电阻减小;光线变弱时,电阻增大。这种特性使得它非常适合用于光照检测场景。
在实际应用中,我们通常将光敏电阻与一个固定电阻串联,形成一个分压电路。当光照变化时,光敏电阻的阻值变化会导致分压点的电压发生变化。STM32的ADC模块就是通过采集这个电压值来间接测量光照强度的。
ADC(模数转换器)的作用就是将连续的模拟电压信号转换为离散的数字值。STM32的ADC是12位精度的,这意味着它可以将0-3.3V的电压范围分成4096个等级(2^12=4096)。每个等级对应一个数字值,从0到4095。
2. 硬件电路设计与连接
光敏电阻的典型连接方式是这样的:将光敏电阻的一端连接到3.3V,另一端连接到一个固定电阻(比如10kΩ),固定电阻的另一端接地。两个电阻的连接点就是我们的测量点,这个点要连接到STM32的ADC输入引脚。
在选择固定电阻时,有个小技巧:最好选择与光敏电阻在常用光照条件下的阻值相近的电阻。比如在室内光照下,光敏电阻的阻值大约在10kΩ左右,那么我们就选择10kΩ的固定电阻,这样能获得最佳的测量灵敏度。
在实际布线时,有几点需要特别注意:ADC引脚的走线要尽量短,远离高频信号线,避免电磁干扰。可以在测量点并联一个0.1μF的电容到地,起到滤波作用,使测量值更加稳定。
STM32的ADC引脚需要配置为模拟输入模式,这个模式下IO口的数字功能被禁用,减少了数字信号对模拟测量的干扰。以STM32F103系列为例,ADC输入引脚分布在GPIOA、GPIOB和GPIOC上,具体哪个引脚支持ADC功能需要查阅芯片的数据手册。
3. STM32 ADC模块的寄存器配置
配置ADC模块时,我们需要直接操作相关的寄存器,这样可以更精确地控制ADC的工作方式。首先是开启时钟,ADC模块挂载在APB2总线上,需要使能ADC和GPIO的时钟:
RCC->APB2ENR |= 1 << 9; // 使能ADC1时钟
RCC->APB2ENR |= 1 << 2; // 使能GPIOA时钟
接下来配置ADC的时钟分频。STM32的ADC最大工作频率不能超过14MHz,而APB2的时钟通常是72MHz,所以我们需要进行6分频:
RCC->CFGR &= ~(3 << 14); // 清除原有的分频设置
RCC->CFGR |= 2 << 14; // 6分频,72MHz/6=12MHz
然后配置GPIO引脚为模拟输入模式。以PA5引脚为例:
GPIOA->CRL &= ~(0x0F << 20); // 清除PA5的模式配置
GPIOA->CRL |= 0x00 << 20; // 模拟输入模式
现在开始配置ADC的工作模式。ADC有两个控制寄存器:CR1和CR2。我们先配置CR1:
ADC1->CR1 &= ~(0x0F << 16); // 独立模式
ADC1->CR1 &= ~(1 << 8); // 非扫描模式
对于单通道采集,我们不需要使用扫描模式。接着配置CR2寄存器:
ADC1->CR2 |= 1 << 20; // 使用外部触发
ADC1->CR2 |= 0x07 << 17; // 软件触发方式
ADC1->CR2 &= ~(1 << 11); // 数据右对齐
ADC1->CR2 &= ~(1 << 1); // 单次转换模式
ADC1->CR2 |= 1 << 0; // 使能ADC
外部触发和软件触发的组合使得我们可以通过软件命令启动转换,这样更加灵活可控。
4. 通道配置与采样时间设置
STM32的ADC有多个输入通道,我们需要指定使用哪个通道进行采集。每个通道都可以独立配置采样时间,采样时间越长,转换结果越准确,但转换速度会变慢。
首先配置规则序列寄存器。因为我们只使用一个通道,所以将序列长度设置为1:
ADC1->SQR1 &= ~(0x0F << 20); // 只有1个转换在序列中
然后指定具体的通道。PA5对应的是通道5:
ADC1->SQR3 &= ~0x1F; // 清除通道设置
ADC1->SQR3 |= 5 << 0; // 通道5作为第一个转换
接下来设置采样时间。STM32的采样时间可以设置为1.5、7.5、13.5、28.5、41.5、55.5或239.5个ADC时钟周期。对于光敏电阻这种变化相对缓慢的信号,我们可以选择较长的采样时间来提高精度:
ADC1->SMPR2 |= 0x07 << 15; // 通道5使用239.5周期采样
较长的采样时间允许ADC模块更充分地采集信号,减少噪声的影响,特别是在使用较高阻抗的传感器时效果更加明显。
5. ADC校准与精度优化
ADC模块在使用前需要进行校准,这个步骤很重要但经常被忽略。校准可以消除ADC内部的偏移误差,提高测量精度。
首先进行复位校准:
ADC1->CR2 |= 1 << 3; // 启动复位校准
while (ADC1->CR2 & (1 << 3)); // 等待校准完成
复位校准完成后,进行正式校准:
ADC1->CR2 |= 1 << 2; // 启动AD校准
while (ADC1->CR2 & (1 << 2)); // 等待校准完成
校准过程中,ADC内部会自动进行一系列测量和计算,并调整内部的校准寄存器。等待校准完成标志位清零后,说明校准已经成功完成。
除了硬件校准,我们还可以通过软件方式进一步提高测量精度。多次采样取平均值是一个有效的方法:
uint32_t sum = 0;
for (int i = 0; i < 16; i++) {
// 启动转换并读取值
sum += adc_value;
}
uint16_t average = sum >> 4; // 除以16
这种过采样技术可以有效减少随机噪声的影响,使测量结果更加稳定。在实际应用中,可以根据需要调整采样次数,在精度和速度之间取得平衡。
6. 数据采集与转换处理
数据采集的核心是启动转换并等待转换完成。我们通过设置CR2寄存器的第22位来启动转换:
ADC1->CR2 |= 1 << 22; // 启动转换
然后等待转换完成标志位置位。转换完成后,状态寄存器SR的第1位(EOC位)会被置1:
while (!(ADC1->SR & (1 << 1))); // 等待转换完成
读取转换结果时,直接读取数据寄存器DR的值:
uint16_t adc_value = ADC1->DR;
这个值范围是0-4095,对应0-3.3V的电压。如果需要将其转换为实际电压值,可以使用下面的公式:
float voltage = (float)adc_value * 3.3f / 4095.0f;
对于光照强度,我们通常更关心相对值而不是绝对值。可以根据ADC值划分几个光照等级:
if (adc_value < 1000) {
// 强光环境
} else if (adc_value < 2000) {
// 中等光照
} else {
// 弱光环境
}
具体的阈值需要根据实际的光敏电阻特性和应用环境来调整。最好在实际使用环境中进行测试和校准。
7. 串口通信与实时数据输出
将采集到的数据通过串口输出,可以方便我们实时监控光照变化。首先需要配置串口,以USART1为例:
// 使能USART1时钟
RCC->APB2ENR |= 1 << 14;
// 配置TX引脚(PA9)为复用推挽输出
GPIOA->CRH &= ~(0x0F << 4);
GPIOA->CRH |= 0x0B << 4;
// 设置波特率(以115200为例)
USART1->BRR = 72000000 / 115200;
// 使能发送器
USART1->CR1 |= 1 << 3;
// 使能USART1
USART1->CR1 |= 1 << 13;
为了方便使用printf函数,需要重写fputc函数:
int fputc(int ch, FILE *f) {
while (!(USART1->SR & (1 << 7))); // 等待发送缓冲区空
USART1->DR = ch;
return ch;
}
在主循环中,我们可以定期读取并发送光照数据:
while (1) {
if (timer_elapsed()) { // 每隔一段时间读取一次
uint16_t light = read_light_sensor();
printf("光照值: %d\n", light);
reset_timer();
}
}
为了避免串口输出过于频繁,可以设置适当的输出间隔,比如每500ms输出一次数据。这样既能够实时监控,又不会让输出信息过于密集。
8. 实际应用中的注意事项
在实际项目中,可能会遇到各种问题。比如电源噪声会影响ADC精度,建议在VDDA和VSSA引脚附近放置去耦电容,通常用10μF和0.1μF电容并联。
环境温度变化也会影响测量精度。光敏电阻本身有温度系数,ADC的基准电压也会随温度变化。如果要求较高的精度,可能需要考虑温度补偿。
电磁兼容性也是需要注意的问题。长导线容易引入干扰,建议使用屏蔽线或者双绞线连接传感器。在软件层面,可以加入数字滤波算法:
#define FILTER_DEPTH 8
uint16_t filter_buffer[FILTER_DEPTH];
uint8_t filter_index = 0;
uint16_t filtered_read() {
filter_buffer[filter_index] = read_light_sensor();
filter_index = (filter_index + 1) % FILTER_DEPTH;
uint32_t sum = 0;
for (int i = 0; i < FILTER_DEPTH; i++) {
sum += filter_buffer[i];
}
return sum / FILTER_DEPTH;
}
这种移动平均滤波简单有效,能够平滑掉突然的干扰脉冲。滤波深度可以根据实际需要调整,深度越大滤波效果越好,但响应速度会变慢。
功耗也是需要考虑的因素。如果使用电池供电,可以适当降低采样频率,或者在不需要测量时进入低功耗模式。STM32的ADC模块在不用时可以关闭时钟以节省功耗。
最后要注意的是量程范围。确保光敏电阻的分压电压不会超过ADC的输入范围(0-3.3V),否则可能损坏ADC模块。可以在输入端加入保护电路,如串联电阻和钳位二极管。



被折叠的 条评论
为什么被折叠?



