本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

基于HMC5883L的三轴磁力计

发布时间:2022-06-09
分享到:

基于HMC5883L的三轴磁力计

发布时间:2022-06-09
分享到:

在这个项目中,你可以使用 HMC5883L 三轴磁力计测量地球磁场的大小和方向。

介绍
在这个项目中,我们将 HMC5883L 3 轴磁力计连接到 Arduino Uno。该设备可以测量地球磁场的大小和方向。它是一种低功耗设备,可以在手机或导航系统中找到,以提供准确的指南针航向。您还可以使用它们来检测含铁(含铁)金属,因为金属中的铁在靠近传感器时会改变磁场。

不要迷路,让我们开始吧!

20x4 字符 LCD
与 16x2 LCD 相比,20x4 LCD 每行增加了两行和四列。与我们在之前的项目中使用的 16x2 类似,20x4 LCD 使用日立控制器,因此命令和接口是相同的。它还具有相同的 16 针接头,允许您在不更改任何接线的情况下拔下 16x2 LCD 并插入 20x4。我们唯一需要更改的是一行代码lcd.begin(20, 4) ,它指定 LCD 的列(第一个参数)和行(第二个参数)。20x4 LCD 的图像如下所示:

HMC5883L 三轴磁力计
HMC5883L 3轴磁力计可以准确测量地球磁场在x、y、z方向的大小和方向。因此,它可以用来提供罗盘航向,这就是它也被称为数字罗盘的原因。它是一款外形小巧的低功耗设备,可让您将其嵌入到几乎任何需要指南针航向的项目中。下表提供了有关模块的一些规格:

尽管 HMC5883L IC 的数据表中的分线板由 5 个引脚组成:GND、VIN、DRDY、SCL 和 SDA(如下图所示)。GND 和 VIN 引脚用于为器件供电。虽然 Parallax 的数据表表明该模块可以在 2.7V 至 6.5V 的电压范围内工作,但我们无法让磁力计在 5V 电压下工作,因此我们建议使用 3.3Vdc 。GND 和 VIN 引脚将分别连接到 Uno 上的 GND 和 3.3V 引脚。

为了与设备通信,我们使用仅使用两个引脚 SCL 和 SDA 的 I2C 协议。我们使用它来配置设备上的寄存器(即设置测量模式和输出速率),并获取 X、Y 和 Z 磁场测量值。该设备只能在 0x1E 处进行 7 位寻址,因此在 I2C 总线上一次不能有多个这些设备。SCL 和 SDA 引脚将分别连接到 Uno 模拟引脚 A5 和 A4。

DRDY(数据就绪)引脚用于告诉主设备 (Uno) X、Y 和 Z 寄存器中的数据已就绪。最大输出速率为 75Hz,但通过使用 DRDY 引脚,您可以达到高达 160Hz。您可以将此引脚连接到 Uno 上的中断引脚,以便有效地获取数据。但是,我们不会在这个项目中使用这个 DRDY 引脚。

该设备在其 3 个轴的每个轴上都有一个磁阻传感器,用于测量磁场。在存在磁场的情况下,这些元件的电阻会发生变化,从而导致输出电压发生变化。该电压变化由器件的 12 位 ADC 在每个轴上测量,然后将测量结果写入相应的 X、Y 和 Z 8 位数据寄存器。

每个寄存器的地址映射如下表所示。以黄色突出显示的寄存器表示我们将从中读取的寄存器,以获得每个轴上的磁场测量值。要了解有关每个寄存器的详细信息,请参阅 HMC5883L IC 的数据表:

当您围绕 X、Y 和 Z 轴旋转设备时,每个轴的磁场都会发生变化,如下图所示。设备的方向必须使 XY 平面(板的顶部平面)平行于地面,并且也指向上方。

将磁力计安装到 Adapticon
您可以将磁力计安装到 Adapticon 以提供稳定性。您将需要一个 M2 公对母六角支架、M2 螺母和 M2 螺钉来安装它。这使 XY 平面与地面保持平行,并允许您围绕 Z 轴旋转 FuelCan,因此您可以获得准确的罗盘航向。确保使用有色金属安装硬件,以避免干扰磁场。安装到 Adapticon 的磁力计图像如下所示。

我们只是将设备连接到 12 英寸跨接线上,这样我们就可以在各个方向自由移动它。

无焊面包板
如果您使用的是无焊面包板,请使用下面的示意图为 16×2 LCD 进行必要的连接。磁力计通过 12 英寸 F/M 跳线直接连接到 Uno。我们使用的是 Uno 的 3V3 电源,而不是 FuelCan 的 3V3 导轨,因为公头针的 0.1 英寸间距太近,无法将测试引线夹电缆安装在一起。

