当前位置: 首页 > news >正文

STM32F103C8T6驱动光敏传感器+OLED 显示实验(ADC 采集光敏电阻完整教程)

一、前言

本篇基于 STM32F103C8T6 最小系统板,搭配光敏电阻模块、0.96 寸 I2C OLED 屏幕,完成光照强度模拟量采集与实时显示。程序可输出 ADC 原始采样值、引脚电压、相对亮度百分比,自动区分昏暗 / 正常 / 明亮三种光照状态,加入 16 次均值滤波解决采样跳变问题,非常适合单片机 ADC 模拟采集入门学习,配套完整硬件接线、原理讲解、代码逻辑、调试标定方案,新手可直接复刻。

视频讲解:STM32F103C8T6驱动光敏传感器+OLED 显示实验(ADC 采集光敏电阻完整教程)https://www.bilibili.com/video/BV1xuMy6WENP/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

二、整体实现流程

整体信号转换链路:环境光照强度变化 → 光敏电阻阻值改变 → 模块分压输出 AO 模拟电压 → STM32 PB1(ADC1 通道 9)采集 → 16 次均值滤波降噪 → 换算电压、相对亮度 → OLED 屏幕刷新所有数据 简单概括:光敏模块把光信号转为电压模拟量,STM32 通过 ADC 将电压转为数字信号,最终 OLED 可视化展示光照数据。

三、硬件接线说明

主控:STM32F103C8T6 外设:光敏传感器模块、I2C OLED 屏幕

3.1 引脚对应表

外设引脚STM32 引脚功能说明
光敏模块 AOPB1模拟信号输出,ADC1_IN9 采集通道
光敏模块 VCC3.3V推荐 3.3V 供电,防止 AO 电压超 ADC 量程
光敏模块 GNDGND主控与传感器共地,保证电平基准统一
OLED SCLPB8软件 I2C 时钟线
OLED SDAPB9软件 I2C 数据线
OLED VCC3.3V屏幕供电
OLED GNDGND屏幕接地

3.2 AO 电压保护注意事项

STM32F103 内置 ADC 输入最大电压等于芯片供电 3.3V,因此光敏模块优先使用 3.3V 供电。 若项目必须使用 5V 给光敏模块供电:

  • 先测量模块 AO 空载最高电压;
  • 若电压超过 3.3V,必须在 AO 与 PB1 之间增加电阻分压电路降压,避免烧毁 ADC 外设。

补充:OLED 上显示的 Volt 为 PB1 引脚实际分压后电压,加装分压电路后数值不等于光敏模块原始 AO 输出电压。

3.3 AO 模拟输出与 DO 数字输出区别

市面上多数光敏模块同时引出 AO、DO 引脚 + 可调电位器,两者作用完全不同:

  1. AO:连续变化模拟电压,光照强弱全程线性变化,本实验使用该引脚,可精准读取光照变化数值;
  2. DO:比较器数字输出,只有高低电平两种状态,仅能判断光照是否超过阈值;
  3. 电位器:仅调节 DO 引脚电平翻转阈值,不会改变 AO 模拟电压输出。

对比 51 单片机常用 DO 数字读取方案:仅能获取亮 / 暗两种状态;本实验采集 AO 模拟量,可量化光照强度,教学与实操价值更高。

四、光敏传感器核心工作原理

模块核心器件为光敏电阻(LDR 光导电阻),材质为光敏半导体:

  1. 弱光 / 黑暗环境:内部导电载流子数量少,光敏电阻阻值极大;
  2. 强光环境:光能激发大量载流子,导电能力提升,光敏电阻阻值大幅减小。

光敏电阻仅能改变自身电阻,无法直接输出电压,因此模块内置分压电路,把电阻变化转化为可采集的 AO 电压信号。

标准分压电路原理图

3.3V ── 光敏电阻 ──┬── AO输出引脚 │ 固定下拉电阻 │ GND

分压计算公式: AO 电压 = 3.3V × 固定电阻 ÷(光敏电阻阻值 + 固定电阻) 电路特性:环境越亮,光敏电阻阻值越小,AO 输出电压越低。

