研究了一晚上稍微有點成果分享下
HMC5883L使用i2c接口,接線很容易
以Arduino Uno為例:
SDA to A4
SCL to A5
Vcc to 3.3V
GND to GND
基本原理很簡單:
方向角其實就是X軸和Y軸讀數的反正切
而校準其實就是要排除環境中的磁場對地磁場的幹擾
另外别忘了當地的磁偏角
如下代碼沒有使用專門的傳感器庫
上電後先進行20秒校準,請把傳感器任意亂轉,各個方向都要轉到
然後就會顯示校準值,然後持續顯示初始值和方向角
不知道怎麼在ide裡面用中文寫注釋,是以就保留英文了
個人測試下來和手機上的指南針相差不超過5度,更精細的校準待研究
剛剛接觸Arduino,望高手指教
[cpp] view plain copy
- #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
轉自:http://blog.csdn.net/do335maomao/article/details/43916467