:原理图显示 16×2 字符 LCD,但 20×4 LCD 与引脚兼容。您唯一需要做的改变是在软件中。

接线
如果您还没有将 Uno 安装到 FuelCan 的原型制作区域,请继续执行此操作。如果您使用的是面包板而不是 Modulus,请将面包板放在底部的储物箱中以限制跳线的长度。您需要使用提供的香蕉插孔来测试引线夹电缆,为面包板上的电源和接地轨提供 +5V 和 GND。您将需要两个公头针将测试引线夹安装在面包板一侧。这用于为 LCD 供电。

接下来,将 USB 电缆的 A 型端插入 USB1 插座,将 B 型端插入 Uno 的插座。插入 6' A 型到 A 型电缆 - 一端插入外部 USB 连接器,另一端插入主机(即计算机)。使用 AC-DC 电源适配器为 FuelCan 供电。

软件说明
我们通过 I2C 与设备通信,因此请务必包含Wire.h库。在我们开始从设备获取数据之前,我们首先必须使用函数setOperatingMode()和setSamples()对其进行配置,以将操作模式设置为连续,并将每个测量输出的平均样本数设置为 8。其他所有内容都将保留默认。

现在寄存器已配置,我们可以开始使用函数getXYZ()获取原始 X、Y 和 Z 数据。此函数获取 X、Y 和 Z 的每个 16 位数据。getXYZ() 中的Wire.requestFrom ()函数用于请求 6 个字节的数据。

一旦我们有了原始的 X、Y 和 Z 值,就会调用函数convert()将原始计数转换为高斯(用于测量磁场的单位)。要进行转换,我们必须查阅IC 数据表的表 9 (增益设置)。由于增益保留为默认值,我们使用 1090 的增益。我们可以简单地将每个原始计数除以增益,将计数转换为高斯单位。

在我们将数据输出到串行监视器之前要做的最后一件事是计算航向。如果设备的 XY 平面与地面平行,我们可以使用 X 和 Y 值来获得指示航向的矢量。这是通过函数getHeading()完成的。反正切是在 (X, Y) 点和 x 轴之间计算的,如下图所示。我们使用atan2的原因是能够计算所有四个象限的反正切。

atan2以弧度返回角度,因此我们可以将其转换为度数。指南针只能给出 0 到 360 度的航向,如果我们得到的航向超出了这个范围,如果它是负数,我们可以加上 360,如果大于 360,我们可以减去 360。现在数据已经归一化,并且航向已计算,我们可以通过串行监视器显示它。

