This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMAG5170UEVM: SPI communication with Arduino Uno

Part Number: TMAG5170UEVM
Other Parts Discussed in Thread: TMAG5170, , TMAG5170-CODE-EXAMPLE

Hi all,

I'm currently trying to work with the Hall-Effect Sensor TMAG5170.TMAG5170UEVM
It uses SPI protocol to communicate with microcontrollers. I am using the Arduino IDE 2.3.0 and an Arduino Uno to try control the sensor and I am having much difficulty writing into the registers of the TMAG5170. It seems I am able to read the registers but I cannot write in them. I am quite certain my pins are all correctly connected, I have VCC=5V. I previously tried VCC=3.3V as well. I am unsure where to connect the ALARM pin, I assumed any GPIO pin can work, but in this project I don't even use the alarm feature. I am trying to use code given by TI Example Code by TI 1 . I have then tried to use code I found on GithubGithub Project 2 . It is essentially the same stuff as the TI example code. Using both examples (code from TI and from Github) I could read the registers but not write in them. I am really stumped, so here is my code so far and hopefully someone has time and expertise to see where I am going wrong:

#include "SPI.h"
#include "tmag5170.h"
#include "hal.h"

// Global Variables 
static uint16_t SYSTEM_CONFIG_stored = 0;
#define DATA_TYPE_RESULTS    ((SYSTEM_CONFIG_stored & ~(SYSTEM_CONFIG_DATA_TYPE_MASK)) >> 6)

// Pin definitions
const int CSpin = 10;

void setup() {
  // put your setup code here, to run once:
  pinMode(CSpin, OUTPUT);

  digitalWrite(CSpin, HIGH); // Deselect the TMAG5170 initially
  // SPI.beginTransaction(SPISettings(100000, MSBFIRST, SPI_MODE0));
  SPI.begin();

  Serial.begin(9600);

  // Configure device:
  TMAG5170startup();
  // Configuration Mode
  enterConfigurationMode();
  // Enable/disable functionalities and measurements
  enableMagChannels(MAG_CH_EN_BITS_XYZ);
  enableTemperatureMeasurement();
  // Sample Rate
  setSampleRate(CONV_AVG_BITS_16X);
  // Set ranges magnetic measurement
  setRanges(SENSOR_CONFIG_X_RANGE_50mT, SENSOR_CONFIG_Y_RANGE_50mT, SENSOR_CONFIG_Z_RANGE_50mT);

  delay(100);
}

void loop() {
  // put your main code here, to run repeatedly:

  // // Example magnetic sensor temperature read
  // float magTemperature = getMeasurementNrmlTEMP();
  
  // // Print magnetic sensor temperature readout
  // Serial.print("Magnetic Sensor Temperature: ");
  // Serial.print(magTemperature);
  // Serial.println(" °C");
  
  // // Example magnetic readout
  // float magX = getMeasurementNrmlX();

  // // Print magnetic readout
  // Serial.print("Magnetic Flux X-component: ");
  // Serial.print(magX);
  // Serial.println(" mT");

  // Some checks
  int response = normalReadRegister(X_THRX_CONFIG_ADDRESS);
  Serial.print("X_THRX_CONFIG_ADDRESS: ");
  Serial.print("Data: 0x");
  Serial.println(response, HEX);

  int response1 = normalReadRegister(SENSOR_CONFIG_ADDRESS);
  Serial.print("SENSOR_CONFIG_ADDRESS: ");
  Serial.print("Data: 0x");
  Serial.println(response1, HEX);

  delay(1000);  // Adjust the delay based on your desired reading frequency
}


//****************************************************************************
//****************************************************************************
//
// Note (Matis)
//
// These functions are to 'initiate/connect/communicate' with the hardware. 
// They are taken from the TI example code (hal_ex.c) and edited to work in
// this environment
//
//****************************************************************************
//****************************************************************************

void spiSendReceiveArrays(const uint8_t dataTx[], uint8_t dataRx[], const uint8_t byteLength)
{

    // Require that dataTx and dataRx are not NULL pointers
    assert(dataTx && dataRx);

    // Set the nCS pin LOW
    digitalWrite(CSpin, LOW);

    // Send all dataTx[] bytes on MOSI, and capture all MISO bytes in dataRx[]
    int i;
    for (i = 0; i < byteLength; i++)
    {
        dataRx[i] = spiSendReceiveByte(dataTx[i]);
    }

    // Set the nCS pin HIGH
    digitalWrite(CSpin, HIGH);
}

uint8_t spiSendReceiveByte(const uint8_t dataTx)
{
    // Remove any residual or old data from the receive FIFO
    // Could come later; something along the lines of making sure dataRx[] is empty

    // SSI TX & RX
    uint8_t dataRx;
    dataRx = SPI.transfer(dataTx);

    // TODO: can we remove the SSIDataGetNonBlocking() call here and move it to spiSendReceiveArrays()?
    // TODO: Add error checking and handling here in case of TX or RX problems...

    return dataRx;
}

//****************************************************************************
//****************************************************************************
//
// Note (Matis)
//
// From now on this are functions given in the TI example code (tamg5170_ex.c)
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Initialization function
//!
//! This function MUST be ran at the beginning of any implementation using this
//! example code. This is needed because the SYSTEM_CONFIG register is needed to
//! be tracked and updated accordingly to prevent losing track of the DATA_TYPE
//! in use by the device.
//!
//! If CRC is being disabled per the header definition being enabled, then this
//! function also sends the command to disable CRC on the device as well.
//! NOTE: if CRC is being disabled then the RESET_DEVICE_IN_STARTUP should be
//! enabled as well
//****************************************************************************
void TMAG5170startup()
{
    // (OPTIONAL) Provide additional delay time for power supply settling
    delayMicroseconds(50);

#ifdef RESET_DEVICE_IN_STARTUP
    // SYSTEM_CONFIG_stored will be set to default in this function
    resetDevice();
#else
    // The default implementation of this initialize function is resetting only
    // the SYSTEM_CONFIG register and leaving the other registers unchanged.
    writeToRegister( SYSTEM_CONFIG_ADDRESS, SYSTEM_CONFIG_DEFAULT );
    SYSTEM_CONFIG_stored = SYSTEM_CONFIG_DEFAULT;
#endif

#ifdef DISABLE_CRC_IN_STARTUP
    // Sends command to disable CRC verfication and calculation on device-side
    // MUST be called first in implementation for commands from other functions
    // to be accepted when DISABLE_CRC_IN_STARTUP is used.
    uint8_t dataTx[4] = {0x0F,0x00,0x04,0x07};
    uint8_t dataRx[4] = {0};
    sendAndReceiveFrame(dataTx,dataRx);
#endif
}

//****************************************************************************
//! Reset Device to Factory Settings
//!
//! This function uses the DeepSleep functions to reset the device's registers back to the
//! default settings outlined in the datasheet. This function also resets the
//! SYSTEM_CONFIG_stored variable to the default value in the enterDeepSleepMode function.
//****************************************************************************
void resetDevice()
{
    enterDeepSleepMode(); // Deep Sleep Mode resets the device to its default settings
    exitDeepSleepMode();
}

