Igor Skochinsky / Mbed 2 deprecated DCSS504

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MMCx12xM.cpp Source File

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, &reg, 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 }