Post Go back to editing

To use ADcmXL 3021 right way.

My ADcmXL3021 with Raspberry pi 4.

And use python code

# adcmxl3021 Driver in Python
# SPI Master is using Raspberry Pi
# spidev is the Raspberry Pi spi communication library
# Date: 2019.7.11

import sys
import spidev
import time

SPI_MAX_SPEED   =1953125

class adcmxl3021():
    
    #Configuration Constant based on ADcmXL3021 datasheet
    
    PAGE_ID         =0x00
    TEMP_OUT        =0x02
    SUPPLY_OUT      =0x04
    FFT_AVG1        =0x06
    FFT_AVG2        =0x08
    BUF_PNTR        =0x0A
    REC_PNTR        =0x0C
    X_BUF           =0x0E
    Y_BUF           =0x10
    Z_BUF           =0x12
    X_ANULL         =0x14
    Y_ANULL         =0x16
    Z_ANULL         =0x18
    REC_CTRL        =0x1A
    REC_PRD         =0x1E
    ALM_F_LOW       =0x20
    ALM_F_HIGH      =0x22    
    ALM_X_MAG1      =0x24   
    ALM_Y_MAG1      =0x26
    ALM_Z_MAG1      =0x28   
    ALM_X_MAG2      =0x2A   
    ALM_Y_MAG2      =0x2C
    ALM_Z_MAG2      =0x2E
    ALM_PNTR        =0x30
    ALM_S_MAG       =0x32
    ALM_CTRL        =0x34
    DIO_CTRL        =0x36
    FILT_CTRL       =0x38
    AVG_CNT         =0x3A
    DIAG_STAT       =0x3C
    GLOB_CMD        =0x3E
    
    ALM_X_STAT      =0x40   
    ALM_Y_STAT      =0x42
    ALM_Z_STAT      =0x44   
    ALM_X_PEAK      =0x46   
    ALM_Y_PEAK      =0x48
    ALM_Z_PEAK      =0x4A

    TIME_STAMP_L    =0x4C
    TIME_STAMP_H    =0x4E

    REV_DAY         =0x52
    YEAR_MON        =0x54
    PROD_ID         =0x56

    SERIAL_NUM      =0x58
    USER_SCRATCH    =0x5A
    REC_FLASH_CNT   =0x5C

    MISC_CTRL       =0x64
    REC_INFO1       =0x66
    REC_INFO2       =0x68
    REC_PNTR        =0x6A

    ALM_X_FREQ      =0x6C
    ALM_Y_FREQ      =0x6E   
    ALM_Z_FREQ      =0x70
    STAT_PNTR       =0x72
    X_STASTIC       =0x74
    Y_STASTIC       =0x76
    Z_STASTIC       =0x78
    FUND_FREQ       =0x7A
    FLASH_CNT_L     =0x7C
    FLASH_CNT_H     =0x7E

    PAGE_ID         =0x00



    def __init__(selfdevice=0ce_pin=0):
        """
        device: the SPI device (often 0)
        ce_pin: pass 0 for CE0, 1 for CE1, etc.
        device and ce_pin map to device file /dev/spidev{device}.{ce_pin}
        """
        self.spi_init(device, ce_pin)

    def spi_init(self,device,ce_pin):
        """
        Using "pinout" command to check the raspberry spi definition.
        device: SPI0 (0), SPI1 (1)
        ce_pin: CE0 (0), CE1 (1)
        
        SPI0 definition
        CE0 :  GPIO8 (0)  PIN24
        CE1 :  GPIO7 (1)  PIN26
        MOSI:  GPIO10     PIN19
        MISO:  GPIO9      PIN21
        SCLK:  GPIO11     PIN23

        SPI1 definition
        CE0 :  GPIO18 (0) PIN12 
        CE1 :  GPIO17 (1) PIN11PAGE_ID
        CE2 :  GPIO16 (2) PIN36
        MOSI:  GPIO20     PIN38
        MISO:  GPIO19     PIN35
        SCLK:  GPIO21     PIN40   
        """
        # init spi for communication
        self.spi = spidev.SpiDev()
        self.spi.open(device, ce_pin) # (x,0) == CE0, (x,1) == CE1

        # Set 4-line spi mode
        self.spi.threewire   =False

        # Set spi msb first
        self.spi.lsbfirst    =False

        # Set clock phase and polarity to default
        # SPI_CPOL=0, SPI_CPHA=0
        self.spi.mode       =3

        # Set SPI CS Active Low
        self.spi.cshigh     =False

        # Set SPI clock rate: 7.8125MHz, cdiv=32, fsclk=250MHz/cdiv, cdiv = [2,4,8,16,32,...32768]
        self.spi.max_speed_hz=SPI_MAX_SPEED

        self.spi.bits_per_word =8

        
    
    def spi_write(selfaddressvalue):
        # Write value (1 byte) to address
        # Send instruction (command bit7=1 for write), address[6:0], and value
        address=address+0x80
        self.spi.xfer([address, value])


    def spi_write_word(self,address,value_16b):
        value_l=value_16b & 0xFF
        value_h=value_16b>>8
        self.spi_write(address,value_l)
        self.spi_write(address+1,value_h)


    def spi_read_bytes(selfaddress):
        # Read
        # Send instruction (command bit7=0 for read), address bit6:0
        rb=self.spi.xfer([address,0],SPI_MAX_SPEED,16,8)
        return rb

    def spi_read(selfaddress):
        # Read
        # Send instruction (command bit7=0 for read), address bit6:0
        rb=self.spi.xfer([address,0],SPI_MAX_SPEED,16,8)
        rbc=  rb[0]*256 + rb[1]
        return rbc
        
    def spi_read2s(selfaddress):
        # Read
        # Send instruction (command bit7=0 for read), address bit6:0
        rb=self.spi.xfer([address,0],SPI_MAX_SPEED,16,8)
        rbc=  rb[0]*256 + rb[1]
        rbc2= self.twos_comp(rbc,16)
        return rbc2


        

    def twos_comp(self,valbits):
        # two's complement of value in bits
        if val&(1<<(bits-1)) != 0:
            val = val - (1<<bits)
        return val


    def check_spi_rd(self):
        #Readback Device ID to check the SPI Read
        self.get_prod_id()
        rb=self.get_prod_id()
        print ("--------------")
        print ("product_id readback:%s"%(rb))
        if (rb == 3021) :
            print ("spi Read OK, Find DUT=ADcmXL3021")
        else:
            print ("spi Read Failure, Please check SPI timing and hardware connections")


    def software_reset(self):
        self.spi_read(self.GLOB_CMD)
        rb=self.spi_read(self.GLOB_CMD)
        rb=rb | 0x80
        self.spi_write_word(self.GLOB_CMD,rb)
        #wait s
        time.sleep(0.5)


    def get_diag_sts(self):
        self.spi_read(self.DIAG_STS)
        rb=self.spi_read(self.DIAG_STS)
        return hex(rb)


    def get_temp_out(self):
        #return unit:C
        #self.set_page_id(page_id=0)
        self.spi_read(self.TEMP_OUT)
        rb=self.spi_read(self.TEMP_OUT)
        return (self.twos_comp(rb,16)*(-0.46)+460)   

    
    def get_prod_id(self):
        #self.set_page_id(page_id=0)
        self.spi_read(self.PROD_ID)
        rb=self.spi_read(self.PROD_ID)
        return rb


    def set_user_scratch(self,user_scratch):
        #self.set_page_id(page_id=0)
        self.spi_write_word(self.USER_SCRATCH,user_scratch)

    def get_user_scratch(self):
        #self.set_page_id(page_id=0)
        self.spi_read(self.USER_SCRATCH)
        rb=self.spi_read(self.USER_SCRATCH)
        return rb

    def get_year_mon_day_rev(self):
        #self.set_page_id(page_id=0)
        self.spi_read(self.YEAR_MON)
        year_mon=self.spi_read(self.YEAR_MON)
        self.spi_read(self.REV_DAY)
        rev_day=self.spi_read(self.REV_DAY)
        return year_mon, rev_day


    def set_page_id(self,page_id):
        self.spi_write_word(self.PAGE_ID,page_id)

    def get_page_id(self):
        self.spi_read(self.PAGE_ID)
        rb=self.spi_read(self.PAGE_ID)
        return rb

     
        

    # MTC mode Register Settings:
    # AVG_CNT: decimation filter D after ADC fs=220ksps
    # FILT_CTL: FIR filter 32 taps
    # REC_CTRL: [bit1:0] Record mode control = 0b10 (MTC)
    #           [bit13:12] Window Setting    =01 (hanning default)
    #           [bit11:8] SR settings        =0b0001 (sr0 enabled, sr1~3 disabled)
    # GLOB_CMD: Bit11= record start/stop or external trigger
    # /BUSY signal will toggle when the data record USER_SCRATCHis stored and the alarms are checked.
    # 4096 DATA will be stored in X/Y/Z_BUF

    def mtc_set(self):
        self.spi_write_word(self.AVG_CNT,0x7421)
        self.spi_write_word(self.FILT_CTRL,0x0000)
        self.spi_write_word(self.REC_CTRL,0x1102)
        self.record_start()
        time.sleep(2)

    def mtc_read(self):
        gain=-0.001907     
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read2s(self.X_BUF) 
            x=self.spi_read2s(self.X_BUF)*gain
            print (x)
            
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read2s(self.Y_BUF) 
            y=self.spi_read2s(self.Y_BUF)*gain
            print (y)
            
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read2s(self.Z_BUF) 
            z=self.spi_read2s(self.Z_BUF)*gain
            print (z)  




    # MFFT mode Register Settings:
    # AVG_CNT: decimation filter D after ADC fs=220ksps
    # FILT_CTL: FIR filter 32 taps
    # REC_CTRL: [bit1:0] Record mode control = 0b00 (MFFT)
    #           [bit13:12] Window Setting    =01 (hanning default)
    #           [bit11:8] SR settings        =0b0001 (sr0 enabled, sr1~3 disabled)
    # FFT_AVG1: spectral average settings for sr0 and sr1, default (0x0108)
    # FFT_AVG2: spectral average settings for sr2 and sr3, default (0x0101)
    # GLOB_CMD: Bit11= record start/stop or external trigger
    # /BUSY signal will toggle when the data record USER_SCRATCHis stored and the alarms are checked.
    # 4096 DATA will be stored in X/Y/Z_BUF

    def mfft_set(self):
        self.spi_write_word(self.AVG_CNT,0x0000)
        self.spi_write_word(self.FILT_CTRL,0x0000)
        self.spi_write_word(self.REC_CTRL,0x1100)
        self.spi_write_word(self.FFT_AVG1,0x0108)
        self.spi_write_word(self.FFT_AVG2,0x0101)
        self.record_start()
        time.sleep(10)

    def mfft_read(self):
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read(self.X_BUF) 
            x=self.spi_read(self.X_BUF)
            print (x)
            
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read(self.Y_BUF) 
            y=self.spi_read(self.Y_BUF)
            print (y)
            
        self.set_buf_pntr(buf_pntr=0)
        for i in range(10):
            if i==0:
               self.spi_read(self.Z_BUF) 
            z=self.spi_read(self.Z_BUF)
            print (z)         



    def get_avg_cnt(self):
        #self.set_page_id(page_id=0)
        self.spi_read(self.AVG_CNT)
        rb=self.spi_read_bytes(self.AVG_CNT)
        sr3=rb[0]>>4
        sr2=rb[0]&0x0F
        sr1=rb[1]>>4
        sr0=rb[1]&0x0F       
        return [sr3,sr2,sr1,sr0]

    def set_buf_pntr(self,buf_pntr):
        #self.set_page_id(page_id=0)
        self.spi_write_word(self.BUF_PNTR,buf_pntr)

    def record_start(self):
        self.spi_read(self.GLOB_CMD)
        rb=self.spi_read(self.GLOB_CMD)
        rb=rb | 0x0800
        self.spi_write_word(self.GLOB_CMD,rb)

        
        
    def self_test(self):
        self.spi_read(self.GLOB_CMD)
        rb=self.spi_read(self.GLOB_CMD)
        rb=rb | 0x0004
        self.spi_write_word(self.GLOB_CMD,rb)
        time.sleep(1)
        rb=self.spi_read_bytes(self.DIAG_STAT)
        return rb
        

