2
\$\begingroup\$

I am using the MLX90393 Magnetometer IC from Melexis. More specifically, I am using the breakout board by Sparkfun. I am still a beginner when it comes to programming, and this is my first time trying to read data via I2C. I have made sure to progress slowly (started by toggling LEDs, then writing to LCDs etc) and now want to try my hand at sensing. However, I am really struggling with this. I have read the I2C protocol many times to try and get my head around it and I believe I am starting to.

I am using a STM32F303K8 Nucleo board to communicate with the IC. I am using a standard 16x2 serial LDC to display information. The IDE I am using is mbed (it is free and I have used it before, and its not complicated, and doesn't require anything else other than a PC, Nucleo board and USB cable)

For now, all I want to do is be able to read the data. I have looked on the mbed website for example codes to start out with and found none. I can't even find a library, although their quick start guide (can't find a link, it just auto downloads a PDF) claims there is one on the mbed site. I have been able to find 2 elsewhere, one HERE from GitHub and one HERE for ESP32. The problem is, these are both examples for using different equipment. The ESP32 one is written for the Arduino, and I don't wan't to be stuck doing Arduino forever, so I'd rather not use that. I have attempted to convert it for use with mbed but obviously I have not been successful. I tried the same with the GitHub code, but was unsuccessful with that too.

Going through both of these examples, I have attempted to decipher them and convert them for use in mbed and have come up with the following code:

CODE UPDATED TO CURRENT VERSION USE.

#include "mbed.h"

int addr = 0x0C <<1; // 8bit I2C address

I2C i2c(PB_7 , PB_6);   //sda, scl

Serial pc(PA_9, PA_10); //Tx/Rx

int main()
{
    char config [4];
    char data[7] = {0};

    config[0] = 0x60;
    config[1] = 0x00;
    config[2] = 0x5C;
    config[3] = 0x00;

    i2c.write(addr, config, 4, false);

    i2c.read(addr, data, 1);

    config[0] = 0x60;
    config[1] = 0x02;
    config[2] = 0xB4;
    config[3] = 0x02;

    i2c.write(addr, config, 4, false);

    i2c.read(addr, data, 1);

    wait(0.25);

    while (1) {

        config[0] = 0x3E; // Single measurement mode, ZYX enabled

        i2c.write(addr, config, 1, false);
        i2c.read(addr, data, 1);

        wait(0.1);

        config[0] = 0x4E;

        i2c.write(addr, config, 1, false); // Read command, followed by ZYX bits set
        i2c.read(addr, data, 7); 

        if(i2c.read(addr, data, 7) == !7) {
            printf("ERROR \n");
        } else {
            int xMag = i2c.read(data[1] * 256 + data[2]);
            int yMag = i2c.read(data[3] * 256 + data[4]);
            int zMag = i2c.read(data[5] * 256 + data[6]);

            printf("X Axis = %d \n", xMag);
            printf("Y Axis = %d \n", yMag);
            printf("Z Axis = %d \n", zMag);
        }
        wait(5);
    }
}

I have ran the Arduino code on the STM32 hardware and have managed to get the data displayed on a serial monitor. When I run my code, I get -1 read on all 3 axis.

I have scoped the data lines, and I can confirm that the SDA and SCL lines match that of the working Arduino code, so the majority of this is working. It seems that I am deciphering the data incorrectly, or displaying it incorrectly. One of those 2 as the serial monitor is showing the wrong thing. This means it is all correct up untill here:

if(i2c.read(addr, data, 7) == !7) {
                printf("ERROR \n");
            } else {
                int xMag = i2c.read(data[1] * 256 + data[2]);
                int yMag = i2c.read(data[3] * 256 + data[4]);
                int zMag = i2c.read(data[5] * 256 + data[6]);

                printf("X Axis = %d \n", xMag);
                printf("Y Axis = %d \n", yMag);
                printf("Z Axis = %d \n", zMag);
            }

