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.