//****************************************************************************
//! Enter Deep Sleep Mode
//!
//! Make sure to use the exitDeepSleepMode function to properly exit Deep Sleep Mode.
//! Deep Sleep Mode can be alternately exited with a short pulse on the CS pin.
//!
//! WILL WORK IN SPECIAL READ MODE, DEEP SLEEP MODE RESETS DEVICE TO FACTORY SETTINGS
//! (EXITS SPECIAL READ MODE)
//****************************************************************************
void enterDeepSleepMode()
{
    // Send Write command, Deep Sleep resets device to factory settings so
    // an initial register read is not needed (DEVICE_CONFIG default is 0x0000)
    writeToRegister( DEVICE_CONFIG_ADDRESS, DEVICE_CONFIG_OPERATING_MODE_DeepSleepMode );
    SYSTEM_CONFIG_stored = SYSTEM_CONFIG_DEFAULT;
    delayMicroseconds(100);
}
//****************************************************************************
//! Exit Deep Sleep Mode
//! (Use this function instead of a different operation mode change function to
//! exit Deep Sleep mode properly)
//!
//! Exits Deep Sleep Mode by pulsing LOW on the CS pin and waiting for the chip
//! to start up. (t_start_deep_sleep)
//!
//! DOES NOT WORK IN SPECIAL READ MODE [DATA_TYPE field at 0x028-6 does not equal 000b]
//****************************************************************************
void exitDeepSleepMode()
{
    // A LOW pulse is needed on CS to exit Deep Sleep Mode (enters Configuration Mode)
    csPulse();

#ifdef  MAX_DELAYS_IN_OPMODE_CHANGES
    delayMicroseconds(500); // max expected delay as given by t_start_deep_sleep (datasheet pg. 10)
#else
    delayMicroseconds(260); // typical delay for t_start_deep_sleep at Vcc = 2.3V (datasheet pg. 10)
#endif
}

//****************************************************************************
//****************************************************************************
//
// SPI fucntions
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Send and Receive Frame
//!
//! Takes in the frame to transmit by SPI and the array to put the received frame in
//!
//! dataTx[4] - unint8_t array of length 4 containing the bytes frame to transmit
//!             ordered with CRC-containing byte last
//! dataRx[4] - unint8_t array of length 4 of zeroes on input, will have the received
//!             frame bytes assigned to it ordered with CRC bits last
//****************************************************************************
void sendAndReceiveFrame(uint8_t dataTx[], uint8_t dataRx[])
{

#ifdef DISABLE_CRC_IN_STARTUP
    // When CRC is disabled upon system initialization, the need to calculate
    // the dataTx CRC for the TMAG5170 to accept commands is gone at the risk
    // of transmission errors being passed through.

    spiSendReceiveArrays(dataTx, dataRx, TMAG5170_FRAME_NUM_BYTES);

    // Check if SYSTEM_CONFIG was written to and update SYSTEM_CONFIG_stored if so
    if ( dataTx[0] == SYSTEM_CONFIG_ADDRESS ) SYSTEM_CONFIG_stored = (dataTx[1] << 8) | dataTx[2];
    SYSTEM_CONFIG_stored &= ~(0xC818); // Reserved bits cannot be 1, this ensures the
                                       // stored variable doesn't have them changed
#else
    // When CRC is enabled (it is by default), the CRC must be calculated and
    // included in dataTx for the TMAG5170 to accept commands.

    uint8_t crc = calculateCRC( dataTx );
    dataTx[3] = dataTx[3] | crc;

    spiSendReceiveArrays(dataTx, dataRx, TMAG5170_FRAME_NUM_BYTES);

    // The TMAG5170 also calculates the CRC for the frame it sends back to its
    // MCU. The TMAG5170 will purposely return a bad CRC if it received a
    // transmitted command with a bad CRC as well. Verifying the CRC of dataRx
    // helps confirm if dataTx was received successfully and if dataRx is valid.
    if (verifyCRC(dataRx) == 0)
    {
        // Without GLOBAL_CRC_ERROR_VAR_ENABLED defined or another error response
        // implemented, this function will not give any indication that the
        // received data has an error

        #ifdef  SEND_RECIEVE_REDUNDANCY_ENABLED

        for ( i = 0; i<4; i++ ) { dataRx[i] = 0 };
        spiSendReceiveArrays(dataTx, dataRx, TMAG5170_FRAME_NUM_BYTES);
        if ( verifyCRC(dataRx) )
        {
            // Check if SYSTEM_CONFIG was written to and update SYSTEM_CONFIG_stored if so
            if ( dataTx[0] == SYSTEM_CONFIG_ADDRESS ) SYSTEM_CONFIG_stored = (dataTx[1] << 8) | dataTx[2];
            SYSTEM_CONFIG_stored &= ~(0xC818); // Reserved bits cannot be 1, this ensures the
                                               // stored variable doesn't have them changed
            return;
        }

        #endif

        #ifdef  GLOBAL_CRC_ERROR_VAR_ENABLED
        crc_error_occurence = 1; // If redundancy is enabled too, the error bit
                                 // only flips if both sent TXs receive bad CRCs
        #endif

        return; // If a received data CRC error is detected, SYSTEM_CONFIG_stored
                // will not be updated under the assumption that whatever command
                // sent was not accepted.
    }

    // Check if SYSTEM_CONFIG was written to and update SYSTEM_CONFIG_stored if so
    if ( dataTx[0] == SYSTEM_CONFIG_ADDRESS ) SYSTEM_CONFIG_stored = (dataTx[1] << 8) | dataTx[2];
    SYSTEM_CONFIG_stored &= ~(0xC818); // Reserved bits cannot be 1, this ensures the
                                       // stored variable doesn't have them changed
#endif
}

//****************************************************************************
//! Write to Register Function (no CMD sent or value returned)
//!
//! Takes in the address of the register to edit and the 16-bit frame to overwrite it with and writes
//! the input frame to the register.
//!
//! This function replaces the whole register with the input data, make sure the values desired to be
//! unchanged are in the input data_to_write!
//!
//! This function will work in Normal and Special Read Mode.
//!
//! address       - uint8_t value from 0x00 to 0x14 containing the register address to write over
//! data_to_write - the 16-bit frame to be written to the register at address.
//****************************************************************************
void writeToRegister(uint8_t address, uint16_t data_to_write)
{
    // Check that the input address is in range
    assert(address < NUM_REGISTERS);

    // Build TX and RX byte arrays
    uint8_t dataTx[4] = { 0 };
    uint8_t dataRx[4] = { 0 };

    // MSB is 0 for WRITE command
    dataTx[0] = (address);
    dataTx[1] = (data_to_write >> 8);
    dataTx[2] = (data_to_write);
    dataTx[3] = 0x00;

    sendAndReceiveFrame(dataTx, dataRx);
}

//****************************************************************************
//! Full Read Function for Normal Data Mode (DATA_TYPE = 000b)
//!
//! Takes in an output array of length 2, address to read from, and CMD bits to send along,
//! then creates the dataTx array and calls the sendAndReturnFrame function, interpreting
//! dataRx and putting the read register in output[0] and status bits in output[1]
//!
//! output[2] - empty uint16_t array of length 2, output[0] will be assigned the returned register
//!             at the given address, output[1] will be assigned the returned status bits.
//! address   - uint8_t value from 0x00 to 0x14 containing the register address to read from
//! cmd_bits  - uint8_t value from 0x00 to 0x03 containing the CMD0 and CMD1 bits that will be sent
//!             in dataTx. LSB is CMD0, next bit is CMD1. (see header file or datasheet pg. 29 for CMD functions)
//****************************************************************************
void normalRead( uint16_t output[], uint8_t address, uint8_t cmd_bits )
{
    // Check that the input address is in range
    assert(address < NUM_REGISTERS);

    // Build TX and RX byte arrays
    uint8_t dataTx[4] = { 0 };
    uint8_t dataRx[4] = { 0 };

    // MSB is 1 for READ command
    dataTx[0] = (address | 0x80);
    dataTx[1] = 0x00;
    dataTx[2] = 0x00;
    dataTx[3] = cmd_bits << 4;

    sendAndReceiveFrame(dataTx, dataRx);
    output[0] = (dataRx[1] << 8) | dataRx[2];
    output[1] = (dataRx[0] << 4) | (dataRx[3] >> 4);
}

