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: Maximum readout rate for EVM

Part Number: TMAG5170UEVM
Other Parts Discussed in Thread: TI-SCB, INA228, TMAG5170

Hi,

Currently I am evaluating the TMAG5170A1 device with TI-SCB using python script (UART, 921600) to readout and save data as mat file.

It seems that even though the device is configured as 4x - 3.1Ksps (3-axes) / 25mT / MAG_CH_EN = 9 (xyz), the readout speed is quite low (~500Hz for all 3 axis). Register 00-03 written to device are 0x2000, 0x25C0, 0x0200 and 0x0000.

May I know what is the maximum readout rate for this EVM?

Python script can be provided if needed.

Best,

Kit

  • Kit,

    I understand the difficulty this can cause.  I believe it is related to the command structure being sent to the TI-SCB to initiate the read.  Using individual register reads strung together can be quite slow.  It would be useful to see your Python script in order to better understand how you are interfacing with the TI-SCB.  

    Thanks,

    Scott

  • Hi Scott,

    Please check attached. There are two threads, one for matplotlib animation (refreshing every 200ms), and one for serial readlines and decode. After Ctrl+C, script will exit and save the list as mat file.

    import random
    from urllib3 import add_stderr_logger
    import queue
    import matplotlib.animation as animation
    import matplotlib.pyplot as plt
    import serial
    import json
    import time
    import logging
    from time import sleep
    import threading
    import signal
    from scipy.io import savemat
    import matplotlib
    matplotlib.use('TkAgg')
    
    # plt.switch_backend('agg')
    
    plot_queue = queue.Queue()
    plot_start_semaphore = threading.Semaphore(0)
    sample_start_sem = threading.Semaphore(0)
    will_exit = False
    
    global x_val
    global y_val
    global z_val
    x_val = []
    y_val = []
    z_val = []
    
    global x_val_all
    global y_val_all
    global z_val_all
    x_val_all = []
    y_val_all = []
    z_val_all = []
    timestamp_nsval = []
    
    # Plot Parameters
    mtscale = 25
    t_len = 5000         # Number of points to display
    y_range = [-1*mtscale, mtscale]  # Range of possible Y values to display
    z_range = [-1*mtscale, mtscale]  # Range of possible Z values to display
    fig = plt.figure(figsize=[12, 10], dpi=300)
    ax = fig.add_subplot(1, 3, 1)
    ay = fig.add_subplot(1, 3, 2)
    az = fig.add_subplot(1, 3, 3)
    
    ts = list(range(0, t_len))
    
    xs = [0] * t_len
    ys = [0] * t_len
    zs = [0] * t_len
    
    lineax = ax.plot(ts, xs)[0]
    lineay = ay.plot(ts, ys)[0]
    lineaz = az.plot(ts, ys)[0]
    
    try:
        s = serial.Serial('COM7', 921600, timeout=1)
    except:
        raise Exception("serial cannot open")
    else:
        logging.info("Serial: OK")
        # s.close()
    
    
    def current_milli_time():
        return round(time.time() * 1000)
    
    
    class ReadLine:
        def __init__(self, s):
            self.buf = bytearray()
            self.s = s
    
        def readline(self):
            i = self.buf.find(b"\n")
            if i >= 0:
                r = self.buf[:i+1]
                self.buf = self.buf[i+1:]
                return r
            while True:
                i = max(1, min(2048, self.s.in_waiting))
                data = self.s.read(i)
                i = data.find(b"\n")
                if i >= 0:
                    r = self.buf + data[:i+1]
                    self.buf[0:] = data[i+1:]
                    return r
                else:
                    self.buf.extend(data)
    
    
    def writetmag(tx, reader, addr, value, logprint=None):
        # purge all incoming data
        lineindex = 0
        resp_w = []
        addr_strs = "{0:#0{1}x}".format(addr, 4)
        value_strs = "{0:#0{1}x}".format(value, 6)
        wr_cmd = ('wreg ' + addr_strs + ' ' + value_strs).encode()
        try:
            tx.write(wr_cmd)
        except:
            if logprint != None:
                logging.info("Writetmag: Error")
        else:
            # sleep(0.005) # allow all response from write arrive
            if logprint != None:
                for lineindex in range(3):
                    resp_w.append(reader.readline())
                print(resp_w)
            else:
                for lineindex in range(3):
                    reader.readline()
                return 0
    
    
    def readtmag(tx, reader, addr, logprint=None):
        addr_strs = "{0:#0{1}x}".format(addr, 4)
        rd_cmd = ('rreg ' + addr_strs).encode()
        if logprint != None:
            print(rd_cmd)
        try:
            tx.write(rd_cmd)
        except:
            if logprint != None:
                logging.info("Readtmag: Error")
        else:
            resp_r = reader.readline()  # 1st ack
            if logprint != None:
                print(resp_r)
            resp_r = reader.readline()  # 2nd val
            json_reg_data = json.loads(resp_r)
            if ('register' not in json_reg_data):
                raise Exception("no key in resp")
            resp_r = reader.readline()  # 3rd status
            return json_reg_data["register"]["value"]
    
    
    def readtmag_xyz(tx, reader, addr, logprint=None):
        addr_strs = "{0:#0{1}x}".format(addr, 4)
        rd_cmd = ('rreg ' + addr_strs).encode()
        if logprint != None:
            print(rd_cmd)
        try:
            tx.write(rd_cmd)
        except:
            if logprint != None:
                logging.info("Readtmag: Error")
        else:
            resp_r = reader.readline()  # 1st ack
            if logprint != None:
                print(resp_r)
            resp_r = reader.readline()  # 2nd val
            json_reg_data = json.loads(resp_r)
            if ('register' not in json_reg_data):
                raise Exception("no key in resp")
            resp_r = reader.readline()  # 3rd status
            if json_reg_data["register"]["value"] > 32768:  # means negative
                tmag_xyz = (-32768 + (json_reg_data["register"]["value"] - 32768))
            else:
                tmag_xyz = json_reg_data["register"]["value"]
            return tmag_xyz
    
    
    def animate(i, xs, ys, zs):
        if will_exit:
            print('sleep 2s')
            sleep(2)
            plt.close()
        else:
            plot_start_semaphore.acquire()
            xs.extend(x_val)
            ys.extend(y_val)
            zs.extend(z_val)
            sample_start_sem.release()
            logging.info("animate: sem post")
            # Limit y list to set number of items
            xs = xs[-t_len:]
            ys = ys[-t_len:]
            zs = zs[-t_len:]
    
            # Update line with new Y values
            lineax.set_ydata(xs)
            lineay.set_ydata(ys)
            lineaz.set_ydata(zs)
    
        return [lineax, lineay, lineaz]
    
    
    def plotter():
        # Create figure for plotting
        ax.set_ylim(y_range)
        ay.set_ylim(y_range)
        az.set_ylim(y_range)
    
        # Create a blank line. We will update the line in animate
    
        # Add labels
        plt.title('Hall Reading')
        plt.xlabel('Samples')
        plt.ylabel('Amp')
    
        # Set up plot to call animate() function periodically
        ani = animation.FuncAnimation(fig,
                                      animate,
                                      fargs=(xs, ys, zs),
                                      interval=200,  # 100ms
                                      blit=True)
        plt.show()
    
    def signal_handler(signum, frame):
        global will_exit
        will_exit = True
    
    def tmag_reader(serial):
        reader = ReadLine(serial)
        starttime_str = time.strftime("%Y%m%d_%H%M%S")
        starttime = time.time_ns()
        # try:
        while True:
            for runtime in range(50):
                tmag_x = readtmag_xyz(serial, reader, 0x09) * mtscale / 32768
                tmag_y = readtmag_xyz(serial, reader, 0x0A) * mtscale / 32768
                tmag_z = readtmag_xyz(serial, reader, 0x0B) * mtscale / 32768
                timestamp_nsval.append(time.time_ns() - starttime)
                x_val.append(tmag_x)
                y_val.append(tmag_y)
                z_val.append(tmag_z)
            # post a semaphore every 250 samples
            plot_start_semaphore.release()
            logging.info("sample: sem post")
            x_val_all.extend(x_val)
            y_val_all.extend(y_val)
            z_val_all.extend(z_val)
        ## generate a dictionary & save
            if will_exit:
                matdic = {"ts_ns": timestamp_nsval, "x_mt": x_val_all,
                            "y_mt": y_val_all, "z_mt": z_val_all}
                timestr = time.strftime("%Y%m%d_%H%M%S")
                filename = 'tmag_' + starttime_str + '-' + timestr + '.mat'
                # savemat(filename, matdic)
                print('[tmag_reader]: Saved mat file ('+ filename + ')')
                break
            sample_start_sem.acquire()
            x_val.clear()
            y_val.clear()
            z_val.clear()
        print('[tmag_reader]: Thread exit')
    
    if __name__ == "__main__":
        cnt_real = 0
        cnt = 0
        format = "%(asctime)s: %(message)s"
        logging.basicConfig(format=format, level=logging.INFO,
                            datefmt="%H:%M:%S")
    
        # Create figure for plotting
        ax.set_ylim(y_range)
        ay.set_ylim(y_range)
        az.set_ylim(z_range)
    
        # init setup
        num_of_line = 3
        tstart = time.time_ns()
        reader = ReadLine(s)
    
        # a. write 0x2000 to 0x00
        writetmag(s, reader, 0x00, 0x2000)
        reg_val = readtmag(s, reader, 0x00)
        if reg_val != 0x2000:
            raise Exception("error in 00")
    
        # b. write 0x25C0 to 0x01 (25mT / xyz / 1000ms sleep)
        writetmag(s, reader, 0x01, 0x25C0)
        reg_val = readtmag(s, reader, 0x01)
        if reg_val != 0x25C0:
            raise Exception("error in 01")
    
        # c. write 0x0200 to 0x02
        writetmag(s, reader, 0x02, 0x0200)
        reg_val = readtmag(s, reader, 0x02)
        if reg_val != 0x0200:
            raise Exception("error in 02")
    
        # d. write 0x0000 to 0x03
        writetmag(s, reader, 0x03, 0x0000)
        reg_val = readtmag(s, reader, 0x03)
        if reg_val != 0x0000:
            raise Exception("error in 03")
    
        # start conversion
        # e. write 0x2020 to 0x00
        writetmag(s, reader, 0x00, 0x2020)
        reg_val = readtmag(s, reader, 0x00)
    
        # add a signal handler for capturing Ctrl+C
        signal.signal(signal.SIGINT, signal_handler)
        signal.signal(signal.SIGTERM, signal_handler)
    
        if reg_val != 0x2020:
            raise Exception("error in 00")
        try:
            x = threading.Thread(target=tmag_reader, args=(s,))
            # x.daemon = True
            x.start()
        except:
            raise Exception('fail to create thread')
    
        # Create a blank line. We will update the line in animate
    
        # Add labels
        plt.title('Hall Reading')
        plt.xlabel('Samples')
        plt.ylabel('Amp')
    
        # Set up plot to call animate() function periodically
        ani = animation.FuncAnimation(fig,
                                    animate,
                                    fargs=(xs, ys, zs),
                                    interval=20,  # 100ms
                                    blit=True)
        plt.show()
        x.join()
        tstop = (time.time_ns() - tstart) / 1000000
    
        s.close()
        exit()

    Thank you and best,

    Kit

  • Kit,

    From your code, it looks like you are implementing the read using a standard USB communication.  Unfortunately the standard control interface is not able to send SPI transactions at the maximum sample rate of the sensor. We experience the same delay with this mode of data transfer on the EVM GUI.  To improve throughput, the TI-SCB is capable of implementing bulk USB data communication.  You'll need to setup your Python code to first connect to the EVM as a bulk device. 

    There is some more guidance about how this mode is used with TI-SCB in this User's guide:

    https://www.ti.com/lit/ug/sbou241d/sbou241d.pdf#page=20

    The flags shown in this user's guide reflect what is needed for INA228.  For TMAG5170 the flags are defined as follows:

    XAXIS            = 0b10000000;
    YAXIS            = 0b01000000;
    ZAXIS            = 0b00100000;
    TEMP             = 0b00010000;
    ANGLE           = 0b00001000;
    MAGNITUDE  = 0b00000100;

    Also, INA228 supports connecting multiple devices in a daisy chain fashion.  For TMAG5170 channelAddressIDs  and numDevices should be set to 1.

    The data will be returned in packets which you'll have to parse.  I've captured how this is done within the published GUI:

    	packetHandler: function (rxData) {
    
    				// Accumulate data
    				// CONSIDER: Checking if we are receiving more bytes than expected
    				//console.log(`${rxData.data.length} bytes comming in`);
    				rxData.data.forEach(byte => { 
    				    
    				    this.packetData[this.byteCount++] = byte; 
    				    
    				    if(this.byteCount > this.packetData.length)
    				    {
    				        console.error(this.is, `packetData overflow! (packet length: '${this.packetData.length}', byte count: '${this.byteCount}')`);
    				    }
    				    if(this.packetData.length === this.byteCount)
    				    {
    				        if(templateObj.$.ti_widget_checkbox_autoCollect.checked && numSamplesCollected >= templateObj.$.ti_widget_numberbox_samples_all.value)
    				        {
    				            this.fire('abort-collect');
    				        }
    				        else
    				        {
    				            this.dataParser();
    				        }
    				        this.byteCount = 0;
    				        numSamplesCollected++; 
    				    }
    				});
                    
    				// Debugging...
    				//console.log(rxData);
    			},
    
    			dataParser: function () {
    				let index = 0;
    				
    				//.log(`parsing data`);
    				
    				while (index < this.packetData.length) {
    
                        //Format: Frame ID, Address, Data
                        
    					// Check for frame ID and two data bytes
    					if (this.packetData[index] !== 0x00) {
    						console.error(this.is, `USB data misaligned - Cannot parse! (Index: '${index}', Value: '${this.packetData[index]}')`);
    						
    						this.fire('abort-collect');
    						index++; //needed?
    						return;
    					}
                        index++;
    				// 	let decimal = (this.packetData[index + 1] << 8) | this.packetData[index + 2];	// Unsigned
    				// 	if (!this.isADS1118) { decimal = this.signExtend(decimal >> 4, 12); }			// ADS1018 (ADC + Temp sensor): 12-bit left-aligned data
    				// 	else if (isTempMode) { decimal = this.signExtend(decimal >> 2, 14); }			// ADS1118 (Temp sensor): 14-bit left-aligned data
    				// 	else { decimal = this.signExtend(decimal, 16); }								// ADS1118 (ADC data):  16-bit data
    				// 	this.channelData.push(decimal);
    				// 	index += 3;
    				
        			
        				let Address = this.packetData[index++];
        				let Data = this.packetData[index++];
        				let RegisterSize = 2;//bytes
        				
    
        				
        				if (templateObj.$.ti_widget_checkbox_XAXIS.checked) //X
        				{
        				    CHX = 0;
        				    templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_label = "X-Component";
        				    templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_color = "#00BBCC"
        				    if (templateObj.$.ti_widget_checkbox_YAXIS.checked) //XY
        				    {
        				         CHY=1;
        				         templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_label = "Y-Component";
        				         templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_color = "#115566";
        				         if (templateObj.$.ti_widget_checkbox_ZAXIS.checked) //XYZ
        				         {
        				             CHZ=2;
        				             templateObj.$.ti_widget_scalargraph_MAGFIELD.series_2_label = "Z-Component";
        				             templateObj.$.ti_widget_scalargraph_MAGFIELD.series_2_color = "#AAAAAA";
        				         }
        				         else {CHZ = -1;}
        				    }
        				    else
        				    
        				    if (templateObj.$.ti_widget_checkbox_ZAXIS.checked) //XZ
        				    {
        				        CHY=-1;
        				        CHZ=1;
        				        templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_label = "Z-Component";
        				        templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_color = "#AAAAAA";
        				    }
        				    else //X only
        				    {
        				        CHY=-1;
        				        CHZ=-1;
        				    }
        				}
        				
        				else //No X
        				{
        				    CHX = -1;
        				    if (templateObj.$.ti_widget_checkbox_YAXIS.checked) 
        				    {
        				        CHY = 0;
        				        templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_label = "Y-Component";
        				        templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_color = "#115566";
        				        if (templateObj.$.ti_widget_checkbox_ZAXIS.checked) 
        				        {
        				            CHZ=1;
        				            templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_label = "Z-Component";
        				            templateObj.$.ti_widget_scalargraph_MAGFIELD.series_1_color = "#AAAAAA";
        				        }
        				        else {CHZ = -1;}
        				    }
        				    else
        				    {
        				        CHY = -1;
        				        if (templateObj.$.ti_widget_checkbox_ZAXIS.checked) 
        				        {
        				            CHZ = 0;
        				            templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_label = "Z-Component";
        				             templateObj.$.ti_widget_scalargraph_MAGFIELD.series_0_color = "#AAAAAA";
        				        }
        				        else {CHZ=-1;}
        				        
        				    }
        				}
        				
        				for(let i = 1; i < RegisterSize; i++)
        				{
        				    Data = (Data << 8) | this.packetData[index++];
        				}
        				let temp = Data;
        				
        			
                      
        				
        				if(Address === 0x9 || Address === 0xA || Address === 0xB) //X,Y,Z
        				{
    
    				         if(Data >= 0x7FFF)
    				        {
    				        	Data = Data - 0x10000;
    				        }
    				        
    				        let maxVal = 32767;
                            let minVal = -32768;
                            if(Data > maxVal) {Data = -Data/minVal;}
                            else{Data = Data/maxVal;}
    				       
    				        let ChannelID;
                            if (Address === 0x09) 
                            {
                                ChannelID = CHX;
                                Data = Data * adcrange[0];
                            }
                            else if (Address === 0x0A)
                            { 
                                ChannelID = CHY;
                                 Data = Data * adcrange[1];
                            }
                            else if (Address === 0x0B)
                            { 
                                ChannelID = CHZ;
                                Data = Data * adcrange[2];
                            }
    				        //put data into plot
                            if(templateObj.$.ti_widget_scalargraph_MAGFIELD[`value_${ChannelID}`] === Data)
                            {
                                if(ChannelID === 0)
                                {
                                    templateObj.$.ti_widget_scalargraph_MAGFIELD.value_0Changed();
                                }
                                else if(ChannelID === 1)
                                {
                                    templateObj.$.ti_widget_scalargraph_MAGFIELD.value_1Changed();
                                }
                                else if(ChannelID === 2)
                                {
                                    templateObj.$.ti_widget_scalargraph_MAGFIELD.value_2Changed();
                                }
                            }
                            else
                            {
                                templateObj.$.ti_widget_scalargraph_MAGFIELD[`value_${ChannelID}`] = Data;
                            }
                            
        				}
    				   else if(Address === 0xC) //DIE TEMP
    				    {
    				        //Data = 65/256*(Data/16-1066)+25;
    				        let T0 = 25;//Ref temp
    				        let ADCT0 = 17522;//ADC value at T0
    				        let TRES = 60; //LSB per C
    				        
    				        Data = T0 + (Data-ADCT0)/TRES;
    				         //optionally convert to F
    				        if(ti_widget_droplist_dietemp_unit.selectedText == "°F")
    				        {
    				            Data = (Data * (9/5)) + 32;
    				        }
    				       
                             if(templateObj.$.ti_widget_scalargraph_DIETEMP.value_0 === Data)
                            {
    				        templateObj.$.ti_widget_scalargraph_DIETEMP.value_0Changed();
        			        }
        			         else
                            {
                                templateObj.$.ti_widget_scalargraph_DIETEMP.value_0 = Data;
                            }
    				    }    			
    				    else if(Address === 0x13) //ANGLE
    				    {
    				        let fraction = Data & 0xF;
    				        Data = Data >> 4;
    				        Data = Data + fraction/16;
    				       
    				        if(templateObj.$.ti_widget_scalargraph_ANGLE.value_0 === Data)
                            {
                            	templateObj.$.ti_widget_scalargraph_ANGLE.value_0Changed();
                            }
                            else
                            {
                                templateObj.$.ti_widget_scalargraph_ANGLE.value_0 = Data;
                            }
                           
    				    }
    				    else if(Address === 0x14) //Magnitude
    				    {
    				        let result = Data;
                            let maxVal = 32767; //use xyz chanel max val, for scale
                            
                           	let mRange;
        				
                            if('ti_model_register.sensor_config_register.angle_en' == 2 )
                            {
                                mRange = adcrange[1];
                            }
                            else 
                            {
                                mRange = adcrange[0];
                            }
                
                            Data = 16*Data*mRange/maxVal; //multiply by 16 for magnitude conversion (from 12 bit xyz chanel values)
                            if(templateObj.$.ti_widget_scalargraph_MAGNITUDE.value_0 === Data)
                            {
                            	templateObj.$.ti_widget_scalargraph_MAGNITUDE.value_0Changed();
                            }
                            else
                            {
                                templateObj.$.ti_widget_scalargraph_MAGNITUDE.value_0 = Data;
                            }
    				    }
    				   
    				// Debugging...
    				//console.log(this.channelData);
    			}},

    Hopefully this helps you sort out the connection so that you can gather data at the maximum rate.

    Thanks,

    Scott

  • Hi Scott,

    Yes you are correct. Cause this is the only mode hinted by the EVM guide sbau388a.pdf. Perhaps the BULK mode should be added to the guide.

    I will look through and try out the resource/example you provided. If got any other problem I will ask a new question.

    Thank you and best,

    Kit