注意:部分厂家分压电路元器件调换,会出现光照越强 AO 电压越高,程序提供宏定义一键适配两种硬件接法,无需修改复杂运算逻辑。

五、STM32 ADC 采集原理

基础参数

  • 使用通道:PB1 映射 ADC1 通道 9
  • ADC 分辨率:12 位,采样数值范围 0 ~ 4095
  • 输入电压量程:0V ~ 3.3V
  • 电压换算公式:电压 (mV) = ADC 原始值 × 3300 ÷ 4095

采样数值对应参考表

PB1 输入电压对应 ADC 数值满量程占比
0V00%
0.6V74418%
1.65V204850%
2.31V286770%
3.3V4095100%

ADC 时钟与采样周期配置

  1. ADC 时钟分频:系统 72MHz 主频 ÷ 6 = 12MHz,低于 F103 ADC 最大 14MHz 限制,符合规格;
  2. 采样周期设置 239.5 个 ADC 时钟周期:光敏分压电路输出阻抗较高,长采样时间让芯片内部采样电容充分充电,降低测量误差,读数更稳定。

六、均值滤波算法(解决数值跳动)

光敏模拟信号易受市电灯光频闪、电源纹波、电磁干扰影响,单次采样数值会持续跳变,肉眼观察 OLED 数字抖动严重。 本工程采用16 次采样均值滤波降噪处理,执行逻辑:

  1. 循环启动 ADC 转换,连续读取 16 次原始 ADC 值并累加;
  2. 16 次采集完成后,总和除以 16 得到稳定平均 ADC 值;
  3. 使用平均值计算电压、亮度百分比,刷新屏幕。

补充:均值滤波只能消除随机噪声,无法过滤光源周期性频闪带来的规律性数值波动。

工程核心文件功能划分

文件路径函数名功能介绍
GuangMin.cGuangMin_Init()PB1 引脚、ADC1 初始化,内置 ADC 自校准
同上GuangMin_ReadRaw()单次读取 ADC 原始采样值
同上GuangMin_ReadAverage()16 次采样均值滤波函数
同上GuangMin_RawToMilliVolts()ADC 值转换为引脚实际电压
同上GuangMin_RawToPercent()ADC 数值换算相对亮度百分比
main.cGuangMin_ShowData()OLED 屏幕打印所有光照数据、明暗状态

七、OLED 屏幕显示格式与参数说明

程序上电后 OLED 固定排版输出,显示示例:

Light Sensor PB1 ADC:2048 Volt:1.65V Light:050% State:NORMAL

各字段释义:

  1. ADC:12 位模数转换原始数值,区间 0~4095;
  2. Volt:PB1 引脚实测模拟电压;
  3. Light:相对亮度百分比,仅用于对比光照变化趋势,非标准照度勒克斯 (lx)
  4. State:光照分级状态,分三档:
    • DARK:亮度百分比 < 30%(昏暗)
    • NORMAL:30% ≤ 亮度 < 70%(正常光照)
    • BRIGHT:亮度 ≥ 70%(强光)

八、亮度方向适配与阈值配置

8.1 硬件分压方向宏定义适配

不同光敏模块分压电路接法相反,电压随光照变化趋势不同,工程通过宏一键切换计算逻辑,宏定义存放于GuangMin.h

#define GUANGMIN_BRIGHT_VALUE_INCREASES 0
  • 配置为 1:ADC 数值越大,亮度百分比越高(光照越强电压越高);
  • 配置为 0:ADC 数值越小,亮度百分比越高(光照越强电压越低,本实验模块默认配置)。

本模块换算公式(反向模式): 亮度百分比 = 100 - ADC 原始值 × 100 ÷ 4095 实测效果:手遮挡光敏电阻 → ADC≈3971 → 亮度 3% → 屏幕显示 DARK;手电筒强光照射 → ADC 降低,亮度百分比上升,切换 NORMAL/BRIGHT。

8.2 明暗分级阈值

阈值宏定义在 main.c 中,可自由修改适配使用环境:

#define GUANGMIN_DARK_PERCENT 30 // 昏暗临界值 #define GUANGMIN_BRIGHT_PERCENT 70 // 强光临界值