This bit doesn't seem to be doing what it should. Everything else is working as it should do, the correct registers are being read and written to, and the data is being sent back. I have confirmed this by comparing the signals against the Arduino code version that works.

Before asking 'Can you scope this bit' or 'Can you measure that' please read the full chat logs HERE and HERE which will show everything I have done so far to get up to this point.

I just need to know how to get that last bit working to display the information correctly.

\$\endgroup\$
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Dave Tweed Nov 16 '18 at 1:28
  • \$\begingroup\$ Please insert your hardware wiring and schematic \$\endgroup\$ – M KS Nov 16 '18 at 13:13
  • \$\begingroup\$ @MKS the wiring is not the issue. Read the question. The same hardware worked fine when using the arduino IDE, it was using the mbed code to get it running that didn't work. The reason I used the arduino language and IDE was to verify this. You can use STM nucleo boards with arduino if you import some files to the arduino IDE. That's what I did to verify. Please read the whole question thoroughly \$\endgroup\$ – Curious Nov 16 '18 at 13:35
  • \$\begingroup\$ Put a scope on the I2C clock and data lines to get a clue as to what your code is doing. \$\endgroup\$ – EE_socal Nov 16 '18 at 17:40
  • \$\begingroup\$ @EE_social as it's my first time with I2C I'm not 100% sure what is right. I've scoped it and saw the SDA and SCL lines doing stuff, whether it was right or not is a different story! Can you see anything wrong with the code translation? \$\endgroup\$ – Curious Nov 16 '18 at 23:56
2
+150
\$\begingroup\$

Working with the OP and building on the investigation explained in my answer to this related question "I2C Scanner not working properly", the same two problems needed to be fixed first:

  • De-solder STM32F303K8 Nucleo-32 (NUCLEO-F303K8) board solder bridges SB16 and SB18 when using Mbed I2C functions.
  • Use the "extended" 4-parameter Mbed i2c.write() call with the STM32F303K8 Nucleo-32 board.

As noted on another answer, a logic analyser would be helpful in this situation (and should be used, if possible) the following problems were found and fixed without using one, within the limited time available:

  • The sensor's correct 7-bit I2C address is 0x0C, but the Mbed I2C calls want the 8-bit version.

    Earlier versions of the code were misled by a misprint in the SparkFun documents for their breakout board using this sensor, where they said the address was 0xC0.

    Also some code versions didn't left-shift that I2C address value, however the Mbed I2C functions want the address passed to them as an 8-bit version (e.g. 0x18 in this case). So the fix was either to pass it as 0x0C << 1 or 0x18 (same thing) to the Mbed I2C calls.

  • As noted in that other linked question, I2C access using this STM32F303K8 Nucleo board is only successful when using the Mbed i2c.write() and i2c.read() functions, with (up to) 4 parameters (3 required, 4th optional) and not using the lower level single parameter i2c.write() and i2c.read() calls instead.

  • At this point, comparing I2C bus activity on the scope between the Arduino code and the Mbed code, the OP could see that I2C bus activity was now identical, up to the point of triggering the measurement by the sensor. That left the final problem in the reading and display of the values from the sensor in the Mbed code.

    Looking in that part of the code, I removed the part which was reading from the sensor multiple times and checking for an incorrect return code. Therefore I changed part of the original code from:

config[0] = 0x4E;

i2c.write(addr, config, 1, false); // Read command, followed by ZYX bits set
i2c.read(addr, data, 7); 

if(i2c.read(addr, data, 7) == !7) {
printf("ERROR \n"); 
} else { 
int xMag = i2c.read(data[1] * 256 + data[2]); 
int yMag = i2c.read(data[3] * 256 + data[4]); 
int zMag = i2c.read(data[5] * 256 + data[6]); 

printf("X Axis = %d \n", xMag); 
printf("Y Axis = %d \n", yMag); 
printf("Z Axis = %d \n", zMag); 
}

to

config[0] = 0x4E; 

i2c.write(addr, config, 1, false); // Read command, followed by ZYX bits set 
i2c.read(addr, data, 7); 