//Interface a HMC5883L 3-axis digital compass to an Arduino Uno
/*Copyright (c) 2019, ProteShea LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Wire.h>
//Address map for registers
#define configA 0x00
#define configB 0x01
#define mode 0x02
#define dataOutX_U 0x03
#define dataOutX_L 0x04
#define dataOutZ_L 0x05
#define dataOutZ_L 0x06
#define dataOutY_L 0x07
#define dataOutY_L 0x08
#define statusReg 0x09
//Operating modes sent to Mode register (0x02)
#define continuous 0x00
#define single 0x01
#define idle 0x02
#define i2c_addr 0x1E
#define gain 1090
int16_t x = 0;
int16_t y = 0;
int16_t z = 0;
float heading;
float gaussX;
float gaussY;
float gaussZ;
void setup() {
 Wire.begin();
 Serial.begin(9600);
 setOperatingMode(continuous);
 setSamples();
}
void loop() {
 getXYZ();
 convert(x,y,z);
 getHeading(gaussX,gaussY,gaussZ);
 Serial.print("X: ");
 Serial.print(gaussX);
 Serial.print(" Y: ");
 Serial.print(gaussY);
 Serial.print(" Z: ");
 Serial.println(gaussZ);
 Serial.print("Heading: ");
 Serial.println(heading);
 delay(500);
}
//Convert the raw X, Y, Z counts to Gauss
void convert(int16_t rawX, int16_t rawY, int16_t rawZ){
 gaussX = (float)rawX/gain;
 gaussY = (float)rawY/gain;
 gaussZ = (float)rawZ/gain;    
}
//accounts for declination (error in magnetic field which is dependent on location)
void getHeading(float X, float Y, float Z){
 heading = (atan2(Y,X) - 0.1) * 180 / PI;
 if (heading < 0) heading += 360;
 if (heading > 360) heading -= 360;
}
void setSamples(void){
 Wire.beginTransmission(i2c_addr);
 Wire.write(configA);        //write to config A register
 Wire.write(0x70);           //8 samples averaged, 15Hz output rate, normal measurement
 Wire.endTransmission();
 delay(10);
}
void setOperatingMode(uint8_t addr){
 Wire.beginTransmission(i2c_addr);
 Wire.write(mode);           //write to mode register
 Wire.write(addr);           //set measurement mode
 Wire.endTransmission();
 delay(10);
}
//get the raw counts of X, Y, Z from registers 0x03 to 0x08
void getXYZ(void){
 Wire.beginTransmission(i2c_addr);
 Wire.write(0x03);
 Wire.endTransmission();
 Wire.requestFrom(i2c_addr, 6);
 if (Wire.available() >= 6){
   int16_t temp = Wire.read();     //read upper byte of X
   x = temp << 8;
   temp = Wire.read();             //read lower byte of X
   x = x | temp;
   temp = Wire.read();             //read upper byte of Z
   z = temp << 8;
   temp = Wire.read();             //read lower byte of Z
   z = z | temp;
   temp = Wire.read();             //read upper byte of Y
   y = temp << 8;
   temp = Wire.read();             //read lower byte of Y
   y = y | temp;
 }
}

下一个示例通过调用函数writeLCD()在 20×4 LCD 上显示数据。

//Interface a HMC5883L 3-axis digital compass to an Arduino Uno and display
//data on 20x4 LCD
/*Copyright (c) 2019, ProteShea LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Wire.h>
#include <LiquidCrystal.h>
//Address map for registers
#define configA 0x00
#define configB 0x01
#define mode 0x02
#define dataOutX_U 0x03
#define dataOutX_L 0x04
#define dataOutZ_L 0x05
#define dataOutZ_L 0x06
#define dataOutY_L 0x07
#define dataOutY_L 0x08
#define statusReg 0x09
//Operating modes sent to Mode register (0x02)
#define continuous 0x00
#define single 0x01
#define idle 0x02
#define i2c_addr 0x1E
#define gain 1090
const int RS = 2, EN = 3, D4 = 4, D5 = 5, D6 = 6, D7 = 7;
LiquidCrystal lcd(RS,EN,D4,D5,D6,D7);   //set Uno pins that are connected to LCD, 4-bit mode
int16_t x = 0;
int16_t y = 0;
int16_t z = 0;
float heading;
float gaussX;
float gaussY;
float gaussZ;
void setup() {
 Wire.begin();
 lcd.begin(20,4);    //set 20 columns and 4 rows of 20x4 LCD
 setOperatingMode(continuous);
 setSamples();
}
void loop() {
 getXYZ();
 convert(x,y,z);
 getHeading(gaussX,gaussY,gaussZ);
 writeLCD();
 delay(500);
}
void writeLCD(void){
 lcd.clear();
 lcd.print("X: ");
 lcd.print(gaussX);
 lcd.setCursor(0,1);
 lcd.print("Y: ");
 lcd.print(gaussY);
 lcd.setCursor(0,2);
 lcd.print("Z: ");
 lcd.print(gaussZ);
 lcd.setCursor(0,3);
 lcd.print("Heading: ");
 lcd.print(heading);
}
//Convert the raw X, Y, Z counts to Gauss
void convert(int16_t rawX, int16_t rawY, int16_t rawZ){
 gaussX = (float)rawX/gain;
 gaussY = (float)rawY/gain;
 gaussZ = (float)rawZ/gain;    
}
//accounts for declination (error in magnetic field which is dependent on location)
void getHeading(float X, float Y, float Z){
 heading = (atan2(Y,X) - 0.1) * 180 / PI;
 if (heading < 0) heading += 360;
 if (heading > 360) heading -= 360;
}
void setSamples(void){
 Wire.beginTransmission(i2c_addr);
 Wire.write(configA);        //write to config A register
 Wire.write(0x70);           //8 samples averaged, 15Hz output rate, normal measurement
 Wire.endTransmission();
 delay(10);
}
void setOperatingMode(uint8_t addr){
 Wire.beginTransmission(i2c_addr);
 Wire.write(mode);           //write to mode register
 Wire.write(addr);           //set measurement mode
 Wire.endTransmission();
 delay(10);
}
//get the raw counts of X, Y, Z from registers 0x03 to 0x08
void getXYZ(void){
 Wire.beginTransmission(i2c_addr);
 Wire.write(0x03);
 Wire.endTransmission();
 Wire.requestFrom(i2c_addr, 6);
 if (Wire.available() >= 6){
   int16_t temp = Wire.read();     //read upper byte of X
   x = temp << 8;
   temp = Wire.read();             //read lower byte of X
   x = x | temp;
   temp = Wire.read();             //read upper byte of Z
   z = temp << 8;
   temp = Wire.read();             //read lower byte of Z
   z = z | temp;
   temp = Wire.read();             //read upper byte of Y
   y = temp << 8;
   temp = Wire.read();             //read lower byte of Y
   y = y | temp;
 }
}

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:Ejshea,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论