A class and a demo program to use with the DC-SS504 board from SureElectronics which uses MMC2120MG magnetometer from Memsic. The program glows leds depending on the direction it is turned to.

Dependencies:   mbed

Committer:
igorsk
Date:
Wed Dec 02 23:03:25 2009 +0000
Revision:
0:a44429321af8

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
igorsk 0:a44429321af8 1 #include "MMCx12xM.h"
igorsk 0:a44429321af8 2 #include <stdarg.h>
igorsk 0:a44429321af8 3 #include <limits.h>
igorsk 0:a44429321af8 4
igorsk 0:a44429321af8 5 /*
igorsk 0:a44429321af8 6
igorsk 0:a44429321af8 7 Memsic datasheets use very obtuse language, here I tried to summarize it in plain English.
igorsk 0:a44429321af8 8
igorsk 0:a44429321af8 9 The device works over I2C ("fast" mode, i.e. 400 kHz max)
igorsk 0:a44429321af8 10 the slave address is determined by the last digit (x in MMC212x) :
igorsk 0:a44429321af8 11 0: 0x60, 1: 0x64, 2: 0x68, 3: 0x6C
igorsk 0:a44429321af8 12
igorsk 0:a44429321af8 13 Register map:
igorsk 0:a44429321af8 14 00 control register
igorsk 0:a44429321af8 15 01 most significant byte x axis
igorsk 0:a44429321af8 16 02 least significant byte x axis
igorsk 0:a44429321af8 17 03 most significant byte y axis
igorsk 0:a44429321af8 18 04 least significant byte y axis
igorsk 0:a44429321af8 19 05 most significant byte z axis (MMC312xM only)
igorsk 0:a44429321af8 20 06 least significant byte z axis (MMC312xM only)
igorsk 0:a44429321af8 21
igorsk 0:a44429321af8 22 Operation is initiated by sending the register address (must be 0, since
igorsk 0:a44429321af8 23 only control register is writable) and then the command code, which is one
igorsk 0:a44429321af8 24 of the bits from the control register.
igorsk 0:a44429321af8 25
igorsk 0:a44429321af8 26 Control register layout:
igorsk 0:a44429321af8 27 bit 0: TM (take measurements)
igorsk 0:a44429321af8 28 set to get measurements (i.e. send 0x01)
igorsk 0:a44429321af8 29 bit is reset when measurement is done, so you can use it
igorsk 0:a44429321af8 30 to check if the operation is finished
igorsk 0:a44429321af8 31 bit 1: SET (set coil)
igorsk 0:a44429321af8 32 set to send a large current through the set/reset coil
igorsk 0:a44429321af8 33 should be done if sensor was affected by a large magnetic field (>5.5 gauss)
igorsk 0:a44429321af8 34 also after power on
igorsk 0:a44429321af8 35 result can be checked same as above
igorsk 0:a44429321af8 36 bit 2: RESET (reset coil)
igorsk 0:a44429321af8 37 same as above but sends current in the opposite direction
igorsk 0:a44429321af8 38 set/reset should be interleaved in "low-power mode", whatever that is
igorsk 0:a44429321af8 39 bits 3-6: reserved
igorsk 0:a44429321af8 40 bit 7: not described
igorsk 0:a44429321af8 41
igorsk 0:a44429321af8 42 To read a register, write its address and then read the value. The address auto-increments
igorsk 0:a44429321af8 43 after reading, so it's not necessary to send it again if reading sequentially. The address
igorsk 0:a44429321af8 44 is reset to 0 on power-on.
igorsk 0:a44429321af8 45
igorsk 0:a44429321af8 46 Taking measurements works like following:
igorsk 0:a44429321af8 47 1. write 0 (control register address), then 0x01 (take measurements)
igorsk 0:a44429321af8 48 2. wait 5ms for the measurement to complete
igorsk 0:a44429321af8 49 3. write 0 again (control register address), then read the register value
igorsk 0:a44429321af8 50 4. check if the bit 0 is cleared. If not, repeat from 3.
igorsk 0:a44429321af8 51 5. read x msb
igorsk 0:a44429321af8 52 6. read x lsb
igorsk 0:a44429321af8 53 7. read y msb
igorsk 0:a44429321af8 54 8. read y lsb
igorsk 0:a44429321af8 55 9. (if MMC312xM) read z msb
igorsk 0:a44429321af8 56 10. (if MMC312xM) read z lsb
igorsk 0:a44429321af8 57
igorsk 0:a44429321af8 58 N.B.: ADC resolution is only 12 bits, so four high bits of values will be 0
igorsk 0:a44429321af8 59
igorsk 0:a44429321af8 60 You can also skip some values and read directly the value wanted
igorsk 0:a44429321af8 61 by sending its address before reading.
igorsk 0:a44429321af8 62 */
igorsk 0:a44429321af8 63
igorsk 0:a44429321af8 64 // uncomment to show protocol debug tracing
igorsk 0:a44429321af8 65 //#define DEBUG
igorsk 0:a44429321af8 66
igorsk 0:a44429321af8 67 int dprintf(const char *fmt, ...)
igorsk 0:a44429321af8 68 {
igorsk 0:a44429321af8 69 #ifdef DEBUG
igorsk 0:a44429321af8 70 va_list args;
igorsk 0:a44429321af8 71 va_start (args, fmt);
igorsk 0:a44429321af8 72 int ret = vprintf(fmt, args);
igorsk 0:a44429321af8 73 va_end(args);
igorsk 0:a44429321af8 74 return ret;
igorsk 0:a44429321af8 75 #else
igorsk 0:a44429321af8 76 return 0;
igorsk 0:a44429321af8 77 #endif
igorsk 0:a44429321af8 78 }
igorsk 0:a44429321af8 79
igorsk 0:a44429321af8 80 enum command {
igorsk 0:a44429321af8 81 TM = 1, // take measurements
igorsk 0:a44429321af8 82 SET = 2, // set
igorsk 0:a44429321af8 83 RESET = 4 // reset
igorsk 0:a44429321af8 84 };
igorsk 0:a44429321af8 85
igorsk 0:a44429321af8 86 MMCx12xM::MMCx12xM(I2C &i2c, int address, const char *name) : Base(name),
igorsk 0:a44429321af8 87 _I2C(&i2c), _addr(address), _own_i2c(false)
igorsk 0:a44429321af8 88 {
igorsk 0:a44429321af8 89 }
igorsk 0:a44429321af8 90
igorsk 0:a44429321af8 91 MMCx12xM::MMCx12xM(PinName sda, PinName scl, int address, const char *name) : Base(name),
igorsk 0:a44429321af8 92 _addr(address)
igorsk 0:a44429321af8 93 {
igorsk 0:a44429321af8 94 _I2C = new I2C(sda, scl);
igorsk 0:a44429321af8 95 // we own the bus, so we can set frequency
igorsk 0:a44429321af8 96 // MMCx12x claims to handle 400 kHz
igorsk 0:a44429321af8 97 _I2C->frequency(400000);
igorsk 0:a44429321af8 98 _own_i2c = true;
igorsk 0:a44429321af8 99 }
igorsk 0:a44429321af8 100
igorsk 0:a44429321af8 101 MMCx12xM::~MMCx12xM()
igorsk 0:a44429321af8 102 {
igorsk 0:a44429321af8 103 if (_own_i2c)
igorsk 0:a44429321af8 104 delete _I2C;
igorsk 0:a44429321af8 105 }
igorsk 0:a44429321af8 106
igorsk 0:a44429321af8 107 // send the specified command: write it into the control register
igorsk 0:a44429321af8 108 bool MMCx12xM::_send_command(int command)
igorsk 0:a44429321af8 109 {
igorsk 0:a44429321af8 110 dprintf("* send command %d\n", command);
igorsk 0:a44429321af8 111 const char writecmd[] = {0, command}; // address: 0 (control reg)
igorsk 0:a44429321af8 112 int res = _I2C->write(_addr, writecmd, 2);
igorsk 0:a44429321af8 113 dprintf("write: %d\n", res);
igorsk 0:a44429321af8 114 return res == 0;
igorsk 0:a44429321af8 115 }
igorsk 0:a44429321af8 116
igorsk 0:a44429321af8 117 // wait until the command is done
igorsk 0:a44429321af8 118 // this is signalled by clearing of the
igorsk 0:a44429321af8 119 // corresponding bit in the control register
igorsk 0:a44429321af8 120 bool MMCx12xM::_wait_ready(int command)
igorsk 0:a44429321af8 121 {
igorsk 0:a44429321af8 122 dprintf("* wait ready %d\n", command);
igorsk 0:a44429321af8 123 const char writecmd = 0; // set read address to 0 (control reg)
igorsk 0:a44429321af8 124 int res = _I2C->write(_addr, &writecmd, 1);
igorsk 0:a44429321af8 125 dprintf(" write: %d\n", res);
igorsk 0:a44429321af8 126 if ( res != 0 )
igorsk 0:a44429321af8 127 return false;
igorsk 0:a44429321af8 128 char reg;
igorsk 0:a44429321af8 129 while ( 1 )
igorsk 0:a44429321af8 130 {
igorsk 0:a44429321af8 131 // read control register value
igorsk 0:a44429321af8 132 res = _I2C->read(_addr, &reg, 1);
igorsk 0:a44429321af8 133 dprintf(" read: %d, reg=%08X\n", res, reg);
igorsk 0:a44429321af8 134 if ( res != 0 )
igorsk 0:a44429321af8 135 return false;
igorsk 0:a44429321af8 136 // check if the command bit is cleared
igorsk 0:a44429321af8 137 if ( (reg & command) == 0 )
igorsk 0:a44429321af8 138 break; // data ready
igorsk 0:a44429321af8 139 dprintf("* Not ready, try again\n");
igorsk 0:a44429321af8 140 // otherwise tell the device that we want to read the register (address 0) again
igorsk 0:a44429321af8 141 res = _I2C->write(_addr, &writecmd, 1);
igorsk 0:a44429321af8 142 dprintf(" write: %d\n", res);
igorsk 0:a44429321af8 143 if ( res != 0 )
igorsk 0:a44429321af8 144 return false;
igorsk 0:a44429321af8 145 }
igorsk 0:a44429321af8 146 return true;
igorsk 0:a44429321af8 147 }
igorsk 0:a44429321af8 148
igorsk 0:a44429321af8 149 // read one axis value (two bytes)
igorsk 0:a44429321af8 150 // set read address if index specified explicitly
igorsk 0:a44429321af8 151 bool MMCx12xM::_read_axis(int *axis, int index)
igorsk 0:a44429321af8 152 {
igorsk 0:a44429321af8 153 dprintf("* read axis %d\n", index);
igorsk 0:a44429321af8 154 // accept only x, y or z (0, 1, 2)
igorsk 0:a44429321af8 155 if ( index > 2 )
igorsk 0:a44429321af8 156 return false;
igorsk 0:a44429321af8 157 int res;
igorsk 0:a44429321af8 158 if ( index != - 1 )
igorsk 0:a44429321af8 159 {
igorsk 0:a44429321af8 160 const char writecmd = index*2 + 1; // set read address for the axis value
igorsk 0:a44429321af8 161 res = _I2C->write(_addr, &writecmd, 1);
igorsk 0:a44429321af8 162 dprintf(" write: %d\n", res);
igorsk 0:a44429321af8 163 if ( res != 0 )
igorsk 0:a44429321af8 164 return false;
igorsk 0:a44429321af8 165 }
igorsk 0:a44429321af8 166 uint8_t pair[2]; // msb, lsb
igorsk 0:a44429321af8 167 res = _I2C->read(_addr, (char*)&pair, 2);
igorsk 0:a44429321af8 168 dprintf(" read: %d, msb=%02X, lsb=%02X\n", res, pair[0], pair[1]);
igorsk 0:a44429321af8 169 if ( res != 0 )
igorsk 0:a44429321af8 170 return false;
igorsk 0:a44429321af8 171 // make an integer from msb and lsb
igorsk 0:a44429321af8 172 *axis = (pair[0] << 8) | pair[1];
igorsk 0:a44429321af8 173 return true;
igorsk 0:a44429321af8 174 }
igorsk 0:a44429321af8 175
igorsk 0:a44429321af8 176 // ask chip to take measurements and
igorsk 0:a44429321af8 177 // read specified number of raw axis values
igorsk 0:a44429321af8 178 bool MMCx12xM::read_raw_values(int *values, int count)
igorsk 0:a44429321af8 179 {
igorsk 0:a44429321af8 180 dprintf("* read_raw_values\n");
igorsk 0:a44429321af8 181 if ( !_send_command(TM) )
igorsk 0:a44429321af8 182 {
igorsk 0:a44429321af8 183 dprintf(" send_command(TM) failed\n");
igorsk 0:a44429321af8 184 return false;
igorsk 0:a44429321af8 185 }
igorsk 0:a44429321af8 186 wait_ms(5);
igorsk 0:a44429321af8 187 if ( !_wait_ready(TM) )
igorsk 0:a44429321af8 188 {
igorsk 0:a44429321af8 189 dprintf(" wait_ready(TM) failed\n");
igorsk 0:a44429321af8 190 return false;
igorsk 0:a44429321af8 191 }
igorsk 0:a44429321af8 192 // we have read the control register, so continue reading the data
igorsk 0:a44429321af8 193 // which will be the axis values
igorsk 0:a44429321af8 194 for ( int i=0; i < count; i++ )
igorsk 0:a44429321af8 195 {
igorsk 0:a44429321af8 196 // we're reading values sequentially, so no need to set the index
igorsk 0:a44429321af8 197 if ( !_read_axis(&values[i]) )
igorsk 0:a44429321af8 198 {
igorsk 0:a44429321af8 199 dprintf(" _read_axis() failed\n");
igorsk 0:a44429321af8 200 return false;
igorsk 0:a44429321af8 201 }
igorsk 0:a44429321af8 202 }
igorsk 0:a44429321af8 203 return true;
igorsk 0:a44429321af8 204 }
igorsk 0:a44429321af8 205
igorsk 0:a44429321af8 206 bool MMCx12xM::coil_set()
igorsk 0:a44429321af8 207 {
igorsk 0:a44429321af8 208 return _send_command(SET) && _wait_ready(SET);
igorsk 0:a44429321af8 209 }
igorsk 0:a44429321af8 210
igorsk 0:a44429321af8 211 bool MMCx12xM::coil_reset()
igorsk 0:a44429321af8 212 {
igorsk 0:a44429321af8 213 return _send_command(SET) && _wait_ready(SET);
igorsk 0:a44429321af8 214 }
igorsk 0:a44429321af8 215
igorsk 0:a44429321af8 216 void MMCx12xM::calibrate_begin()
igorsk 0:a44429321af8 217 {
igorsk 0:a44429321af8 218 // begin calibration: init the values arrays
igorsk 0:a44429321af8 219 for ( int i=0; i < 3; i++ )
igorsk 0:a44429321af8 220 {
igorsk 0:a44429321af8 221 _maxvals[i] = 0;
igorsk 0:a44429321af8 222 _minvals[i] = INT_MAX;
igorsk 0:a44429321af8 223 }
igorsk 0:a44429321af8 224 }
igorsk 0:a44429321af8 225
igorsk 0:a44429321af8 226 void MMCx12xM::calibrate_step(int count)
igorsk 0:a44429321af8 227 {
igorsk 0:a44429321af8 228 // take a measurement and update min-max values
igorsk 0:a44429321af8 229 int values[3];
igorsk 0:a44429321af8 230 if ( read_raw_values(values, count) )
igorsk 0:a44429321af8 231 {
igorsk 0:a44429321af8 232 for ( int i=0; i < count; i++ )
igorsk 0:a44429321af8 233 {
igorsk 0:a44429321af8 234 if ( _maxvals[i] < values[i] )
igorsk 0:a44429321af8 235 _maxvals[i] = values[i];
igorsk 0:a44429321af8 236 if ( _minvals[i] > values[i] )
igorsk 0:a44429321af8 237 _minvals[i] = values[i];
igorsk 0:a44429321af8 238 }
igorsk 0:a44429321af8 239 }
igorsk 0:a44429321af8 240 }
igorsk 0:a44429321af8 241
igorsk 0:a44429321af8 242 void MMCx12xM::calibrate_end()
igorsk 0:a44429321af8 243 {
igorsk 0:a44429321af8 244 // calculate sensitivity and offset for each axis
igorsk 0:a44429321af8 245 // see Memsic app note AN-00MM-003
igorsk 0:a44429321af8 246 dprintf("* calibration end\n");
igorsk 0:a44429321af8 247 for ( int i=0; i < 3; i++ )
igorsk 0:a44429321af8 248 {
igorsk 0:a44429321af8 249 _sensitivity[i] = (_maxvals[i] - _minvals[i]) / 2;
igorsk 0:a44429321af8 250 _offset[i] = (_maxvals[i] + _minvals[i]) / 2;
igorsk 0:a44429321af8 251 dprintf(" %i: min = %d, max = %d, s = %d, o = %d\n", i, _minvals[i], _maxvals[i], _sensitivity[i], _offset[i]);
igorsk 0:a44429321af8 252 }
igorsk 0:a44429321af8 253 }
igorsk 0:a44429321af8 254
igorsk 0:a44429321af8 255 bool MMCx12xM::read_values(float *values, int count)
igorsk 0:a44429321af8 256 {
igorsk 0:a44429321af8 257 int rvalues[3];
igorsk 0:a44429321af8 258 if ( read_raw_values(rvalues, count) )
igorsk 0:a44429321af8 259 {
igorsk 0:a44429321af8 260 // transform into calibrated values in the range -1.0 .. +1.0
igorsk 0:a44429321af8 261 for ( int i=0; i < count; i++ )
igorsk 0:a44429321af8 262 {
igorsk 0:a44429321af8 263 // NB: we use a temp float so that the division is not integer
igorsk 0:a44429321af8 264 float d = (rvalues[i] - _offset[i]);
igorsk 0:a44429321af8 265 values[i] = d / _sensitivity[i];
igorsk 0:a44429321af8 266 }
igorsk 0:a44429321af8 267 return true;
igorsk 0:a44429321af8 268 }
igorsk 0:a44429321af8 269 return false;
igorsk 0:a44429321af8 270 }