//****************************************************************************
//! Register-only Read Function for Normal Data Mode (DATA_TYPE = 000b)
//!
//! Takes in address to read from and returns register at address without the status bits or
//! triggering any CMD function (cmd_bits = 0x00).
//!
//! address   - uint8_t value from 0x00 to 0x14 containing the register address to read from
//****************************************************************************
uint16_t normalReadRegister( uint8_t address )
{
    uint16_t output[2] = { 0 };
    normalRead( output, address, 0x00 );
    return output[0];
}

// Note (Matis): I have excluded all Special Read Mode capabilities

//****************************************************************************
//****************************************************************************
//
// Change Device Operation Mode
//
// Note (Matis): other modes available in TI example code are Stand-by, Active Measure, Active Trigger, Sleep, Wake and Sleep, Deep Sleep
// Exit Sleep, Exit Wake Up and Sleep, Exit Deep Sleep Modes
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Enter Configuration Mode
//!
//! DOES NOT WORK IN SPECIAL READ MODE [DATA_TYPE field at 0x028-6 does not equal 000b]
//****************************************************************************
void enterConfigurationMode()
{
    // To prevent undefined behavior, this function does not perform its operation
    // when the DATA_TYPE field (address: 0x028-6) is not set to Normal Read Mode (000b)
    if ( DATA_TYPE_RESULTS != DATA_TYPE_RESULTS_NormalMode ) return;

    uint16_t input;
    // Set OPERATING_MODE (address: 0x006-4) to Configuration Mode (0h)
    input = normalReadRegister(DEVICE_CONFIG_ADDRESS);
    input = ( input & ~(DEVICE_CONFIG_OPERATING_MODE_MASK) ) | DEVICE_CONFIG_OPERATING_MODE_ConfigurationMode;
    writeToRegister( DEVICE_CONFIG_ADDRESS, input );

#ifdef  MAX_DELAYS_IN_OPMODE_CHANGES
    delayMicroseconds(50); // max expected delay as given by t_start_sleep (datasheet pg. 10)
#endif
}

//****************************************************************************
//****************************************************************************
//
// Notes (Matis)
// 
// Functionalities omitted:
// Functions to Configure Trigger Settings
// Threshold Detection + ALERT output Settings
//
//****************************************************************************
//****************************************************************************


//****************************************************************************
//****************************************************************************
//
// Measurement Configuration Functions
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Enable Magnetic Axes for Measurement (also can turn all channels off)
//!
//! Takes in a 4-bit value for the MAG_CH_EN field (0x019-6)
//! mag_ch_en_bits must not be greater than 0x0F
//!
//! When mag_ch_en_bits < 0x08 its three LSBs act as a three bit enable/disable command for ZYX
//! (examples:  0x05 (0101b) => ZX enabled  |  0x02 (0010b) => Y enabled)
//!
//! When mag_ch_en_bits >= 0x08 it configures alternative sampling orders along with enabling
//! specific channels (see chart below)
//!
//!                          | enabled channels ||                  | enabled channels
//!          mag_ch_en_bits  | + sampling order ||  mag_ch_en_bits  | + sampling order
//!         _________________|__________________||__________________|__________________
//!               0x00               none       ||       0x08               XYX
//!               0x01                X         ||       0x09               YXY
//!               0x02                Y         ||       0x0A               YZY
//!               0x03               XY         ||       0x0B               ZYZ
//!               0x04                Z         ||       0x0C               ZXZ
//!               0x05               ZX         ||       0x0D               XZX
//!               0x06               YZ         ||       0x0E              XYZYX
//!               0x07               XYZ        ||       0x0F              XYZZYX
//!
//! Definitions for descriptive inputs to this function are provided in the header file.
//!
//! DOES NOT WORK IN SPECIAL READ MODE [DATA_TYPE field at 0x028-6 does not equal 000b]
//****************************************************************************
void enableMagChannels( uint8_t mag_ch_en_bits )
{
    // To prevent undefined behavior, this function does not perform its operation
    // when the DATA_TYPE field (0x028-6) is not set to Normal Read Mode (000b)
    if ( DATA_TYPE_RESULTS != DATA_TYPE_RESULTS_NormalMode ) return;

    // Check that inputs are valid
    if ( !( mag_ch_en_bits <= 0x0F ) ) return;

    uint16_t input;
    // Set MAG_CH_EN (0x019-6) to mag_ch_en_bits
    input = normalReadRegister(SENSOR_CONFIG_ADDRESS);
    input = ( input & ~(SENSOR_CONFIG_MAG_CH_EN_MASK) ) | (mag_ch_en_bits << 6);
    writeToRegister( SENSOR_CONFIG_ADDRESS, input );
}

//****************************************************************************
//! Enable Angle Measurement (also can turn off angle measurement)
//!
//! Takes in a 2-bit value for the ANGLE_EN field (0x01F-E) to determine which two
//! axes to measure the angle off of for the on-board CORDIC function in the device.
//!
//! Based on the ANGLE_EN setting, the angle will be calculated using the first axis
//! as the "horizontal" axis (positive side of axis is 0 degrees) and the second as
//! the "vertical" axis (positive side of axis is 90degrees)
//!
//! angle_en_bits can be set to 0x00 to 0x03 to configure these settings for ANGLE_EN:
//!              ANGLE_EN value | horizontal axis | vertical axis
//!                   0x00              none            none  --  (angle measurement disabled)
//!                   0x01               X               Y
//!                   0x02               Y               Z
//!                   0x03               X               Z
//****************************************************************************
void enableAngleMeasurement( uint8_t angle_en_bits )
{
    // To prevent undefined behavior, this function does not perform its operation
    // when the DATA_TYPE field (0x028-6) is not set to Normal Read Mode (000b)
    if ( DATA_TYPE_RESULTS != DATA_TYPE_RESULTS_NormalMode ) return;

    // Check that inputs are valid
    if ( !( angle_en_bits <= 0x03 ) ) return;

    // To prevent undefined behavior, this function does not perform its operation
    // when the DATA_TYPE field (0x028-6) is not set to Normal Read Mode (000b)
    if ( DATA_TYPE_RESULTS != DATA_TYPE_RESULTS_NormalMode ) return;

    uint16_t input;
    // Set ANGLE_EN (0x01F-E) to mag_ch_en_bits
    input = normalReadRegister(SENSOR_CONFIG_ADDRESS);
    input = ( input & ~(SENSOR_CONFIG_ANGLE_EN_MASK) ) | (angle_en_bits << 14);
    writeToRegister( SENSOR_CONFIG_ADDRESS, input );
}

//****************************************************************************
//! Enable Temperature Measurement
//!
//! Begins Temperature Measurements by changing the T_CH_EN field in the
//! DEVICE_CONFIG to 1b.
//****************************************************************************
void enableTemperatureMeasurement() {
    uint16_t input = normalReadRegister(DEVICE_CONFIG_ADDRESS);
    input &= ~(DEVICE_CONFIG_T_CH_EN_MASK);
    input |= DEVICE_CONFIG_T_CH_EN_TemperatureChannelEnabled;
    writeToRegister( DEVICE_CONFIG_ADDRESS, input );
}

