microcontrolleresp32spimicropython

SPI communication from ESP32 to TLE5012B magnetic enoder


Summary: I've had this bump in the road for one of my projects for a long time and I was wondering if anyone knew anything about it. I found a wonderful magnetic encoder (tle5012b) but I have had a lot of trouble hooking it up with the ESP32. The TLE5012B has a few options for interfaces but the primary one is SSC (which is just 3 wire SPI where the MOSI and MISO are tied together). It also has an incremental interface but I could not get the necessary accuracy from it. I wrote a micro python program to communicate with the TLE5012B over SPI communication. The code is below:

Code:

from machine import SPI, Pin

spi = SPI(1, 40000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) #defining the SPI object
cs = Pin(19,mode=Pin.OUT, value=1) #defining chip select pin


rxdata = bytearray(4) #defining a 4 byte array
txdata = b'32801'
print(rxdata,"rxdata") #reading empty rx data as bit strings 
print(txdata,"txdata") #reading tx data before sent as bit string 

cs(0)                               # Select peripheral.
spi.write(txdata)              # Write 8 bytes, and don't care about received data.

    

                              # Select peripheral.
spi.readinto(rxdata)#, 0x42)          # Read 8 bytes inplace while writing 0x42 for each byte.

cs(1)

print(rxdata,"rxdata")

Schematic: This is how I wired the chip on the breadboard. I followed instructions I found online and data from the product datasheet. My Project Schematic

Problem: When running this code with this schematic, I receive an empty register and I don't really know how to move forward. Is there anything I am doing wrong?

Sources: Finally, here are all of the sources that I found might be remotely helpful

Code for someone who got it working on an Arduino Nano: https://forum.arduino.cc/t/arduino-spi-code-for-tle5012-magnetic-encoder-not-working-with-esp32/1144303

Micropython SPI tutorials: https://learn.adafruit.com/micropython-hardware-spi-devices/spi-main https://www.engineersgarage.com/micropython-esp8266-esp32-spi-software-spi/

Datasheet of component: file:///C:/Users/micha/Desktop/%C2%A0/Encoder/Infineon-Angle_Sensor_TLE5012B-UM-v01_02-en-UM-v01_02-EN.pdf

Register settings: (gives the register to read the data: 1000000000100001) https://www.infineon.com/dgdl/TLE5012B_Register_Setting_AN_Rev1.1.pdf?folderId=db3a30431ce5fb52011d29810c9e1b6a&fileId=db3a304330f68606013141cf6ae75690

Website with the circuit of TLE5012B for SPI: file:///C:/Users/micha/Desktop/%C2%A0/Encoder/SimpleBGC_32bit_Encoders.pdf

Thanks: I greatly appreciate anyone's help! Thank you so much! Sorry if I'm missing something obvious, I'm pretty new to all of this.

Already tried: I tried using the incremental interface as described in the datasheet but the ESP32 was not able to keep up and kept missing increments leading to wildly inaccurate data.

EDIT: I followed Mat's advice about not deselecting the chip right after the spi.write. I ran the adjusted code with the same problem of the rx register being empty. I still have some more issues in the code most likely or possibly I fried it as Mat warned.

I also changed the schematic slightly (just moving some resistors around)


