你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】基于STM32的三轴数字罗盘HMC5883L模块的测试

[复制链接]
STMCU小助手 发布时间:2022-2-9 20:25
     最近买了个数字罗盘模块,调通后发现很不错,非常灵敏,测试的时候精度在1°以内。连续测量模式下,最快测量、输出速率可达75hz,模块每次测量完毕并将数据更新至寄存器后,其DRDY引脚便产生一个低电平脉冲(可以配置一个外部中断捕获DRDY引脚的下降沿,并在中断服务程序中读取数据),在STM32中可以设置一个下降沿触发的外部中断,并在中断服务程序中调用角度数据读取函数。以下为操作该模块的主要步骤。
一、IIC协议相关操作(单片机作为主机控制时钟线)
宏定义:
  1. //这里用到了STM32的位带区操作,方便实现对一个位的操作
    7 Y6 D& H/ }4 {8 C; E! N: a
  2. //PB13配置为OD输出,同时外部给上拉电阻,这样既可输出信号给从机,也能$ V4 L$ P; ~; z8 N
  3. //在PB13为漏极开路状态时接收从机的信号(STM32的IO配置为输出模式时,
    9 X& e1 b  J6 i; N
  4. //IO口的电平也会不断地被捕获到输入寄存器中)" e1 V& M9 Y+ w9 l2 u9 K9 V- s
  5. //PB14配置为推挽输出,PB15配置为浮空输入
    ' w8 W+ p: c  m. X5 l3 j5 u
  6. #define R_SDA    IPB13          // PB13输入寄存器
    1 A( u" I8 v$ b! D! i/ }" p
  7. #define W_SDA    OPB13          // PB13输出寄存器# K# w% d$ M* e
  8. #define W_SCL    OPB14          // PB14输出寄存器
    ! b. i% F0 q, d% D- _; o3 E
  9. #define R_DRDY   IPB15          // PB15输入寄存器
复制代码
  1. #define Xmsb 0     //X轴数字量的高8位- A3 b( Y2 R; Y  m! {7 Z% a+ U
  2. #define Xlsb 1       //X轴数字量的低8位" ~1 p% Q9 U$ K3 o2 q; B" o: k
  3. #define Zmsb 2     //Z轴数字量的高8位3 }; ~5 q4 l# A" h4 P
  4. #define Zlsb 3       //Z轴数字量的低8位
    ! s8 i6 S5 F" B7 v7 Y
  5. #define Ymsb 4     //Y轴数字量的高8位
    : T$ l9 G( m+ _. U# s
  6. #define Ylsb 5       //Y轴数字量的低8位
复制代码

6 i+ ]9 `) Y4 t
附位带宏定义:
  1. #define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C
    ; z2 I9 w& s& k: r6 }6 u
  2. #define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08   
    ! M7 l: G' P- @

  3. 9 B; Y3 D* K# r/ N
  4. #define BITBAND_Addr(Addr,num)  ((volatile unsigned long *)(0x42000000+32*(Addr-0x40000000)+4*num))' S# Z: C- t7 i
  5. + D, B+ `! E8 q' y
  6. #define IPB13    *BITBAND_Addr(GPIOB_IDR_Addr,13)$ x3 Z2 a. Z7 r5 Z" P
  7. #define OPB13   *BITBAND_Addr(GPIOB_ODR_Addr,13)
    * i; L. i0 M( m5 A
  8. #define OPB14   *BITBAND_Addr(GPIOB_ODR_Addr,14)
    8 a% L, N* D. O2 j7 A+ k
  9. #define IPB15    *BITBAND_Addr(GPIOB_IDR_Addr,15)
复制代码
1 d6 S# ^1 e8 n& `! y  j
启动IIC传输:
  1. void _iic_Start(). ~5 V% c$ e1 v: P, X
  2. {
    : {3 N3 [6 b, ], B' q3 C
  3.      W_SCL=1;    9 x8 f: ~" C; @( @7 R# n: ~9 n6 ^6 j
  4.      W_SDA=1;3 [; Z' ?% [7 E) s
  5.      _delay();
    6 r( O. p: M$ @) j) c. o% i
  6.      W_SDA=0;     //SCL高时,拉低SDA,表示开始IIC传输,占用总线8 @* p/ G! `' j. W
  7.      _delay();
    5 l& z# j+ A( b# v
  8.      W_SCL=0;     //控制SCL% i8 f  S7 w& K; D1 ^
  9.      _delay();, U: |& W; F  A& z. E" Y
  10. }
复制代码

" R8 F# R# X+ k1 ^: M* h0 z* L$ L
停止IIC传输:
  1. void _iic_Stop()3 C) [( {+ n) K3 p& q
  2. {# Z, ^  |3 F* T! K
  3.      W_SCL=1;     //释放SCL(由于没有其他主器件,SCL无需开漏)- C0 z9 P+ I3 v6 z" w
  4.      W_SDA=0;     
    : @/ h7 W- x7 n/ F4 Y  L6 z8 A# h
  5.      _delay();9 u( P: g* U; D7 _) z; a
  6.      W_SDA=1;   //SCL为高时,拉高SDA表示结束ICC传输,释放总线
    + b* t+ i  d" p  `/ B
  7. }
复制代码

" \/ p/ n' F) p( x4 g
发送一个字节:
  1. uint8_t _iic_SendByte(uint8_t dat)         
    " X3 r) m7 \2 x4 A; ?" Q8 J: V
  2. {7 f9 f9 v' a. S* ^  C
  3.         uint8_t i;6 S4 S1 |7 J$ E. b( B
  4.      for(i=0;i<8;i++)' Q* I; u: Y, ~
  5.     {" w( ]% T6 z1 B! ^& W8 u  T
  6.         _delay();
    - E5 J2 f( ?+ Y! I& \
  7.         W_SDA=dat>>7;     //SCL拉高之前写SDA
      p) |/ d5 e4 ?6 S3 r' F
  8.         dat=dat<<1;
    * w- d1 [5 W1 ?5 H+ h& l" s
  9.         _delay();
    - ^" O( T# q1 {' K! X
  10.         W_SCL=1;         //拉高SCL,从器件开始读取SDA 5 f/ u( ]2 j; o* N. ?$ ~* h3 b4 y
  11.         _delay();5 [+ K" S' X1 _  q" B
  12.         W_SCL=0;         //重新拉低SCL
    - M4 {0 j/ N/ F  h5 [- I
  13.     }4 G) b) N5 }- a2 A1 _$ _) M/ q
  14.        W_SDA=1;             //释放SDA
    , k( c1 O( ]  b: K9 ~
  15.        W_SCL=1;             //拉高SCL,读取从器件应答信号  E# V- r1 z4 l
  16.       //   等待应答 $ m( P( w- Z' \  ^. v2 s
  17.     i=100;
      p: L! Y8 F" |
  18.     while(i&&R_SDA)  {i--;_delay();}, _" i4 N* C; ~1 ^& y! F0 D, H" B
  19.     if(i==0)               //无应答
    % G2 W& b) q* s. S. d- j4 e2 c
  20.     {0 K) `* S/ E! l2 X4 m" X
  21.         W_SCL=0;         //重新拉低SCL6 q8 h9 o4 `+ H+ O
  22.         return 0;
    , ]% o1 ], h- F# T$ U
  23.     }
    / V( i5 ?" O$ c' W
  24.     else {                 //有应答4 V2 @0 C# H' v; I# n0 `) N% F
  25.         _delay();" k8 d& t% q9 M% I- Y
  26.         W_SCL=0;         //重新拉低SCL
    % B2 p- W: D" O7 i/ r
  27.         return 1;
    & R9 g0 w/ E$ m6 a/ _
  28. }
    - f0 L+ z0 m, y- h/ h/ Z/ u
  29. }
复制代码

7 e9 H) _, o" G9 r
接收一个字节:
  1. uint8_t _iic_ReadByte(uint8_t Ack)  
    0 j% l  h$ i# u3 q+ S
  2. {1 R( n( N! I9 D- s) f
  3.      uint8_t temp,i;4 F  ]6 I$ h- O+ c
  4.      W_SDA=1;              //释放SDA
    & E6 O- b% P% ~& ]
  5.      _delay();
    8 b* f1 I* n/ l2 g
  6.     for(i=0;i<8;i++)3 e0 K9 [$ I8 X; ]4 W: L1 ]
  7.     {( [' v$ [( R' ?$ d
  8.         _delay();, w) H+ \. t; {$ f' F$ D& C
  9.         W_SCL=1;          //拉高SCL开始读取SDA          & f! Y6 Z1 Q! f3 a7 i/ l
  10.         temp=temp<<1;      
    - E0 C. s* v6 W! v: b
  11.         temp|=R_SDA;      //SCL拉高之后读取SDA, ^' c" A% p) j' C7 N; z5 c8 Q) i4 o3 N
  12.         W_SCL=0;              //拉低SCL,从器件开始放置数据
    5 o% y+ p) b$ I4 a2 }& L: y
  13.      }
    - Q# T6 D3 c+ q6 q
  14.      //发送应答信号3 Y* }% l% a: U
  15.     if(Ack)W_SDA=0;             //拉低SDA表示应答
    ' A# l9 E/ j' N- [
  16.     W_SCL=1;             //拉高SCL,从器件接收应答信号& u2 j% c$ l  C' L0 I# \; V3 m
  17.     _delay();
    - l% B7 C7 P+ |& J% y+ o9 c: f
  18.     W_SCL=0;             //重新拉低SCL/ }  u; g  ]- D) Z6 s
  19.     W_SDA=1;             //释放SDA
    & i+ U; x  O) V) j
  20.     return temp;- w& @3 O/ Q4 G, z+ l+ j* K- w) E
  21. }
