01. 光敏传感器简介6 g0 b! Q2 l: y9 i, f2 I O8 _
光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD 和 CMOS 图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常重要的地位。: `3 k e5 }* D4 h3 I V
4 }/ J) E2 g* ]% g- f6 _
光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。
! Q% r/ @, [! c+ }" r+ }0 H: u
3 ?# l( m" B6 S% \7 c探索者 STM32F4 开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器,它对光的变化非常敏感。光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的,其管芯是一个具有光敏特征的 PN 结,具有单向导电性,因此工作时需加上反向电压。无光照时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射 PN 结时,可以使 PN 结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流增加。因此可以利用光照强弱来改变电路中的电流。
4 f: M# Y) K5 X, \5 E
, F, x& R9 {3 L e8 Z利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过 ADC 读取电压值,判断外部光线的强弱。
, V" n* m; D1 z" g+ Z& w1 X+ C; y0 |/ H) G
02. 硬件模块
: ~" X- i7 ~$ d( o用到的硬件资源有:" d. S: w/ Z' P o$ x9 A, G
1) 指示灯 DS0+ ]$ G f+ n# e; B. Z- r6 z* ?, B3 g
2) TFTLCD 模块
7 R7 S( ?' v6 N$ g3 F. [" Z: i8 q3) ADC; |0 w C- `, Y8 _
4) 光敏传感器& E1 O( }( h R/ T) ?8 e
) `( i' n5 W7 L1 ?
! X. k( Q; y( v' Q
# g' N5 U! S- T0 N! n
LS1 是光敏二极管(实物在开发板摄像头接口右侧),R58 为其提供反向电压,当环境光线变化时,LS1 两端的电压也会随之改变,从而通过 ADC3_IN5 通道,读取LIGHT_SENSOR(PF7)上面的电压,即可得到环境光线的强弱。光线越强,电压越低,光线越暗,电压越高。3 c7 u- H" Y6 o/ `
B Q. u0 t1 B- d- P' ^+ d03. 程序设计# R2 v7 I2 R( W$ M: g" H
adc.h文件/ @/ k( i8 S# J2 ?! g2 q
' ?: H4 l. s: g" n1 W- //-----------------------------------
2 ~# P. H0 h8 p5 z- h* U2 N5 U - //ADC3通道初始化+ A7 x, |% Q: l' U: T3 x
- void ADC3_Init(void);; e5 r* k( B& q% `7 L. [
- //获取ADC3某个通道的值. Z1 p$ f( J" z$ K8 C
- u16 Get_Adc3(u8 ch);
6 {& Z9 z+ y$ B" \! L9 |- I - //获取某个通道给定次数采样的平均值
# P) l" S. ^* l9 H$ ~ - u16 Get_Adc3_Average(u8 ch, u8 times);
复制代码
7 V( e8 Z5 q* I. z' X4 Xadc.c文件
+ ?" Y! ^5 t( d) m0 }4 _- j, v/ Q5 a/ T9 r+ w+ N4 [- \
- //-------------------------ADC3------------------------
: M3 J! a; L0 ]& Y% i - //ADC3通道初始化" ]. W$ ?# |1 n1 }4 Y" L
- void ADC3_Init(void)8 h2 r: T; k+ [/ p
- {
+ ~' X1 q/ P/ J# O# p9 y1 r0 [! x - GPIO_InitTypeDef GPIO_InitStructure; j& p3 J! t9 z2 j8 P! v. }, _
- ADC_CommonInitTypeDef ADC_CommonInitStructure;) P3 c3 Z8 s! H3 u/ I
- ADC_InitTypeDef ADC_InitStructure;
( e r* p% g, [0 C+ M - 4 ?6 j7 j% `# Q% Y) C+ r
- //开启GPIO时钟 PA5
4 {) ^& R6 D; z/ a7 ` - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
. H! w; q& B3 o: y3 `2 B - P; S5 E4 C. C- a$ s1 x
- //开启ADC1时钟
: ]5 ` z+ Q7 e k% W- p" Z9 Y, r - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
" ^0 Q5 p* t- n6 d# A+ e g -
* j+ o7 K7 l9 B+ q) z/ n0 g - //GPIO初始化 初始化为模拟功能
8 H2 _0 x1 i- e" e - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;, O" d8 U, Q! w* }2 r2 F8 W
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
6 L c4 F; d& n! n - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
. }. x+ P& x# u0 w - GPIO_Init(GPIOF, &GPIO_InitStructure);
! W+ ?3 h( a) b7 S/ h - 7 ^( i$ c; B5 r
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE); //ADC1复位
. P+ q, i% m9 p- Z! t# U - RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE); //复位结束
0 {7 H5 K# i3 b3 g -
" t2 O# \8 ]! l; ^4 u; j -
: d" I$ d$ J3 W5 P& Z$ ` - //初始化ADC_CCR寄存器
7 Z3 I4 x" ^0 B( K, @' j* u1 n
' v" M% u$ G0 ~' i9 Y6 W# z- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;7 D" y# G) y9 l0 b# o
- ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
$ I* P3 S% W9 U - ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
- a2 {0 p; y; R8 D1 Z - ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;; y5 O0 N0 T1 t! Q/ H5 B
- ADC_CommonInit(&ADC_CommonInitStructure);
7 D4 N6 H% z4 ~& m5 ]/ p -
1 W x4 n# H9 m8 g' [
! [; f3 z8 d% R& x. ]( Y- //初始化ADC1
0 B$ P! T0 _) K2 `8 v4 u' l3 Y - ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
9 _2 o. U. M) B/ i! q- `) w6 c, |# ] - ADC_InitStructure.ADC_ScanConvMode = DISABLE;8 s q7 R; q9 J
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
9 g! |& D# c K - ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;8 L4 g0 K2 r- z3 K3 D; p* [4 p
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
! V+ P+ q6 r9 P4 f$ v - ADC_InitStructure.ADC_NbrOfConversion = 1;
( ?0 i9 w s3 O5 i7 D; S8 V - $ N6 ?% L. v0 p& w# J0 n
- ADC_Init(ADC3, &ADC_InitStructure);
& I, y2 }5 O% V% {& x! O - 3 K4 f0 q |# e
- 2 n; n0 _2 ]2 t+ \8 L7 b* B% o
- //设置ADC规则组通道 一个序列 采样时间
5 [% j1 v# L6 c, m+ c6 G+ U - //ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_480Cycles);
; T7 A2 f6 C5 W - 0 {. N. N; X+ {9 o$ P
- 0 M! { |0 b6 v3 |" v
- //使能ADC
- T8 X7 B# j! s - ADC_Cmd(ADC3, ENABLE);
% R% f) g* y8 E. C. } - 8 @& F4 ]! u+ a+ ]. D1 H' a
- }
! a; p9 _0 h* M8 ]+ T - 5 U& D4 J e/ k; ?
- //获取ADC3某个通道的值$ B$ B. o Y& U1 v z5 R6 ]+ ?6 G
- u16 Get_Adc3(u8 ch)
% d% p \6 u, c9 m" s! p# L! a4 ` - { d% @+ ?: `6 R% i% _
- //设置ADC规则组通道 一个序列 采样时间: R8 e( u9 }. B' b4 A
- ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_480Cycles);
7 U7 d' t6 m& V# h m9 e3 g - 9 R7 X: @7 p7 t# g
- //开启ADC3的软件转换启动功能# q9 P% ?1 F" U9 K) d
- ADC_SoftwareStartConv(ADC3);
, `9 J4 \/ [. M! p& ` - * T3 j% m L; |
- //等待转换结束
2 U* Y: C9 N2 ^- j5 C - while(ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC) == RESET)
2 E2 D. n4 V4 J* s% U. b+ ] - ;
3 d( O) b, A# A; S& X- b
! Z6 u6 \# V( q* p- //读取ADC的值# p2 j$ T! T9 U. g: c0 e y1 e1 p* C
- return ADC_GetConversionValue(ADC3);9 h2 r# {( J* c) @# Y+ h, x; ]
- 6 o; Z. g) F* k/ o2 ]6 h
- }; ^" j( L+ B) T7 ^3 L- m
4 _8 J2 N) ?: x- J7 q" Q- //获取某个通道给定次数采样的平均值
: X2 |( E1 i7 Z/ l+ O2 d - u16 Get_Adc3_Average(u8 ch, u8 times)
3 Y( I: {1 m) T7 q4 a - {, @. p( \0 n' L
- u32 tmp = 0;
" [! l: Q8 j! p6 Z' d - u8 i = 0;
, M' A8 M U3 d5 v; W - 0 C! Z' P" \7 O0 h* W# D
- for (i = 0; i < times; i++)+ u0 M' j' ]) t I" ? J& t
- {
$ O) g& p4 \2 Q$ x - tmp += Get_Adc3(ch);
( D7 J& Z0 ?2 `. _+ A - delay_ms(5);
! {. U5 Y* A, v/ ] - V' Q# q: c1 ]" R
- }1 o g3 V) |. q3 M# A2 V
- . n9 z9 [, c& w" \2 H
- //得到平均值9 @; X1 S, a c0 h* j/ g. V
- tmp /= times;
. }2 w u, q6 s- U/ X3 M6 a -
: _" I$ G% W7 E1 k/ P& ^* D - if (tmp > 4000)
+ o9 O0 r8 S4 _+ U. B' E - {
: B0 I' w; ^4 O: p/ {. H - tmp = 4000;# P5 Y7 K$ t$ J4 V6 M6 g/ }
- }; y1 j* C9 e E- Z8 ^
- ( g& ]' \3 e6 f0 i+ r0 L: }
-
" m5 K s% w6 e& O' F; W - return (u16)(100 - (tmp / 40));1 D9 w( x4 Q7 H( I
- }
复制代码 1 b! I9 b: |1 J: U
main.c文件2 s$ ?: L+ A: P# J, L
2 P0 K1 G) N- J$ n2 B- #include "sys.h"7 r' a' J( m o2 b
- #include "delay.h"2 v/ h7 n3 G8 }% T8 E6 v
- #include "usart.h"- N l @% N& G/ _% K
- #include "led.h"( G/ q& \2 u% y8 h p' f% {; i& b
- #include "beep.h"* i: N p4 N2 `( A5 |" n) c! u( h( x
- #include "key.h"3 ]/ c! M h! d1 m! F
- #include "usmart.h"
, j! e% B% Y) n) h - #include "lcd.h"! b% e5 o9 S, L1 [
- #include "rtc.h"
/ l6 \# v: u" M5 T6 j: H1 L3 C$ ^: U - #include "rng.h"/ Z: f3 k+ M* S
- #include "key.h"
" ?- O9 q: ]2 B" W" M& J8 X0 J - #include "wkup.h"+ ~0 F0 U* ?9 r
- #include "adc.h") T) z! C+ @ M9 D' @
- ) A" d6 f2 u- l
- int main(void). _1 O# ?' q" d$ x8 Q& s: O$ {+ d$ F- z7 T7 n
- {
: R& ~0 L% M U% |) ]0 X5 _, ] - //u16 value = 0;
' i5 M/ i6 `, Q8 Y7 N' y3 r; m) S - 2 g$ I% W& Z5 Y$ u/ v) }
- u16 temp = 0;
. M8 W4 G9 k3 L' n; s/ o1 U6 q
% h) j4 V+ y* c* g+ a0 f3 _" S1 C. b- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
* W8 ?8 y" u/ @& ~ - # m2 }& f. U1 |6 {. ^- d
- delay_init(168);- i/ U, W' M4 P% i
-
2 h! \# N* Q8 ~. F2 D% I! A - uart_init(115200);$ y S" P7 P) i* v
- 1 W5 c3 l! z+ a+ }5 ^$ O" M0 b
- //usmart_dev.init(84);
S3 g5 T0 p# n9 c9 j; m8 y$ P - 1 I. @0 u: {. f/ k+ Q0 m q
- , H* @; L6 Q. f- X
- LED_Init();
v8 S! v# O7 {0 m5 s) q - 3 V3 b" z V0 B# F! u6 X5 e1 y+ m' N
- , b" T, n2 c0 Z2 ~
- LCD_Init();3 B; o* w7 |2 [/ N0 m
' ~* f: H$ _+ J7 _4 M- ADC3_Init();3 S$ G6 h: r% K( {) G/ k8 L7 {
-
$ g" f# J9 }3 H [0 i0 n$ \4 a - + B0 g# v+ x* ]
- POINT_COLOR = RED;6 c4 O. W8 o7 ^" F; ~& r
-
7 ?+ k- V% a, z9 L; ]/ s - LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
5 s1 q! D' _& x L- u. i- w7 _- j - LCD_ShowString(30,70,200,16,16,"LSENS TEST");
+ }9 i# g. A, j; H% z5 ] - LCD_ShowString(30,90,200,16,16,"ATOM@tom");- A( M/ V/ c6 Z
- LCD_ShowString(30,110,200,16,16,"2020/09/15"); * D. t! i9 e/ F$ C% ^" I, i* F
- * O& N$ _: s K9 r# C
- //设置字体为蓝色
4 ?5 d# n7 j) [; R/ J5 v {' d - POINT_COLOR = BLUE;
; U- W# }% Z$ [ - LCD_ShowString(30,140,200,16,16,"LSENS VAL:"); # [5 D2 r% Z9 t" K" k
-
9 A* X0 v* u& e' v b$ W: A' ~ - while(1)
0 M- A+ J" @6 s% f - {/ B. ^5 X- M, f2 p1 `
- //得到温度值
1 ]$ Z6 I' ^7 t5 b - temp = Get_Adc3_Average(ADC_Channel_5, 10);3 m" P% F1 q8 H) I
-
' R, R5 M$ C* u0 N - Y/ F$ T8 C1 e! i) |
- LCD_ShowxNum(30+10*8,130,temp,3,16,0);//显示ADC的值
, c# [6 o& L# k" C( Q2 a- {$ N5 K - # c( {( e" _1 f2 h
- printf("Light: %d\r\n", temp);" h! \- E3 ~3 x# ?
-
0 I) z. b- f7 ~8 L) @0 | - LED1 = !LED1;
9 ^, i$ ` e- p1 G' K5 D -
" [8 A2 H# _3 g: s6 l6 b4 } - delay_ms(250);3 N: C' y, F8 }5 X- t" M
- }
' h- Z K, P; p2 h - }
复制代码
' N+ b! ? J" m( O U. X- _1 f04. 结果验证
! J4 |& k$ V; b9 R; G伴随 DS0 的不停闪烁,提示程序在运行。此时,我们可以通过给 LS1 不同的光照强度,来观察 LSENS_VAL 值的变化,光照越强,该值越大,光照越弱,该值越小。0 K2 e1 M" `! t+ ~
7 V1 ]/ V1 V/ ?+ u& o/ m
3 V; B. s6 r X3 n4 D7 O; F |