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.
Revision 0:a44429321af8, committed 2009-12-02
- Comitter:
- igorsk
- Date:
- Wed Dec 02 23:03:25 2009 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MMCx12xM.cpp Wed Dec 02 23:03:25 2009 +0000
@@ -0,0 +1,270 @@
+#include "MMCx12xM.h"
+#include <stdarg.h>
+#include <limits.h>
+
+/*
+
+ Memsic datasheets use very obtuse language, here I tried to summarize it in plain English.
+
+ The device works over I2C ("fast" mode, i.e. 400 kHz max)
+ the slave address is determined by the last digit (x in MMC212x) :
+ 0: 0x60, 1: 0x64, 2: 0x68, 3: 0x6C
+
+ Register map:
+ 00 control register
+ 01 most significant byte x axis
+ 02 least significant byte x axis
+ 03 most significant byte y axis
+ 04 least significant byte y axis
+ 05 most significant byte z axis (MMC312xM only)
+ 06 least significant byte z axis (MMC312xM only)
+
+ Operation is initiated by sending the register address (must be 0, since
+ only control register is writable) and then the command code, which is one
+ of the bits from the control register.
+
+ Control register layout:
+ bit 0: TM (take measurements)
+ set to get measurements (i.e. send 0x01)
+ bit is reset when measurement is done, so you can use it
+ to check if the operation is finished
+ bit 1: SET (set coil)
+ set to send a large current through the set/reset coil
+ should be done if sensor was affected by a large magnetic field (>5.5 gauss)
+ also after power on
+ result can be checked same as above
+ bit 2: RESET (reset coil)
+ same as above but sends current in the opposite direction
+ set/reset should be interleaved in "low-power mode", whatever that is
+ bits 3-6: reserved
+ bit 7: not described
+
+ To read a register, write its address and then read the value. The address auto-increments
+ after reading, so it's not necessary to send it again if reading sequentially. The address
+ is reset to 0 on power-on.
+
+ Taking measurements works like following:
+ 1. write 0 (control register address), then 0x01 (take measurements)
+ 2. wait 5ms for the measurement to complete
+ 3. write 0 again (control register address), then read the register value
+ 4. check if the bit 0 is cleared. If not, repeat from 3.
+ 5. read x msb
+ 6. read x lsb
+ 7. read y msb
+ 8. read y lsb
+ 9. (if MMC312xM) read z msb
+ 10. (if MMC312xM) read z lsb
+
+ N.B.: ADC resolution is only 12 bits, so four high bits of values will be 0
+
+ You can also skip some values and read directly the value wanted
+ by sending its address before reading.
+*/
+
+// uncomment to show protocol debug tracing
+//#define DEBUG
+
+int dprintf(const char *fmt, ...)
+{
+#ifdef DEBUG
+ va_list args;
+ va_start (args, fmt);
+ int ret = vprintf(fmt, args);
+ va_end(args);
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+enum command {
+ TM = 1, // take measurements
+ SET = 2, // set
+ RESET = 4 // reset
+};
+
+MMCx12xM::MMCx12xM(I2C &i2c, int address, const char *name) : Base(name),
+ _I2C(&i2c), _addr(address), _own_i2c(false)
+{
+}
+
+MMCx12xM::MMCx12xM(PinName sda, PinName scl, int address, const char *name) : Base(name),
+ _addr(address)
+{
+ _I2C = new I2C(sda, scl);
+ // we own the bus, so we can set frequency
+ // MMCx12x claims to handle 400 kHz
+ _I2C->frequency(400000);
+ _own_i2c = true;
+}
+
+MMCx12xM::~MMCx12xM()
+{
+ if (_own_i2c)
+ delete _I2C;
+}
+
+// send the specified command: write it into the control register
+bool MMCx12xM::_send_command(int command)
+{
+ dprintf("* send command %d\n", command);
+ const char writecmd[] = {0, command}; // address: 0 (control reg)
+ int res = _I2C->write(_addr, writecmd, 2);
+ dprintf("write: %d\n", res);
+ return res == 0;
+}
+
+// wait until the command is done
+// this is signalled by clearing of the
+// corresponding bit in the control register
+bool MMCx12xM::_wait_ready(int command)
+{
+ dprintf("* wait ready %d\n", command);
+ const char writecmd = 0; // set read address to 0 (control reg)
+ int res = _I2C->write(_addr, &writecmd, 1);
+ dprintf(" write: %d\n", res);
+ if ( res != 0 )
+ return false;
+ char reg;
+ while ( 1 )
+ {
+ // read control register value
+ res = _I2C->read(_addr, ®, 1);
+ dprintf(" read: %d, reg=%08X\n", res, reg);
+ if ( res != 0 )
+ return false;
+ // check if the command bit is cleared
+ if ( (reg & command) == 0 )
+ break; // data ready
+ dprintf("* Not ready, try again\n");
+ // otherwise tell the device that we want to read the register (address 0) again
+ res = _I2C->write(_addr, &writecmd, 1);
+ dprintf(" write: %d\n", res);
+ if ( res != 0 )
+ return false;
+ }
+ return true;
+}
+
+// read one axis value (two bytes)
+// set read address if index specified explicitly
+bool MMCx12xM::_read_axis(int *axis, int index)
+{
+ dprintf("* read axis %d\n", index);
+ // accept only x, y or z (0, 1, 2)
+ if ( index > 2 )
+ return false;
+ int res;
+ if ( index != - 1 )
+ {
+ const char writecmd = index*2 + 1; // set read address for the axis value
+ res = _I2C->write(_addr, &writecmd, 1);
+ dprintf(" write: %d\n", res);
+ if ( res != 0 )
+ return false;
+ }
+ uint8_t pair[2]; // msb, lsb
+ res = _I2C->read(_addr, (char*)&pair, 2);
+ dprintf(" read: %d, msb=%02X, lsb=%02X\n", res, pair[0], pair[1]);
+ if ( res != 0 )
+ return false;
+ // make an integer from msb and lsb
+ *axis = (pair[0] << 8) | pair[1];
+ return true;
+}
+
+// ask chip to take measurements and
+// read specified number of raw axis values
+bool MMCx12xM::read_raw_values(int *values, int count)
+{
+ dprintf("* read_raw_values\n");
+ if ( !_send_command(TM) )
+ {
+ dprintf(" send_command(TM) failed\n");
+ return false;
+ }
+ wait_ms(5);
+ if ( !_wait_ready(TM) )
+ {
+ dprintf(" wait_ready(TM) failed\n");
+ return false;
+ }
+ // we have read the control register, so continue reading the data
+ // which will be the axis values
+ for ( int i=0; i < count; i++ )
+ {
+ // we're reading values sequentially, so no need to set the index
+ if ( !_read_axis(&values[i]) )
+ {
+ dprintf(" _read_axis() failed\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MMCx12xM::coil_set()
+{
+ return _send_command(SET) && _wait_ready(SET);
+}
+
+bool MMCx12xM::coil_reset()
+{
+ return _send_command(SET) && _wait_ready(SET);
+}
+
+void MMCx12xM::calibrate_begin()
+{
+ // begin calibration: init the values arrays
+ for ( int i=0; i < 3; i++ )
+ {
+ _maxvals[i] = 0;
+ _minvals[i] = INT_MAX;
+ }
+}
+
+void MMCx12xM::calibrate_step(int count)
+{
+ // take a measurement and update min-max values
+ int values[3];
+ if ( read_raw_values(values, count) )
+ {
+ for ( int i=0; i < count; i++ )
+ {
+ if ( _maxvals[i] < values[i] )
+ _maxvals[i] = values[i];
+ if ( _minvals[i] > values[i] )
+ _minvals[i] = values[i];
+ }
+ }
+}
+
+void MMCx12xM::calibrate_end()
+{
+ // calculate sensitivity and offset for each axis
+ // see Memsic app note AN-00MM-003
+ dprintf("* calibration end\n");
+ for ( int i=0; i < 3; i++ )
+ {
+ _sensitivity[i] = (_maxvals[i] - _minvals[i]) / 2;
+ _offset[i] = (_maxvals[i] + _minvals[i]) / 2;
+ dprintf(" %i: min = %d, max = %d, s = %d, o = %d\n", i, _minvals[i], _maxvals[i], _sensitivity[i], _offset[i]);
+ }
+}
+
+bool MMCx12xM::read_values(float *values, int count)
+{
+ int rvalues[3];
+ if ( read_raw_values(rvalues, count) )
+ {
+ // transform into calibrated values in the range -1.0 .. +1.0
+ for ( int i=0; i < count; i++ )
+ {
+ // NB: we use a temp float so that the division is not integer
+ float d = (rvalues[i] - _offset[i]);
+ values[i] = d / _sensitivity[i];
+ }
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MMCx12xM.h Wed Dec 02 23:03:25 2009 +0000
@@ -0,0 +1,73 @@
+#include <mbed.h>
+#ifndef __MMCx12xM__
+#define __MMCx12xM__
+
+// possible I2C addresses, depending on the chip number
+enum
+{
+ MMCx120M = 0x60,
+ MMCx121M = 0x64,
+ MMCx122M = 0x68,
+ MMCx123M = 0x6C,
+};
+
+/* Class: MMCx12xM
+ * Control a Memsic MMC212xM magnetometer over I2C
+ *
+ * Example:
+ * > // MMC2120M at address 0x60
+ * >
+ * > #include "mbed.h"
+ * >
+ * > I2C i2c(p28, p27);
+ * > MMC212xM memsic1(i2c);
+ * >
+ * > int main() {
+ * > int data[2];
+ * > memsic1.read_raw_values(data, 2);
+ * > }
+ */
+
+class MMCx12xM : public Base
+{
+public:
+ // constructor in case you already have an I2C bus instance
+ MMCx12xM(I2C &i2c, int address = MMCx120M, const char *name = NULL);
+ // use this constructor if the sensor is the only device on the bus
+ MMCx12xM(PinName sda, PinName scl, int address = MMCx120M, const char *name = NULL);
+ // send a SET coil command
+ bool coil_set();
+ // send a RESET coil command
+ bool coil_reset();
+ // read raw (12-bit) axis values
+ bool read_raw_values(int *values, int count = 2);
+ // start calibration
+ void calibrate_begin();
+ // take a single measurement for calibration
+ void calibrate_step(int count = 2);
+ // finish calibration and calculate offset and sensitivity values
+ void calibrate_end();
+ // read calibrated (-1.0 .. +1.0) axis values
+ bool read_values(float *values, int count = 2);
+ virtual ~MMCx12xM();
+
+private:
+ bool _send_command(int command);
+ bool _wait_ready(int command);
+ bool _read_axis(int *value, int index = -1);
+
+ // reference to the I2C bus
+ I2C *_I2C;
+ // sensor slave address
+ int _addr;
+ // did we create the bus instance? (i.e. we should delete it on destruct)
+ bool _own_i2c;
+ // calibration values
+ int _sensitivity[3];
+ int _offset[3];
+ // temporaries for calibration
+ int _maxvals[3];
+ int _minvals[3];
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Wed Dec 02 23:03:25 2009 +0000
@@ -0,0 +1,53 @@
+#include "mbed.h"
+#include "MMCx12xM.h"
+
+I2C i2c(p9, p10); // sda, scl
+DigitalOut memsic_power(p8);
+
+MMCx12xM memsic(i2c);
+
+PwmOut led1(LED1);
+PwmOut led2(LED2);
+PwmOut led3(LED3);
+PwmOut led4(LED4);
+
+int main()
+{
+ printf("MMC2120M demo\n");
+ memsic_power = 1;
+ bool ok = memsic.coil_set();
+ printf("Set: %d\n", ok);
+
+ /*int values[2];
+ for (;;)
+ {
+ ok = memsic.read_raw_values(values);
+ printf("ok: %d, x: %d, y: %d\n", ok, values[0], values[1]);
+ wait(2);
+ }*/
+
+ printf("Starting calibration. Turn the sensor in all possible directions for 10 seconds.\n");
+ memsic.calibrate_begin();
+ int cal_count = 0;
+ while (1)
+ {
+ memsic.calibrate_step();
+ cal_count++;
+ wait_ms(100);
+ if ( cal_count > 100 )
+ break;
+ }
+ memsic.calibrate_end();
+ //printf("%d samples were used for calibration\n", cal_count);
+ float fvalues[2];
+ for (;;)
+ {
+ ok = memsic.read_values(fvalues);
+ printf("ok: %d, x: %f, y: %f\n", ok, fvalues[0], fvalues[1]);
+ led1 = fvalues[0] > 0 ? fvalues[0] : 0;
+ led2 = fvalues[1] > 0 ? fvalues[1] : 0;
+ led3 = fvalues[0] < 0 ? -fvalues[0] : 0;
+ led4 = fvalues[1] < 0 ? -fvalues[1] : 0;
+ wait_ms(100);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Wed Dec 02 23:03:25 2009 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/32af5db564d4