Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 18:47 15 Jul 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : uM2(+): HMC5883 sensor calibration

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10250
Posted: 05:29am 11 Mar 2016
Copy link to clipboard 
Print this post

This program generates calibration parameters for the HMC5883L magnetic sensor.
To run it you need to know the magnetic field strength in your location. This site will give you that. Then run the program and rotate the sensor randomly around all axis as the program gathers data. The program will then report the multiplier in each axis that scales the measured readings to the actual field strength and also the offset of each axis about zero.

In your real program you should then use the reported values to scale each axis and add in the reported offset. This should remove any fixed iron effects and improve accuracy.

The program will need modification to address sensors other than the HMC5883 but the principles are the same in all cases

'Magnetometer Registers and constants
const HMC5883L_I2C = &H1E
const HMC5883_MAGGAIN_4_7 = &hA0' +/- 4.7
const hmc5883_Gauss_LSB_X = 390
const hmc5883_Gauss_LSB_Y = 390
const hmc5883_Gauss_LSB_Z = 390
const GAUSS_TO_MICROTESLA = 100
Const HMC5883_CONFIG_A = 0
Const HMC5883_CONFIG_B = 1
Const HMC5883_MODE = 2
Const HMC5883_MSB_X = 3
Const HMC5883_LSB_X = 4
Const HMC5883_MSB_Y = 5
Const HMC5883_LSB_Y = 8
Const HMC5883_MSB_Z = 7
Const HMC5883_LSB_Z = 8
Const HMC5883_STATUS = 9
Const HMC5883_ID_A = 10
Const HMC5883_ID_B = 11
Const HMC5883_ID_C = 12
dim float maxx=0, minx=0, maxy=0, miny=0, maxz=0, minz=0, fields(2), x(1000),y(1000),z(1000)
dim float magbias(2)=(0,0,0)
dim integer iterations=1000
dim maxfield ' maximum uT reading for this location
i2c open 400,1000
readByteString(HMC5883L_I2C,HMC5883_ID_A,3,ID$)
if ID$<>"H43" then
Print "HMC5883 not found"
end
endif
initHMC5883
print "Rotate the sensor around all axis whilst the program is sampling"
input "local field strength in uT ? ",maxfield
for iterations=0 to 999
readMagData(fields())
x(iterations)=fields(0)
y(iterations)=fields(1)
z(iterations)=fields(2)
if (iterations mod 100)=0 then print iterations/10,"% ";
next iterations
print "100%"

floatsort(x(),1000)
floatsort(y(),1000)
floatsort(z(),1000)
print "x-min = ",x(10)," x-max = ",x(990)
print "y-min = ",y(10)," y-max = ",y(990)
print "z-min = ",z(10)," z-max = ",z(990)
new_hmc5883_Gauss_LSB_X = hmc5883_Gauss_LSB_X * ((abs(x(10))+abs(x(990)))/2)/maxfield 'scale at 99th percentiles
new_hmc5883_Gauss_LSB_Y = hmc5883_Gauss_LSB_Y * ((abs(y(10))+abs(y(990)))/2)/maxfield
new_hmc5883_Gauss_LSB_Z = hmc5883_Gauss_LSB_Z * ((abs(z(10))+abs(z(990)))/2)/maxfield
print "hmc5883_Gauss_LSB_X should be ",new_hmc5883_Gauss_LSB_X
print "hmc5883_Gauss_LSB_Y should be ",new_hmc5883_Gauss_LSB_Y
print "hmc5883_Gauss_LSB_Z should be ",new_hmc5883_Gauss_LSB_Z
print "x offset (to add) = ",((x(990)-x(10))/2-x(990))*hmc5883_Gauss_LSB_X/new_hmc5883_Gauss_LSB_X
print "y offset (to add) = ",((y(990)-y(10))/2-y(990))*hmc5883_Gauss_LSB_Y/new_hmc5883_Gauss_LSB_Y
print "z offset (to add) = ",((z(990)-z(10))/2-z(990))*hmc5883_Gauss_LSB_Z/new_hmc5883_Gauss_LSB_Z
end
sub initHMC5883()
I2C WRITE HMC5883L_i2c, 0, 2, HMC5883_CONFIG_A, &h14 '1-average, 30 Hz, normal measurement
I2C WRITE HMC5883L_i2c, 0, 2, HMC5883_CONFIG_B, HMC5883_MAGGAIN_4_7 'Gain=4, or any other desired gain
I2C WRITE HMC5883L_i2c, 0, 2, HMC5883_MODE, &h00 'Single-measurement mode
end sub

