本文记录了我使用STM32F103结合VL53L0测距模块的一个学习过程。 p% v/ M- ]8 t' J( v) }' t$ l
VL53L0是由ST公司最新推出的一款飞行时间测距+手势检测的微型模块,它应用非常广泛包括:激光测距、避障检测、一维手势识别等;接下来我们重点介绍它的激光测距功能。
2 m6 ^4 {( R4 T: Y6 r- a. B0 S( H
一、模块的硬件连接
( ]1 n3 M( {! Q8 p7 ?1 J
3 f- X' I3 U x" ~1 D2 r
! F% r3 B2 [/ |1 D" m
9 g! S* y& X" E/ t7 I/ N
; w+ {0 u% }+ f/ S1 {1 z. d% V上图便是我所使用的测距模块GYVL53L0XV2,它预留了六个接口:
/ b! R/ r# q6 _2 M1、VIN:供电电压,可在3V~5V范围内进行供电,但如果自己进行电路搭建的话,则需要一个线性稳压电路,将输入电压稳定在2.8V进行芯片供电和芯片I/O端口的上拉,这个可以自己看它的数据手册;
5 {$ f$ k% ~' G Y$ h3 `+ ?2 t7 t2、GND:参考地;0 T1 J6 X& t5 e
3、SCL:I2C通信的时钟线;
" i8 \: Z! o* h% U4、SDA:I2C通信的数据线;! c; ^. p% C5 d1 v% a6 \# ~
5、XSHUT:芯片的使能管脚,电平1代表有效,电平0代表无效;% z6 k& K7 V" d" x# ~
6、GPIO1:芯片的中断管脚。
& J4 i! b# K9 g1 g4 L" y6 ^* }/ {( w0 y+ n" |3 S2 {
本文与STM32F103的连线如下:. Y& C* d0 g6 Q9 j2 l+ O6 h- V: q* S
VIN--------------->3.3V
}0 Y/ M: e- L% s" [8 BGND------------->GND
5 } b, U& q f( iSCL-------------->PB6! j/ q. L) v% h7 b2 Q; B! A8 Q
SDA-------------->PB7; ]- g* ]& }' N. u
+ m7 d% y; T3 Z8 {5 W0 Y; [" l
其中XSHUT与GPIO1没有连接,由电路可知,未连接时它们均上拉为高电平,XSHUT一直为高则代表芯片始终有效工作,如果有需要也可以进行连接,用单片机控制。在这里我没有使用到,因此直接不连接就行。
" H) P; i) g6 l5 r. h
6 g! ?. ^7 j' v+ [% t3 U
% c7 l, h$ y+ s: r" Y, K3 e二、程序代码
. e- q1 F8 o# @- #ifndef _VL53L0_H
9 A2 l3 `) G# }1 {0 S2 R$ } - #define _VL53L0_H5 N5 e# F# V7 {9 x/ K. z6 c1 O1 K
0 V3 Y' h" a) ]" W- #define VL53L0X_REG_IDENTIFICATION_MODEL_ID 0xc0
; d; Q! [: | u1 Q( D! r1 Z - #define VL53L0X_REG_IDENTIFICATION_REVISION_ID 0xc2' j2 d9 |, T5 b+ M
- #define VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x50, W+ @! ~) L5 L' d+ x: o) u
- #define VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x70' w' c" t7 Q+ }$ O- a# o1 v5 ]
- #define VL53L0X_REG_SYSRANGE_START 0x00
1 U% Q8 @8 s9 X: q7 E; p - #define VL53L0X_REG_RESULT_INTERRUPT_STATUS 0x13! y1 o' c9 M2 K R6 ~
- #define VL53L0X_REG_RESULT_RANGE_STATUS 0x146 s8 S" p; C& l( \
- #define VL53L0X_Add 0x29# `0 j& [- J$ c- o# \( p
- #include "stm32f10x.h"
1 k; A$ Y p; G' m) `; w% Y - #include "bsp_usart1.h"
! b9 ~5 V+ X4 p$ B: Z - #include "bsp_i2c_gpio.h"
5 S( q+ e: f+ [ - 6 t+ v4 h s4 @, z) ^
- u8 VL53L0X_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf);; L1 q( z& _& T! q* R: s g
- u8 VL53L0X_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf);# v, v# J( ?% D2 M/ H* r* M
- u8 VL53L0X_Write_Byte(u8 reg,u8 data);
: ` b( {* f q; T - u8 VL53L0X_Read_Byte(u8 reg);
/ d9 e8 D7 p5 m, C$ | - uint16_t bswap(u8 b[]);
4 w% ?$ F/ v7 L! o; f. s - uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg); & I7 z, X ]* }4 L' i
- uint16_t makeuint16(int lsb, int msb);
3 _0 {9 g' a. u4 L - " u- m! x7 z: ~7 g
- #endif( e- v9 p! s+ ?; g( \5 o" _' P
9 A( d5 J/ W, a. V' I- C# n% ~- #include "VL53L0.h"
& C/ U6 v" @" D0 k2 L4 G - uint16_t bswap(u8 b[])
; _7 _6 J7 ]4 i/ F+ f B6 z - {0 J' |7 [9 P! s2 E1 w. a7 H' y* K5 A
- uint16_t val = ((b[0]<< 8) & b[1]);
( e7 }' I6 T6 a/ h$ h - return val;7 z6 \1 t: W8 N1 H! P& @3 {
- }
8 L( c) Y2 T3 B/ a t - $ r9 [5 U) v4 S& t
- uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg)
1 v' X, o0 D' v0 _ - {
: u4 d; o) G! L) r7 o - uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) <<1;* ?7 q# Y$ E& z H# M2 s: g( X
- return vcsel_period_pclks;9 p I) a- t6 ~/ F) M4 t
- }1 p, u. `$ G# L' J5 ]5 J
, G- X( P2 ^9 L/ T5 V- uint16_t makeuint16(int lsb, int msb)& _2 O" ^) H6 Y/ C# D; n
- {
4 u6 o3 C# ^0 P( p4 u" H - return ((msb & 0xFF) << 8) | (lsb & 0xFF);% A8 {5 F/ O {+ R# y) d
- }' D' j" R- y/ K/ u
9 i! [, \9 f( o2 R0 g$ _- u8 VL53L0X_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)' _7 Y8 J7 ?( s- k }. G: ?7 |; y9 d
- {" M) p1 {( M9 i( z7 u
- u8 i;
# c# t6 P4 l& ^! w; p - i2c_Start(); 1 j: v/ {7 W- a7 b4 ^6 w
- i2c_SendByte((addr<<1)|0);2 x& P8 s/ G7 v3 I" h3 M8 B# o) u
- if(i2c_WaitAck())
" C0 W( s$ H7 J# |6 d! u' z - {
( ]. ?) a1 r( Q* J# H - i2c_Stop();
; ]" f: Z) Y% q1 u - return 1;
+ ?; ]0 [% Y/ `; r2 G8 |: @ [. R - }4 ^* r' Y2 }; S8 _) y% m
- i2c_SendByte(reg);, r4 ]: I, ]& U! P& E
- i2c_WaitAck();
3 @5 d) ]0 a# ~' r - for(i=0;i<len;i++)
. k% t* I6 \- V - {
* i+ N2 p# @$ G4 | - i2c_SendByte(buf[i]);
$ |3 Y; `3 P9 H: }: E' ~' i4 n - if(i2c_WaitAck())
$ _% ~7 ^4 Z# u' y! { - {
# P% K: b7 f/ E: y- Q- n6 M I - i2c_Stop(); ( j/ }1 b [0 M/ r) S# Z8 `
- return 1;
) ]/ Q. M$ [' u. \8 J& b \ - } $ K4 B: l3 [# d
- } 6 @. @6 `0 P' c
- i2c_Stop(); ; X, l7 H6 g. T& a
- return 0;
6 w; S+ c4 O. ^; |! M - } 0 n. }2 [3 L' ]6 B# i- V
- G# N6 O `4 b6 S- n( P! C- x/ F# U
- u8 VL53L0X_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)' [# o' z9 F5 }$ v# Q1 ^5 f
- {
/ \- |& ^% J. l) r - i2c_Start();
; G" `" J7 {1 O9 k1 r4 p9 f - i2c_SendByte((addr<<1)|0);
% q6 q7 g- Q2 F) ~. p/ [4 x& z7 ~- b7 l - if(i2c_WaitAck()) . @4 s5 e7 y+ |) K: l
- i2c_Stop();
* r+ u' k3 @) k" D - return 1; 0 x, b* T. b( b" k
- }6 E4 `5 k9 |$ }( U0 `
- i2c_SendByte(reg); # m$ M6 O0 E: }6 D
- i2c_WaitAck();
, V0 B4 ?) S* ], P3 B - i2c_Start();
* b, ?6 q5 Q9 I4 b1 M - i2c_SendByte((addr<<1)|1);
8 U3 G) y1 B' b) d1 [. T - i2c_WaitAck(); 1 Q; l5 h1 k4 p# ~! e9 P" H
-
: g! b$ y. o8 S1 a/ m; y7 o - while(len)
; L* p, k( k2 w9 i. A' E" D: {+ n - {
& ]' q n5 e2 _/ N, p. W# P" Z - if(len==1): k5 M3 m* j5 ]4 n: T% n) J
- {, C/ m0 x8 |! D, s
- *buf=i2c_ReadByte();
x7 s5 V. k* e* Z - i2c_NAck();* h/ g7 |0 `( y) T
- }
/ [/ m, l- N/ L - else
9 U6 q* U1 h1 y t - {5 w2 n3 V9 c, [, B- k, q# Q) H
- *buf=i2c_ReadByte();
0 ]+ v% E+ R) ^7 w - i2c_Ack();
4 Q- h4 X5 ? [; L& Y& [ - }
9 j" f( l7 B. J: h# e2 l0 \4 k - len--;
8 P+ c, y" A7 `* P8 ~6 r - buf++;
7 K' J% w. I' f# U - } 4 e" B& S9 t0 E P. S" W
- i2c_Stop();( W$ b; }; L' S2 v. T, N
- return 0; * b# V1 E# s& `
- }! O; l P4 l# ` f0 J$ m" c' `
, E) M% i% c$ s# H3 h) o s1 G- //IIC写一个字节
) w! j7 c' k: H0 W; y - //reg:寄存器地址( l) ~+ l, f6 d% V. E
- //data:数据8 ?( {, p0 o* U. S/ z/ a& d% H
- //·返回值:0,代表正常
7 K. G1 y& {" w5 S, I - // 其他,错误代码& |. d. c7 {+ s9 [: [8 B- {
- u8 VL53L0X_Write_Byte(u8 reg,u8 data) 6 ~' [& m* A' _
- { 9 [0 A: |, z! l6 A# k
- i2c_Start(); 4 e$ b8 P0 B K
- i2c_SendByte((VL53L0X_Add<<1)|0);//发送器件地址+写命令
% ^( o+ h% I8 }% r1 k9 o6 i9 L- o - if(i2c_WaitAck()) //等待应答, j$ I) h; \& g2 Z% M# A
- {- ~$ V' A) A) g. y7 `+ ^$ R" y* V( h
- i2c_Stop();
6 ^% H' u7 c" W" f$ M0 K& e" @ - return 1; 2 g1 a5 ]! M/ r2 q. _
- }
0 q; c3 P! G, h) p* M' R1 a( F - i2c_SendByte(reg); //写寄存器地址1 }- ~! e. g5 V8 k
- i2c_WaitAck(); //等待应答
9 M. w4 W- |9 V - i2c_SendByte(data);//发送数据
9 _4 y8 t+ g* u. C! a' d, B7 n - if(i2c_WaitAck()) //等待应答3 x% H# L: a9 O: o* a: d
- {" a: e. y# q. o- S
- i2c_Stop(); ; I) o! e! F' i3 l4 G' I/ `9 \0 _; M
- return 1;
6 A7 f/ P& g+ X) E( h7 Q - } ) Y6 O' V9 Q: A7 u0 \. E- b& c
- i2c_Stop();
$ U' ~. d% M9 P. b" \4 U - return 0;: C( n' N; L- S8 m1 R1 ^4 z' P: Y, c
- }
7 t6 X8 c* Z, ?, F5 T! e - //IIC读一个字节6 ]6 C/ f. f! `
- //reg:寄存器地址. U( _/ [ M7 z3 }! `; y
- //返回值:读到的数据
. F% I5 S) q$ T+ p - u8 VL53L0X_Read_Byte(u8 reg)) `6 o: t! ]; [0 g
- {0 S6 c; L- I* A/ U0 }: z
- u8 res;4 d9 q( h5 N7 Z0 X7 Q
- i2c_Start(); 3 o# h) P( M1 Q; T; O$ ]* L
- i2c_SendByte(0x52);//发送器件地址+写命令 * W" F: [" ]( |; a+ F
- i2c_WaitAck(); //等待应答6 e% Q2 X& m9 \) ?9 h
- i2c_SendByte(reg); //写寄存器地址
1 J1 f% t8 H" U; E. U+ [% I - i2c_WaitAck(); //等待应答
9 n. t% k l6 V# M ~ - i2c_Start();
% ]( m8 r1 J+ R( i$ w - i2c_SendByte(0x53 );//发送器件地址+读命令/ f9 y; d7 t x+ ?2 }2 Q
- i2c_WaitAck();
- K1 o2 u$ J' t - i2c_SendByte(reg); //写寄存器地址
4 Q" q% ^! a2 s' Y- Y - res=i2c_ReadByte(); //读取数据,发送nACK
( `+ T* A1 r+ c$ P - i2c_Stop(); //产生一个停止条件2 b' H8 C; G$ w5 C* L: W4 j8 @$ B
- return res;
, d! s. ^, t( z2 C; n' |5 C - }
$ g$ r+ C+ G& ~' C/ J - % D" c) J* O V7 M
- main.c:
4 g! f8 }) x) i' b4 y, M - #include "stm32f10x.h"8 L2 [5 I( x: x3 {+ v( y
- #include "bsp_usart1.h"+ u- g1 ?- f" I. O: U1 T2 _
- #include "bsp_i2c_gpio.h"
: k* a5 f4 _7 R4 J6 P; T2 } - #include "VL53L0.h"# c! M* F3 ?* f& @+ J
- # I' C" l- b# g% ^4 m! ~
- uint8_t Rxflag=0;
/ K# b% p% V: ^: W u - uint8_t ucTemp;# ?! s H- F5 F% M+ |# J3 l% v( A, ~9 F
; l: \# M0 ?) ^* j, L# l- int main(void)* D, e& w+ x, [
- {
, m# e: m; q4 ~/ S, Y -
, Z1 \. v& Q* j, i7 D5 J - u8 val = 0;
4 z- G# q9 Q5 p2 K: x& X - u8 gbuf[16];
" Z8 B4 [! ~+ P- F) L \ - u8 DeviceRangeStatusInternal;
% s% P+ V! V4 K: Y) h - u8 tp=0;
; ?- f7 j4 z- m5 e: F2 f; H! S -
( R. B. e+ r4 w. ~+ ^) ^ h# ^ - uint32_t cnt = 0;8 {( Y& F; o. D. H
- uint16_t count[3];) ]! l K# c- g9 w
- + B7 {5 X7 `) @* L/ p: q& H) }
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
: S/ |6 z2 h6 f( h+ v - USARTx_Config();/ f* Z/ q+ K9 q* E6 x H/ \- P4 G
- NVIC_Configuration();8 H3 M2 ^# s/ Y c+ e' ]; t$ c
- InitSysTick();+ `' k0 f# S8 P2 j, g
- bsp_InitI2C();
; \5 D5 i, g! c3 p7 b f; M3 N - while(1)4 i5 Z$ ~3 a# d1 x0 |
- {7 e9 F2 F+ b4 l# r; K6 F7 N
- 5 Z# d% u% J% m4 v
- VL53L0X_Write_Byte(VL53L0X_REG_SYSRANGE_START, 0x01);
8 {* V& Z D; n$ c. |& a - 1 |* v0 u0 e8 A: Z& t4 g0 P
- while(cnt < 100)( W; @$ A5 W' ?6 ^
- {
3 ?+ c! W2 i. B3 \) `9 ]) [$ o - delay_ms(10);) C9 [ O$ ^1 q- R" Q' z
- val = VL53L0X_Read_Byte(VL53L0X_REG_RESULT_RANGE_STATUS);0 s2 }0 R4 f) Y& F8 q7 Z) i* t2 k
- if( val & 0x01) break;
7 C- G( p7 m! v/ h - cnt++;
4 O5 C: k7 s/ i4 h - }
0 S' y4 G! Y. M5 t* s6 S9 R8 U( d
2 k- q8 ]7 o7 ]7 c8 Z" d, ]- x- if( val & 0x01)/ X6 s4 K4 Z+ T' ?, f- S; O# K1 d
- printf("\r\n readey \r\n");
# d; X: x9 J/ \' Z - else6 R; C- R) h f; j8 X, L3 H
- printf("\r\n not readey \r\n");
" u. @$ ~" o9 S3 n/ Z - VL53L0X_Read_Len(VL53L0X_Add, 0x14 , 12, gbuf);
3 r6 G6 x: x3 J1 }2 k7 O* B& ^ - 6 |" m$ w2 Q2 y: m
- count[0] = makeuint16(gbuf[7], gbuf[6]);
& d( [ B5 f9 ~ - count[1] = makeuint16(gbuf[9], gbuf[8]);
2 X. r+ R5 \. H5 ~5 p' O* } - count[2] = makeuint16(gbuf[11], gbuf[10]);# f, j2 @# X6 n) ?6 T* c8 j
- DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3);
: |5 B: u; L4 |/ F3 }8 T
- {. m/ ~9 z/ P4 q- printf("\r\n ambient count = %4d signal count = %4d distance = %4d status = %d ",count[0],count[1],count[2],DeviceRangeStatusInternal); k2 @& l8 E9 x* F& t) y
- 8 G* S4 t* j# B* V' Z& P6 ~/ T
- delay_ms(1000);7 B, X; D/ O- B3 g" S3 e
- . P( m3 \; A) N [; Q2 w7 a/ e
-
/ l+ g# O# f4 ~) [4 u - }+ \$ }$ y9 Y9 H$ z$ M
- }
) z& m% {" Y8 t9 x- Q( o - % w' g @5 q# E% O% J
复制代码
; c& R* k W; X |1 @$ V4 D- g————————————————6 r0 z1 u/ i* q% W
版权声明:TangPeng_HanMO" _- I* i# B4 j/ u0 \& @
5 N! @% E# D0 y
7 \" q# {1 L0 a* R( J$ i
|