//****************************************************************************
//! Disable Temperature Measurement
//!
//! Ends Temperature Measurements by changing the T_CH_EN field in the
//! DEVICE_CONFIG to 0b.
//****************************************************************************
void disableTemperatureMeasurement() {
    uint16_t input = normalReadRegister(DEVICE_CONFIG_ADDRESS);
    input &= ~(DEVICE_CONFIG_T_CH_EN_MASK);
    input |= DEVICE_CONFIG_T_CH_EN_TemperatureChannelDisabled;
    writeToRegister( DEVICE_CONFIG_ADDRESS, input );
}


//****************************************************************************
//! Set Sampling Rate (configure the amount of additional samples to reduce noise/increase resolution)
//!
//! CONV_AVG_bits - what value to set for the CONV_AVG (0x00E-C) value (from datasheet pg. 34):
//!                      | num. samples | 3-axes speed | 1-axis speed |
//!                 0x00 -       1x         10.0Ksps         20Ksps
//!                 0x01 -       2x          5.7Ksps       13.3Ksps
//!                 0x02 -       4x          3.1Ksps        8.0Ksps
//!                 0x03 -       8x          1.6Ksps        4.4Ksps
//!                 0x04 -      16x          0.8Ksps        2.4Ksps
//!                 0x05 -      32x          0.4Ksps        1.2Ksps
//****************************************************************************
void setSampleRate( uint8_t CONV_AVG_bits )
{
    uint16_t input;
    // Set CONV_AVG (0x00E-C) to CONV_AVG_bits
    input = normalReadRegister(DEVICE_CONFIG_ADDRESS);
    input = ( input & ~(DEVICE_CONFIG_CONV_AVG_NUM_MASK) ) | (CONV_AVG_bits << 12);
    writeToRegister( DEVICE_CONFIG_ADDRESS, input );
}


//****************************************************************************
//! Set Ranges for X, Y, and Z axes
//!
//! Sets the X, Y, and Z_RANGE fields in the SENSOR_CONFIG register to the bits
//! determined by the function inputs.
//!
//! x_range_bits - bits for X_RANGE field (must be no greater than 0x02)
//! y_range_bits - bits for Y_RANGE field (must be no greater than 0x02)
//! z_range_bits - bits for Z_RANGE field (must be no greater than 0x02)
//!
//! According to the TMAG5170 version used, the mT range for the bits are as follows:
//!
//!                 *_range_bits  |   TMAG5170A1   |   TMAG5170A2
//!                    input      | mT range value | mT range value
//!              _________________|________________|________________
//!                    0x00       |      50 mT     |     150 mT
//!                    0x01       |      25 mT     |      75 mT
//!                    0x02       |     100 mT     |     300 mT
//****************************************************************************
void setRanges( uint8_t x_range_bits, uint8_t y_range_bits, uint8_t z_range_bits )
{
    // To prevent undefined behavior, this function does not perform its operation
    // when the DATA_TYPE field (0x028-6) is not set to Normal Read Mode (000b)
    if ( DATA_TYPE_RESULTS != DATA_TYPE_RESULTS_NormalMode ) return;
    // Check that inputs are valid
    if ( x_range_bits > 0x02 || y_range_bits > 0x02 || z_range_bits > 0x02 ) return;

    uint16_t input = normalReadRegister(SENSOR_CONFIG_ADDRESS) & ~(SENSOR_CONFIG_FULL_RANGE_MASK);
    input |= (z_range_bits << 4) | (y_range_bits << 2) | x_range_bits;
    writeToRegister( SENSOR_CONFIG_ADDRESS, input );
}


//****************************************************************************
//****************************************************************************
//
// Get Results/Measurement Functions (Normal Read Mode)
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Get and return the *_CH_RESULT (or TEMP_RESULT for T) register for an axis/measurement
//!
//! These functions explicitly return the unsigned 16-bit register of their respective
//! measurement address.
//****************************************************************************

uint16_t getXresult() { return normalReadRegister(X_CH_RESULT_ADDRESS); }
uint16_t getYresult() { return normalReadRegister(Y_CH_RESULT_ADDRESS); }
uint16_t getZresult() { return normalReadRegister(Z_CH_RESULT_ADDRESS); }
uint16_t getTEMPresult() { return normalReadRegister(TEMP_RESULT_ADDRESS); }
uint16_t getANGLEresult() { return normalReadRegister(ANGLE_RESULT_ADDRESS); }
uint16_t getMAGresult() { return normalReadRegister(MAGNITUDE_RESULT_ADDRESS); }
// NOTE: These functions returned the unsigned integer corresponding to the register value
//       for easier bit operations. To convert the unsigned using the equations used in the
//       example code, it must be casted to an signed integer.

//****************************************************************************
//! Get Magnetic Results Registers (Normal Read Mode)
//!
//! In 32-bit normal read mode:
//! Takes in a size 3 array of int16_t values and assigns it the the
//! three *_CH_RESULT registers. (order XYZ)
//!
//! INPUT ARRAY MUST BE OF LENGTH 3
//!
//! NOTE: The uint16_t variables returned by the get*result functions are casted
//! into int16_t variables for use with the other provided functions that take
//! signed integers.
//!
//! DOES NOT WORK IN SPECIAL READ MODE [DATA_TYPE field at 0x028-6 does not equal 000b]
//****************************************************************************
void getMagResultsRegistersNrml( int16_t meas_arr[] )
{
    meas_arr[0] = (int16_t) getXresult();
    meas_arr[1] = (int16_t) getYresult();
    meas_arr[2] = (int16_t) getZresult();
}

//****************************************************************************
//! Get X-axis Magnetic Flux Measurement in mT
//!
//! Returns a float containing the mT magnetic flux measurement converted from the
//! X_CH_RESULT register.
//****************************************************************************
float getMeasurementNrmlX()
{

    uint16_t range = getXrange();
    int16_t data = getXresult(); // separate variable used to cast to a signed int
                                         // for the float cast to work correctly

    return (((float) data) / 32768) * range;
}

//****************************************************************************
//! Get Y-axis Magnetic Flux Measurement in mT
//!
//! Returns a float containing the mT magnetic flux measurement converted from the
//! Y_CH_RESULT register.
//****************************************************************************
float getMeasurementNrmlY()
{

    uint16_t range = getYrange();
    int16_t data = getYresult(); // separate variable used to cast to a signed int
                                         // for the float cast to work correctly

    return (((float) data) / 32768) * range;
}

//****************************************************************************
//! Get Z-axis Magnetic Flux Measurement in mT
//!
//! Returns a float containing the mT magnetic flux measurement converted from the
//! Z_CH_RESULT register.
//****************************************************************************
float getMeasurementNrmlZ()
{

    uint16_t range = getZrange();
    int16_t data = getZresult(); // separate variable used to cast to a signed int
                                         // for the float cast to work correctly

    return (((float) data) / 32768) * range;
}

//****************************************************************************
//! Get Temperature Measurement in degrees C (Normal Read Mode)
//!
//! Currently the 'Typical' Electrical Characteristics (ECHAR) of the device are set
//! in the header file. If the device has been calibrated and different ECHAR values
//! are found, please edit the ECHAR values in the header file for more accurate
//! temperature measurement. The header file also contains more information on ECHAR values.
//****************************************************************************
float getMeasurementNrmlTEMP()
{
    uint16_t tADC_T = getTEMPresult();

    float temp_val = ECHAR_T_SENS_T0 + ( ((((float) tADC_T) - ECHAR_T_ADC_T0)) / ECHAR_T_ADC_RES );
    return temp_val;
}

