Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
MMCx12xM.cpp
00001 #include "MMCx12xM.h" 00002 #include <stdarg.h> 00003 #include <limits.h> 00004 00005 /* 00006 00007 Memsic datasheets use very obtuse language, here I tried to summarize it in plain English. 00008 00009 The device works over I2C ("fast" mode, i.e. 400 kHz max) 00010 the slave address is determined by the last digit (x in MMC212x) : 00011 0: 0x60, 1: 0x64, 2: 0x68, 3: 0x6C 00012 00013 Register map: 00014 00 control register 00015 01 most significant byte x axis 00016 02 least significant byte x axis 00017 03 most significant byte y axis 00018 04 least significant byte y axis 00019 05 most significant byte z axis (MMC312xM only) 00020 06 least significant byte z axis (MMC312xM only) 00021 00022 Operation is initiated by sending the register address (must be 0, since 00023 only control register is writable) and then the command code, which is one 00024 of the bits from the control register. 00025 00026 Control register layout: 00027 bit 0: TM (take measurements) 00028 set to get measurements (i.e. send 0x01) 00029 bit is reset when measurement is done, so you can use it 00030 to check if the operation is finished 00031 bit 1: SET (set coil) 00032 set to send a large current through the set/reset coil 00033 should be done if sensor was affected by a large magnetic field (>5.5 gauss) 00034 also after power on 00035 result can be checked same as above 00036 bit 2: RESET (reset coil) 00037 same as above but sends current in the opposite direction 00038 set/reset should be interleaved in "low-power mode", whatever that is 00039 bits 3-6: reserved 00040 bit 7: not described 00041 00042 To read a register, write its address and then read the value. The address auto-increments 00043 after reading, so it's not necessary to send it again if reading sequentially. The address 00044 is reset to 0 on power-on. 00045 00046 Taking measurements works like following: 00047 1. write 0 (control register address), then 0x01 (take measurements) 00048 2. wait 5ms for the measurement to complete 00049 3. write 0 again (control register address), then read the register value 00050 4. check if the bit 0 is cleared. If not, repeat from 3. 00051 5. read x msb 00052 6. read x lsb 00053 7. read y msb 00054 8. read y lsb 00055 9. (if MMC312xM) read z msb 00056 10. (if MMC312xM) read z lsb 00057 00058 N.B.: ADC resolution is only 12 bits, so four high bits of values will be 0 00059 00060 You can also skip some values and read directly the value wanted 00061 by sending its address before reading. 00062 */ 00063 00064 // uncomment to show protocol debug tracing 00065 //#define DEBUG 00066 00067 int dprintf(const char *fmt, ...) 00068 { 00069 #ifdef DEBUG 00070 va_list args; 00071 va_start (args, fmt); 00072 int ret = vprintf(fmt, args); 00073 va_end(args); 00074 return ret; 00075 #else 00076 return 0; 00077 #endif 00078 } 00079 00080 enum command { 00081 TM = 1, // take measurements 00082 SET = 2, // set 00083 RESET = 4 // reset 00084 }; 00085 00086 MMCx12xM::MMCx12xM(I2C &i2c, int address, const char *name) : Base(name), 00087 _I2C(&i2c), _addr(address), _own_i2c(false) 00088 { 00089 } 00090 00091 MMCx12xM::MMCx12xM(PinName sda, PinName scl, int address, const char *name) : Base(name), 00092 _addr(address) 00093 { 00094 _I2C = new I2C(sda, scl); 00095 // we own the bus, so we can set frequency 00096 // MMCx12x claims to handle 400 kHz 00097 _I2C->frequency(400000); 00098 _own_i2c = true; 00099 } 00100 00101 MMCx12xM::~MMCx12xM() 00102 { 00103 if (_own_i2c) 00104 delete _I2C; 00105 } 00106 00107 // send the specified command: write it into the control register 00108 bool MMCx12xM::_send_command(int command) 00109 { 00110 dprintf("* send command %d\n", command); 00111 const char writecmd[] = {0, command}; // address: 0 (control reg) 00112 int res = _I2C->write(_addr, writecmd, 2); 00113 dprintf("write: %d\n", res); 00114 return res == 0; 00115 } 00116 00117 // wait until the command is done 00118 // this is signalled by clearing of the 00119 // corresponding bit in the control register 00120 bool MMCx12xM::_wait_ready(int command) 00121 { 00122 dprintf("* wait ready %d\n", command); 00123 const char writecmd = 0; // set read address to 0 (control reg) 00124 int res = _I2C->write(_addr, &writecmd, 1); 00125 dprintf(" write: %d\n", res); 00126 if ( res != 0 ) 00127 return false; 00128 char reg; 00129 while ( 1 ) 00130 { 00131 // read control register value 00132 res = _I2C->read(_addr, ®, 1); 00133 dprintf(" read: %d, reg=%08X\n", res, reg); 00134 if ( res != 0 ) 00135 return false; 00136 // check if the command bit is cleared 00137 if ( (reg & command) == 0 ) 00138 break; // data ready 00139 dprintf("* Not ready, try again\n"); 00140 // otherwise tell the device that we want to read the register (address 0) again 00141 res = _I2C->write(_addr, &writecmd, 1); 00142 dprintf(" write: %d\n", res); 00143 if ( res != 0 ) 00144 return false; 00145 } 00146 return true; 00147 } 00148 00149 // read one axis value (two bytes) 00150 // set read address if index specified explicitly 00151 bool MMCx12xM::_read_axis(int *axis, int index) 00152 { 00153 dprintf("* read axis %d\n", index); 00154 // accept only x, y or z (0, 1, 2) 00155 if ( index > 2 ) 00156 return false; 00157 int res; 00158 if ( index != - 1 ) 00159 { 00160 const char writecmd = index*2 + 1; // set read address for the axis value 00161 res = _I2C->write(_addr, &writecmd, 1); 00162 dprintf(" write: %d\n", res); 00163 if ( res != 0 ) 00164 return false; 00165 } 00166 uint8_t pair[2]; // msb, lsb 00167 res = _I2C->read(_addr, (char*)&pair, 2); 00168 dprintf(" read: %d, msb=%02X, lsb=%02X\n", res, pair[0], pair[1]); 00169 if ( res != 0 ) 00170 return false; 00171 // make an integer from msb and lsb 00172 *axis = (pair[0] << 8) | pair[1]; 00173 return true; 00174 } 00175 00176 // ask chip to take measurements and 00177 // read specified number of raw axis values 00178 bool MMCx12xM::read_raw_values(int *values, int count) 00179 { 00180 dprintf("* read_raw_values\n"); 00181 if ( !_send_command(TM) ) 00182 { 00183 dprintf(" send_command(TM) failed\n"); 00184 return false; 00185 } 00186 wait_ms(5); 00187 if ( !_wait_ready(TM) ) 00188 { 00189 dprintf(" wait_ready(TM) failed\n"); 00190 return false; 00191 } 00192 // we have read the control register, so continue reading the data 00193 // which will be the axis values 00194 for ( int i=0; i < count; i++ ) 00195 { 00196 // we're reading values sequentially, so no need to set the index 00197 if ( !_read_axis(&values[i]) ) 00198 { 00199 dprintf(" _read_axis() failed\n"); 00200 return false; 00201 } 00202 } 00203 return true; 00204 } 00205 00206 bool MMCx12xM::coil_set() 00207 { 00208 return _send_command(SET) && _wait_ready(SET); 00209 } 00210 00211 bool MMCx12xM::coil_reset() 00212 { 00213 return _send_command(SET) && _wait_ready(SET); 00214 } 00215 00216 void MMCx12xM::calibrate_begin() 00217 { 00218 // begin calibration: init the values arrays 00219 for ( int i=0; i < 3; i++ ) 00220 { 00221 _maxvals[i] = 0; 00222 _minvals[i] = INT_MAX; 00223 } 00224 } 00225 00226 void MMCx12xM::calibrate_step(int count) 00227 { 00228 // take a measurement and update min-max values 00229 int values[3]; 00230 if ( read_raw_values(values, count) ) 00231 { 00232 for ( int i=0; i < count; i++ ) 00233 { 00234 if ( _maxvals[i] < values[i] ) 00235 _maxvals[i] = values[i]; 00236 if ( _minvals[i] > values[i] ) 00237 _minvals[i] = values[i]; 00238 } 00239 } 00240 } 00241 00242 void MMCx12xM::calibrate_end() 00243 { 00244 // calculate sensitivity and offset for each axis 00245 // see Memsic app note AN-00MM-003 00246 dprintf("* calibration end\n"); 00247 for ( int i=0; i < 3; i++ ) 00248 { 00249 _sensitivity[i] = (_maxvals[i] - _minvals[i]) / 2; 00250 _offset[i] = (_maxvals[i] + _minvals[i]) / 2; 00251 dprintf(" %i: min = %d, max = %d, s = %d, o = %d\n", i, _minvals[i], _maxvals[i], _sensitivity[i], _offset[i]); 00252 } 00253 } 00254 00255 bool MMCx12xM::read_values(float *values, int count) 00256 { 00257 int rvalues[3]; 00258 if ( read_raw_values(rvalues, count) ) 00259 { 00260 // transform into calibrated values in the range -1.0 .. +1.0 00261 for ( int i=0; i < count; i++ ) 00262 { 00263 // NB: we use a temp float so that the division is not integer 00264 float d = (rvalues[i] - _offset[i]); 00265 values[i] = d / _sensitivity[i]; 00266 } 00267 return true; 00268 } 00269 return false; 00270 }
Generated on Sat Jul 23 2022 02:36:01 by
1.7.2