Solution

  • I managed to solve it myself so here is the code for anyone that my want to use this object for their own projects. This sensor is pretty useful. Works like a charm.

    from machine import Pin, SoftSPI
    import time
    
    class TLE5012B() :
        def __init__(self,sck_pin=2,mosi_pin=13,miso_pin=15,cs_pin=4):
            self.spi = SoftSPI(baudrate=8000000, sck=Pin(sck_pin), mosi=Pin(mosi_pin), miso=Pin(miso_pin))          #defining the SPI object
            #self.spi.init()
            self.cs = Pin(cs_pin,mode=Pin.OUT, value=1) #defining chip select pin
            self.rotation_counter = 0
            self.previous_angle = 0
            self.start_angle = 0
            self.zero()
            
        def zero(self):
            self.start_angle = self.get_angle_displacement()
            
    
        def get_angle(self):
            rxdata = bytearray(2) #defining a 4 byte array
            command = '1000000000100001'  #defining the command that reads the chip
            txdata = int(command,2).to_bytes(2,'big')  #converting to bytes
            self.cs(0) #activating the chip through the chip select pin
            self.spi.write(txdata) # Write the command which takes up two bytes
            self.spi.readinto(rxdata)# Reading the data that was recieve from the chip and placing it the rx register
            self.cs(1) #deactivating the chip once transaction is finished
            byte1 = '{0:08b}'.format(rxdata[0]) #converting first bit to a string of ones and zeros
            byte2 = '{0:08b}'.format(rxdata[1]) #converting second bit to a string of ones and zeros
            data = byte1[1:]+byte2 #adding all of the strings together and extracting the angle data (total is a 15 bit number)
            steps = int(data,2) #getting the 15 bit number as an integer
            angle = (360*steps)/32768 #converting to angle
            if ((self.previous_angle > 240) and (self.previous_angle <= 360)) and ((angle >= 0) and (angle < 120)):
                self.rotation_counter += 1
            elif ((angle > 240) and (angle <= 360)) and ((self.previous_angle >= 0) and (self.previous_angle < 120)):
                self.rotation_counter -= 1
            self.previous_angle = angle
            return(angle)
        
        def get_revs_slave(self):
            rxdata = bytearray(4) #defining a 4 byte array
            command = '1000000000110010'  #defining the command that reads the chip
            txdata = int(command,2).to_bytes(2,'big')  #converting to bytes
            self.cs(0) #activating the chip through the chip select pin
            self.spi.write(txdata) # Write the command which takes up two bytes
            self.spi.readinto(rxdata)# Reading the data that was recieve from the chip and placing it the rx register
            self.cs(1) #deactivating the chip once transaction is finished
            byte1 = '{0:08b}'.format(rxdata[2]) #converting third bit to a string of ones and zeros
            byte2 = '{0:08b}'.format(rxdata[3]) #converting fourth bit to a string of ones and zeros
            data_rev = byte1[-1]+byte2 #adding all of the strings together and extracting the rev data (total is a 9 bit number)
            revs = int(data_rev,2) #getting the 9 bit number as an integer
            return revs
        
        def print_all_data(self,command):
            #command = '1000000000100001' read angle command
            #command = '1000000001000001' read revolutions command (TLE5012B Ragester Settings datasheet has more information https://www.infineon.com/dgdl/TLE5012B_Register_Setting_AN_Rev1.1.pdf?folderId=db3a30431ce5fb52011d29810c9e1b6a&fileId=db3a304330f68606013141cf6ae75690) 
            txdata2 = int(command,2).to_bytes(2,'big')  #converting to bytes
            rxdata2 = bytearray(10)
            self.cs(0) #activating the chip through the chip select pin
            self.spi.write(txdata2) # Write the command which takes up two bytes
            self.spi.readinto(rxdata2)# Reading the data that was recieve from the chip and placing it the rx register
            self.cs(1) #deactivating the chip once transaction is finished
            for x in rxdata2:
                print('{0:08b}'.format(x))
            print("######################")
            
        def get_revs_master(self):
            return self.rotation_counter
        
        def get_angle_displacement(self):
            return self.get_angle() + (self.get_revs_master()*360) - self.start_angle
            
    ############################################################################
        #Example     
             
             
    encoder = TLE5012B(sck_pin=2,mosi_pin=13,miso_pin=15,cs_pin=4) #defining sensor object
    while True:#creating a loop to get live updates
        #print(encoder.get_angle()) #printing sensor's position
        #encoder.print_all_data('1000000000100001') #printing the binary that is returned from a certain command as a debugging tool
        #print(encoder.get_revs_slave())
        #print(encoder.get_revs_master())
        print(encoder.get_angle_displacement())
        time.sleep(.05) #control the speed of the loop by sleeping for .4 second
    

    Here were the problems:

    #1 The command was not converted from bits to bytes correctly. b'32801' does not equal '1000000000100001'

    #2 The baud rate was incorrect

    #3 the SPI object didn't work very well

    Here are the solutions:

    #1 This is how to convert bits to bytes properly

    command = '1000000000100001'
    txdata = int(command,2).to_bytes(2,'big')
    

    #2 I changed the baud rate from 40000000 to 8000000 as specified in the datasheet.

    #3 SoftSPI object magically worked for some reason.

    Thanks yall!