复制代码
; U* l4 r1 b2 o
0 Q7 B) |4 D% i% H$ F: {
二、配置HMC5883L模块) K9 b# P- G7 |$ q* ]0 h% ^
  1. void HMC5883L_Init()
    3 E* O9 D6 Z+ y! C! Y# m/ t
  2. {     $ F9 S+ p: ?. x5 ]. G7 S
  3.      _iic_Start();* k- j8 C- C1 [; V& ]7 q
  4.      _iic_SendByte(0x3c); //写操作9 Q" R6 S) K5 G( p0 g
  5.      _iic_SendByte(0x00); //指针指向00,配置寄存器A
    - V, J' Z* I6 k* E
  6.      _iic_SendByte(0x78);  //数据测量、输出速率75hz
    8 ~- u* X* ~! `9 s6 H1 O2 M) m
  7.      _iic_Start();          //指针定位到02,模式寄存器; P) t( g+ w% B" e$ ]5 e) \3 Q  o7 F! o) l
  8.      _iic_SendByte(0x3c);
    4 z; l6 y9 t6 j& ]
  9.      _iic_SendByte(0x02);
    - N, l/ T( {3 N- L, R9 C$ W
  10.      _iic_SendByte(0x00);  //连续测量模式5 T+ d$ R( i6 G# R+ l( Z1 O
  11.      _iic_Stop();
    ( N" Q) v9 T. i
  12. }
复制代码
/ Z/ k0 N" v' b: x3 q  s, `# }' Q
三、读取角度数据
接收三轴数据,处理X,Y轴的数据并计算角度:
  1. int16_t HMC5883L_ReadAngle()
    ' X6 s; b& H+ a. j8 O$ o
  2. {
    " B/ v) h  @6 g; l
  3. static uint8_t i; + v  c' j0 F1 J, B: S
  4. static uint8_t XYZ_Data[6]; //用来存储三个轴输出的数字量; o" A3 }. _( W; t) }+ w! n( j

  5. 5 B, A# p* C/ C9 A
  6. _iic_Start();
    ! A5 P  j# ~; H# l  z3 c( P
  7. _iic_SendByte(0x3c); // 发送HMC5883L的器件地址0x3c,写操作
    ) i0 O2 p* y/ X% G+ z2 [
  8. _iic_SendByte(0x03); //指针指向03,X msb寄存器   2 R" b( }/ v( d  l& D$ Q2 n
  9. _iic_Start();          - x% {$ |* v% }" f
  10. _iic_SendByte(0x3d); //改为读操作2 H3 @! |( x3 N+ S. i% b+ J/ A

  11. . F/ K3 g2 I& c. @- q
  12. //依次读取三个轴的数字量
    " s" }! D! T7 A0 t/ A0 F
  13. for(i=0;i<5;i++)        //前5次读取发送应答信号! Q' y* M! u7 T  V( i
  14. {: M- ?0 b$ Z" {2 \& Q/ s
  15. XYZ_Data[i]=_iic_ReadByte(1);" R$ L- O% \/ W& y, H
  16. }$ Q3 q9 [) i: w$ x- _0 _
  17. XYZ_Data[5] =_iic_ReadByte(0);  //不应答
    # \! j* }7 @/ C; `& c
  18. _iic_Stop();
    ! B+ |2 v6 x, U3 w2 I
  19. return atan2( (double)((int16_t)((XYZ_Data[Ymsb]<<8)+XYZ_Data[Ylsb]) ),(double)((int16_t)((XYZ_Data[Xmsb]<<8)+XYZ_Data[Xlsb])))*(180/3.14159265)+180;       //计算角度,需要包含math.h头文件
    1 d1 S0 Z. G! Q/ E) R9 R& n
  20. }
复制代码

& p# G' S( B+ Y
2 {- O8 G' K( w6 V. Z  ^  _3 I1 H
配置好IO口,调用HMC5883L_Init()后,便可调用HMC5883L_ReadAngle()读取角度值,0~360°。
以下为测试时的截图:
272324479545732.png , j3 O5 t+ n7 u$ w3 `0 X6 z
" E3 u( f) W# _8 m( G" ^7 Y, Y9 u
测试时,模块比较灵敏且精确,稍微旋转模块便有精确的变化。由于该模块是基于对地磁场的测量,此模块容易受到其他磁场的干扰,比如将该模块靠近直流电机时,
便会因为电机内的磁场而降低精度甚至失灵(之前做智能小车时就遇到这个问题,要将电机内的磁场屏蔽起来才行)。

3 c9 p8 `5 K( p( \1 P
赞 收藏 评论0 发布时间:2022-2-9 20:25

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版