判断逻辑: 亮度<30% → DARK;30%≤亮度<70% → NORMAL;亮度≥70% → BRIGHT。 教室环境光线偏暗 / 偏亮时,可通过遮挡、强光照射记录实际亮度数值,修改阈值切换状态区间。

九、实验代码

main.c代码

/* * Photoresistor teaching demonstration * Sensor AO -> PB1 (ADC1 channel 9) * OLED SCL -> PB8, SDA -> PB9 */ #include "delay.h" #include "sys.h" #include "oled.h" #include "GuangMin.h" #define GUANGMIN_SAMPLE_COUNT 16 #define GUANGMIN_REFRESH_MS 200 #define GUANGMIN_DARK_PERCENT 30 #define GUANGMIN_BRIGHT_PERCENT 70 static void GuangMin_ShowData(u16 raw, u16 millivolts, u8 percent); int main(void) { u16 raw; u16 millivolts; u8 percent; delay_init(); NVIC_Configuration(); OLED_Init(); OLED_ColorTurn(0); OLED_DisplayTurn(0); OLED_Clear(); OLED_ShowString(8, 0, (u8 *)"Light Sensor", 16); OLED_ShowString(24, 24, (u8 *)"AO -> PB1", 16); OLED_ShowString(8, 48, (u8 *)"Initializing...", 16); OLED_Refresh(); GuangMin_Init(); delay_ms(500); while(1) { raw = GuangMin_ReadAverage(GUANGMIN_SAMPLE_COUNT); millivolts = GuangMin_RawToMilliVolts(raw); percent = GuangMin_RawToPercent(raw); GuangMin_ShowData(raw, millivolts, percent); delay_ms(GUANGMIN_REFRESH_MS); } } static void GuangMin_ShowData(u16 raw, u16 millivolts, u8 percent) { OLED_Clear(); OLED_ShowString(16, 0, (u8 *)"Light Sensor PB1", 12); OLED_ShowString(0, 13, (u8 *)"ADC:", 12); OLED_ShowNum(30, 13, raw, 4, 12); OLED_ShowString(0, 26, (u8 *)"Volt:", 12); OLED_ShowNum(30, 26, millivolts / 1000U, 1, 12); OLED_ShowString(36, 26, (u8 *)".", 12); OLED_ShowNum(42, 26, (millivolts % 1000U) / 10U, 2, 12); OLED_ShowString(54, 26, (u8 *)"V", 12); OLED_ShowString(0, 39, (u8 *)"Light:", 12); OLED_ShowNum(36, 39, percent, 3, 12); OLED_ShowString(54, 39, (u8 *)"%", 12); OLED_ShowString(0, 52, (u8 *)"State:", 12); if(percent < GUANGMIN_DARK_PERCENT) { OLED_ShowString(42, 52, (u8 *)"DARK", 12); } else if(percent < GUANGMIN_BRIGHT_PERCENT) { OLED_ShowString(42, 52, (u8 *)"NORMAL", 12); } else { OLED_ShowString(42, 52, (u8 *)"BRIGHT", 12); } OLED_Refresh(); }

GuangMin.c代码

