動(dòng)光敏傳感器+OLED 顯示實(shí)驗(yàn)(ADC 采集光敏電阻完整教程))
一、前言本篇基于 STM32F103C8T6 最小系統(tǒng)板搭配光敏電阻模塊、0.96 寸 I2C OLED 屏幕完成光照強(qiáng)度模擬量采集與實(shí)時(shí)顯示。程序可輸出 ADC 原始采樣值、引腳電壓、相對(duì)亮度百分比自動(dòng)區(qū)分昏暗 / 正常 / 明亮三種光照狀態(tài)加入 16 次均值濾波解決采樣跳變問(wèn)題非常適合單片機(jī) ADC 模擬采集入門學(xué)習(xí)配套完整硬件接線、原理講解、代碼邏輯、調(diào)試標(biāo)定方案新手可直接復(fù)刻。視頻講解STM32F103C8T6驅(qū)動(dòng)光敏傳感器OLED 顯示實(shí)驗(yàn)ADC 采集光敏電阻完整教程https://www.bilibili.com/video/BV1xuMy6WENP/?share_sourcecopy_webvd_source91b967b41a17a5686913521545a8c3fb二、整體實(shí)現(xiàn)流程整體信號(hào)轉(zhuǎn)換鏈路環(huán)境光照強(qiáng)度變化 → 光敏電阻阻值改變 → 模塊分壓輸出 AO 模擬電壓 → STM32 PB1ADC1 通道 9采集 → 16 次均值濾波降噪 → 換算電壓、相對(duì)亮度 → OLED 屏幕刷新所有數(shù)據(jù) 簡(jiǎn)單概括光敏模塊把光信號(hào)轉(zhuǎn)為電壓模擬量STM32 通過(guò) ADC 將電壓轉(zhuǎn)為數(shù)字信號(hào)最終 OLED 可視化展示光照數(shù)據(jù)。三、硬件接線說(shuō)明主控STM32F103C8T6 外設(shè)光敏傳感器模塊、I2C OLED 屏幕3.1 引腳對(duì)應(yīng)表外設(shè)引腳STM32 引腳功能說(shuō)明光敏模塊 AOPB1模擬信號(hào)輸出ADC1_IN9 采集通道光敏模塊 VCC3.3V推薦 3.3V 供電防止 AO 電壓超 ADC 量程光敏模塊 GNDGND主控與傳感器共地保證電平基準(zhǔn)統(tǒng)一OLED SCLPB8軟件 I2C 時(shí)鐘線OLED SDAPB9軟件 I2C 數(shù)據(jù)線OLED VCC3.3V屏幕供電OLED GNDGND屏幕接地3.2 AO 電壓保護(hù)注意事項(xiàng)STM32F103 內(nèi)置 ADC 輸入最大電壓等于芯片供電 3.3V因此光敏模塊優(yōu)先使用 3.3V 供電。 若項(xiàng)目必須使用 5V 給光敏模塊供電先測(cè)量模塊 AO 空載最高電壓若電壓超過(guò) 3.3V必須在 AO 與 PB1 之間增加電阻分壓電路降壓避免燒毀 ADC 外設(shè)。補(bǔ)充OLED 上顯示的 Volt 為 PB1 引腳實(shí)際分壓后電壓加裝分壓電路后數(shù)值不等于光敏模塊原始 AO 輸出電壓。3.3 AO 模擬輸出與 DO 數(shù)字輸出區(qū)別市面上多數(shù)光敏模塊同時(shí)引出 AO、DO 引腳 可調(diào)電位器兩者作用完全不同AO連續(xù)變化模擬電壓光照強(qiáng)弱全程線性變化本實(shí)驗(yàn)使用該引腳可精準(zhǔn)讀取光照變化數(shù)值DO比較器數(shù)字輸出只有高低電平兩種狀態(tài)僅能判斷光照是否超過(guò)閾值電位器僅調(diào)節(jié) DO 引腳電平翻轉(zhuǎn)閾值不會(huì)改變 AO 模擬電壓輸出。對(duì)比 51 單片機(jī)常用 DO 數(shù)字讀取方案僅能獲取亮 / 暗兩種狀態(tài)本實(shí)驗(yàn)采集 AO 模擬量可量化光照強(qiáng)度教學(xué)與實(shí)操價(jià)值更高。四、光敏傳感器核心工作原理模塊核心器件為光敏電阻LDR 光導(dǎo)電阻材質(zhì)為光敏半導(dǎo)體弱光 / 黑暗環(huán)境內(nèi)部導(dǎo)電載流子數(shù)量少光敏電阻阻值極大強(qiáng)光環(huán)境光能激發(fā)大量載流子導(dǎo)電能力提升光敏電阻阻值大幅減小。光敏電阻僅能改變自身電阻無(wú)法直接輸出電壓因此模塊內(nèi)置分壓電路把電阻變化轉(zhuǎn)化為可采集的 AO 電壓信號(hào)。標(biāo)準(zhǔn)分壓電路原理圖3.3V ── 光敏電阻 ──┬── AO輸出引腳 │ 固定下拉電阻 │ GND分壓計(jì)算公式 AO 電壓 3.3V × 固定電阻 ÷光敏電阻阻值 固定電阻 電路特性環(huán)境越亮光敏電阻阻值越小AO 輸出電壓越低。注意部分廠家分壓電路元器件調(diào)換會(huì)出現(xiàn)光照越強(qiáng) AO 電壓越高程序提供宏定義一鍵適配兩種硬件接法無(wú)需修改復(fù)雜運(yùn)算邏輯。五、STM32 ADC 采集原理基礎(chǔ)參數(shù)使用通道PB1 映射 ADC1 通道 9ADC 分辨率12 位采樣數(shù)值范圍 0 ~ 4095輸入電壓量程0V ~ 3.3V電壓換算公式電壓 (mV) ADC 原始值 × 3300 ÷ 4095采樣數(shù)值對(duì)應(yīng)參考表PB1 輸入電壓對(duì)應(yīng) ADC 數(shù)值滿量程占比0V00%0.6V74418%1.65V204850%2.31V286770%3.3V4095100%ADC 時(shí)鐘與采樣周期配置ADC 時(shí)鐘分頻系統(tǒng) 72MHz 主頻 ÷ 6 12MHz低于 F103 ADC 最大 14MHz 限制符合規(guī)格采樣周期設(shè)置 239.5 個(gè) ADC 時(shí)鐘周期光敏分壓電路輸出阻抗較高長(zhǎng)采樣時(shí)間讓芯片內(nèi)部采樣電容充分充電降低測(cè)量誤差讀數(shù)更穩(wěn)定。六、均值濾波算法解決數(shù)值跳動(dòng)光敏模擬信號(hào)易受市電燈光頻閃、電源紋波、電磁干擾影響單次采樣數(shù)值會(huì)持續(xù)跳變?nèi)庋塾^察 OLED 數(shù)字抖動(dòng)嚴(yán)重。 本工程采用16 次采樣均值濾波降噪處理執(zhí)行邏輯循環(huán)啟動(dòng) ADC 轉(zhuǎn)換連續(xù)讀取 16 次原始 ADC 值并累加16 次采集完成后總和除以 16 得到穩(wěn)定平均 ADC 值使用平均值計(jì)算電壓、亮度百分比刷新屏幕。補(bǔ)充均值濾波只能消除隨機(jī)噪聲無(wú)法過(guò)濾光源周期性頻閃帶來(lái)的規(guī)律性數(shù)值波動(dòng)。工程核心文件功能劃分文件路徑函數(shù)名功能介紹GuangMin.cGuangMin_Init()PB1 引腳、ADC1 初始化內(nèi)置 ADC 自校準(zhǔn)同上GuangMin_ReadRaw()單次讀取 ADC 原始采樣值同上GuangMin_ReadAverage()16 次采樣均值濾波函數(shù)同上GuangMin_RawToMilliVolts()ADC 值轉(zhuǎn)換為引腳實(shí)際電壓同上GuangMin_RawToPercent()ADC 數(shù)值換算相對(duì)亮度百分比main.cGuangMin_ShowData()OLED 屏幕打印所有光照數(shù)據(jù)、明暗狀態(tài)七、OLED 屏幕顯示格式與參數(shù)說(shuō)明程序上電后 OLED 固定排版輸出顯示示例Light Sensor PB1 ADC:2048 Volt:1.65V Light:050% State:NORMAL各字段釋義ADC12 位模數(shù)轉(zhuǎn)換原始數(shù)值區(qū)間 0~4095VoltPB1 引腳實(shí)測(cè)模擬電壓Light相對(duì)亮度百分比僅用于對(duì)比光照變化趨勢(shì)非標(biāo)準(zhǔn)照度勒克斯 (lx)State光照分級(jí)狀態(tài)分三檔DARK亮度百分比 30%昏暗NORMAL30% ≤ 亮度 70%正常光照BRIGHT亮度 ≥ 70%強(qiáng)光八、亮度方向適配與閾值配置8.1 硬件分壓方向宏定義適配不同光敏模塊分壓電路接法相反電壓隨光照變化趨勢(shì)不同工程通過(guò)宏一鍵切換計(jì)算邏輯宏定義存放于GuangMin.h#define GUANGMIN_BRIGHT_VALUE_INCREASES 0配置為 1ADC 數(shù)值越大亮度百分比越高光照越強(qiáng)電壓越高配置為 0ADC 數(shù)值越小亮度百分比越高光照越強(qiáng)電壓越低本實(shí)驗(yàn)?zāi)K默認(rèn)配置。本模塊換算公式反向模式 亮度百分比 100 - ADC 原始值 × 100 ÷ 4095 實(shí)測(cè)效果手遮擋光敏電阻 → ADC≈3971 → 亮度 3% → 屏幕顯示 DARK手電筒強(qiáng)光照射 → ADC 降低亮度百分比上升切換 NORMAL/BRIGHT。8.2 明暗分級(jí)閾值閾值宏定義在 main.c 中可自由修改適配使用環(huán)境#define GUANGMIN_DARK_PERCENT 30 // 昏暗臨界值 #define GUANGMIN_BRIGHT_PERCENT 70 // 強(qiáng)光臨界值判斷邏輯 亮度30% → DARK30%≤亮度70% → NORMAL亮度≥70% → BRIGHT。 教室環(huán)境光線偏暗 / 偏亮?xí)r可通過(guò)遮擋、強(qiáng)光照射記錄實(shí)際亮度數(shù)值修改閾值切換狀態(tài)區(qū)間。九、實(shí)驗(yàn)代碼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 /* * 函數(shù)名稱GuangMin_Init * 函數(shù)功能初始化光敏模塊使用的 PB1 和 ADC1。 * 硬件連接光敏模塊 AO - PB1 - ADC1 通道 9。 */ void GuangMin_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; /* 開啟 GPIOB 和 ADC1 的外設(shè)時(shí)鐘。 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_ADC1, ENABLE); /* * 系統(tǒng)時(shí)鐘為 72MHz 時(shí)PCLK2 六分頻后 ADC 時(shí)鐘為 12MHz * 不超過(guò) STM32F103 數(shù)據(jù)手冊(cè)規(guī)定的 14MHz 上限。 */ RCC_ADCCLKConfig(RCC_PCLK2_Div6); /* PB1 配置為模擬輸入模擬輸入模式下不使用內(nèi)部上拉或下拉。 */ 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 使用獨(dú)立、單通道、單次轉(zhuǎn)換和軟件觸發(fā)模式。 */ 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并依次完成復(fù)位校準(zhǔn)和 ADC 校準(zhǔn)。 */ ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1) SET) { } ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1) SET) { } } /* * 函數(shù)名稱GuangMin_ReadRaw * 函數(shù)功能讀取一次光敏模塊的 ADC 原始值。 * 返 回 值04095數(shù)值對(duì)應(yīng) PB1 上 03.3V 的模擬電壓。 */ u16 GuangMin_ReadRaw(void) { /* * 選擇 ADC1 通道 9。光敏電阻分壓電路的輸出阻抗可能較高 * 因此使用 239.5 個(gè) ADC 周期的長(zhǎng)采樣時(shí)間使采樣結(jié)果更穩(wěn)定。 */ ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_239Cycles5); /* 啟動(dòng)軟件轉(zhuǎn)換并等待轉(zhuǎn)換完成標(biāo)志置位。 */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET) { } return ADC_GetConversionValue(ADC1); } /* * 函數(shù)名稱GuangMin_ReadAverage * 函數(shù)功能對(duì)多次 ADC 采樣結(jié)果求平均降低隨機(jī)噪聲和顯示抖動(dòng)。 * 輸入?yún)?shù)sample_count采樣次數(shù)傳入 0 時(shí)自動(dòng)執(zhí)行一次采樣。 * 返 回 值多次采樣的平均 ADC 原始值。 */ u16 GuangMin_ReadAverage(u8 sample_count) { u8 i; /* 使用 32 位變量累加避免多次采樣時(shí)發(fā)生 16 位數(shù)據(jù)溢出。 */ 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); } /* * 函數(shù)名稱GuangMin_RawToMilliVolts * 函數(shù)功能把 12 位 ADC 原始值換算成毫伏值。 * 計(jì)算公式電壓(mV) ADC原始值 × 3300 ÷ 4095。 */ u16 GuangMin_RawToMilliVolts(u16 raw) { /* 對(duì)異常輸入進(jìn)行限幅保證換算結(jié)果不超過(guò)參考電壓。 */ if(raw GUANGMIN_ADC_MAX_VALUE) { raw GUANGMIN_ADC_MAX_VALUE; } /* 除法前加上除數(shù)的一半實(shí)現(xiàn)整數(shù)計(jì)算的四舍五入。 */ return (u16)(((u32)raw * GUANGMIN_ADC_REFERENCE_MV (GUANGMIN_ADC_MAX_VALUE / 2U)) / GUANGMIN_ADC_MAX_VALUE); } /* * 函數(shù)名稱GuangMin_RawToPercent * 函數(shù)功能把 ADC 原始值換算成 0100% 的亮度百分比。 * 說(shuō) 明亮度增減方向由 GUANGMIN_BRIGHT_VALUE_INCREASES 宏決定。 */ u8 GuangMin_RawToPercent(u16 raw) { u32 percent; /* 對(duì)異常輸入進(jìn)行限幅避免計(jì)算結(jié)果超過(guò) 100%。 */ if(raw GUANGMIN_ADC_MAX_VALUE) { raw GUANGMIN_ADC_MAX_VALUE; } /* 使用整數(shù)運(yùn)算計(jì)算百分比并通過(guò)加上除數(shù)的一半完成四舍五入。 */ percent ((u32)raw * 100U (GUANGMIN_ADC_MAX_VALUE / 2U)) / GUANGMIN_ADC_MAX_VALUE; #if GUANGMIN_BRIGHT_VALUE_INCREASES /* AO 電壓隨環(huán)境亮度增加時(shí)直接返回計(jì)算出的百分比。 */ return (u8)percent; #else /* AO 電壓隨環(huán)境亮度降低時(shí)對(duì)百分比進(jìn)行反向換算。 */ return (u8)(100U - percent); #endif }GuangMin.h代碼#ifndef __GUANGMIN_H #define __GUANGMIN_H #include sys.h /* * 光敏模塊驅(qū)動(dòng)頭文件 * * 硬件連接 * 光敏模塊 AO - STM32F103C8T6 的 PB1ADC1 通道 9 * 光敏模塊 VCC - 3.3V * 光敏模塊 GND - GND */ /* STM32F103 內(nèi)部 ADC 為 12 位參考電壓按 3.3V 計(jì)算。 */ #define GUANGMIN_ADC_MAX_VALUE 4095U #define GUANGMIN_ADC_REFERENCE_MV 3300U /* * 亮度方向配置 * 1環(huán)境越亮AO 電壓和 ADC 數(shù)值越大 * 0環(huán)境越亮AO 電壓和 ADC 數(shù)值越小。 * * 本工程實(shí)測(cè)模塊在遮擋時(shí) AO 約為 3.2V說(shuō)明其輸出電壓隨環(huán)境 * 變暗而升高因此這里設(shè)置為 0對(duì) ADC 百分比進(jìn)行反向換算。 */ #define GUANGMIN_BRIGHT_VALUE_INCREASES 0 /* 初始化 PB1 和 ADC1。 */ void GuangMin_Init(void); /* 進(jìn)行一次 ADC 轉(zhuǎn)換返回范圍為 04095 的原始值。 */ u16 GuangMin_ReadRaw(void); /* 連續(xù)采樣 sample_count 次并返回平均值用于減小顯示抖動(dòng)。 */ u16 GuangMin_ReadAverage(u8 sample_count); /* 把 ADC 原始值換算為毫伏返回范圍為 03300mV。 */ u16 GuangMin_RawToMilliVolts(u16 raw); /* 把 ADC 原始值換算為亮度百分比返回范圍為 0100%。 */ u8 GuangMin_RawToPercent(u16 raw); #endif十、實(shí)驗(yàn)標(biāo)定與誤差影響因素光敏電阻存在明顯非線性特性與個(gè)體差異實(shí)驗(yàn)讀數(shù)會(huì)受多重外界條件干擾光源類型太陽(yáng)光、白熾燈、LED 燈光譜不同同等距離下采樣數(shù)值差距大光源距離、照射角度光照入射角度、遠(yuǎn)近直接改變光敏電阻接收光能量遮擋干擾外殼、導(dǎo)線、手部陰影遮擋感光區(qū)域供電電壓波動(dòng)3.3V 電源紋波會(huì)同步影響分壓 AO 與 ADC 換算精度器件非線性光敏電阻阻值和真實(shí)照度不成線性正比百分比僅為相對(duì)參考。