//****************************************************************************
//! Get Internal Angle Measurement in Degrees
//!
//! Returns a float containing the degree value converted from the ANGLE_RESULT register.
//! The value corresponds to the calculated angle created by the two magnetic flux axis
//! measurements selected by the ANGLE_EN bits.
//!
//! For the angle to be properly measured, the two axes selected by ANGLE_EN must share the
//! same selected range.
//****************************************************************************
float getMeasurementNrmlANGLE()
{
    uint16_t data = getANGLEresult();
    float angle = ( (float) data / 16  );
    return angle;
}

//****************************************************************************
//! Get Internal Magnitude Measurement in mT
//!
//! Returns a float containing the mT value converted from the MAGNITUDE_RESULT register.
//! The value corresponds to the calculated magnitude created by the two magnetic flux axis
//! measurements selected by the ANGLE_EN bits.
//!
//! For the magnitude to be properly measured, the two axes selected by ANGLE_EN must share the
//! same selected range.
//****************************************************************************
float getMeasurementNrmlMAG()
{
    uint16_t data = getMAGresult();
    // TODO: verify magnitude correlates with expected
    float magnitude = (((float) data)/8192) * getMAGrange() * 4;
    return magnitude;

}


//****************************************************************************
//! Get Magnetic Measurements in mT (Normal Read Mode)
//!
//! In 32-bit normal read mode:
//! Takes in a size 3 array of floats and updates its measurements of the
//! three magnetic axes in mT. (order XYZ)
//!
//! INPUT ARRAY MUST BE SIZE 3 (or at least have meas_arr to meas_arr + 2 within scope)
//!
//! DOES NOT WORK IN SPECIAL READ MODE [DATA_TYPE field at 0x028-6 does not equal 000b]
//****************************************************************************
void getMagMeasurementsNrml( float meas_arr[] )
{

    uint8_t i;

    // Array to store ranges for coordinates in the order XYZ
    uint16_t ranges[3] = {50,50,50}; // The default value for coordinate ranges is 50 mT (A1)
    ranges[0] = getXrange();
    ranges[1] = getYrange();
    ranges[2] = getZrange();

    int16_t data;

    for (i=0; i<3; ++i)
    {
        data = normalReadRegister(X_CH_RESULT_ADDRESS + i); // read in
        meas_arr[i] = (((float) data) / 32768) * ranges[i];
    }
}


//****************************************************************************
//****************************************************************************
//
// Get Range Functions
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Get and return the integer value of the X_RANGE bits for an axis
//!
//! Returns an unsigned 16-bit integer value of the X axis range in mT.
//****************************************************************************
uint16_t getXrange()
{
    // Get SENSOR_CONFIG and isolate X_RANGE bits.
    uint16_t config = normalReadRegister(SENSOR_CONFIG_ADDRESS) & SENSOR_CONFIG_X_RANGE_MASK;
    uint16_t range;
    if ( getVersion() == 1 )
    {
        // range values for TMAG5170A2
        range = 150;
        if ( config == 0x0001  ) range = 75; // If examined bits equal 01b, range is set to 75 mT (for A2)
        else if ( config == 0x0002 ) range = 300; // If examined bits equal 10b, range is set to 300 mT (for A2)
    }
    else
    {
        // range values for TMAG5170A1
        range = 50;
        if ( config == 0x0001  ) range = 25; // If examined bits equal 01b, range is set to 25 mT (for A1)
        else if ( config == 0x0002 ) range = 100; // If examined bits equal 10b, range is set to 100 mT (for A1)
    }
    return range;
}

//****************************************************************************
//! Get and return the integer value of the Y_RANGE bits for an axis
//!
//! Returns an unsigned 16-bit integer value of the Y axis range in mT.
//****************************************************************************
uint16_t getYrange()
{
    // Get SENSOR_CONFIG and isolate Y_RANGE bits, shifting them to LSB.
    uint16_t config = normalReadRegister(SENSOR_CONFIG_ADDRESS) & SENSOR_CONFIG_Y_RANGE_MASK >> 2;
    uint16_t range;
    if ( getVersion() == 1 )
    {
        // range values for TMAG5170A2
        range = 150;
        if ( config == 0x0001  ) range = 75; // If examined bits equal 01b, range is set to 75 mT (for A2)
        else if ( config == 0x0002 ) range = 300; // If examined bits equal 10b, range is set to 300 mT (for A2)
    }
    else
    {
        // range values for TMAG5170A1
        range = 50;
        if ( config == 0x0001  ) range = 25; // If examined bits equal 01b, range is set to 25 mT (for A1)
        else if ( config == 0x0002 ) range = 100; // If examined bits equal 10b, range is set to 100 mT (for A1)
    }
    return range;
}

//****************************************************************************
//! Get and return the integer value of the Z_RANGE bits for an axis
//!
//! Returns an unsigned 16-bit integer value of the Z axis range in mT.
//****************************************************************************
uint16_t getZrange()
{
    // Get SENSOR_CONFIG and isolate Z_RANGE bits, shifting them to LSB.
    uint16_t config = normalReadRegister(SENSOR_CONFIG_ADDRESS) & SENSOR_CONFIG_Z_RANGE_MASK >> 4;
    uint16_t range;
    if ( getVersion() == 1 )
    {
        // range values for TMAG5170A2
        range = 150;
        if ( config == 0x0001  ) range = 75; // If examined bits equal 01b, range is set to 75 mT (for A2)
        else if ( config == 0x0002 ) range = 300; // If examined bits equal 10b, range is set to 300 mT (for A2)
    }
    else
    {
        // range values for TMAG5170A1
        range = 50;
        if ( config == 0x0001  ) range = 25; // If examined bits equal 01b, range is set to 25 mT (for A1)
        else if ( config == 0x0002 ) range = 100; // If examined bits equal 10b, range is set to 100 mT (for A1)
    }
    return range;
}


//****************************************************************************
//! Get Range used for Magnitude register mT conversion
//!
//! Returns the range selected by the first ANGLE_EN axis. For proper use of the magnitude register
//! both of the ANGLE_EN axes must share the same range.
//!
//! If ANGLE_EN is disabled the function will still return the X-axis range.
//****************************************************************************
uint16_t getMAGrange()
{
    uint16_t angle_en = normalReadRegister(SENSOR_CONFIG_ADDRESS) & SENSOR_CONFIG_ANGLE_EN_MASK;
    if ( angle_en == SENSOR_CONFIG_ANGLE_EN_YZ ) return getYrange();
    else return getXrange();
}


//****************************************************************************
//****************************************************************************
//
// Get Device Info Functions
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Get TMAG5170 Version (A1 or A2)
//!
//! Sends a read command for TEST_CONFIG and returns the VER field bit.
//!      VER == 0b --> TMAG5170A1    |    VER == 1b --> TMAG5170A2
//****************************************************************************
uint8_t getVersion()
{ return (normalReadRegister(TEST_CONFIG_ADDRESS) & TEST_CONFIG_VER_MASK) >> 4; }

//****************************************************************************
//! Check if CRC is enabled
//!
//! Sends a read command for TEST_CONFIG and returns the whether the CRC_DIS field
//! corresponds to an enabled CRC or not. (1b == enabled)
//****************************************************************************
uint8_t isCRCenabled()
{
    return ((normalReadRegister(TEST_CONFIG_ADDRESS) & TEST_CONFIG_CRC_DIS_MASK) >> 2) == 0;
}


//****************************************************************************
//****************************************************************************
//
// Notes (Matis)
// 
// Functionalities omitted:
// Offset and Gain Correction Functions
// Special 32-bit Data Read Functions (DATA_TYPE != 000b)
// Supplemental Functions
// Helper Functions (except CRC Functions)
//
//****************************************************************************
//****************************************************************************


//****************************************************************************
//****************************************************************************
//
// CRC Functions from Helper Functions
//
//****************************************************************************
//****************************************************************************

