STM32光敏模块实战:从ADC采样到环境光强监测

张开发
2026/4/18 19:04:04 15 分钟阅读

分享文章

STM32光敏模块实战:从ADC采样到环境光强监测
1. 光敏模块与STM32的硬件连接光敏模块是检测环境光强度的常用传感器它通过光敏电阻将光照变化转化为电信号。我手头用的是四线制模块包含VCC、GND、DO数字输出和AO模拟输出四个引脚。实际项目中我们主要使用AO引脚连接STM32的ADC通道因为模拟量能反映光照的连续变化。接线时要注意三点一是VCC接3.3V或5V需与模块规格匹配我用的是3.3V供电二是AO引脚必须连接到支持ADC功能的GPIO我选择的是PA1STM32F103C8T6的ADC1通道1三是GND一定要共地。DO引脚可以暂时悬空它是用于阈值报警的开关量输出后续扩展功能时再用。这里有个坑我踩过早期测试时发现数据跳动严重后来发现是电源干扰。建议在VCC和GND之间加个0.1μF的滤波电容实测能有效稳定信号。模块的AO输出电压范围是0-VCC对应光照从暗到亮的变化。使用前最好用万用表测量AO电压范围确认模块工作正常。2. CubeMX的ADC配置详解打开CubeMX新建工程时芯片型号要选对。我用的是STM32F103C8T6属于Medium-density系列。配置ADC分几个关键步骤2.1 基础外设配置先在SYS里启用Serial Wire调试接口否则后续无法烧录程序。时钟配置选择外部晶振HSE主频设为72MHz。ADC时钟不能超过14MHz我选择6分频得到12MHz时钟。2.2 ADC参数设置在Analog标签下启用ADC1将PA1配置为ADC1_IN1。关键参数如下Resolution12位4096级精度Data Alignment右对齐Scan Conversion Mode禁用单通道不需要扫描Continuous Conversion Mode禁用采用手动触发End Of Conversion SelectionEOC标志在每次转换后置位特别要注意Rank设置里的Sampling Time采样时间。光敏模块输出阻抗较高建议设为239.5周期约20μs这样ADC电容能充分充电提高采样精度。2.3 串口辅助调试配置为方便观察数据我额外配置了USART1PA9-TX,PA10-RX用于打印电压值。波特率设为115200数据位8无校验位。记得在NVIC中使能串口中断虽然本例用轮询方式发送数据。3. HAL库的ADC采样代码实现CubeMX生成代码后需要添加核心业务逻辑。先包含标准输入输出库用于格式化字符串#include stdio.h定义全局变量存储采样值uint16_t adc_value 0; // 原始ADC值 uint16_t voltage_mv 0; // 换算后的电压值(mV) char msg_buf[64]; // 串口消息缓冲区电压转换函数是关键void read_light_sensor(void) { HAL_ADC_Start(hadc1); // 启动ADC转换 if(HAL_ADC_PollForConversion(hadc1, 10) HAL_OK) { adc_value HAL_ADC_GetValue(hadc1); voltage_mv adc_value * 3300 / 4095; // 3.3V参考电压 } HAL_ADC_Stop(hadc1); }串口打印函数这样实现void print_sensor_data(void) { sprintf(msg_buf, ADC: %4d | Voltage: %4dmV\r\n, adc_value, voltage_mv); HAL_UART_Transmit(huart1, (uint8_t*)msg_buf, strlen(msg_buf), HAL_MAX_DELAY); }主循环中每500ms采样一次while (1) { read_light_sensor(); print_sensor_data(); HAL_Delay(500); }4. 从电压到光照强度的转换原始电压值需要经过两步处理才有实际意义4.1 校准与滤波实测发现ADC值会有±5左右的波动。我采用滑动平均滤波存储最近10次采样值求平均#define SAMPLE_SIZE 10 uint16_t adc_samples[SAMPLE_SIZE]; uint8_t sample_index 0; void update_sample_buffer(uint16_t new_sample) { adc_samples[sample_index] new_sample; if(sample_index SAMPLE_SIZE) sample_index 0; } uint16_t get_average_value(void) { uint32_t sum 0; for(int i0; iSAMPLE_SIZE; i) { sum adc_samples[i]; } return sum / SAMPLE_SIZE; }4.2 光照强度换算不同光敏电阻的响应曲线不同。以GL5516为例其电阻-照度关系近似满足R 500*(100/Lux)^0.9 // 单位kΩ结合模块的分压电路设计推导出电压-照度公式float convert_to_lux(uint16_t mv) { float v mv / 1000.0; float rldr (3.3 - v) * 10.0 / v; // 10kΩ分压电阻 return 100 * pow(500.0/rldr, 1/0.9); }实际应用时建议分段线性化处理。我在不同光照条件下实测得到校准表电压(mV)近似照度(lux)适用场景0-1000-10全黑暗环境100-50010-100夜间室内500-1500100-1000普通室内照明1500-25001000-5000明亮办公室2500-33005000阳光直射5. 实际应用中的优化技巧5.1 动态采样频率根据应用场景调整采样率可以降低功耗。比如智能灯控系统在稳定光照时可以延长采样间隔uint32_t sample_interval 500; // 默认500ms void adjust_sample_rate(void) { static uint16_t last_value 0; uint16_t change abs(adc_value - last_value); if(change 50) sample_interval 100; // 变化剧烈时加速采样 else if(change 20) sample_interval 250; else sample_interval 1000; // 稳定时降低频率 last_value adc_value; }5.2 阈值触发功能除了模拟量采集还可以利用DO引脚实现快速响应。比如检测突然开灯事件void check_light_event(void) { static uint8_t last_state 1; uint8_t curr_state HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // DO接PA0 if(last_state 1 curr_state 0) { sprintf(msg_buf, Light ON detected!\r\n); HAL_UART_Transmit(huart1, (uint8_t*)msg_buf, strlen(msg_buf), HAL_MAX_DELAY); } last_state curr_state; }5.3 低功耗优化对于电池供电设备可以关闭ADC和串口进入停机模式通过外部中断唤醒void enter_low_power_mode(void) { HAL_ADC_Stop(hadc1); HAL_UART_DeInit(huart1); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后需要重新初始化外设 SystemClock_Config(); MX_ADC1_Init(); MX_USART1_UART_Init(); }6. 常见问题排查调试时遇到过ADC值始终为0的情况检查发现是GPIO模式未正确配置为模拟输入。解决方法是在CubeMX中确认PA1的Mode设置为Analog Mode。另一个典型问题是电压值跳变严重。除了硬件滤波软件上可以适当增加采样时间239.5周期在ADC初始化后添加1ms延时丢弃前几次采样值串口打印乱码时请检查波特率是否匹配双方均为115200电平是否匹配3.3V TTL接线是否交叉TX接RXRX接TX最后分享一个实用技巧用Excel绘制电压-照度曲线可以直观验证转换公式的准确性。将模块放在已知照度的环境中如用手机测光APP参考记录ADC值并拟合曲线。

更多文章