if __name__ == "__main__":
    try:
        if 1:
            time.sleep(1)
    except KeyboardInterrupt:
        spi.close()
        sys.exit(0)
first, I got PROD_ID is 3021, with "check_spi_rd()"
but, I got PROD_ID is XXXX(garbage), with "check_spi_rd() and another "spi_read()".
I don't know why this read function is changed the read value.
  • Interesting development you have here. Although support from 'Analog Devices' is non-existent. Have to consider using different controller than raspberry pi? I have been working on raspberry pi 4 and after a week of struggle, I decided to check the SPI output of EZUSB-FX3 and the evaluation software provided by analog devices and found out that the control frequency is about 14MHz and somehow the output format for cs, sclk and MOSI is very different than of spidev of raspberry pi module. The SYNC/RTS pin also enabled throughout the read process in the eval software. I tried to emulate the similar spi behavior using python and spidev and GPIO pins but somehow the reads are always zeros.

    I believe that it has something to do with the SCLK frequency. Have you ever got your code to work? Me with my limited knowledge of visual basic. It's almost impossible for me to reverse engineering the evaluation program source code provided on GitHub. Worst case scenario a c++ source code for fx3 controller would have been easier for developers to understand.

    Wish you all the best.

  • Hi,

    I've written an application for the Raspberry Pi to do vibration measurement in C++. This might be a good starting point to adapt it to your needs. The source code can be found on: https://github.com/jolau/VibrationDAQ

    Best,
    Jonas

  • Hello . I know my reply is too late, but thought it might be useful for others who run into the same issue later.

    For me too, all the reads are always zero. I have also check my SPI signals (SCK, CS and MOSI) and everythign is perfectly fine. But I tried resetting the sensor by holding the RESETn pin low for enough time (Check datasheet for exact timing) and then performed SPI transactions. I was successfully able to communicate with the sensor.

    I hope this helps anyone.

    Thanks & Regards.