Technical Article

# Tutorial and Overview of Infineon’s 3D Magnetic 2Go Kit

September 30, 2016 by Mark Hughes

## The 3D Magnetic 2GO kit is a development kit for the TLV493D from Infineon that can detect magnetic field strength in x, y, and z directions.

Infineon's 3D Hall-effect sensor allows 3-dimensional contactless position detection using an ultra low power integrated circuit. This tutorial will use their standard development kit to create a joystick for use in your projects.

### Getting Started

The TLV493D is a 6-pin 3.3V I²C magnetic field and temperature sensor. The 3D Magnetic Sensor 2GO Development Kit contains the TLV493D (magnetic field sensor), the XMC1100 microcontroller (datasheet), the XMC4200 microcontroller (datasheet), and a micro USB port.

To follow along with this tutorial, you will need to purchase the 3D Magnetic Sensor 2GO Development Kit and the Magnetic Joystick. If you wish to interface with Arduino, you only need to purchase the TLV493D, but I recommend against this approach because it is difficult to work with the sensor's extremely small package.

ItemPurchaseReferences
3D Magnetic Sensor TLV493D$2Datasheet | Manual | Brief TLV493D Development Kit$28Datasheet | Manual
Magnetic Joystick$22none Arduino Uno R3 (or compatible)$15Reference
4 position Header (0.100")$1Datasheet Breadboard Jumper Wires$3Datasheet
Bi-Directional
Logic Level Converter
\$3Datasheet

### Getting Started with Demonstration Software

Infineon provides a graphical user interface for its sensors. Go to their download page and install the GUI for 3D Magnetic Sensor. It installs programs in the folders "3D Magnetic 2 GO" and "Segger."

• Use a micro-USB cable to connect the evaluation board to your PC
• Open "3D Magnetic 2 GO"
• Underneath the Programmer box, select the XMC2Go on COM5 (adjust the COM port as necessary)
• Click the icon beneath and to the left of the Programmer box to connect—a drop down menu will appear
• Click to select "TLV493D"—a Configuration drop down menu will appear
• Click to select "Fast Mode"
• Click "Start"
• Select either "Graph View" or "Joystick View"

### The Hall Effect and Hall-Effect Sensors

The Hall Effect describes a potential difference created by charges that separate to opposite sides of a conductor in the presence of a magnetic field.

##### A visualization of the Hall Effect. Video courtesy of FraunhoferIIS via Wikimedia.

The TLV493D sensor is able to detect the magnetic field intensity in three orthogonal directions. The evaluation kit makes use of the Hall effect in the TLV493D and the calibration data from the magnet included in the joystick to determine the orientation of the joystick.

##### Image of magnetic field intensities in x, y, and z directions.

You can create magnetic field simulations using the three default magnets in the joystick simulation software or based on magnet parameters you specify at Design.Infineon.com/3dsim.

### Infineon 3D Magnetic Sensor 2GO Kit

The Infineon 3D Magnetic Sensor evaluation board also has an Infineon XMC4200 microcontroller and an Infineon XMC1100 microcontroller. While there are no Arduino libraries for the 3D Magnetic Sensor, Infineon's technical support directed me to the following tools:

GUI for 3D Magnetic Sensor v 2.0.1 (used in the above example)

The graphical user interface (GUI) is able to connect to the evaluation board via USB, and it provides the following:

• Data readings from each axis as well as temperature data
• A 3D representation of the joystick
• A 2D representation of a turning knob (using an accessory knob not included in the eval kit)

DAVE — Development Platform for XMC Microcontrollers

DAVE is a free Eclipse-based IDE. Here are some relevant links:

### Using the TLV493D with an Arduino

The TLV493D is a 6-pin 3.3V I²C magnetic field and temperature sensor. The evaluation board allows the TLV493D and four 0.100" through holes (3.3V, GND, SDA, SCL) to be mechanically separated from another identical row of 0.100" through-holes and the two microcontrollers.

I chose not to break my TLV493D off from the evaluation board. Whether you use a separate TLV493D, separate the one from the evaluation board, or choose to leave it intact as I did should not affect the rest of this project.

### Setting Up the Circuit

The Arduino board that I use for this tutorial operates with 5V logic. The TLV493D on the 3D 2GO Eval Kit board uses 3.3V logic. Thus, a bi-directional logic-level converter must be inserted between the Arduino and the TLV493D.

Connect the circuit as shown below, using the Arduino's onboard voltage regulators to power both sides of the Bi-Directional Logic-Level Converter (BDLLC). On the BDLLC, the high voltage (HV) side is connected to the 5V source on the Arduino and the Arduino data pins, and the low voltage (LV) side is connected to the 3.3 V source on the Arduino and the TLV493D data pins.

Infineon offers no Arduino libraries through their website or customer support. At the time of writing, an online search of Arduino sketches that use the TLV493D returns no results. That means I get to work through the process of creating a program based on information in the datasheet.

The manual "Low Power 3D Magnetic Sensor with I²C Interface" provides most of the necessary information.

First, we need to know the sensor address. Page 20 of the manual provides an 8-bit address. If the TLV493D has logic high on its SDA pin at startup, the 8-bit address of the sensor will be 0xBC. Wire.h only allows for 7-bit addresses up to 0x7F (127 or 111 11112). To convert from 8-bit to 7-bit, just shift the address one bit to the right. In hexadecimal notation, for the program to function, the write address must be 0x5E.

n-bit Slave

at power up

Dec Bin Hex Dec Bin Hex
8 0 High (1) 189 1011 1101 0xBD 188 1011 1100 0xBC
1 Low (0) 63 00111111 0x3F 62 0011 1110 0x3E
7 0 High (1) 95 101 1111 0x5F 94 101 1110 0x5E

I was not initially aware of Wire.h's limitation as I had never encountered it before—so when the TLV493D did not respond to my initial program, I had to do some troubleshooting. I used a bit of code to send write commands to every address and looked for ACK signals using the Tektronix MDO3104. (See Reading I²C Bus on an Oscilloscope for more information.)

### Reading and Writing the TLV493D I²C Registers

At power up, the TLV493D defaults to low power mode (datasheet page 14) where no measurements are taken. The datasheet indicates that users should read registers 7H, 8H, 9H (FactSet1, FactSet2, FactSet3) for writing configuration information back to the chip later.

I'll read them in the setup part of the code and rewrite them to the appropriate registers when I bring the TLV493D out of power down mode and start reading measurement data.

#### Writing to the TLV493D

The standard power modes are controlled by the first three bits of the write register MOD1 (1H) and the last two bits of MOD 2 (3H). Write register 0H is reserved and non-configurable, so I will send a 0x00 byte. The remaining bits will be written with bits from read registers 7H, 8H, and 9H.

Bits 1:0 in MOD1 and Bit 7 in MOD2 control the rate at which the TLV493D collects data and makes it available for reading. Bit 3 in MOD1 determines whether the TLV493 should use the interrupt line to notify the microcontroller that data is ready to be read. Bit 8 in Mod2 turns temperature measurements on. Doing so increases power consumption by 33%, but it allows temperature compensation for data measurements to be performed.

#### Reading the TLV493D Magnetic Data

Bx, By, Bz and temperature data are stored across seven separate 8-bit registers. The resolution of the sensors is 12 bits (1.5 bytes), the size of each register is 8 bits (1 byte), and I am storing the information in Arduino in 16-bit (2 bytes) variables. This is accomplished by reading registers 0H, 1H, 2H, 3H, 4H, 5H, and 6H into the Arduino and using both bitshift and logical operations to move the proper values into bits 11:0 of each 16-bit variable.

##### Sample data read and data write to set the mode of the TLV493D
/*   Infinenon 3D Magnetic I2C
*   TLV493D
*   by Mark J. Hughes
*   20160817
*/

//--- Begin Includes ---//
#include <Wire.h>       // I²C Libraries

// Variable Declaration
const byte addr = 0x5E; // default address of magnetic sensor 0x5E or 0x3E
byte rbuffer[10];       // store data from sensor read registers
byte wbuffer[4];        // store data for sensor write registers.
byte debugcounter;      // variable for debug counter
byte delaytime = 1;     // time to wait before next read.  Delay will increase with errors.

//--- Begin Write Registers ---//
/*
*  Mode 1 is the second write register
*  Mode1_Int   Bxxxxx1xx  Interrupt Enable "1" / Disable "0"
*  Mode1_Fast  Bxxxxxx1x  Fast Mode Enable "1" / Disable "0" must be 0 for power down
*  Mode1_Low   Bxxxxxxx1  Low Power Mode Enable "1" / Disable "0"
*
*  Mode 2 is the fourth write register
*  Mode2_T     B1xxxxxxx  Temperature Measurement Enable "1" / Disable "0"
*  Mode2_LP    Bx1xxxxxx  LP Period "1" = 12ms / "0"=100ms
*  Mode2_PT    Bxx1xxxxx  Parity test Enable "1" / Disable "0"
*
*/

Example settings for Ultra-Low Power, Low Power, Fast Mode, and Power Down.
Reg 1      Reg 2      Reg 3      Reg 4
const byte ulpm[] = { B00000000, B00000101, B00000000, B00000000 }; // ultra low power mode
const byte lpm[]  = { B00000000, B00000101, B00000000, B01000000 }; // low power mode
const byte fm[]   = { B00000000, B00000110, B00000000, B00000000 }; // fast mode (unsupported)
const byte pd[]   = { B00000000, B00000001, B00000000, B00000000 }; // power down mode.

//--- Begin Setup ---//
void setup() {
Serial.begin(115200);      // Begin serial connection for debug.
Wire.begin();              // Begin I²C wire communication

/* Read all registers, although only interested in configuration data
* stored in rbuffers 7,8,9, as 0-6 might be empty or invalid at the moment.
*/
for(int i=0; i < sizeof(rbuffer); i++){
}
// Write Register 0H is non configurable.  Set all bits to 0
wbuffer[0] = B00000000;

// Read Register 7H 6:3 -> Write Register 1H 6:3
wbuffer[1] = rbuffer[7] & B01111000;

// Read Register 8H 7:0 -> Write Register 2H 7:0
wbuffer[2] = rbuffer[8];

// Read Register 9H 4:0 -> Write Register 3H 4:0 (Mod2)
wbuffer[3] = rbuffer[9] & B00001111;

// Set Power Mode (ulpm, lpm, fm, pd)
for(int i=0; i < sizeof(wbuffer); i++){
wbuffer[i] |= lpm[i]
}

for(int i=0; i < sizeof(wbuffer); i++){
Wire.write(wbuffer[i]);
}
Wire.endTransmission();
}
//--- End of Setup --//

//--- Begin Main Program Loop --//
void loop() {

delay(delaytime); // wait time between reads.
// Read sensor registers and store in rbuffer
for(int i=0; i < 6; i++){
}

// Goto decode functions below
int x = decodeX(rbuffer[0],rbuffer[4]);
int y = decodeY(rbuffer[1],rbuffer[4]);
int z = decodeZ(rbuffer[2],rbuffer[5]);
int t = decodeT(rbuffer[3],rbuffer[6]);

if(debugcounter % 15 == 0){   // reprint x, y, z, t header every 15 lines.
Serial.print("x"); Serial.print("\t");Serial.print("y");Serial.print("\t");Serial.print("z");Serial.print("\t");Serial.println("t");
}
debugcounter++;               // increment debug counter.

if(rbuffer[3] & B00000011 != 0){ // If bits are not 0, TLV is still reading Bx, By, Bz, or T
//delaytime += 10;
} else { Serial.print(x); Serial.print("\t");Serial.print(y);Serial.print("\t");Serial.print(z);Serial.print("\t");Serial.println(t);}
}
//-- End of Main Program Loop --//

//-- Begin Buffer Decode Routines --//
int decodeX(int a, int b){
/* Shift all bits of register 0 to the left 4 positions.  Bit 8 becomes bit 12.  Bits 0:3 shift in as zero.
* Determine which of bits 4:7 of register 4 are high, shift them to the right four places -- remask in case
* they shift in as something other than 0.  bitRead and bitWrite would be a bit more elegant in next version
* of code.
*/
int ans = ( a << 4 ) | (((b & B11110000) >> 4) & B00001111);
if( ans > 1023){ ans -= 2048; } // Interpret bit 12 as +/-
return ans;
}

int decodeY(int a, int b){
/* Shift all bits of register 1 to the left 4 positions.  Bit 8 becomes bit 12.  Bits 0-3 shift in as zero.
* Determine which of the first four bits of register 4 are true.  Add to previous answer.
*/

int ans = (a << 4) | (b & B00001111);
if( ans > 1024){ ans -= 2048;} // Interpret bit 12 as +/-
return ans;
}

int decodeZ(int a, int b){
/* Shift all bits of register 2 to the left 4 positions.  Bit 8 becomes bit 12.  Bits 0-3 are zero.
* Determine which of the first four bits of register 5 are true.  Add to previous answer.
*/
int ans = (a << 4) | (b & B00001111);
if( ans > 1024){ ans -= 2048;}
return ans;
}

int decodeT(int a, int b){
/* Determine which of the last 4 bits of register 3 are true.  Shift all bits of register 3 to the left
* 4 positions.  Bit 8 becomes bit 12.  Bits 0-3 are zero.
* Determine which of the first four bits of register 6 are true.  Add to previous answer.
*/
int ans;
a &= B11110000;
ans = (a << 4) | b;
if( ans > 1024){ ans -= 2048;}
return ans;
}
//-- End Buffer Decode Routines --//

//-- Begin Trig Conversion Routines --//

/*  r=sqrt(x^2+y^2+z^2)
*  Θ=acos(z/r)
*  if x > 0 -> Φ=atan(y/x)
*  if x = 0 & y > 0 -> Φ=pi/2
*  if x = 0 & y < 0 -> Φ=-pi/2
*  if x < 0 & y >= 0 -> Φ=atan(y/x)+pi
*  if x < 0 & y < 0 -> Φ=atan(y/x)-pi
*/

//-- End Trig Conversion Routines

Infineon_3D_I2C_GettingStarted_r1.zip

### What to Do with the Data

As you move a magnet around the sensor, the magnetic field strength increases from 0 in the positive and negative direction and then abruptly changes sign at the maximum readings (-2047 becomes +2047). How you deal with that is entirely up to you.

One option would be to use trigonometry to interpret the data, another would be to use the map() function, and yet another option is a lookup table. It really depends on what you plan to use this sensor for.

To convert from x, y, z to spherical coordinates $${r, \theta, \phi}$$, use the conversions supplied in the datasheet:

$$r=\sqrt{x^2+y^2+z^2}$$

$$\theta=cos^{-1}\left(\frac{z}{r}\right)$$

 $$\phi=tan^{-1}\left(\frac{y}{x}\right)$$ x > 0 $$\frac{pi}{2}$$ x = 0 & y > 0 $$-\frac{pi}{2}$$ x = 0 & y < 0 $$\phi=tan^{-1}\left(\frac{y}{x}\right)+\pi$$ x < 0 & y ≥ 0 $$\phi=tan^{-1}\left(\frac{y}{x}\right)-\pi$$ x < 0 & y < 0

### A Case for the TLV493D

The case I made for the 3D2GO eval kit is made of laser-cut 1/8" Baltic-birch 3-ply.

The circuit board has a single screw hole that is used to secure the joystick holder. The joystick holder wraps around and below the circuit board in such a manner that the plane of the circuit board is recessed from the lowest protrusion of the joystick holder by approximately 1/8".

##### Image courtesy of Mouser.

This positioning makes it slightly difficult to use the hole in the circuit board for securing the circuit board to a case. So I chose a case design that uses an interference fit between the walls of the case and the joystick holder, which means you will find no extra room inside the case.

#### Assembly Instructions

1. Use a laser engraver/cutter to cut out the design out of 1/8" plywood. If you are going to stain the wood, stain one side only. Oil-based stains can interfere with the wood glue, and no-one will see the inside of your case anyway.
2. Glue five of the six sides of the case with wood glue and let it dry. Leave the bottom of the case unglued (the part with 3.3V, GND, SCL, SCA, and USB labeled). Wipe away any unused glue before it has a chance to dry.

1. While the glue is drying, cover the top and bottom of the board with Kapton tape or electrical tape.
2. The tight tolerances of the design should be sufficient to secure the circuit board in most cases. If there is any movement, consider using ESD-safe foam or UV-cure glue to secure the circuit board to the sides and top of the case. One or two drops will be sufficient to hold the board in place and can easily be pried loose from the Kapton tape if you ever need to remove it.
3. Secure the bottom part of the case. If you never want to open it again, secure it with wood glue. If you might want to recover or remove the development kit at some point in the future, use one or two drops of UV cure glue on the outside of the case. As long as you do not get glue between the fingers of the joints, you will be able to reopen it with minimal damage to the case.

I attached the files needed to make the case below.

3DMag2Go_Case_for_AAC.zip

### What Next?

There are several more hours of work to fully implement the sensor in a production design and verify the integrity of the data. Here are a few other tasks that can be completed:

• Verify that the data is valid and from a single read. You can determine whether the data is from a single read by using the frame-rate counter; this functionality is not currently implemented in the code.
• Implement the parity check in the microcontroller and enable parity check on the TLV493D.
• Implement the interrupt and power-saving circuitry.
• If it's being used in a safety-critical application, use multiple sensors near the same magnet to verify data integrity.
• Implement Master-Controlled Mode & Master-Controlled Low Power Mode.

### Conclusion

#### Pros

• Ultra-low power consumption (at 3.3V, 10 µA during operation, 10 nA during sleep)
• Small 6-pin TSOP package size will fit virtually anywhere
• I²C communication
• Robust design with three different data error checks

#### Cons

• The size of the chip makes it difficult, if not impossible, for all but the most experienced users to solder at home.
• Amateurs are not currently using this chip, which means the only online resources are the published datasheets. There aren't any designs or code to copy yet.
• The tech support available through the manufacturer's website is able to do little more than redirect users to published datasheets.

If you want to incorporate the TLV493D in your designs, I highly encourage you to start with the 3D2GO development kit. You can physically separate the TLV493D from the rest of the circuit board and still use the evaluation kit.

If you want an I²C Hall-effect joystick in your design without the hassle of building a case and debugging it, consider purchasing a complete package.

• G
gavindarcey November 21, 2016

Mark, thanks for an excellent writeup.  I’m working on connecting the TLV493D to an NXP board now and this is very useful.

One question - when you’re reading in the configuration registers and pulling specific bits out to include in the write, for example:

// Read Register 7H 6:3 -> Write Register 1H 6:3
wbuffer[1] = rbuffer[7] && B01111000;

You seem to be using a logical AND.  Shouldn’t this be bitwise?

Like.
• Mark Hughes November 21, 2016
Hi @gavindarcey, Yes sir, it absolutely should. Nice catch! If you see anything else, please let me know. Also, consider posting your project in the forums so others can benefit from it -- there's still not a lot out there on the chip yet. Thanks again! Mark
Like.
• Mark Hughes November 21, 2016
@gavindarcey, Fixed. Thanks!
Like.
• B
Benjamin Imbach April 24, 2017

Thanks a lot for this! It helped me quite a bit with getting started with this sensor.

I have a small question though regarding your ‘Decoding Routines’:
Shouldn’t the IF statement in all decoding routines be:
if( ans > 2047){ ans -= 4096; } ?
As per the documentation the sensor data is 12 bit, with Bit12/MSB having the value -2048. If the final value is ‘-1’, the 12 bits should all read as ones.
The calculation is: −2048+1024+512+256+128+64+32+16+8+4+2+1 = -1
However since arduino doesn’t see Bit12/MSB as the sign bit it calculates:
2048+1024+512+256+128+64+32+16+8+4+2+1 = 4095. To get the desired ‘-1’ I have to subtract by 4096, and not by 2048.

Like.
• Mark Hughes April 24, 2017
Hi @Benjamin Imbach, Glad you found the article useful (or at least saved you a bit of time). There was a very brief forum discussion here about that issue: https://forum.allaboutcircuits.com/threads/help-interpreting-data-from-an-ic-sensor.126397/#post-1047238. In short, I too had 4096 at one point and likely should have kept it there instead of changing it to 2048. Thanks for providing your comment! After you finish your project, consider posting it in our forums and then providing a link here. And if you see any other corrections/suggestions, please continue to point them out! Mark
Like.
• Jason Talley July 28, 2017

Mark,

I have been having fun playing around with both your arduino library and the sensor for some robotics applications.  It has been positive, but I am a bit unsure of the possibility to achieve the full potential with the combination of the sensor and an arduino.

Do you recall if the “fast mode” can be engaged with the limitations of an arduino?  Your library noted it as unsupported, but I did not know if that meant it was not capable or simply not yet implemented.

Thanks for your thoroughly detailed work.

Like.
• Mark Hughes July 31, 2017
Hi @Jason Talley, I'm glad that you are enjoying the sensor. It's been a little while since I've played with the device, so take the following advice with a grain of salt. I believe the sensor is capable of supporting up to a 1 MHz clock speed (I²C fast mode is 400 kHz). The ATMega328 datasheet (the chip that is the "Arduino" operates at 16 MHz), states the SCL frequency maxes out at 400 kHz (http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf page 373). So it is possible to get to 400 kHz. You will have to use a pull-up resistor on the line according to R=(Vcc-0.4 V)/3 ma, and you might have to adjust the Arduino's wire library to compensate. Once you get the data into the Arduino, you've got to do some calculations with it, and that takes one to several clock cycles per instruction, and then you've got to send it somewhere, and that takes additional clock cycles. Somewhere in between the low-power mode and the fast-mode, there is a point of diminishing returns. My suspicion is that getting it to work reliably is likely more trouble than it is worth on the 8-bit 16 MHz Arduino. http://www.aloriumtech.com/xlr8/ Makes an Arduino implementation on an FPGA that may/may not be the upgrade you need -- it has the same board footprint and Arduino IDE that you will be familiar with, but can create separate "XLR8" blocks that use the FPGA's gates without affecting the rest of the program. I would contact them before buying to ensure it meets your needs. It might be time for you to graduate to a more powerful microprocessor (e.g. http://www.ti.com/tool/msp-exp432p401r). Good luck, and glad that you enjoyed the article! Mark
Like.
• Lesther SK October 11, 2017

Mark,
thanks for an excellent article, it helped me a lot to get started with this sensor, I’ll working on connecting it to a Attiny without the Arduino core.

I’ve got to point out, it seems to be that at the temperature decoding part, there seems to be missing the subtraction of the offset out of the temperature readout according to the datasheet.

Like.
• Mark Hughes October 11, 2017
@Lesther SK, Glad you were able to get it going. It's been a little while since I worked with this, but I don't recall ever making an attempt to convert the temperature values to SI units -- I just read the numbers from the chip and threw the code to the wind. And that's a great thing about the internet -- somebody picked up the code, improved it, and shared it with the world. I haven't tried it myself, as I returned the chip to the company after my article, but the following code looks solid: https://github.com/IRNAS/TLV493D-3D-Magnetic-Sensor-Arduino-Library/tree/master/TLV493D It might save you a bit of work. Good luck! Mark
Like.
• I
iamcalledusman June 01, 2019

Hi Mark ,

Thanks a lot for such a nice effort . Dear I have implemented your code on NodeMCU . I found a little issue that when I run the code . It gives me values x=5 y =4 z= 2046 . Without any magnet . But when I place a magnet in static position values fluctuates between +- 3 . Is it right ? Any expert opinion from your side that without magnet it should show me x=0 , y=0, z=0 .