ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

STM32F103C8T6驱动光敏传感器+OLED 显示实验(ADC 采集光敏电阻完整教程) 一、前言本篇基于 STM32F103C8T6 最小系统板搭配光敏电阻模块、0.96 寸 I2C OLED 屏幕完成光照强度模拟量采集与实时显示。程序可输出 ADC 原始采样值、引脚电压、相对亮度百分比自动区分昏暗 / 正常 / 明亮三种光照状态加入 16 次均值滤波解决采样跳变问题非常适合单片机 ADC 模拟采集入门学习配套完整硬件接线、原理讲解、代码逻辑、调试标定方案新手可直接复刻。视频讲解STM32F103C8T6驱动光敏传感器OLED 显示实验ADC 采集光敏电阻完整教程https://www.bilibili.com/video/BV1xuMy6WENP/?share_sourcecopy_webvd_source91b967b41a17a5686913521545a8c3fb二、整体实现流程整体信号转换链路环境光照强度变化 → 光敏电阻阻值改变 → 模块分压输出 AO 模拟电压 → STM32 PB1ADC1 通道 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 引脚 可调电位器两者作用完全不同AO连续变化模拟电压光照强弱全程线性变化本实验使用该引脚可精准读取光照变化数值DO比较器数字输出只有高低电平两种状态仅能判断光照是否超过阈值电位器仅调节 DO 引脚电平翻转阈值不会改变 AO 模拟电压输出。对比 51 单片机常用 DO 数字读取方案仅能获取亮 / 暗两种状态本实验采集 AO 模拟量可量化光照强度教学与实操价值更高。四、光敏传感器核心工作原理模块核心器件为光敏电阻LDR 光导电阻材质为光敏半导体弱光 / 黑暗环境内部导电载流子数量少光敏电阻阻值极大强光环境光能激发大量载流子导电能力提升光敏电阻阻值大幅减小。光敏电阻仅能改变自身电阻无法直接输出电压因此模块内置分压电路把电阻变化转化为可采集的 AO 电压信号。标准分压电路原理图3.3V ── 光敏电阻 ──┬── AO输出引脚 │ 固定下拉电阻 │ GND分压计算公式 AO 电压 3.3V × 固定电阻 ÷光敏电阻阻值 固定电阻 电路特性环境越亮光敏电阻阻值越小AO 输出电压越低。注意部分厂家分压电路元器件调换会出现光照越强 AO 电压越高程序提供宏定义一键适配两种硬件接法无需修改复杂运算逻辑。五、STM32 ADC 采集原理基础参数使用通道PB1 映射 ADC1 通道 9ADC 分辨率12 位采样数值范围 0 ~ 4095输入电压量程0V ~ 3.3V电压换算公式电压 (mV) ADC 原始值 × 3300 ÷ 4095采样数值对应参考表PB1 输入电压对应 ADC 数值满量程占比0V00%0.6V74418%1.65V204850%2.31V286770%3.3V4095100%ADC 时钟与采样周期配置ADC 时钟分频系统 72MHz 主频 ÷ 6 12MHz低于 F103 ADC 最大 14MHz 限制符合规格采样周期设置 239.5 个 ADC 时钟周期光敏分压电路输出阻抗较高长采样时间让芯片内部采样电容充分充电降低测量误差读数更稳定。六、均值滤波算法解决数值跳动光敏模拟信号易受市电灯光频闪、电源纹波、电磁干扰影响单次采样数值会持续跳变肉眼观察 OLED 数字抖动严重。 本工程采用16 次采样均值滤波降噪处理执行逻辑循环启动 ADC 转换连续读取 16 次原始 ADC 值并累加16 次采集完成后总和除以 16 得到稳定平均 ADC 值使用平均值计算电压、亮度百分比刷新屏幕。补充均值滤波只能消除随机噪声无法过滤光源周期性频闪带来的规律性数值波动。工程核心文件功能划分文件路径函数名功能介绍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各字段释义ADC12 位模数转换原始数值区间 0~4095VoltPB1 引脚实测模拟电压Light相对亮度百分比仅用于对比光照变化趋势非标准照度勒克斯 (lx)State光照分级状态分三档DARK亮度百分比 30%昏暗NORMAL30% ≤ 亮度 70%正常光照BRIGHT亮度 ≥ 70%强光八、亮度方向适配与阈值配置8.1 硬件分压方向宏定义适配不同光敏模块分压电路接法相反电压随光照变化趋势不同工程通过宏一键切换计算逻辑宏定义存放于GuangMin.h#define GUANGMIN_BRIGHT_VALUE_INCREASES 0配置为 1ADC 数值越大亮度百分比越高光照越强电压越高配置为 0ADC 数值越小亮度百分比越高光照越强电压越低本实验模块默认配置。本模块换算公式反向模式 亮度百分比 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% → DARK30%≤亮度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 原始值。 * 返 回 值04095数值对应 PB1 上 03.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 原始值换算成 0100% 的亮度百分比。 * 说 明亮度增减方向由 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 的 PB1ADC1 通道 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 转换返回范围为 04095 的原始值。 */ u16 GuangMin_ReadRaw(void); /* 连续采样 sample_count 次并返回平均值用于减小显示抖动。 */ u16 GuangMin_ReadAverage(u8 sample_count); /* 把 ADC 原始值换算为毫伏返回范围为 03300mV。 */ u16 GuangMin_RawToMilliVolts(u16 raw); /* 把 ADC 原始值换算为亮度百分比返回范围为 0100%。 */ u8 GuangMin_RawToPercent(u16 raw); #endif十、实验标定与误差影响因素光敏电阻存在明显非线性特性与个体差异实验读数会受多重外界条件干扰光源类型太阳光、白炽灯、LED 灯光谱不同同等距离下采样数值差距大光源距离、照射角度光照入射角度、远近直接改变光敏电阻接收光能量遮挡干扰外壳、导线、手部阴影遮挡感光区域供电电压波动3.3V 电源纹波会同步影响分压 AO 与 ADC 换算精度器件非线性光敏电阻阻值和真实照度不成线性正比百分比仅为相对参考。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进