一、 MPU6050简介* x3 O8 F3 K3 }4 |1 j
MPU6050集成了3轴加速度和3轴陀螺仪,是一款不错的传感器模块,可以用在很多方面,比如:四轴飞控、空中鼠标、两轮平衡车、GPS定位方面、游戏机、3D遥控器、平板设备等等,此模块给我们提供了强大的数据供应,由于将加速度和陀螺仪集成到了一起,免去了组合这两个模块时之间的轴差问题,减少了包装问题,这两年很火,成了DIY制作者的最爱。" T" _' d+ j9 ]6 J( w4 k m
0 c- T$ C) ~' k1 G8 f
二、 细节问题
$ M* w, v$ v. |' b2 a$ h在淘宝上买MPU6050时,要注意一下几点:
9 J3 O1 s) k; Z( Q1 r4 M; L/ l. L( a# D$ ?7 b0 G+ R
1.查看卖家给出的介绍信息,是否刻意夸大,结合自己所学的知识进行判断。比如:当时在某个论坛上看到某位大神将的,tb上说他们的模块采用高性能的微处理器和先进的动力学解算与卡尔曼动态滤波算法,能够快速求解出模块当前的实时运动姿态。细心的你就会发现它的这种处理器根本就不能提供这种需求,也就是会所计算不出来。2.不要让模块受到碰撞,否则会影响他的性能。
3 T! x2 Y) S1 o' D& }. e. B. S1 A2 j( b3 b/ ]" A" | ?
三、相关技术
" s T0 ?) S4 zMPU6050数据是用IIC进行读取的,So必须学会IIC。类似于USB协议,不过和USB比起来可以说是小巫见大巫。
9 @0 s$ H7 b# @/ Y! X3 J2 E4 E
0 H' C* z% N2 r1.IIC技术概述
" @9 x! n& F+ w t. ?: |) |# lIIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是 由飞利浦半导体公司在八十年代初设计出来的两线式串行总线
3 l9 Z a6 `, F+ C+ Q特点:接口线少、器件封装形式小、通信速率较高
" K3 K# E3 w" _- Q6 M0 sIIC总线只有两根双向信号线,如下图所示:' a' u, f4 t; D2 t9 S+ E
/ R7 l) b2 }2 \) z
, G( L8 l" ?' y' H
8 T1 R. Y9 C" D2 \8 c* l, f
2.IIC数据传输 U4 x1 a" V6 P" \
数据有效位的定义:
' @( i, G' R9 t3 b2 ?1 K, H: ?( l IIC总线在进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线的高电平或低电平状态才允许变化
4 {& V& u7 Q5 q起始和停止条件:: T; s, c& p5 j ?% r% C
当SCL线是高电平时,SDA线从高电平向低电平切换,这个情况视为起始条件。. \0 `/ d* [* w
当SCL线是高电平时,SDA线有低电平向高电平切换,表示停止条件0 Y: T M* Y6 W! g' D8 D' {
数据传输格式:
% f }2 j8 s* b4 n) B
. u1 E) A6 h, c0 n
( ]8 o8 ?' z) I8 B T2 P/ {
5 w" a7 W; a+ B; V. D5 m' [
3.IIC总线寻址
- I- b9 q3 ^, L! L& ?- x) OIIC总线规定:从机地址有第一个字节的7位组成
( T* g5 K0 h) D; O+ m: e(想要插入表格,可是在这里不会用了,直接从world中截张图算了)
3 o: f/ o; d) s; c- e
/ H$ D h% _/ q m* A" b2 [
: A! E! l7 ~, e. `1 v, ]9 w( A D) w0 B1 r7 N8 S
4.IIC总线编号
+ N4 V q) R5 c! }9 J1 m6 P从机的地址有固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中科编程部分决定了可接入总线该类期间的最大数目6 `, f& Y% ?6 J
) `2 Y$ ~/ \+ R" B W4 _* {( U
例如:一个从机的7位寻址中有4为是固定的,3位是可编程的,那个这时仅能寻址8个同类期间。' L A; T3 g- m' m9 k- Y+ ?& P# Y
0 M: v0 L5 e1 F# @* S
5.STM32F103中的IIC
/ W. Z0 `* D0 J7 n这里主要看下载STM32中,IIC是怎么个分布:
8 G+ k; o( H7 b l4 o) ]) R! W/ v0 Y% ^4 F
8 z* h" ?8 ^2 g2 ~" K. B) _& K2 p- ?. v) W% T% O* L
四、操作步骤
2 `) h) Y" O9 }1.熟悉MPU6050管脚2 f u* J: ~6 w( Y7 ~
+ }. B( l* s* L
8 w% M; i* t* A' T9 Y9 w# Q! R
: d" `" Y2 V+ O- h
上图是我自己的模块,由于连上了线,管脚不是很清楚,可以看着一张,管脚和清楚的。
% E( |, G" V' g! V4 w; ^, K* e' x7 B: y6 n# }
- h0 ~; T9 A+ C+ J9 _1 }
+ d5 ^+ ^8 t5 @0 [& d! v2.管脚功能介绍
7 v4 P/ b: p% Y0 s; m: c$ h
" u3 M. }: X; ]
' R7 [7 q% i1 Q$ g
3 R% x, d" u, N$ K这里已经很详细的介绍了各个管脚的功能,在心里就有个大概了解,在后面编写代码中就会明白好多,更多细节可以查看手册的!
+ B. a; s; p. n- |# e- P0 _2 c& ^! R i& n. B9 h
8 S# L3 [" F! ?3 l& u7 g1 K五、实现代码
' k; Z$ T& b( |9 U当时开始做这个东西的时候参考了原子、野火的各个例子,再次感谢你们。' k- X: }' f$ Y& J" S
! }, o& M) h# e代码块# ^3 L0 `" k0 A2 A9 A& H9 E+ E+ h" z
MPU初始化:' ~6 }# o7 S4 t) o( U
- k* |7 \& W# L- void MPU_Init()
; O! C2 F3 i( h: L - {; b* X- T7 K: x$ M. r. o, `
- ANBT_I2C_Configuration(); //IIC初始化
5 m8 p: u, J' B; ]- j - delay_ms(30);+ p3 F- P5 K/ E0 s8 g$ O/ b9 P9 H
- AnBT_DMP_MPU6050_Init(); //MPU6050 的DMP初始化
* b% X9 z* i, [0 B6 Y; U - }
复制代码 + @- s' V& g; R" l
下边是读取陀螺仪的数据函数:; a( a+ C& h" Z
% O8 { c) D! x- void Read_Gyro_data(short *rxbuf)# ^/ X- O8 U9 d2 S
- {1 E. w0 ^, N1 ^9 Q7 J
- unsigned long sensor_timestamp;* M, S. U4 ~! T c. f/ U
- unsigned char i = 0;
. o: E8 |- v) u. Z8 I0 p - short gyro[3], accel[3], sensors;//陀螺仪存放数组,加速度存放数组,返回状态量
2 I5 u6 h$ _+ F2 ?, E' J/ C& G' Y - unsigned char more;# W6 A$ Q/ t5 U1 m! T6 T- o1 Q
- long quat[4]; //四元数存放数组$ S# U. e: @3 Q) \6 E
- ! ?; P. a) p5 U; w
- dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more);$ s0 @: S" }+ l5 x: ?/ W
- if(sensors & INV_WXYZ_QUAT)* @! j! X3 }& ~: L0 y4 s' H2 G
- {% h. b) R% b( L4 o8 S0 W+ _6 [
- rxbuf[0] = gyro[0];6 z" A) i8 J/ A h) Y
- rxbuf[1] = gyro[1];1 j; {: U7 S$ M Y' O: X6 D: }$ |
- rxbuf[2] = gyro[2];. ^) `8 ?* b% k1 U. }% M
- , x+ ]7 B% l. X8 [" }
- printf("\r\nRead_Gyro_data gyro:\r\n");
. L" z1 ~3 J( s6 p - for (i = 0; i < 3; i++)
) ~2 T* i i6 I+ o; X - {# P0 W g+ P! k- |, y s9 P" D
- printf(" %d ",gyro<i>);3 V) A4 }) [6 G* e
- }
, D R! O" _# [ v - printf("\r\n");
. g1 r' }: z+ C6 v; @( \. c. v - }
* p/ Y1 i Y0 u - }</i>
复制代码 # e# m2 V( U3 P$ \1 y" D
其实参考了圆点博士的开源代码的,看了下匿名四轴下位机协议,当初试了下自己获取的数据是否正确,很不错的,下面是匿名的协议:
T8 L8 y; ?3 s: O' H A
- J6 S; z9 O6 d" D7 Q- /*$ ^* k2 a0 W- B3 _* A/ t) o
- * 函数名:Data_Send_Status
: G0 n* e8 ^4 u4 g; U - * 描述 :数据发送 传感器 的状态. z7 |; P& i, a6 ^4 H7 e- T6 \
- 根据匿名四轴最新上位机编写的显示姿态的程序$ o [% r: \9 k& `8 O" I
- * 输入 :Pitch:俯仰角" ~8 A+ }2 j( W3 p9 T0 E
- Roll :横滚角
) @7 ?! |( O, t- v - Yaw :航向角
( ^6 F4 O- m5 J5 N! Z1 z - gyro :陀螺仪7 y4 r* Z3 p* J4 Q
- accel:加速度! ^: ~4 x) W9 v' s2 {0 Z/ ?+ H
- * 输出 :) a! j. A# T. y8 r
- * 调用 :
- {/ e% K o4 w W! J V* O - */
3 v Q7 p+ T; h/ Z3 S5 V - void Data_Send_Status(float Pitch,float Roll,float Yaw,int16_t *gyro,int16_t *accel): w& b1 L5 D! n5 P) b
- {0 Z3 \) f ^6 i6 `. E: u. q3 {
- unsigned char i = 0;
4 Y* e3 @0 g0 Y: X! m7 Q6 G* L - unsigned char j = 0;
! b; N6 l8 l3 u: o; g: {3 I - unsigned char _cnt = 0,sum = 0;
3 S1 Y8 O" b G, E9 C M, R+ g - unsigned int _temp;# c$ N1 G1 Y- x+ f, p/ A4 r
- " k6 e D% n6 @, I1 W/ K3 a
- u8 data_to_send[12] = {0}; //发送数组,初始化为0, P: h$ O* `2 x6 s
- 8 ?: Y2 e3 b. R% }
- data_to_send[_cnt++] = 0xAA; // 帧头 1706 Q/ i4 t; @. W6 {; k
- data_to_send[_cnt++] = 0xAA; // 170
: I' G* e- n& C$ U$ N# o) |+ H - data_to_send[_cnt++] = 0x01; // 功能字 16 t% l, E& t3 m* O4 X& z; Y' `- B
- data_to_send[_cnt++] = 0; // 长度 09 O' j( W8 Y- w
- / G$ o9 j& n" Y& Q. x
- //横滚角% [# ~/ ~8 p/ j4 U4 U
- _temp = (int)(Roll * 100); $ M a& P: F% U9 ]
- data_to_send[_cnt++] = BYTE1(_temp); // 高 8 位4 \$ ^9 T. Q# D8 }
- data_to_send[_cnt++] = BYTE0(_temp); // 低8位% q( W2 g- y2 ~' r6 I. m
- ; n' g6 e p6 L) ?8 [; o' ]
- //俯仰角 d; W' k3 _) g1 S
- _temp = 0 - (int)(Pitch * 100);
1 h, P' l# ^9 c; J$ ? - data_to_send[_cnt++] = BYTE1(_temp);
; F& W$ F' o1 F8 I - data_to_send[_cnt++] = BYTE0(_temp);0 P0 u9 r: ^: C7 ]
8 l" Z: N# s7 B5 b+ G& ^' K1 R- //航向角' q! Q$ p; u: g' E- t2 z
- _temp = (int)(Yaw * 100);$ F" {) N1 B+ U$ l3 R: U' b
- data_to_send[_cnt++] = BYTE1(_temp);8 Y( V7 \8 [$ O& c
- data_to_send[_cnt++] = BYTE0(_temp);
6 [( s. W) P' b$ G) J/ J6 K7 p% a
, h$ y$ f& i8 ?8 N( M# ~- data_to_send[3] = _cnt - 4; //数据长度" k3 R6 E' E, [+ a! Y( J
- % P" {$ O* P* R" p6 W6 ^
- //和校验
$ F. W) S/ z8 K9 [: \! L( F, p. Y - for(i = 0;i < _cnt;i++)
! p e8 k. S6 ^2 J - <i> sum += data_to_send;5 G( A6 L" v& U* D: D" l. X8 F
- data_to_send[_cnt++] = sum;
. D8 s! W6 [6 D0 u
0 r3 _+ T2 t1 K# v) S! Z$ h- data_to_send[_cnt++] = '\0';
/ s$ x. o, z$ s4 G/ Z - 4 S) R1 n6 C! t/ y( l
- //NRF_Tx_Dat(data_to_send); //发送到NRF缓冲区: U! O, p9 O( n& u. l' \0 a
- //printf("\r\nData_Send_Status:\r\n");" C0 i( w- I0 L/ h3 u: J( F' W. w% f# O
- // for (j = 0; j < 12;j++)( Q# f4 K4 e+ Q! d) ?
- // {" @* i7 j$ G6 X8 @
- // printf(" %d ",data_to_send[j]);6 h6 {! E& g1 l8 C* Z% N
- // }
4 Y/ d, V' e) a, f& a8 q# z - // printf("\r\n");, [8 L' |, p! m# @$ s" H$ ~
- // //串口发送数据
0 R2 J: C3 @8 u2 o. b" Q - for(i=0;i<_cnt;i++)
4 W% s3 X: k& O& [% v0 T* f - AnBT_Uart1_Send_Char(data_to_send);& O) w2 E p+ a. T' t: m+ d
- ) b7 z( ?: G: x( C
- }
, G% V1 u( n1 z2 f1 b
# C' F& A1 ]5 U4 W4 l X. A. a) {- /*- O# I/ R' { Y6 e
- * 函数名:Send_Data
6 M d5 M) D; k5 w) x - * 描述 :用于发送传感器的数据
( K! f! B# t5 D7 l. T+ b+ E1 Q; H - * 输入 :Gyro:陀螺仪存放字符指针
* j1 o- D1 u; N9 U - Accel:加速度存放指针
( k, X+ f5 j6 [% k0 V4 H - * 输出 :
- ^. [8 B6 p* G2 ^3 F - * 调用 :
7 |) y4 U% q. P$ Y6 X5 O K - */
4 _) W9 x$ I+ Q+ d8 M7 B - void Send_Data(int16_t *Gyro,int16_t *Accel): g. z7 I/ @4 o
- {
/ h" |) |5 i/ j - unsigned char j = 0;. g# S* {" Q8 N6 X7 G+ y* w
- unsigned char i = 0;* o4 t7 a: F/ H$ N7 z
- unsigned char _cnt = 0;$ v# |" Y& ?" z' }# D
- unsigned char sum = 0;$ u' M! }: G3 {2 e- o. X
- u8 status;
0 a6 T9 V6 S$ a! O$ ` - // unsigned int _temp;2 l+ g0 b6 f S# s. }! @
- /*0 L$ K$ k' C7 q6 j! n
- 匿名数据协议:
# a z8 B& D/ ~9 H% Q% H - 帧头、功能字、长度(len)、数据、校验和(Sum) 共32字节
. B" y* J/ J1 z8 [ - 接收端在接收到数据的时候进行对应的解析即可+ V9 L8 O: w& x0 s" D6 ]
- */
$ y7 @2 U8 N, a - u8 data_to_send[12];
% k# Y$ Q* a' t8 L& y - & |' }) d# @, [' Y: H
- data_to_send[_cnt++] = 0xAA; //帧头 AAAA
6 k8 P* {1 Q7 P- d* J* D/ G - data_to_send[_cnt++] = 0xAA;
2 L* ~ D' g1 _" N# y6 u - data_to_send[_cnt++] = 0x02; //功能字
3 \9 y- o* q' Z. d+ W* G3 T ~ - data_to_send[_cnt++] = 0; //长度: @$ H: x& F7 ?3 p$ a
- ( \6 S( o7 w# V M" b7 L0 b8 l
6 @- q+ M2 y( a3 w5 n/ c" ?- data_to_send[_cnt++] = BYTE1(Accel[0]);+ B% _6 @! K0 [+ P
- data_to_send[_cnt++] = BYTE0(Accel[0]);3 U4 n# ]- l3 i: X5 s0 G- n1 E
- data_to_send[_cnt++] = BYTE1(Accel[1]);
0 F; |1 r% s* V$ ?+ ?. N - data_to_send[_cnt++] = BYTE0(Accel[1]);
7 g' g/ P. T2 Z; @2 \* b# o! H+ l- Z5 T - data_to_send[_cnt++] = BYTE1(Accel[2]);
5 |7 \7 F! n4 Q" g v, p1 Z% L - data_to_send[_cnt++] = BYTE0(Accel[2]);: D0 n- o$ W$ E, u% M9 E7 x, k$ h( h# \
; U. a: J, g# u- N) d# ?6 Z- R4 E- data_to_send[_cnt++] = BYTE1(Gyro[0]);
$ e: v- F4 g2 \: y2 H7 I - data_to_send[_cnt++] = BYTE0(Gyro[0]);
Y, F+ t; E8 N$ g2 R+ n. }0 A - data_to_send[_cnt++] = BYTE1(Gyro[1]);
+ ~( `. ~, T# L( ^ - data_to_send[_cnt++] = BYTE0(Gyro[1]);1 r) I, Z' p$ O( Z3 C
- data_to_send[_cnt++] = BYTE1(Gyro[2]);
% u& }" F+ e, ~2 p8 V: ~/ C F( Q5 } - data_to_send[_cnt++] = BYTE0(Gyro[2]);2 \! b+ M% C( f' }! T# i3 r
- data_to_send[_cnt++] = 0;
: O! q6 ~$ S. E6 f4 {/ L - data_to_send[_cnt++] = 0;/ ~8 o6 @7 y( O0 k" O) E
- data_to_send[_cnt++] = 0;
" N6 m$ a7 L: m% y' Q
) ?3 J- T. m' w7 a! U9 X N7 x- A- data_to_send[3] = _cnt - 4; E' p4 X% Z% Y$ p0 S0 I
- for(i = 0;i < _cnt;i++), D R1 J. M; x1 F9 G/ M; m2 g( q
- sum += data_to_send;
4 K5 M! Y! W1 u2 b5 c; i7 o
* k! C4 D2 }6 M2 l0 w+ x' s, }- data_to_send[_cnt++] = sum;
: Z9 _5 u/ T2 g3 `$ n
" G+ O/ x5 \: I' _3 N$ p2 G- data_to_send[_cnt++] = '\0';9 c V% U! q( W: I$ ]3 H
5 \4 g! O; ?# R$ I0 S- // printf("\r\nSend_Data:\r\n");/ L# e. R, V3 p3 b+ {
- // for (j = 0; j < 12;j++)) d+ D$ Y6 ?0 G9 A$ }( G, C
- // {
0 \3 z( i+ g$ `! v - // printf(" %d ",data_to_send[j]);
: V6 M# f; I" D. I% p/ A - // }
) a7 \; U4 j* b( t& {$ a - // printf("\r\n");- z$ ]$ F, y& I4 ]$ x
- // delay_ms(1000);. h2 M! ~/ k# n; p6 D
- // status = NRF_Tx_Dat(data_to_send);
2 D5 g- c. L2 l4 x7 k - // 9 a: L, z; D# G* x
- // /*判断发送状态*/
* y1 A! O; N2 R0 V5 h - // switch(status)
7 Z) f% y* O0 T+ L/ b+ q6 o* a - // {
: L, z8 B( C) l9 s - // case MAX_RT:
: D; |! {4 @5 C$ W6 H9 l& A0 U - // printf("\r\n 主机端 没接收到应答信号,发送次数超过限定值,发送失败。 \r\n");" x, {9 Z4 G$ z5 y! g/ T
- // break;- x9 f$ }6 z. c) P7 u" h7 i
- 7 v! G; i6 x0 U
- // case ERROR:
% d Y0 U6 i$ \$ F& [. w - // printf("\r\n 未知原因导致发送失败。 \r\n");5 I, e( ^/ ]) O' a ]
- // break;2 k. W5 s' ]; Q$ d7 E9 `6 M9 N
# F& ` B4 S7 z# F$ t! }8 `- // case TX_DS:
! R3 P# T( R }9 Y e# E, ]/ ] - // printf("\r\n 主机端 接收到 从机端 的应答信号,发送成功! \r\n");
+ M4 ~( l$ F; ~9 W9 c; S# H/ U& V1 W - // break;
/ V1 w$ S* ~' n- Z+ s6 W - // }
9 R8 j5 R' q5 i. T# o1 H2 B* k/ x - //
/ M# H: M' Q B - // delay_ms(1000);2 B1 P7 f0 C' C9 S% V+ _5 {
- + K8 B1 K( w- z, w+ K
- // //串口发送数据
/ j/ |3 L# X! }/ j6 N7 d1 S - for(i = 0;i <_cnt;i++)& z/ o5 M6 C1 P8 i) b; o5 x
- AnBT_Uart1_Send_Char(data_to_send);
5 P7 p3 l" ~; {: D; ~: ~7 @ - }</i>
复制代码 0 E' D' v9 k3 E$ t4 Z! D
在主函数中调用相应的函数即可获取相关数据,做一个简单的测试。
: I5 E! [; u+ Y/ d1 R" P4 C2 v一下是我从串口中获取的数据:% e# p. D3 g4 A3 `5 ?6 h
! t$ D/ M& ?2 m: {- ]6 `# l
8 e+ O' I( E0 L: a- X2 _$ x以及其他数据:6 v5 `# Y& H0 L: l; b
3 S d. D T! _: ^
) x" y) i* d1 [# E
( {% D$ F* Q8 ?& y+ y5 v1 i
因为代码中使用了圆点博士的开源代码,而且都是现成的,就不往出贴了……, A% f4 l9 ]; [- R1 |) Y
以上只是做了个简单介绍,以及获取到了数据,但是并没有说明怎样处理数据,这是很关键的也是最重要的,本次无线飞鼠的数据不好处理,我做到现在数据也不是很稳定,任然有一定幅度的变化。
, f( S Z4 f3 E+ W5 R& r% y0 ^, ~& n6 f' d$ x' F
! c7 S/ |: @6 ?! R/ y& f: |$ N! a N4 j& v
7 e V! s1 J' L" M, S [ |