#include "GuangMin.h" #include "stm32f10x_adc.h" /* * 函数名称:GuangMin_Init * 函数功能:初始化光敏模块使用的 PB1 和 ADC1。 * 硬件连接:光敏模块 AO -> PB1 -> ADC1 通道 9。 */ void GuangMin_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; /* 开启 GPIOB 和 ADC1 的外设时钟。 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_ADC1, ENABLE); /* * 系统时钟为 72MHz 时,PCLK2 六分频后 ADC 时钟为 12MHz, * 不超过 STM32F103 数据手册规定的 14MHz 上限。 */ RCC_ADCCLKConfig(RCC_PCLK2_Div6); /* PB1 配置为模拟输入,模拟输入模式下不使用内部上拉或下拉。 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /* ADC1 使用独立、单通道、单次转换和软件触发模式。 */ ADC_DeInit(ADC1); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); /* 使能 ADC1,并依次完成复位校准和 ADC 校准。 */ ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1) == SET) { } ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1) == SET) { } } /* * 函数名称:GuangMin_ReadRaw * 函数功能:读取一次光敏模块的 ADC 原始值。 * 返 回 值:0~4095,数值对应 PB1 上 0~3.3V 的模拟电压。 */ u16 GuangMin_ReadRaw(void) { /* * 选择 ADC1 通道 9。光敏电阻分压电路的输出阻抗可能较高, * 因此使用 239.5 个 ADC 周期的长采样时间,使采样结果更稳定。 */ ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_239Cycles5); /* 启动软件转换,并等待转换完成标志置位。 */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET) { } return ADC_GetConversionValue(ADC1); } /* * 函数名称:GuangMin_ReadAverage * 函数功能:对多次 ADC 采样结果求平均,降低随机噪声和显示抖动。 * 输入参数:sample_count,采样次数;传入 0 时自动执行一次采样。 * 返 回 值:多次采样的平均 ADC 原始值。 */ u16 GuangMin_ReadAverage(u8 sample_count) { u8 i; /* 使用 32 位变量累加,避免多次采样时发生 16 位数据溢出。 */ u32 sum = 0; if(sample_count == 0) { return GuangMin_ReadRaw(); } for(i = 0; i < sample_count; i++) { sum += GuangMin_ReadRaw(); } return (u16)(sum / sample_count); } /* * 函数名称:GuangMin_RawToMilliVolts * 函数功能:把 12 位 ADC 原始值换算成毫伏值。 * 计算公式:电压(mV) = ADC原始值 × 3300 ÷ 4095。 */ u16 GuangMin_RawToMilliVolts(u16 raw) { /* 对异常输入进行限幅,保证换算结果不超过参考电压。 */ if(raw > GUANGMIN_ADC_MAX_VALUE) { raw = GUANGMIN_ADC_MAX_VALUE; } /* 除法前加上除数的一半,实现整数计算的四舍五入。 */ return (u16)(((u32)raw * GUANGMIN_ADC_REFERENCE_MV + (GUANGMIN_ADC_MAX_VALUE / 2U)) / GUANGMIN_ADC_MAX_VALUE); } /* * 函数名称:GuangMin_RawToPercent * 函数功能:把 ADC 原始值换算成 0~100% 的亮度百分比。 * 说 明:亮度增减方向由 GUANGMIN_BRIGHT_VALUE_INCREASES 宏决定。 */ u8 GuangMin_RawToPercent(u16 raw) { u32 percent; /* 对异常输入进行限幅,避免计算结果超过 100%。 */ if(raw > GUANGMIN_ADC_MAX_VALUE) { raw = GUANGMIN_ADC_MAX_VALUE; } /* 使用整数运算计算百分比,并通过加上除数的一半完成四舍五入。 */ percent = ((u32)raw * 100U + (GUANGMIN_ADC_MAX_VALUE / 2U)) / GUANGMIN_ADC_MAX_VALUE; #if GUANGMIN_BRIGHT_VALUE_INCREASES /* AO 电压随环境亮度增加时,直接返回计算出的百分比。 */ return (u8)percent; #else /* AO 电压随环境亮度降低时,对百分比进行反向换算。 */ return (u8)(100U - percent); #endif }

GuangMin.h代码

#ifndef __GUANGMIN_H #define __GUANGMIN_H #include "sys.h" /* * 光敏模块驱动头文件 * * 硬件连接: * 光敏模块 AO -> STM32F103C8T6 的 PB1(ADC1 通道 9) * 光敏模块 VCC -> 3.3V * 光敏模块 GND -> GND */ /* STM32F103 内部 ADC 为 12 位,参考电压按 3.3V 计算。 */ #define GUANGMIN_ADC_MAX_VALUE 4095U #define GUANGMIN_ADC_REFERENCE_MV 3300U /* * 亮度方向配置: * 1:环境越亮,AO 电压和 ADC 数值越大; * 0:环境越亮,AO 电压和 ADC 数值越小。 * * 本工程实测模块在遮挡时 AO 约为 3.2V,说明其输出电压随环境 * 变暗而升高,因此这里设置为 0,对 ADC 百分比进行反向换算。 */ #define GUANGMIN_BRIGHT_VALUE_INCREASES 0 /* 初始化 PB1 和 ADC1。 */ void GuangMin_Init(void); /* 进行一次 ADC 转换,返回范围为 0~4095 的原始值。 */ u16 GuangMin_ReadRaw(void); /* 连续采样 sample_count 次并返回平均值,用于减小显示抖动。 */ u16 GuangMin_ReadAverage(u8 sample_count); /* 把 ADC 原始值换算为毫伏,返回范围为 0~3300mV。 */ u16 GuangMin_RawToMilliVolts(u16 raw); /* 把 ADC 原始值换算为亮度百分比,返回范围为 0~100%。 */ u8 GuangMin_RawToPercent(u16 raw); #endif