//****************************************************************************
//! Calculate CRC for SPI data frame
//!
//! Takes in an array containing a SPI data frame (MSB to LSB) with the CRC bits
//! all set to ZERO and calculates and returns the CRC for that data frame.
//****************************************************************************
uint8_t calculateCRC( uint8_t data[] )
{
    int i = 0;
    uint8_t crc = 0x00;
    uint32_t n;

    // Build TX and RX byte array
    uint8_t d[32] = { 0 };

    n = (data[0] << 24)|(data[1] << 16)|(data[2] << 8)|(data[3]);

    while (n>0)
    {
        d[i] = n&1;
        n = n>>1;
        i++;
    }

    crc |= d[30] ^ d[26] ^ d[25] ^ d[24] ^ d[23] ^ d[21] ^ d[19] ^ d[18] ^ d[15] ^ d[11] ^ d[10] ^ d[9] ^ d[8] ^ d[6] ^ d[4] ^ d[3] ^ d[0] ^ 1;
    crc |= (d[31] ^ d[30] ^ d[27] ^ d[23] ^ d[22] ^ d[21] ^ d[20] ^ d[18] ^ d[16] ^ d[15] ^ d[12] ^ d[8] ^ d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[1] ^ d[0] ^ 1 ^ 1) << 1;
    crc |= (d[31] ^ d[28] ^ d[24] ^ d[23] ^ d[22] ^ d[21] ^ d[19] ^ d[17] ^ d[16] ^ d[13] ^ d[9] ^ d[8] ^ d[7] ^ d[6] ^ d[4] ^ d[2] ^ d[1] ^ 1 ^ 1) << 2;
    crc |= (d[29] ^ d[25] ^ d[24] ^ d[23] ^ d[22] ^ d[20] ^ d[18] ^ d[17] ^ d[14] ^ d[10] ^ d[9] ^ d[8] ^ d[7] ^ d[5] ^ d[3] ^ d[2] ^ 1) << 3;

    return crc;
}

//****************************************************************************
//! Verify CRC in SPI data frame
//!
//! Takes in an array containing a SPI data frame (MSB to LSB) and checks if the
//! CRC bits (according to their locations for the TMAG5170) are correct according
//! to the CRC calculation algorithm.
//****************************************************************************
uint8_t verifyCRC( uint8_t data[] )
{
    uint8_t crc_received = data[3] & 0x0F;
    data[3] &= ~(0x0F); // the CRC bits of the data must be 0000b to calculate its CRC correctly
    uint8_t crc_calc = calculateCRC(data);
    data[3] |= crc_received; // the previously removed CRC bits are reinserted

    return crc_received == crc_calc;
}

//****************************************************************************
//! Pulse CS function
//!
//! This function pulses LOW on the GPIO pin connected to the CS pin of the TMAG5170
//! for 2 us. (set pin to HIGH afterwards)
//!
//! Can be used to trigger conversion for TRIGGER_MODE set to 'at CS pulse'
//****************************************************************************
void csPulse()
{
    digitalWrite(CSpin, LOW);
    delayMicroseconds(2);
    digitalWrite(CSpin, HIGH);
}

