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

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

[复制链接]
STMCU小助手 发布时间:2022-2-9 20:25
     最近买了个数字罗盘模块,调通后发现很不错,非常灵敏,测试的时候精度在1°以内。连续测量模式下,最快测量、输出速率可达75hz,模块每次测量完毕并将数据更新至寄存器后,其DRDY引脚便产生一个低电平脉冲(可以配置一个外部中断捕获DRDY引脚的下降沿,并在中断服务程序中读取数据),在STM32中可以设置一个下降沿触发的外部中断,并在中断服务程序中调用角度数据读取函数。以下为操作该模块的主要步骤。
一、IIC协议相关操作(单片机作为主机控制时钟线)
宏定义:
  1. //这里用到了STM32的位带区操作,方便实现对一个位的操作- G' [4 e/ j' T7 h
  2. //PB13配置为OD输出,同时外部给上拉电阻,这样既可输出信号给从机,也能
    % @; U5 O4 D8 b! u7 N' I, A5 V
  3. //在PB13为漏极开路状态时接收从机的信号(STM32的IO配置为输出模式时,
    $ C; k) ?& |! \& R9 O* x1 Z& @! [
  4. //IO口的电平也会不断地被捕获到输入寄存器中)' ^2 m9 E5 ^1 X5 K7 G: r
  5. //PB14配置为推挽输出,PB15配置为浮空输入
    + h- v4 _& z$ p* l
  6. #define R_SDA    IPB13          // PB13输入寄存器! }! C7 i: q* e; S5 I3 T7 a9 l
  7. #define W_SDA    OPB13          // PB13输出寄存器* `4 i& \, f7 j' i3 |# j* y5 G4 q6 h
  8. #define W_SCL    OPB14          // PB14输出寄存器, r4 x; N" O8 J  D! N
  9. #define R_DRDY   IPB15          // PB15输入寄存器
复制代码
  1. #define Xmsb 0     //X轴数字量的高8位0 y4 M; u0 ]2 ~/ T4 \* Q
  2. #define Xlsb 1       //X轴数字量的低8位& k! e7 k- a" d" f/ t; z5 U
  3. #define Zmsb 2     //Z轴数字量的高8位
    6 D5 f7 d. p$ O; v) `# g
  4. #define Zlsb 3       //Z轴数字量的低8位: u/ H- h7 C% ?# |4 F1 B
  5. #define Ymsb 4     //Y轴数字量的高8位7 o4 H* c4 p: @8 v2 g& K4 u
  6. #define Ylsb 5       //Y轴数字量的低8位
复制代码

6 c: D! o9 Z4 ]
附位带宏定义:
  1. #define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C
    0 r! \: l) @! T) d% R
  2. #define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08   
    # K7 Q3 M% K! m( c8 m8 r
  3. 1 N) }' T0 u4 C) `
  4. #define BITBAND_Addr(Addr,num)  ((volatile unsigned long *)(0x42000000+32*(Addr-0x40000000)+4*num))1 s6 d2 D) l; S6 W
  5. 9 C5 a& B6 K" D; S+ i! v% T
  6. #define IPB13    *BITBAND_Addr(GPIOB_IDR_Addr,13): p+ n8 J# G+ o; ]' t
  7. #define OPB13   *BITBAND_Addr(GPIOB_ODR_Addr,13)
    , H/ I3 B- Y* Q/ j+ [' x* h: K
  8. #define OPB14   *BITBAND_Addr(GPIOB_ODR_Addr,14)
    ) r; ^- b: \% v- Z
  9. #define IPB15    *BITBAND_Addr(GPIOB_IDR_Addr,15)
复制代码