十、实验标定与误差影响因素

光敏电阻存在明显非线性特性与个体差异,实验读数会受多重外界条件干扰:

  1. 光源类型:太阳光、白炽灯、LED 灯光谱不同,同等距离下采样数值差距大;
  2. 光源距离、照射角度:光照入射角度、远近直接改变光敏电阻接收光能量;
  3. 遮挡干扰:外壳、导线、手部阴影遮挡感光区域;
  4. 供电电压波动:3.3V 电源纹波会同步影响分压 AO 与 ADC 换算精度;
  5. 器件非线性:光敏电阻阻值和真实照度不成线性正比,百分比仅为相对参考。
http://www.jsqmd.com/news/1337018/

相关文章:

  • 数字电路核心技能:T触发器与SR、JK、D触发器的相互转换原理与工程实践
  • 第 09 篇 快照读 vs 当前读:为什么 RR 下依然可能出现幻读
  • 2026 年现阶段,叶城有实力的锌钢仿竹护栏厂家哪个好,小区里那阵再也没响起的“竹子”敲门声,原来藏着这样的秘密-振昂丝网 - 企业推荐官【认证】
  • GaussDB账户锁定策略配置:从原理到生产环境实战避坑指南
  • WiFi同频干扰诊断与优化:从信道原理到实战解决网络卡顿
  • Python数据分析实战:构建足球明星生涯数据平台
  • I2C总线负载过重?五大场景解析缓冲器选型与实战部署
  • MySQL索引类型详解:B+Tree、Hash等核心原理与实战场景
  • STM32物联网项目实战:智能风扇的硬件设计、固件开发与云平台接入全解析
  • A*-第K短路
  • 蜂助手46亿元算力大单背后:30亿元采购接近蜂助手总资产八成
  • VSCode远程连接阿里云DSW:ProxyClient模式原理与实战指南
  • C++网络编程核心:Socket API、并发模型与高性能服务器实践
  • Linux系统挂载WebDAV远程存储:davfs2配置与实战指南
  • 滨海新区Q235C钢板实力公司如何选更稳妥 - 品牌优推
  • 2026 年新消息:荆门可靠的面包凹槽管定做厂家哪家好,你从没留意过的烘焙小物,竟能让面包成型快10倍? - 企业推荐官-
  • 从苹果300亿美元诉讼看人脸识别开发:BIPA合规与设备端隐私实践
  • 企业微信发Gmail失败?SPF、DKIM、DMARC配置全解析与实战排查
  • 多目标优化实战:无人机配送如何同时打赢“快“与“省“两场仗
  • 揭秘鄂尔多斯网站建设背后的真相与避坑指南,打造真正适合本地企业的数字化门面
  • 鸿蒙PC开发:自适应布局实战与优化技巧
  • 钉钉机器人Markdown消息推送实战:从Webhook到企业级应用
  • 【华为新品发布】尊界MPV+全场景新品集体登场
  • 已授权老客唤醒外呼总封号|3坐席1200包月AXB合规方案
  • 从用户到创造者:技术人如何逆向工程黑盒系统并掌握协议设计
  • 母线槽导体选材:高纯铜、铜排厚度与全长镀锡对运行温升的影响
  • Qt Windows应用管理员权限启动:UAC机制、清单配置与QMake实战
  • Oracle 19C静默安装实战:CentOS 7环境部署与自动化运维指南
  • 基于大模型的Idea提炼智能体:架构设计与工程实践
  • 微信消息防撤回技术全解析:从原理到安卓/iOS/电脑端实现方案