01. 光敏传感器简介& z$ o# J1 ^6 \7 K) x
光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD 和 CMOS 图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常重要的地位。8 Z: p1 J5 J% q+ G* R# ^
! }8 o4 O' S- R- G, X5 s% Y* _光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。' @0 D+ |4 V! { \ |: n
: E9 p" ^( c( E$ ~$ [
探索者 STM32F4 开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器,它对光的变化非常敏感。光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的,其管芯是一个具有光敏特征的 PN 结,具有单向导电性,因此工作时需加上反向电压。无光照时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射 PN 结时,可以使 PN 结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流增加。因此可以利用光照强弱来改变电路中的电流。# L& q2 c2 t" [
! X4 c( y3 E" {; G) [利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过 ADC 读取电压值,判断外部光线的强弱。/ y; S6 @. i! k- k W+ I
2 p |7 T, ]/ T1 X# t
02. 硬件模块
7 o( y6 N2 c7 s( L& X* X用到的硬件资源有:
+ l \6 ]6 {2 G( K; `( v; o1) 指示灯 DS0
. n: W1 B7 b$ s% F2) TFTLCD 模块- y- M0 F6 ?+ p; c# W
3) ADC Z4 X$ p, Q) J0 p6 ?
4) 光敏传感器4 ~# `( L4 w5 I
% c3 u5 S2 c" b2 s
3 l, ~" V$ ^6 g3 I
) b! P5 e! n1 d) w
LS1 是光敏二极管(实物在开发板摄像头接口右侧),R58 为其提供反向电压,当环境光线变化时,LS1 两端的电压也会随之改变,从而通过 ADC3_IN5 通道,读取LIGHT_SENSOR(PF7)上面的电压,即可得到环境光线的强弱。光线越强,电压越低,光线越暗,电压越高。
7 t6 l% A& h! A: [* g. f9 m0 S
" Z9 y$ M, v! V2 i03. 程序设计+ I4 J0 U( l, B# l( U
adc.h文件
' x9 T5 @* q) ~; D* E* W* p
* R6 `3 h Q2 Q- h/ o( x- //-----------------------------------+ q4 L- i& W3 ^2 U
- //ADC3通道初始化
* K) ~. v# Y- o! u - void ADC3_Init(void);
# ~. v; y( Z8 ^6 S. W5 |! h! l - //获取ADC3某个通道的值/ E* A D) w- X" {* {
- u16 Get_Adc3(u8 ch);2 z/ g4 D W8 o6 L/ j8 V
- //获取某个通道给定次数采样的平均值: u, {5 z6 q# @
- u16 Get_Adc3_Average(u8 ch, u8 times);
复制代码
; C, l! |, U/ Iadc.c文件 w% Q1 C. H+ i
' C4 N. A2 Y, ?# d0 j
- //-------------------------ADC3------------------------3 A7 d) E q/ u
- //ADC3通道初始化0 i2 l( S w; K/ i$ Z& D8 Z7 m8 F
- void ADC3_Init(void)
; N/ \8 ]# V# `% F. { - {/ X' |7 l0 s+ H* Q
- GPIO_InitTypeDef GPIO_InitStructure;
2 o" h, |- \& N8 J6 @9 r - ADC_CommonInitTypeDef ADC_CommonInitStructure;
% z% I2 i- V$ k - ADC_InitTypeDef ADC_InitStructure;
# e- c, I1 T* l: n: S - ) T" ^. O8 Q, n E
- //开启GPIO时钟 PA59 _* i" n( z. J
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); ) x7 j w0 T' u& d' d* M
-
' C% ?: {- A% q - //开启ADC1时钟( O' D0 ?5 ~' h5 Q D
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE); " B& \# }4 b% U* @1 d
- " h; i ]4 m; W$ \, l* s
- //GPIO初始化 初始化为模拟功能# ^- N2 j9 V1 p8 ]* a3 U! c' i2 p
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;# U# B0 Y) ~2 k e# b" C
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
. q: [* _9 x' t4 i; T6 v- D - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;# i, m; c6 B, p( Z
- GPIO_Init(GPIOF, &GPIO_InitStructure);; I7 P' y! d1 \8 `
-
" F' u8 X! g' x - RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE); //ADC1复位
/ H" g& i$ M7 H, l - RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE); //复位结束
: p; N1 M* D5 A* a -
) `/ J1 x4 O+ k! E -
; o N# v( D' \, m. ` - //初始化ADC_CCR寄存器4 p9 M" b$ L0 O. o
% R# O8 j: E0 l! Z5 t& s- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
6 ^: R" Z4 l$ T. \0 L" K. w: v) F - ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
" h: v3 z7 [* O - ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
6 D ~! F7 T; ]% N5 P$ ^0 B6 D2 @$ J - ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;) ~( A: t: G0 ] X
- ADC_CommonInit(&ADC_CommonInitStructure);
+ a/ p& l% x$ B - 3 R& V+ H- o4 @& Z+ v6 D
1 U7 a- E9 o& N* j$ A- //初始化ADC1
* A) i5 u% s+ n3 @) _ \0 V( J - ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;. P2 ]$ t2 r6 g9 ~
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;
. o( ~: b6 y% E& s - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;0 R1 H" k- Z$ `1 k
- ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
/ G2 p y2 C# _% v4 A1 m - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;6 r4 ]" O8 V% q: `7 o: V. u" N
- ADC_InitStructure.ADC_NbrOfConversion = 1;
# U. Y) Y, n, \
0 _# ?6 g4 d2 J5 z/ n/ y- ADC_Init(ADC3, &ADC_InitStructure);# {8 m; z9 r! s E" b+ r) R A# v
- 9 Z% z0 `0 X# K! C
- + L6 D6 V, ~; T, g1 r! ?
- //设置ADC规则组通道 一个序列 采样时间 ~+ _, l0 x' x6 v* z% k. k {
- //ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_480Cycles);3 u5 y- O' M. z" p6 s
' `5 [# R7 `( _" |- 1 T5 X; H3 f2 H" Z1 b6 s% f
- //使能ADC
0 S# Z0 ^& f: t6 M( w( I - ADC_Cmd(ADC3, ENABLE);
0 r. q/ k' P9 c& R) K0 P -
$ n6 X5 c0 w2 X! m" R) p1 C - }. u9 [( n, x' P- {- _
- 3 \% q7 C9 {- }9 A
- //获取ADC3某个通道的值
6 i1 [) A# I0 ^4 l* w8 C: b% M - u16 Get_Adc3(u8 ch)' `! f8 k/ H2 }% \& V
- {* [% z) y& ^" L R; k. p) k
- //设置ADC规则组通道 一个序列 采样时间
# o+ y; P) P' D" A! _' @ y: u - ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_480Cycles);; i @% c6 d) z: M" V: Y
- 7 g* v: N8 t6 U L# g; F* `, Y
- //开启ADC3的软件转换启动功能
$ S7 F7 b/ W' @8 ] - ADC_SoftwareStartConv(ADC3);
9 G! |! k7 V8 \9 A -
+ y8 e- a6 U7 W. B3 v5 h - //等待转换结束: c; x' T' G9 c
- while(ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC) == RESET)1 L- Z _- y1 `' Y# G. g) u. E5 Z
- ;
a9 B9 y- }' [ - ' |8 E2 I4 D; I9 `8 }+ U0 K, N1 B
- //读取ADC的值7 K2 T* N& g. Y+ p1 @' q9 N o
- return ADC_GetConversionValue(ADC3);9 a+ A2 t3 N) I0 J
- - X3 Y$ k% s# G, ]2 ?6 \8 j
- }
3 R4 h$ X8 M6 m4 G3 v - 9 J$ k1 H" l3 X, {/ X# t* b7 d3 _
- //获取某个通道给定次数采样的平均值" O, Y2 `( W& K# d0 h
- u16 Get_Adc3_Average(u8 ch, u8 times)
3 ~2 B* K3 K) Z - {
) N% H6 W5 V% t; K+ Y. {3 ` J - u32 tmp = 0;
" s, B2 o* y( M4 W. G2 l7 t - u8 i = 0;
# m8 u4 q( C0 X( T/ n, i, s -
# S7 ]9 k8 O. s; G v* I9 v - for (i = 0; i < times; i++)9 U4 N7 T' x0 g6 N( }+ Q$ y' y- q
- {$ [- Y9 k) G; q+ R$ F
- tmp += Get_Adc3(ch);3 C5 Y1 j, e' u, [# r
- delay_ms(5);- ^* Y/ B' ^9 ?( `% I
-
- }& z: R# v0 S' ]9 f8 j - }5 [6 z, d: ~- F9 [# s% L4 z6 M. Y
- 8 S$ m% }6 w; R- [
- //得到平均值
2 V& T! {8 G l - tmp /= times;
6 N" {! k8 u7 o - # Y7 c' R$ K0 Z H) y
- if (tmp > 4000)( ^/ i/ i% J& y) y9 d c1 Y, x
- {7 ^( \; p4 r3 H* T j* Y" j
- tmp = 4000;
" N, ^& y( P) S2 N - }
. b& R n# Z- d5 n6 J. ^7 R -
% f$ V$ t! V( f' L7 H -
B7 Z7 C! I ~4 w9 c - return (u16)(100 - (tmp / 40));6 y4 V( o& K2 t
- }
复制代码 5 F* c6 F# k# U% b3 y/ w/ Q
main.c文件2 ^5 o+ y3 m: P- y V3 y2 m
9 q" @- `- N. T- #include "sys.h"
* g3 f0 t7 m# O8 g# C- Z1 [ - #include "delay.h"
/ w p0 x1 `$ g0 ~2 l4 v4 | - #include "usart.h"0 c7 L! v% h' n
- #include "led.h"
8 w1 H5 s" b) Y' ?, H+ R" w0 r - #include "beep.h"
4 d# A$ D& T% d2 i$ ? - #include "key.h" O2 ?3 H3 }% e7 C( i
- #include "usmart.h"/ }( B8 V* L0 K5 O. ?4 e# u, l x
- #include "lcd.h"" N9 Z; `! P* o+ p) m0 n1 h
- #include "rtc.h"1 M( L' ^! [- v, k
- #include "rng.h"
( ]5 Y. J* u j4 H - #include "key.h"
6 y5 A8 Q+ F+ P, H/ k3 l; O5 r - #include "wkup.h"+ K! W1 [: |" M! N' f
- #include "adc.h"% w/ ]. D4 R# z
- ' ?# T; w$ ` l% U M* e* b+ h0 g
- int main(void)/ z1 c+ W7 {! P! @+ H# T2 D4 k
- { * ]' l: l4 K/ S) v \2 ]( F
- //u16 value = 0;7 K' h+ K5 v3 m9 E
- $ b- H: U3 [& N& s
- u16 temp = 0;/ T; g) ]: [5 d- b
- 9 U. W$ c8 Q$ E
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组22 l/ p6 B, [) J8 o; M" ~' x
- + v( C% Q, |. r& n( H, S, @
- delay_init(168);
( s* y4 G6 f; U: ~$ m6 n- s! ^, C - " k$ M0 d* R s/ ?
- uart_init(115200);
/ t0 k3 j' ?1 [, ]. o$ k/ K - ' _# }2 C% ?9 H8 g4 G. {
- //usmart_dev.init(84);/ I& w0 v4 T" O' \
" R5 c% l3 V5 k z3 z0 M-
+ ^$ ?2 x% z. U4 _ - LED_Init();
7 }$ T4 g6 k9 g8 M% r, ^ -
( }. i- n$ E) [- ^8 A3 I - 8 v, ^4 N2 |' V& f6 |; o6 _
- LCD_Init();5 v! D. o' r5 d/ j+ C
H$ ]3 |. A/ G' L; c% y t' L: q- ADC3_Init();, r. t, x0 ]3 o2 s/ v- M4 t2 ~# r! `
-
g* M2 d4 N2 _' }5 k0 r- ~ -
+ o9 s8 W1 k9 J1 i( A4 N* D/ N& ]$ e4 L - POINT_COLOR = RED;( q! m# C7 M- C
-
% k$ A/ b+ l6 i: C - LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
/ A* j7 H5 `) w$ a - LCD_ShowString(30,70,200,16,16,"LSENS TEST"); 5 ^7 H/ b0 r0 p, w: w
- LCD_ShowString(30,90,200,16,16,"ATOM@tom");3 t- r: A! O" X
- LCD_ShowString(30,110,200,16,16,"2020/09/15"); Y+ _; G8 K% d
- 1 d; b: [, x7 ]" ?; N7 Z; b
- //设置字体为蓝色' m, f! X: P, D
- POINT_COLOR = BLUE; ' m3 p' l4 r7 w- D, }' W. M. l! }1 Q
- LCD_ShowString(30,140,200,16,16,"LSENS VAL:");
: G. Y! d; f6 _5 p -
* u) N- d- t! p @4 a5 I+ t& v0 r - while(1)( r* ?9 j2 y! ~5 M$ N
- {
t; y9 {# M3 P2 j2 \$ s - //得到温度值
$ D% N+ R$ n3 I8 O) j7 f - temp = Get_Adc3_Average(ADC_Channel_5, 10);( N B" g5 N: o( y
-
) L) s& C( h4 I+ m2 c* V - 6 \0 Z6 m- a$ N# t- g
- LCD_ShowxNum(30+10*8,130,temp,3,16,0);//显示ADC的值+ H$ g) e/ B O; U6 ^; P- i
- 9 g1 C M- T, c( L- O
- printf("Light: %d\r\n", temp);
1 y. D1 ~7 c- W7 l1 L. n -
! s. N7 ]) B- z. H: c$ a) Y - LED1 = !LED1;
6 c k$ u! S0 C, U- q H- ~ - % [8 m2 @2 T) Z6 E
- delay_ms(250);
7 M6 I: Y3 K. `6 Y# s - }$ I+ R4 e: s. _8 c M: K) }( i
- }
复制代码
( a6 J9 E8 T" ^$ n5 h, p* h04. 结果验证- }! f3 ?; U/ C; Z
伴随 DS0 的不停闪烁,提示程序在运行。此时,我们可以通过给 LS1 不同的光照强度,来观察 LSENS_VAL 值的变化,光照越强,该值越大,光照越弱,该值越小。; Z: p- L3 J( K+ I4 | j ~ ?
2 k# k/ S \! a; a- f8 F$ [6 e9 A- h: P* o7 @9 K0 U( o4 W
|