I'm Road Testing the Rohm SensorShield-EVK-003 (Arduino Compatible) and the sensors that come with it. In this post, the Geo Magnetic Sensor BM1422AGMVBM1422AGMV.
|
The Sensor Shield ecosystemSensor Shield ecosystem is ROHM's test bed for their sensors. It's an Arduino form shield with a number of slots.
The small evaluation boards for their sensors plug into that board.
For each of the sensors, there's an example sketch available.
For this blog post, I'm testing the Magnetometer sensor that comes with the kit.
Evaluation Board
This tiny PCB contains the sensor, required bypass capacitors, optional footprints for i2c pull-ups and a jumper to change the address of the sensor.
The sensor comes in a QFN package.
The datasheet does not explain the physical constructions.
But it's safe to assume that there are 3 sensors on board. Each physically mounted in one of the 3 axis.
The quote below - from (deprecated) android documentation - shows how the axis are interpreted in a smartphone.
source: developer.android.com |
Firmware
What surprised me is that the example available from the ROHM website is better than what's on github.
The downloadable one supports a trigger when data is available from the magnetometer.
That's a useful demo for this device and it is interesting for Arduino education.
The Software registers the trigger, and based on the mode of the device attaches that trigger on the falling or rising edge.
image source: BM1422AGMV datasheet
I'm quoting from both sketch and library.
Copyright (c) 2017 ROHM Co.,Ltd.
Please check the - very liberal - copyright notice in the source files.
Here's the trigger handler. It's defined in the sketch.
void bm1422agmv_isr(void) { bm1422agmv.set_drdy_flg(); }
The trigger is attached in the Setup() method in the sketch.
BM1422AGMV bm1422agmv(BM1422AGMV_DEVICE_ADDRESS_0E); // ... void setup() { // ... Wire.begin(); rc = bm1422agmv.init(); // ... bm1422agmv.isr_func(0, bm1422agmv_isr); }
We see the constructor and three methods used from the ROHM library BM1422AGMV.
You can check out the C++ code in the zip file you downloaded from ROHM website.
I'll show some extracts from them, with focus on the steps happening:
Constructor
#define BM1422AGMV_DEVICE_ADDRESS_0E (0x0E) // 7bit Addrss BM1422AGMV::BM1422AGMV(int slave_address) { _device_address = slave_address ; }
init()
byte BM1422AGMV::init(void) { // ... _drdy_flg = 0; rc = read(BM1422AGMV_WIA, ®, sizeof(reg)); // ... if (reg != BM1422AGMV_WIA_VAL) { // ... } // Step1 reg = BM1422AGMV_CNTL1_VAL; rc = write(BM1422AGMV_CNTL1, ®, sizeof(reg)); // ... // Check 12bit or 14bit buf[0] = (BM1422AGMV_CNTL1_VAL & BM1422AGMV_CNTL1_OUT_BIT); if (buf[0] == BM1422AGMV_CNTL1_OUT_BIT) { _sens = BM1422AGMV_14BIT_SENS; } else { _sens = BM1422AGMV_12BIT_SENS; } delay(1); // ... buf[0] = (BM1422AGMV_CNTL4_VAL >> 8) & 0xFF; buf[1] = (BM1422AGMV_CNTL4_VAL & 0xFF); rc = write(BM1422AGMV_CNTL4, buf, sizeof(buf)); // ... // Step2 reg = BM1422AGMV_CNTL2_VAL; rc = write(BM1422AGMV_CNTL2, ®, sizeof(reg)); // ... // Step3 // Option reg = BM1422AGMV_AVE_A_VAL; rc = write(BM1422AGMV_AVE_A, ®, sizeof(reg)); // ... return (rc); }
isr_func()
void BM1422AGMV::isr_func(int int_num, void func(void)) { if (BM1422AGMV_CNTL2_VAL & BM1422AGMV_CNTL2_DRP) { attachInterrupt(int_num, func, RISING); } else { attachInterrupt(int_num, func, FALLING); } }
In the loop() method, the sketch gets data from the sensor.
void loop() { byte rc; float mag[3]; rc = bm1422agmv.get_val(mag); if (rc == 0) { Serial.print(F("BM1422AGMV XDATA=")); Serial.print(mag[0], 3); Serial.println("[uT]"); Serial.print(F("BM1422AGMV YDATA=")); Serial.print(mag[1], 3); Serial.println("[uT]"); Serial.print(F("BM1422AGMV ZDATA=")); Serial.print(mag[2], 3); Serial.println("[uT]"); Serial.println(); } delay(500); }
Here are the library functions used:
get_val()
byte BM1422AGMV::get_val(float *data) { byte rc; unsigned char val[6]; signed short mag[3]; rc = get_rawval(val); if (rc != 0) { return (rc); } mag[0] = ((signed short)val[1] << 8) | (val[0]); mag[1] = ((signed short)val[3] << 8) | (val[2]); mag[2] = ((signed short)val[5] << 8) | (val[4]); convert_uT(mag, data); return (rc); }
get_rawval()
byte BM1422AGMV::get_rawval(unsigned char *data) { // ... // Step4 reg = BM1422AGMV_CNTL3_VAL; rc = write(BM1422AGMV_CNTL3, ®, sizeof(reg)); while(_drdy_flg == 0) { delayMicroseconds(100); }; _drdy_flg = 0; rc = read(BM1422AGMV_DATAX, data, 6); // ... return (rc); }
convert_uT
void BM1422AGMV::convert_uT(signed short *rawdata, float *data) { // LSB to uT data[0] = (float)rawdata[0] / _sens; data[1] = (float)rawdata[1] / _sens; data[2] = (float)rawdata[2] / _sens; }
Here you see how the read from the sensor works in collaboration with the data ready flag and the interrupt.
Measurements
A few measurements, first flat on the table, with the text on the sensor PCB facing:
North:
BM1422AGMV XDATA=1.208[uT]
BM1422AGMV YDATA=-0.375[uT]
BM1422AGMV ZDATA=-76.875[uT]
South:
BM1422AGMV XDATA=-26.333[uT]
BM1422AGMV YDATA=-24.333[uT]
BM1422AGMV ZDATA=-79.417[uT]
East:
BM1422AGMV XDATA=-28.917[uT]
BM1422AGMV YDATA=-4.042[uT]
BM1422AGMV ZDATA=-80.917[uT]
West:
BM1422AGMV XDATA=1.333[uT]
BM1422AGMV YDATA=-26.708[uT]
BM1422AGMV ZDATA=-78.083[uT]
And here pointing the board up and down:
Up West:
BM1422AGMV XDATA=-49.875[uT]
BM1422AGMV YDATA=-41.667[uT]
BM1422AGMV ZDATA=-25.625[uT]
Down West:
BM1422AGMV XDATA=32.375[uT]
BM1422AGMV YDATA=-29.333[uT]
BM1422AGMV ZDATA=-20.542[uT]
i2c Traffic
I've captured the traffic of a measurement sequence. Here you can see the effects of the following lines of code:
rc = read(BM1422AGMV_DATAX, data, 6);
It generates an address write, then 6 blocks of char size data returning.
You may recognise this address on line 3:
#define BM1422AGMV_DATAX (0x10)
I've also measured the impact of moving the sensor in increments of 22°. I'll post them if there's interest.
Top Comments