; {! r( {- t2 }: G
启动IIC传输:
  1. void _iic_Start()7 H2 @8 ^6 [7 {* R
  2. {  B& G2 [; q0 ?9 S7 x
  3.      W_SCL=1;   
    ( j- g# e$ e  E" l% F3 n) K
  4.      W_SDA=1;
    + @) }7 I& Y: A7 o: ~) f3 q
  5.      _delay();; \2 v) q$ z7 e8 |
  6.      W_SDA=0;     //SCL高时,拉低SDA,表示开始IIC传输,占用总线
    , v1 Q8 D+ ^5 m! E
  7.      _delay();
    ; U9 @! l: }8 _$ Y! ~' {/ _4 Q
  8.      W_SCL=0;     //控制SCL
    $ M1 J; o3 B% v9 d% x) b( v7 z5 b
  9.      _delay();8 m' e& Y% v+ l# J! D3 V/ a: |
  10. }
复制代码

; I* G  {! d& a+ v
停止IIC传输:
  1. void _iic_Stop()
    ) V, _- N7 k! Z0 n  V& {7 P$ n
  2. {
    $ O+ _: O5 H  i2 A) D% f
  3.      W_SCL=1;     //释放SCL(由于没有其他主器件,SCL无需开漏)
    , d& C# z7 o* X1 n
  4.      W_SDA=0;       L6 {4 a: T1 m% s( U
  5.      _delay();: e/ X9 k4 @5 o% K0 Y, h
  6.      W_SDA=1;   //SCL为高时,拉高SDA表示结束ICC传输,释放总线
    # I( C' v3 z& k
  7. }
复制代码

& ^: w# d  j8 b8 y9 [! X2 S8 h
发送一个字节:
  1. uint8_t _iic_SendByte(uint8_t dat)          5 U" M7 [2 _' C8 @
  2. {0 }( ~2 m' l1 y( B- n1 N5 _3 J1 _( m
  3.         uint8_t i;" V- q! E% w2 L/ Z7 l$ {" x0 z
  4.      for(i=0;i<8;i++)2 o! d2 `$ T$ _  I' A
  5.     {
    % v4 W" n& `, g5 U  @6 j
  6.         _delay();, h3 {+ K$ e3 ?3 X5 r& d* Q7 K# G
  7.         W_SDA=dat>>7;     //SCL拉高之前写SDA7 r) [  o7 ~/ V! x! q7 A, m! ?, u
  8.         dat=dat<<1;
      f( ^* U: o' a+ ?* {, z4 ]
  9.         _delay();
    / j, I) Q7 x$ d! L2 W5 P
  10.         W_SCL=1;         //拉高SCL,从器件开始读取SDA ( `7 \7 Q. S+ L3 w+ {2 O& F2 H/ \8 k
  11.         _delay();
    6 r2 ~. o9 m' ?
  12.         W_SCL=0;         //重新拉低SCL& N% R% e0 k7 S: P
  13.     }# c& R# \; U. l% F) S( w
  14.        W_SDA=1;             //释放SDA  w! d9 `9 G6 n& n& E- a
  15.        W_SCL=1;             //拉高SCL,读取从器件应答信号
    6 O7 z: ^/ T( v/ q* A5 ~
  16.       //   等待应答
    $ T; g1 I( w) {0 L. I# y
  17.     i=100;7 k- j% N0 a3 E4 S
  18.     while(i&&R_SDA)  {i--;_delay();}1 b, Q6 ^( Z. S" x7 ]7 ~, n3 R
  19.     if(i==0)               //无应答
    , k: c% P+ m9 x: x; y" V
  20.     {3 h  i+ F1 c+ b7 _  F" f0 k- x
  21.         W_SCL=0;         //重新拉低SCL. T$ m% ]5 K' \
  22.         return 0;2 x/ g) Z; d+ C: \# O6 O
  23.     }) V* c! Y2 {; q2 N2 \# B
  24.     else {                 //有应答; V4 p" d' z. I/ p
  25.         _delay();
    ) Z7 k* a' \  B- y- ]$ }$ O0 }3 L
  26.         W_SCL=0;         //重新拉低SCL
    9 s5 K+ k3 v+ y' ?4 h
  27.         return 1;1 f2 |6 H6 M# [4 D" f
  28. }" T3 w, j8 M: T
  29. }
复制代码
5 {& e# L  C3 E8 v: w
接收一个字节:
  1. uint8_t _iic_ReadByte(uint8_t Ack)  
    * P! Y( P+ t" q$ v6 w1 ^* j: S
  2. {
    3 j; ?5 A; V4 U7 Y2 Q
  3.      uint8_t temp,i;( D! R1 g$ G+ p% H% A3 \5 S
  4.      W_SDA=1;              //释放SDA7 |, d9 G9 i5 H" ?1 _- g# i6 F( `
  5.      _delay();
      i8 \" U: V' C1 h+ O; _, r  H* T
  6.     for(i=0;i<8;i++)  O) W2 N; E1 y. l$ m: U0 y
  7.     {
    : h+ e* ^' u' T! Q' G  m5 _. D
  8.         _delay();
    8 Y) s; s5 n* {7 T4 x9 z# y
  9.         W_SCL=1;          //拉高SCL开始读取SDA          0 X$ R- Y! C7 p6 q0 n8 J7 L
  10.         temp=temp<<1;      5 P: l8 {  ^5 S% ]3 a
  11.         temp|=R_SDA;      //SCL拉高之后读取SDA* O; G1 G- j4 `" W! Z
  12.         W_SCL=0;              //拉低SCL,从器件开始放置数据$ C6 a3 a9 F2 {- r1 d7 V
  13.      }5 _- @! @( r2 o# I8 i7 x; P
  14.      //发送应答信号
    ) R( W2 z. W9 w4 a6 R1 V
  15.     if(Ack)W_SDA=0;             //拉低SDA表示应答
    & u3 u. ?& ~( p* J0 Q7 m5 i- Z
  16.     W_SCL=1;             //拉高SCL,从器件接收应答信号' R9 o1 ~/ r8 w9 R
  17.     _delay();
    ! N8 y+ R% p  M" z
  18.     W_SCL=0;             //重新拉低SCL
    ! W7 i; y5 |% S4 z# p( y+ I7 k
  19.     W_SDA=1;             //释放SDA7 W9 h* v8 J) g1 m1 B
  20.     return temp;
    2 r8 m; W, S" y$ v- \
  21. }
复制代码
* T- o" A$ Y* P; i% [4 m+ s* T) t
6 f; n2 ]) e. V, {) W  ^/ F
二、配置HMC5883L模块
) A; J5 N9 Z/ b8 d" r- L6 M
  1. void HMC5883L_Init()! ?- }% p$ g' d* F% i
  2. {     $ D7 `9 z! n9 z) q; h3 i# A& k2 x& N
  3.      _iic_Start();
    3 s! V& u: Q/ A5 J9 g
  4.      _iic_SendByte(0x3c); //写操作
    ) M% T2 r& p: F* G8 Z) H2 B6 m
  5.      _iic_SendByte(0x00); //指针指向00,配置寄存器A # f( k' l: P" F; T2 {1 B. `  o
  6.      _iic_SendByte(0x78);  //数据测量、输出速率75hz
    ! j6 ]6 D. X1 R% ^- Y5 s, a
  7.      _iic_Start();          //指针定位到02,模式寄存器; y0 P4 Y; q1 }. E
  8.      _iic_SendByte(0x3c);
    2 y5 t. P/ \# C/ v
  9.      _iic_SendByte(0x02);
    . X4 p' d, x; k% ]# \, D
  10.      _iic_SendByte(0x00);  //连续测量模式
    6 k: k* ]+ X2 h9 X6 t  B
  11.      _iic_Stop();4 V: F  q' k$ z+ Q' K
  12. }
复制代码
& |, E. G8 y" c* S. U
三、读取角度数据
接收三轴数据,处理X,Y轴的数据并计算角度:
  1. int16_t HMC5883L_ReadAngle()
    ! J+ D" ^0 u+ e0 [
  2. {
    % }9 q  L. q( w% u
  3. static uint8_t i;
    5 b+ j6 k/ p4 h
  4. static uint8_t XYZ_Data[6]; //用来存储三个轴输出的数字量
    : d) o3 [3 f( ^% A

  5. 5 W6 j- e: R6 V* i# O7 A
  6. _iic_Start();
    6 ~8 R0 a2 q& v# ^) _2 X1 O
  7. _iic_SendByte(0x3c); // 发送HMC5883L的器件地址0x3c,写操作
    ' y$ o$ g  W1 \) U( g+ P6 E
  8. _iic_SendByte(0x03); //指针指向03,X msb寄存器   : c0 Z: h* y& Q( p* a! o0 |
  9. _iic_Start();         
    $ |, n! x5 z% R& E5 {9 l6 H
  10. _iic_SendByte(0x3d); //改为读操作
    ) h/ @; z2 W  s

  11. # p: y) d) r: y: @# o' |* g
  12. //依次读取三个轴的数字量0 l% |1 @9 M9 }! T$ l
  13. for(i=0;i<5;i++)        //前5次读取发送应答信号  x+ ~) A6 V2 L( X
  14. {8 f- e( J% b+ @* `* E$ L
  15. XYZ_Data[i]=_iic_ReadByte(1);
    0 @# |; A5 d9 F. e% \) @
  16. }+ T2 h5 g) f3 [5 t2 Q, Z& n
  17. XYZ_Data[5] =_iic_ReadByte(0);  //不应答
    5 {) a6 B: N3 v! I% E# u
  18. _iic_Stop();
    & w. w$ t) d$ p: n6 P/ Y
  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头文件
    % [* R' H7 `( ^- D
  20. }
复制代码
( b6 `1 [! _$ S, h' g

, x7 L9 P; J7 I5 u, D# E
配置好IO口,调用HMC5883L_Init()后,便可调用HMC5883L_ReadAngle()读取角度值,0~360°。
以下为测试时的截图:
272324479545732.png : E. l4 C4 K/ b7 m- G  L" |- s

! }1 J$ k% W' }1 h
测试时,模块比较灵敏且精确,稍微旋转模块便有精确的变化。由于该模块是基于对地磁场的测量,此模块容易受到其他磁场的干扰,比如将该模块靠近直流电机时,
便会因为电机内的磁场而降低精度甚至失灵(之前做智能小车时就遇到这个问题,要将电机内的磁场屏蔽起来才行)。
! V! T: Q( _2 q. r6 ~; b6 e
收藏 评论0 发布时间:2022-2-9 20:25

举报

0个回答

所属标签

相似技术帖

官网相关资源

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