The beginning is the most relevant where I attempt to communicate via SPI and after that the rest of the functions are the ones given by TI. 

  • Hello Matis,

    Can you confirm whether or not you have enabled CRC in your code?  By default, the device is expecting CRC to confirm the quality of the data transmission.  If the correct CRC value is not received, then device will not latch in the write data.

    If the CRC codes are correct, then I might also check that the SPI clock edge polarity matches.  It might be possible to shift out the correct data, but if latching on the wrong clock edge, then the device may see an incomplete data set.

    In either case the device should set a status bit to inform you what is wrong, and you could attempt to read either the AFE_STATUS or SYS_STATUS registers to confirm the cause of the fault during write.

    Also, if it helps, there is an example of what a normal communication looks like in the following e2e thread:

    https://e2e.ti.com/support/sensors-group/sensors/f/sensors-forum/1003071/tmag5170-q1-spi-communication

    Thanks,

    Scott

  • So CRC is enabled and I've been looking into this. I've realised that this could be my issue. When I print out the SYS_STATUS register I get 

    0x11111111111111111001000000000000
     

    If I understand correclty the 12 bit (1) is indicating CRC Error. I've enabled the SEND_RECIEVE_REDUNDANCY_ENABLED and  GLOBAL_CRC_ERROR_VAR_ENABLED definitions but I don't see yet how this can help me. Nor do I have a clue as to why the CRC check is showing an error at this point.

  • Matis,

    From your read we see:

    • Stat Bits 11-4 = 0xFF 
    • SYS_STATUS = 0xFF90
    • STAT Bits 3-0 = 0x0
    • CRC = 0x00

    The status bits are defined here:

    So we can expect that you are not getting CRC to transmit correctly, and that we should expect faults by reading both SYS_STATUS and AFE_STATUS.

    The device also seems to report that a conversion is ready on X,Y,Z, and T.  However, this would require that a write must have gone through to initialize a conversion.

    In the SYS_STATUS register the bits are defined here:

    Based on your result, the following bits are tripped:

    • ALRT_LVL = Alert is High
    • ALRT_DRV = ALERT attempted to drive low, but wasn't able to do so
    • SDO_DRV = Value on SDO didn't match driven value
    • CRC_STAT = Incorrect CRC was detected
    • FRAME_STAT = Incorrect number of clocks detected in SPI transaction
    • OPERATING_STAT = Sleep

    I would first start to troubleshoot by disabling the CRC.  The entire input on SDI to accomplish this is:

    0x0F000407

    If you can get the microcontroller to transmit this value, you should be able to turn off CRC while you troubleshoot the algorithm.  There are more details about CRC in this thread:

    https://e2e.ti.com/support/sensors-group/sensors/f/sensors-forum/938678/tmag5170-q1-start-up-sequence-and-crc

    However, it looks like you have a clock polarity issue as well if we are reading the SYS_STATUS correctly.  I would recommend using a logic analyzer or a o-scope to track all four SPI pins together to confirm the timing is correct.  It would seem the device thinks you didn't send enough clocks before releasing CS.

    If you can share a capture of your communication we can look it over to see if the edge format is setup correctly.

    Thanks,

    Scott

  • Hi Scott,

    Thank you so much for the help so far and excuse me for the late reply. Above you can see some snapshots of the logic. They're the same data, just the second image shows the graphs seperated for potentially better understanding. I tried to disable the CRC as well, but I think it did not work as the SYS_STATUS register showed the same information. I tried to disable CRC by uncommenting  '#define   DISABLE_CRC_ON_INITIALIZE' in the .h file. I also tried by inserting code in the void setup() function before the TMAG5170startup(), like this :

     

    // Attempting to disable CRC
      digitalWrite(CSpin, LOW); 
      SPI.transfer(0x0F000407);
      digitalWrite(CSpin, HIGH);

    In both cases I did not see the SYS_SATUS register change

  • Matis,

    I think it would be worth checking a few things.  It's difficult to tell from the image you supplied, but we will latch data in on the rising edge of SCLK.  At the scale provided I cannot quite tell where the edges are occurring.  There is a lot of ringing and cross talk on all of the signals as well, It might be good to check the ground connection if patching over with wires, or to check the grounding on the scope capturing the signal.  

    Can you confirm that the edge timing matches what is shown below?

    I am attaching a capture from the EVM as a reference:

    Thanks,

    Scott

  • Thanks for the example. This helped understand what we're even looking for. Excuse me for the poor images previously. Here is the same example with a potentially better scale. 

    Here is a second example where it is maybe easier to see the rising and falling edges of SCLK.

    Unfortunately the set up I'm running is using 'DYI cables' to connect the bread board to the oscilloscope and the TMAG5170UEVM. That is why there seems to be crosstalk between the signals. I'll see if I can improve this but at the moment this is the only option I have to read the logic.

    Out of interest, in my code I commented out, from the void setup() function, the lines: 

      enableMagChannels(MAG_CH_EN_BITS_XYZ);
      enableTemperatureMeasurement();

    However this did not make a difference to the SYS_STATUS register which continues to give: 

    0x11111111111111111001000000000000
  • Maybe this is also a good time to make sure I am connecting everything correclty. I am using the TMAG5170UEVM. This is just a hand sketch to make sure I know where all the relevant pins are:

  • Matis,

    Your drawing appears correct

    Can you attempt to capture the end of the CRC disable command with the CRC bits and CS release to 1? If would be good to see the end of the write sequence as well.  Again as a reference, you'll write 0x000407 to register 0x0F.  You should then follow with a read to register 0x0F.  

    Thank you,

    Scott

  • So I am getting conflicting results. 

    Here is the code I've implemented in the void setup() to write 0x000407 to register 0x0F:

    void setup() {
      // put your setup code here, to run once:
      pinMode(CSpin, OUTPUT);
      digitalWrite(CSpin, HIGH); // Deselect the TMAG5170 initially
    
      // Initialize serial communication at 9600 baud rate
      Serial.begin(9600);
      
      SPI.begin();
      SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
    
    
      // Configure device:
    
      // Attempting to disable CRC
      // Write to register 0x0F
      digitalWrite(CSpin, LOW); 
      // Send the address byte
      SPI.transfer(0x0F);
      // Send the upper byte of data_to_write
      SPI.transfer(0x00);
      // Send the lower byte of data_to_write
      SPI.transfer(0x04);
      // Send the lower byte of data_to_write
      SPI.transfer(0x07);
      digitalWrite(CSpin, HIGH);
      Serial.print("CRC disabled");
    
      // Read register 0x0F
      digitalWrite(CSpin, LOW);
      // Send the address byte with the read command (MSB set to 1)
      SPI.transfer(0x8F); // 0x0F | 0x80 to indicate read operation
      uint8_t registerValues[4];
      for(int i = 0; i < 4; i++) {
        registerValues[i] = SPI.transfer(0x00); // Send dummy byte to receive data
      }
      digitalWrite(CSpin, HIGH);
      
      Serial.print("Register values: ");
      for(int i = 0; i < 4; i++) {
        Serial.print(registerValues[i], BIN);
        Serial.print(" ");
      }
      Serial.println();
    
      TMAG5170startup();
      // Configuration Mode
      enterConfigurationMode();
      // // Enable/disable functionalities and measurements
      // enableMagChannels(MAG_CH_EN_BITS_XYZ);
      // enableTemperatureMeasurement();
      // // Sample Rate
      // setSampleRate(CONV_AVG_BITS_16X);
      // // Set ranges magnetic measurement
      // setRanges(SENSOR_CONFIG_X_RANGE_50mT, SENSOR_CONFIG_Y_RANGE_50mT, SENSOR_CONFIG_Z_RANGE_50mT);
      
    
      SPI.endTransaction();
    
      delay(100);
    }

    This will show one line in the serial monitor: 

    CRC disabledRegister values: 0 1010100 10001111 1100000

     

    Out of interest, in the void loop() function I attempt to read registers 0x0F (TEST_CONFIG_ADDRESS) and SYS_STATUS_ADDRES. For the TEST_CONFIG I use 2 methods to rty read and for the SYS_STATUS I use normalReadRegister. The code looks like this:

    void loop() {
      // put your main code here, to run repeatedly:
    
      int response2 = normalReadRegister(SYS_STATUS_ADDRESS);
      Serial.print("SYS_STATUS_value: ");
      Serial.print("Data: 0x");
      Serial.println(response2, BIN);
    
      int response3 = normalReadRegister(TEST_CONFIG_ADDRESS);
      Serial.print("TEST_CONFIG_ADDRESS_value: ");
      Serial.print("Data: 0x");
      Serial.println(response3, BIN);
    
      // Read register 0x0F
      digitalWrite(CSpin, LOW);
      // Send the address byte with the read command (MSB set to 1)
      SPI.transfer(0x8F); // 0x0F | 0x80 to indicate read operation
      uint8_t registerValues[4];
      for(int i = 0; i < 4; i++) {
        registerValues[i] = SPI.transfer(0x00); // Send dummy byte to receive data
      }
      digitalWrite(CSpin, HIGH);
      
      Serial.print("Register values: ");
      for(int i = 0; i < 4; i++) {
        Serial.print(registerValues[i], BIN);
        Serial.print(" ");
      }
      Serial.println();
    
      SPI.endTransaction();
    
      delay(1000);  // Adjust the delay based on your desired reading frequency
    }

    Here are the results shown in the serial monitor: 

    SYS_STATUS_value: Data: 0x11111111111111111001000000000000
    TEST_CONFIG_ADDRESS_value: Data: 0x1010000
    Register values: 0 1010000 10000001 11100000 

     

    From the void setup() function. I then commented out the parts which disable the CRC (so I assume by default CRC will then be enabled) and get this result when I read register 0x0F in the setup() function: 

    Register values: 0 1010000 10000111 11100000

    What's strange in this case, where I have not disabled CRC, is the serial prints I implemented in the loop() function do not change:

    SYS_STATUS_value: Data: 0x11111111111111111001000000000000
    TEST_CONFIG_ADDRESS_value: Data: 0x1010000
    Register values: 0 1010000 10000001 11100000 

    What I also don't understand, is that the normalReadRegister() function should only return data from the register without the status bits, but when I read the SYS_STATUS_ADDRESS it does return status and CRC bits. When I read the TEST_CONFIG_ADDRESS it does not return the same data. This makes me think that my original code is wrong and that my functions spiSendReceiveArrays() and spiSendReceiveByte() need to be revised? Or is the SCLK just not latching on all the data?

  • Hi Matis,

    Scott is out of office today and will be back tomorrow. I will take a look through the thread to see if I can provide some feedback--otherwise Scott should be able to comment further tomorrow.

    Best regards,

    Jesse

  • Matis,

    In both code sections, it appears that you send the command for the read, but do not capture any data from this event.  Instead you are later sending a second write of 0x00, and then attempting to store this data.  Perhaps I'm mistaken with what happens in the back end of your code, but you should be capturing data from the device simultaneous to the transaction where you send the read command.

    I am also unsure about the printed results.  I would expect four 8-bit words in your printout, but there are only 23 bits to look through.  It seems that your suspicion that something is amiss in your SPI protocol code is a good place to look.  When you send the read command what do you see on the scope?  We should be able to manually decode the transaction and confirm whether you are getting a good read.

    Thanks,

    Scott

  • I think the printouts are just values in binary and not the actual four 8-bit words, i.e. if the bits are 00001100 it'll just print 1100, as it leaves out the zero bits on the left. 

    I've managed to make some improvements on the setup, so hopefully the figures are more clear. The TMAG5170 should be in default settings for these results. 

    For SYS_STATUS_ADDRESS:

     int response2 = normalReadRegister(SYS_STATUS_ADDRESS);
      Serial.print("SYS_STATUS_value: ");
      Serial.print("Data: 0x");
      Serial.println(response2, BIN);

    I get 

    SYS_STATUS_value: Data: 0x11111111111111111001000000000000
    on the serial monitor and on the scope is: 

    For TEST_CONFIG_ADDRESS with normalReadRegister()

      int response3 = normalReadRegister(TEST_CONFIG_ADDRESS);
      Serial.print("TEST_CONFIG_ADDRESS_value: ");
      Serial.print("Data: 0x");
      Serial.println(response3, BIN);

    I get 

    TEST_CONFIG_ADDRESS_value: Data: 0x1010000
    on the serial monitor and on the scope is

    And lastly for TEST_CONFIG_ADDRESS without the normalReadRegister()

      digitalWrite(CSpin, LOW);
      // Send the address byte with the read command (MSB set to 1)
      SPI.transfer(0x8F); // 0x0F | 0x80 to indicate read operation
      uint8_t registerValues[4];
      for(int i = 0; i < 4; i++) {
        registerValues[i] = SPI.transfer(0x00); // Send dummy byte to receive data
      }
      digitalWrite(CSpin, HIGH);
      
      Serial.print("Register values: ");
      for(int i = 0; i < 4; i++) {
        Serial.print(registerValues[i], BIN);
        Serial.print(" ");
      }
      Serial.println();

    I get 

    Register values: 0 1010000 10000111 11100000
    on the serial monitor and on the scope is 

    I'm struggling to make sense of this. It seems to me that what the scope is showing does not match the printouts or possibly I am interpreting the graphs wrong? I would also expect the last 2 to show the same results as we are trying to read the same register. 

  • Matis,

    Thank you for sending these clear scope captures this helps a lot.  Here's my analysis of each transmission and your code's interpretation:

    1. You transmitted 0x8E 0000 09
        *Read from register 0x0E (SYS_STATUS)
            -16 bits of ignored input
            - CMD bits = 0x0
            -CRC bits = 0x9 (I calculate that the correct code should be 0x5)

      You received 0xE0 9000 8E
        *Your STAT bits are 0xE08 (Previous CRC Flag, CFG Reset, ALRT_STATUS1, ERROR_STAT)
           -Previous CRC would cause both ALRT_STATUS1 and ERROR_STAT to toggle
           -ALRT_STATUS1 = 1 , ALRT_STATUS0 = 0 -> Fault detected on SYS_STATUS (This register)

        *You interpreted this in code as: 0xFFFF 9000
           -It would seem that you are attempting to extract just the register value, but the value of the register is not being correctly written.  The register
                 value of 0x9000 does appear in your result, but somehow this is appended to 0xFFFF already.
           -A value of 0x9000 indicates the following codes:
                 ALRT_LVL = the input ALERT logic level is HIGH
                 CRC_STAT = CRC error has been detected for a SPI transaction


    2. You transmitted 0x8F 0000 09
        *Read from register 0x0F (TEST_CONFIG)
           -16 bits of ignored input
           -CMD bits = 0x0
           -CRC bits = 0x9 (I calculate that the correct code should be 0x8)

      You recieved 0xE0 0050 80
        *Your STAT bits are 0xE08 (same as above)
       
        *You interpreted this in code as: 0x50
           -It appears that this correctly reports only the register data, but ignores the STAT and CRC bits
           -The register data indicates you are using the A2 version of the device


    3. In this communication you have a 40-bit transaction, which should result in a fault from the device
      You transmitted 0x8F 0000 00 plus and additional 0x00

      You received 0x60 0050 87 plus an additional 0xE0
      You interpreted this as 0x5087E0, which fails to ignore the trailing STAT and CRC bits as well as what appears to be repeated STAT bits
      Your STAT bits are 0x608 (CFG_RESET, ALRT_STATUS1, ERROR_STAT)
      This transaction seems to not have a CRC error STAT bit enabled, so this was likely the first communication after power up.  However, it seems that with the extra 8 bits, the device decided to start shifting out the STAT bits again, and we can see that this is set now.

    Overall, it looks like you're close to having it working.  You should be able to sort disable CRC by sending the exact pattern I sent earlier (0x0F000407).  I'm not sure if you are intending to send the CRC bits that show up in your scope shots, so you should verify that you are actively programming these as well, even if you just send 0x0 after disabling the function.

    Then, secondarily it would seem there's a bug in your algorithm to extract the 16-bit register data from the 32-bit communication.  If you can get this cleaned up then you should be able to both write and read.

    Thanks,

    Scott

  • Thank you for the breakdown! 

    Until now I haven't had the time to try improve my coding and before I start that, I have some questions regarding the CRC. If I understand correctly the CRC is just a calculation to check if the transmission was correct. How will disabling the CRC help me? The transmission errors will still be there. Do I not want to keep the CRC to check if there are transmission errors? 

  • Matis,

    CRC is meant to ensure good data transmissions.  The calculated code for each transmission will vary using an algorithm that considers the entire word.  If the calculation is not correct, however, then the device will never latch any incoming data from the host, and you won't be able to program the sensor.  This is done to meet common safety standards. 

    My recommendation to disable it was to break down the problem into individual steps that are easier to debug. Once we can send the first disable command, and then confirm reading and writing is successful, then we would move on to ensuring that you have CRC implemented successfully.  If you are concerned with the quality of your SPI bus, then I would certainly recommend leaving it enabled in the final application.

    To implement the CRC calculation there are a few approaches in the datasheet:
    https://www.ti.com/lit/ds/symlink/tmag5170.pdf#page=31

    Thanks,

    Scott

  • Thank you so much for all the help so far, I think I am close to being fully operational.

    I am able to disable the CRC, using the sendAndReceiveFrame() function by defining DISABLE_CRC_ON_INITIALIZE. I can also directly write 0x0F000407, but using the DISABLE_CRC_ON_INITIALIZE in th eTMAG5170startup() is cleaner. When CRC is disabled the read and write functions work and I am able to read measurement values (temp and mag flux) in active measure mode as well as trigger on CS pulse (for some reason not default trigger on SPI command).

    When the CRC is enabled the transmission does not work. I often run in the issue, that when I have DISABLE_CRC_ON_INITIALIZE defined and I try change operationg modes or try change the bits of a user register, the CRC will not disable and transmission will not work. I have to comment out all the lines that change the operating modes and register bits and reupload the code to be able to disable the CRC again, eventhough I reset the device in the TMAG5170startup() function. Sometimes I may have to reupload the code multiple times until the CRC will disable. What also helps is just plugging the device out and plugging it back in. Then the CRC will be disabled again. 

    I am unsure how I could troubleshoot this issue? It seems to me the SPI read and write code is functional now but I am having issues with the CRC. Let me know if I should provide more information. 

  • Matis,

    Are you still using the code from the TMAG5170-CODE-EXAMPLE (https://www.ti.com/tool/download/TMAG5170-CODE-EXAMPLE/1.0.0)?  The CRC calculation in this code should work.  

    Here is an excel spreadsheet which you might find helpful as well.  

    CRC_Calc.xlsm

    It uses two separate methods to calculate the code.  On the first tab provide the address, write data, and command bits values.  It should then provide the trailing four bits for the CRC.  

    The second tab allows you to enter the first 28 bits in cells ranging from B2:B29.  In the calculation the CRC bits are initially seeded with 0x00.  Clicking the Calculate CRC after filling in B2:B29 will use an iterative loop to calculate CRC.  If you push alt+F11 it will open the VBA code used to generate the calculation as well.

    This should at least allow you to check that you are able to generate the correct code for each transmission you attempt with CRC enabled.

    Thanks,

    Scott

  • Indeed, I am using the TMAG5170-CODE-EXAMPLE. Thank you for the spreadsheet. I will have to compare some of the values generated to the values of the spreadsheet and will get back to you.