一. 简介
: U, h# O- A6 k3 T. ?4 v7 zMPU6050是一款六轴陀螺仪,可以通过IIC协议输出三个方向上的加速度和角速度,在平衡车和飞控中较为常见。一般情况下,模块有用的引脚只有四个(其它的不常用),即VCC,GND,SDA和SCL。操作它的第一步,即编写IIC协议。
5 P* A/ v' t: Z, O1 e7 g; u
1 \" A5 x8 q( C二. IIC协议
6 {! {+ y' Q" z" P7 ]1 p% t作为三大常用协议之一,掌握它是非常有必要的。IIC为主从协议,分为主机和从机。IIC协议要比SPI要简单那么一点,没有像SPI那样还有模式的配置。下面就简单说说IIC协议。
; l( `; ?+ Z- D" I% x( a+ A l! jSDA: IIC的数据线
- V" r# I& J( _; V3 f5 F& ESCL:IIC的时钟线
2 M9 G$ h. E" {
' m& ~% h: ^. w/ y0 y+ P0 b1.空闲态: SDA和SCL都为高电平。
+ W4 W, U+ u7 X' s; _, Q7 H7 S2.起始态: 主机将SDA拉低
3 Z, d* K1 H6 m! {0 k3.数据传输: 在SCL为低电平的时候,将数据输出,在SCL为高电平的时候,将数据输入。
% D1 e) m& Z, d7 {6 h7 w8 b4.应答态:在SCL为高电平的时候,检测到SDA为低电平。2 K$ Y9 W9 ^! w+ s
5.非应答态:在SCL为高电平的时候,检测到SDA为高电平。; F: P( Y: w0 J, ^3 D e
6.停止态:在SCL为高电平的时候,将SDA由低拉高。& w# S4 a2 ]9 h* c0 A
IIC协议一共可以分为这六种状态,接下来将简要说明如何将这六种状态组成一个完整的IIC协议,来驱动MPU6050。
$ W0 [; @+ ]/ e0 N9 c- l2 O2 T y4 J: m' E+ H' c) ~, o
(1). IIC写0 h1 K) T) ?5 }" R3 y. |; S4 L
数据输出是以一个byte为单位的,从机地址一般为七位,与读写标志一起组成一个byte。0为写,1为读。
, d: p% y/ B: \$ M开始 -》 从机地址 + 0 -》 应答 -》发送寄存器地址 -》 应答 -》 写入数据 -》 非应答(应答) -》 停止# X% N) K8 v# _' T! ?
3 Z2 F$ F& @! K% v* g
3 Q& x* k9 h. f) N c
6 I' A5 p' j# H: @(2). IIC读
- O5 {( i6 K! O9 B/ l J! k( p0 OIIC读数据要比IIC写数据要复杂一点,主要是步骤上多了几步,但是状态什么的都完全一样。0 F5 b9 t- y( h- \
; G+ I! Y4 g# g5 L
开始 -》 从机地址 + 0 -》 应答 -》发送读取寄存器地址 -》 应答 -》开始 -》 从机地址 + 1 -》 读取数据 -》应答 -》 停止
; N* r y! [5 e' X# F
- T9 |% l A" D X可以看到,只要将这几个状态实现了,按照这个流程就可以很容易实现IIC的读和写。大概的时序图如下。
/ [2 N+ x5 h0 k% u- O& G- ?* R/ i. C! p
. I/ K6 `$ [ r- t% |3 @
; E& u; S! X6 Y- ~3 N1 F9 s) R
三. 代码实现0 f. V2 q: r, T" D: b; m- H
根据上面的六个状态,定义出对应的六个函数,对应的只有五个,空闲态忽略,因为经过Stop后,IIC的时钟线和数据线都已经为高了。delay时间需要根据从机所能接受的最大速率来决定,不然的话,会出错,下面就来详细说明这几个函数。
# C3 d. ^0 t- W. f, Z5 x( P) q
* t/ s7 ?' l% k% z4 z5 P. V# N
2 n' H1 o- X9 W) j
4 r, L; i" a% x5 Z7 }8 P(1) MPU6050IICDelay(void)
6 _! o# F# V5 l$ f" A/ c这就不用说明什么了
8 X1 H8 p' }; t' e6 ?
: k' E; |3 ^8 [) L! H' g- void MPU6050IICDelay(void)6 b5 B; x c) o: m" H
- {
) k( d# y, o8 U- D) K- O# [ - uint16_t i = 250;
4 x1 i: ` M( c/ [ - while(i--);# R, W+ `2 j& O9 U2 \, ]' y
- }
复制代码 8 p$ L7 V& G0 N
(2) MPU6050IICStart(void)
: [$ X. p# S4 I* t! b: R将SDA配置为输出模式。然后将SCL和SDA全部拉高,dealy后,将SDA拉低,就ok了。
# P% ~& t# c' C5 o/ R& Z m: l( F: o% o1 G; w3 b
- void MPU6050IICStart(void)- U% ~, c3 |2 Z! p3 G# w! P
- {
; G- O) h* I7 Y) O! U; p - SetSDAOut();
& a* p, a0 j7 ?6 s; o) A - SCLHigh();- }" h, T7 ^! P: E
- SDAHigh();6 \: ?/ j7 g5 h" O W$ h; X
- MPU6050IICDelay();. u( z0 o; j+ C; l* {
- SDALow();' B1 r e( L* G- u8 Y
- MPU6050IICDelay();/ ?+ n3 a) S4 @. f4 x
- }
复制代码
2 t& h$ p2 ]- O$ _" r(3) MPU6050IICACK(void)
, s: x" ?0 L" r- n这里只包含了从机应答。- void MPU6050IICACK(void)
3 t6 g: @- g" L, z - {
1 F' a7 \+ n$ e1 w4 n - SCLLow();' f# j! S" i: | j( K [
- MPU6050IICDelay();
2 ~5 k4 Z# S. q$ X. b2 t& h% Z - SetSDAIn();
- H" }6 b9 K3 W" U6 i/ q - MPU6050IICDelay();4 w* B( d4 j. @% K! k
- SCLHigh();7 A' p; f, I' V3 @# V+ Z; v
- MPU6050IICDelay();
4 K& z4 `8 ]$ ?& C3 B% H7 N - SDARead();' Y& u. b. W& H b: P5 E
- MPU6050IICDelay();
& l; ^7 ?2 H+ Z- e, R1 T5 }6 d - SCLLow();
7 t2 C, ~3 n, B( v - }
复制代码 5 j8 F5 i8 [( Y6 s7 g
(4) MPU6050IICStop(void)9 G0 b6 k3 d' P Z- S" |0 K
停止位这里,需要先将SCL和SDA都拉低,然后先将SCL拉高,后将SDA拉高,
2 c( `, L* a* b* l; Z
" Y7 X3 W2 P2 d' H- void MPU6050IICStop(void)
. J1 F/ p* {1 O: N# ?0 c8 m s - {5 K6 d# S: ~- n4 F, K" Q( L( U( @3 A
- SetSDAOut();
2 `6 _$ Q! W( t% J& H' [ - MPU6050IICDelay();( y) w- [6 v9 R5 z8 Q' O4 e8 w: A
- SCLLow();
6 r ?! C0 K. X8 z. j - MPU6050IICDelay();' r2 j' S: Q Q$ W- D* V0 U
- SDALow();% ~) B+ ~ p+ }. J
- MPU6050IICDelay();
$ d' @2 `# J0 b2 C: E7 T2 w - SCLHigh();
, ]7 M' L5 W7 x8 I& m/ |% T4 W - MPU6050IICDelay();
; q4 W" v+ K9 ^9 q+ t; k1 p - SDAHigh();- R ~+ ]7 j! l* X% s \; {, Q: I0 Z
- MPU6050IICDelay();+ p" Z' q& m+ z) n1 v3 @
- }
复制代码
5 {& G$ ]: N [# o1 q(5) MPU6050IICWrite(void)
& {0 M$ G3 y; m2 b/ a( l2 E- void MPU6050IICWrite(uint8_t data)( \( S2 X/ g5 s
- {
4 S+ O, p2 ~0 ?/ i& s/ W - uint8_t i;
C$ s( m+ D8 i, ~ - SetSDAOut();+ n7 _0 D/ J; J" P3 q9 w# x4 z
- MPU6050IICDelay();
, H l1 B8 h; B$ W& q - for(i = 0 ; i < 8; i++)
8 p( R5 u* r+ _0 d - {
* x. h0 @5 k/ n1 H) j - SCLLow();
0 V- Y/ |! A% ?/ o0 K - MPU6050IICDelay();
8 L7 f. ?( |5 J* s - if(data & 0x80)8 y2 {$ G; }3 P3 V+ V
- SDAHigh();
1 L9 i( \! R9 M5 G4 I - else
) F6 u+ v3 V: \, ~ - SDALow();9 Y& A0 J5 p" ]! {" i
- data = data << 1;, M& B5 T4 r' S6 W. E8 L
- MPU6050IICDelay();& V9 K6 {" ~& e
- SCLHigh();
6 H7 t! W8 ^# `- m6 Z - MPU6050IICDelay();. n& t" E! {9 J$ T
- }+ T7 a: h6 v# a9 E1 y* }0 I9 p1 p/ s
- }
复制代码 / w( L/ M8 `6 p9 [: Z
(6) MPU6050IICRead(void)8 X( m9 A' ^0 H; T! {4 j& ]: u: \
- uint8_t MPU6050IICRead(void)) d$ p+ [7 @ K0 t+ f' [
- {, `1 C( k/ h5 m L1 [
- uint8_t i;* f) G, `) T1 M
- uint8_t data = 0;6 Q: w7 D, b# s1 H( o$ {+ W1 q
- SetSDAIn();9 S3 a, j5 p1 [# k) E9 {
- MPU6050IICDelay();/ i1 \+ I/ F3 e Z; J
- for(i = 0;i < 8; i++) b' x2 ?: w; `8 E7 k% X
- {
9 F/ d0 {* k/ ^/ ?' ]/ Y - SCLLow();! B% z; e0 Z+ Q3 B3 @% j* E
- MPU6050IICDelay();
* Q! {; H9 H. j3 c: p& ? - SCLHigh();
' i# d& V3 H* j I% k/ u5 L Z - MPU6050IICDelay();! H6 A6 ^# Y' _ a; v1 j
- if(SDARead()). _. L: }4 ^ C2 N3 Z2 Y" v& h
- data = data | 0x01;
$ @4 w) F6 o' D' } u9 o - else
- T6 M: Y3 |/ N( M - data = data | 0x00;
# G% Y1 C- B' b - data = data << 1;- O0 K0 k2 F5 x, v! O a' i
- MPU6050IICDelay();. R! b) A9 H0 \) P2 \9 B
- }! w) b0 S) D0 m& O' R* m2 i
- return data;
. o) D. c v, _* R R$ ~, r - }
复制代码 0 N9 R/ h* U @
读写数据,可以搭配时序图来,都是将SCL为低的时候,将数据送出去。在SCL为高的时候,接收数据。5 I2 ?) [/ Z ?/ X' X: b' G
然后将上面的这几个函数依次组合成读写两个函数: H' c6 [( Z* N* _; I. `
+ x* k& E9 W5 F# F4 [9 `4 F
+ @6 T# c. m! q; B
/ z3 k8 D% o0 ~* _9 E$ i这里IIC驱动编写完成。/ n! {8 d3 N& n$ ~& ^
/ |0 \7 f/ p/ l# O* d8 y2 Z四. MPU6050 初始化
1 l4 a& H: A, N$ u9 H$ M初始化的实质就是对寄存器进行配置,这就要查看芯片手册,了解每一个寄存器的作用了,以及初始化的时候寄存器配置的顺序。
& E' ^) s4 i4 S% w0 y7 E2 G/ h这里将重要的寄存器列举出来了. ~" x+ w5 d1 t2 u; f6 b$ s
0 b- z# G9 K* }
- #define MPU6050Addr 0xD0' L9 [) V7 G, z w5 ~7 k5 a
- #define ACCEL_XOUT_H 0x3B7 E% L' _/ a; x3 j0 w
- #define ACCEL_XOUT_L 0x3C
$ N* [" f# g1 Y) K1 S - #define ACCEL_YOUT_H 0x3D
( j9 {4 R% N; V2 x% W+ q, V [ - #define ACCEL_YOUT_L 0x3E* c7 v' m. q0 q. H8 B- p
- #define ACCEL_ZOUT_H 0x3F* O, A# g: v% n6 M9 V: m
- #define ACCEL_ZOUT_L 0x40
6 h+ ^, G. C$ J% D - " |4 k& _( Q1 q1 J( U
- #define GYRO_XOUT_H 0x437 f! c0 b+ Z) c/ \
- #define GYRO_XOUT_L 0x44
, J( V3 `* r/ [) \$ n6 X- o - #define GYRO_YOUT_H 0x45
8 |# D2 }, P' P7 X& X - #define GYRO_YOUT_L 0x46$ M+ c9 O! H$ v2 ^8 m
- #define GYRO_ZOUT_H 0x47
0 |1 p! M. O. W8 q! u( [9 Z - #define GYRO_ZOUT_L 0x487 k! M8 H T( {- k
- : r" p' z3 `3 q
- #define PWR_MGMT_1 0x6B# D8 }* o4 P) U% r% w
- #define PWR_MGMT_2 0x6C
0 `% O& \. W8 R% N$ X - #define SMPLRT_DIV 0x19
1 x1 D. J: K& J) W0 R - #define MPU_CONFIG 0x1A0 d' c! ^6 l" D4 P |
- #define GYRO_CONFIG 0x1B
p, r) B& _$ J2 _" h' i - #define ACCEL_CONFIG 0x1C
: V+ [3 o. r8 [# e
/ }/ u& E7 |1 K' @- #define MPU6050ID 0x75( o4 ^& F/ g- }1 e" @
复制代码 # p; J- O& W# D5 u( d, s7 V I
(1). 初始化. U0 A# Y7 H* V( s
- /*MPU6050初始化*/$ e) f& o1 ~8 I% h
- void MPU6050Init(void)5 z, R, O2 j- n H) I5 `
- {
5 V$ s% s0 f- k5 L# K/ W# e8 ^ - MPU6050GPIOInit();
7 B& m5 _' r3 W# T1 R - MPU6050IICWriteRegister(MPU6050Addr,PWR_MGMT_1,0x80);" ?) E2 u. k" A5 d
- rt_thread_mdelay(100);
! W" t; w/ d! s2 b+ d; T& m" W - MPU6050IICWriteRegister(MPU6050Addr,PWR_MGMT_1,0x00);
: `5 X1 z, d0 t$ E+ @6 d+ U8 G - MPU6050IICWriteRegister(MPU6050Addr,SMPLRT_DIV,0x00);
6 v5 Q5 I. u2 _; Q# P - MPU6050IICWriteRegister(MPU6050Addr,MPU_CONFIG,0x03);/ z- S6 o3 E$ N B# l% {* w
- MPU6050IICWriteRegister(MPU6050Addr,GYRO_CONFIG,0x18);// gyro scale :+-2000°/s$ d3 L) m! }1 |1 w
- MPU6050IICWriteRegister(MPU6050Addr,ACCEL_CONFIG,0x10);// Accel scale :+-8g (65536/16=4096 LSB/g)
8 M5 A. j: T, u, Z' |, W - MPU6050IICWriteRegister(MPU6050Addr,0x6B,0x01);
& v( f, C! T3 L [8 x# ?7 @6 l - }
复制代码 % z4 g: z% [0 O5 N/ I) x/ A
(2)验证
9 I5 l6 V3 V2 e: \& o4 {8 g9 D通过读取芯片的ID,可以确定IIC是否有问题。- /*读取MPU6050的ID*/; [* J1 P" K8 J- |
- uint8_t MPU6050GetId(void) m9 j! o1 A; u" H
- {0 l! P* p0 O1 O
- return MPU6050IICReadRegister(MPU6050Addr,MPU6050ID);
/ G7 \# N! N( i - }
复制代码 6 D, W; E4 y; r
分享一个成果图
$ C0 C u' ] y: C# H9 E! Z5 h, r- I& b+ \ O8 G' W
/ v/ J. o0 D/ G* f
: C3 b( M3 E* _+ p
& h" \5 b$ L& V" w; F* N# ^8 k+ T& d8 ]9 ~0 H$ a
|