sub readMagData(destination() as float)
do
loop while timer<(1000/30)+5 'wait to allow conversion to have taken place
local integer rawData(5)' x/y/z gyro register data, ST2 register stored here, must read ST2 at end of data acquisition
readBytes(HMC5883L_I2C, HMC5883_MSB_X, 6, rawData())' Read the six raw data
destination(0) = (sint16((rawData(0) << 8) OR rawData(1)))/hmc5883_Gauss_LSB_X * GAUSS_TO_MICROTESLA
destination(1) = (sint16((rawData(4) << 8) OR rawData(5)))/hmc5883_Gauss_LSB_Y * GAUSS_TO_MICROTESLA
destination(2) = (sint16((rawData(2) << 8) OR rawData(3)))/hmc5883_Gauss_LSB_Z * GAUSS_TO_MICROTESLA
timer=0
end sub

FUNCTION sint16(x as integer) as float ' convert to signed 16 bit number
local a%=x
IF a% AND &H8000 then a%=a% OR &HFFFFFFFFFFFF0000
sint16 = a%
END FUNCTION

sub writeByte(address%,reg%,value%)
i2c write address%,0,2,reg%,value%
end sub
'
function readByte(address%,reg%) as integer
local r%
i2c write address%,1,1,reg%
i2c read address%,0,1,readByte
end function
'
sub readBytes(address%,reg%,n%,a%())
i2c write address%,1,1,reg%
i2c read address%,0,n%,a%()
end sub
'
sub readByteString(address%,reg%,n%,s$)
i2c write address%,1,1,reg%
i2c read address%,0,n%,S$
end sub

CSUB floatsort
00000000
27BDFFA8 AFBF0054 AFBE0050 AFB7004C AFB60048 AFB50044 AFB40040 AFB3003C
AFB20038 AFB10034 AFB00030 AFA40058 8CA50000 AFA50018 00A09021 10000034
3C159D00 8FA20020 8C5E0000 8FA3001C 0072102A 14400019 0060B821 8FB30020
8FB10028 10000009 8FB00024 AE620000 8FA40014 02048021 02368821 02901021
0052102A 1440000C 02769821 0214B821 AFB00010 8EA20068 03C02021 0040F809
8E250000 2403FFFF 5043FFF0 8E220000 10000003 0017B880 8FB70010 0017B880
8FA40058 0097B821 AEFE0000 8FA2001C 24420001 AFA2001C 8FA30024 24630001
AFA30024 8FA40020 24840004 AFA40020 8FA20028 24420004 AFA20028 8FA30018
8FA4001C 0083102A 1440FFCF 8FA20020 001217C2 00529021 00129043 12400010
8FA30018 0243102A 1040FFFA 001217C2 0240A021 00121080 8FA40058 00821021
AFA20020 AFA40028 AFB2001C AFA00024 00121023 AFA20014 1000FFBA 0002B080
00001021 00001821 8FBF0054 8FBE0050 8FB7004C 8FB60048 8FB50044 8FB40040
8FB3003C 8FB20038 8FB10034 8FB00030 03E00008 27BD0058
End CSUB
Edited by matherp 2016-03-12
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6270
Posted: 02:06pm 11 Mar 2016
Copy link to clipboard 
Print this post

My results are:
Rotate the sensor around all axis whilst the program is sampling
local field strength in uT ? 61.425
0 % 10 % 20 % 30 % 40 % 50 % 60 % 70 % 80 % 90 % 100%
x-min = -53.3333 x-max = 60
y-min = -71.2821 y-max = 57.1795
z-min = -56.4103 z-max = 56.4103
hmc5883_Gauss_LSB_X should be 359.788
hmc5883_Gauss_LSB_Y should be 407.814
hmc5883_Gauss_LSB_Z should be 358.16
x offset (to add) = -3.61323
y offset (to add) = 6.74326
z offset (to add) = 0
>


I put these figures into my program but am still having difficulties with the tilt compensation. Too hard to be sure which way the vectors are pointing.


I still need to play more with your main program.

Jim
VK7JH
MMedit
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025