研究了一晚上稍微有点成果分享下
HMC5883L使用i2c接口,接线很容易
以Arduino Uno为例:
SDA to A4
SCL to A5
Vcc to 3.3V
GND to GND
基本原理很简单:
方向角其实就是X轴和Y轴读数的反正切
而校准其实就是要排除环境中的磁场对地磁场的干扰
另外别忘了当地的磁偏角
如下代码没有使用专门的传感器库
上电后先进行20秒校准,请把传感器任意乱转,各个方向都要转到
然后就会显示校准值,然后持续显示初始值和方向角
不知道怎么在ide里面用中文写注释,所以就保留英文了
个人测试下来和手机上的指南针相差不超过5度,更精细的校准待研究
刚刚接触Arduino,望高手指教
#include <Wire.h> //I2C 库
#define address 0x1E //001 1110b(0x3C>>1), HMC5883的7位i2c地址
#define MagnetcDeclination 4.43 //笔者所在地磁偏角,请根据情况自行百度
#define CalThreshold 0
int offsetX,offsetY,offsetZ;
void setup()
{
//初始化串口和i2c
Serial.begin(9600);
Wire.begin();
//设置HMC5883模式
Wire.beginTransmission(address); //开始通信
Wire.write(0x00); //选择配置寄存器A
Wire.write(0x70); //0111 0000b,具体配置见数据手册
Wire.endTransmission();
Wire.beginTransmission(address);
Wire.write(0x02); //选择模式寄存器
Wire.write(0x00); //连续测量模式:0x00,单一测量模式:0x01
Wire.endTransmission();
calibrateMag();
}
void loop()
{
int x,y,z; //三轴数据
getRawData(&x,&y,&z);
//输出数据
Serial.print("x: ");
Serial.print(x);
Serial.print(" y: ");
Serial.print(y);
Serial.print(" z: ");
Serial.print(z);
Serial.print(" angle(x,y): ");
Serial.println(calculateHeading(&x,&y,&z));//输出x,y平面方向角
delay(250);
}
void getRawData(int* x ,int* y,int* z)
{
Wire.beginTransmission(address);
Wire.write(0x03); //从寄存器3开始读数据
Wire.endTransmission();
//每轴的数据都是16位的
Wire.requestFrom(address, 6);
if(6<=Wire.available()){
*x = Wire.read()<<8; //X msb,X轴高8位
*x |= Wire.read(); //X lsb,X轴低8位
*z = Wire.read()<<8; //Z msb
*z |= Wire.read(); //Z lsb
*y = Wire.read()<<8; //Y msb
*y |= Wire.read(); //Y lsb
}
}
int calculateHeading(int* x ,int* y,int* z)
{
float headingRadians = atan2((double)((*y)-offsetY),(double)((*x)-offsetX));
// 保证数据在0-2*PI之间
if(headingRadians < 0)
headingRadians += 2*PI;
int headingDegrees = headingRadians * 180/M_PI;
headingDegrees += MagnetcDeclination; //磁偏角
// <span style="font-family: Arial, Helvetica, sans-serif;">保证数据在0-360之间</span>
if(headingDegrees > 360)
headingDegrees -= 360;
return headingDegrees;
}
void calibrateMag()
{
int x,y,z; //三轴数据
int xMax, xMin, yMax, yMin, zMax, zMin;
//初始化
getRawData(&x,&y,&z);
xMax=xMin=x;
yMax=yMin=y;
zMax=zMin=z;
offsetX = offsetY = offsetZ = 0;
Serial.println("Starting Calibration......");
Serial.println("Please turn your device around in 20 seconds");
for(int i=0;i<200;i++)
{
getRawData(&x,&y,&z);
// 计算最大值与最小值
// 计算传感器绕X,Y,Z轴旋转时的磁场强度最大值和最小值
if (x > xMax)
xMax = x;
if (x < xMin )
xMin = x;
if(y > yMax )
yMax = y;
if(y < yMin )
yMin = y;
if(z > zMax )
zMax = z;
if(z < zMin )
zMin = z;
delay(100);
if(i%10 == 0)
{
Serial.print(xMax);
Serial.print(" ");
Serial.println(xMin);
}
}
//计算修正量
if(abs(xMax - xMin) > CalThreshold )
offsetX = (xMax + xMin)/2;
if(abs(yMax - yMin) > CalThreshold )
offsetY = (yMax + yMin)/2;
if(abs(zMax - zMin) > CalThreshold )
offsetZ = (zMax +zMin)/2;
Serial.print("offsetX:");
Serial.print("");
Serial.print(offsetX);
Serial.print(" offsetY:");
Serial.print("");
Serial.print(offsetY);
Serial.print(" offsetZ:");
Serial.print("");
Serial.println(offsetZ);
delay(5000);
}
把冗长的数据手册读完了,有人想看的话,可以把自测试模式,空闲模式等的使用方法也写一下
参考资料:
https://www.sparkfun.com/tutorials/301