int xMag = ((data[1] * 256) + data[2]); 
int yMag = ((data[3] * 256) + data[4]); 
int zMag = ((data[5] * 256) + data[6]); 

printf("X Axis = %d \n", xMag); 
printf("Y Axis = %d \n", yMag); 
printf("Z Axis = %d \n", zMag);

So the full version of the working code is:

#include "mbed.h" 

int addr = 0x0C <<1; // 8bit I2C address 

I2C i2c(PB_9 , PB_8); //sda, scl 

Serial pc(PA_2, PA_3); //Tx/Rx 

int main() 
{ 
char config [4]; 
char data[7] = {0}; 

config[0] = 0x60; 
config[1] = 0x00; 
config[2] = 0x5C; 
config[3] = 0x00; 

i2c.write(addr, config, 4, false); 

i2c.read(addr, data, 1); 

config[0] = 0x60; 
config[1] = 0x02; 
config[2] = 0xB4; 
config[3] = 0x02; 

i2c.write(addr, config, 4, false); 

i2c.read(addr, data, 1); 

wait(0.25); 

while (1) { 

config[0] = 0x3E; // Single measurement mode, ZYX enabled 

i2c.write(addr, config, 1, false); 
i2c.read(addr, data, 1); 

wait(0.1); 

config[0] = 0x4E; 

i2c.write(addr, config, 1, false); // Read command, followed by ZYX bits set 
i2c.read(addr, data, 7); 

int xMag = ((data[1] * 256) + data[2]); 
int yMag = ((data[3] * 256) + data[4]); 
int zMag = ((data[5] * 256) + data[6]); 

printf("X Axis = %d \n", xMag); 
printf("Y Axis = %d \n", yMag); 
printf("Z Axis = %d \n", zMag); 

wait(5); 
} 
}

With this Mbed code, the sensor produces correct output, similar to the working Arduino code, and the point of the question was to get to that stage. The code can then be used as a starting point and improved with error checking & status reporting etc.

Again, further investigations of the I2C behaviour of the Mbed libraries and the sensor can be performed with a logic analyser, when an analyser (and more time) is available.

\$\endgroup\$
1
\$\begingroup\$

Instead of guessing and doing random convulsive modifications to your code, you should get a cheap ($7) logic analyzer from eBay, for which you download free software and I2C decoder, and get I2C traces from your hardware bus.

enter image description here

You will immediately see if your address is correct or not, and how the device is responding (or not).

Many more expensive general-purpose digital scopes do have I2C decoders nowadays, but the $7 device does its job well.

You are embarking on embedded design, which meant to produce hardware signaling. Using hardware tools to look into resulting signals is a must. This should be your starting point, everything else is secondary. Looking for few minutes into captured trace will save you days of "random engineering".

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. I may invest in something like this. There were multiple issues with this in the end, but I managed to run an I2C scanner to get the correct address eventually, and I scoped all the signals back and forth, comparing them against the Arduino signals (that worked) to eventually get to this point. It wasn't exactly all guesswork and random changes to the code. If you had read the chat logs then you would have seen quite a bit of effort has gone into debugging. Not just guesses \$\endgroup\$ – Curious Nov 22 '18 at 8:38
  • \$\begingroup\$ @Curious, yes, you undertook a great effort in porting the code and debugging it. My point is that a simple two-wire I2C logic analyzer, which instantly shows decoded packets and all associated errors (missing start-stops, ACKs/NAKs), would reduce your debug efforts by orders of magnitude. \$\endgroup\$ – Ale..chenski Nov 22 '18 at 8:54
  • \$\begingroup\$ Yes, agreed. It would have also helped to have an IDE with break points. Mbed is pretty useless for debugging. I ended up looking through all the bytes sent to and from to find ACK and NACK symbols, check which addresses were being written/read to/from etc on an oscilloscope. I agree, one of those would be a good investment, so +1 for that suggestion \$\endgroup\$ – Curious Nov 22 '18 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.