Post Go back to editing

HMC1023 SPI read is returning incorrect values

I have a circuit with two HMC1023 filters with separate chip selects, being driven by the same SPI lines from a STM32. I am bitbanging as the same SPI lines are used elsewhere.

After setting a register, I perform a second SPI read/write cycle with an invalid Chip_ID so that the SDO line becomes tri-stated. During the second read/write cycle the correct register value is output on the SDO line.

On a subsequent read operation, I set register 0 to the register address I want to read, and then perform the 2nd read cycle to retrieve the register value. However, on this attempt the register values are incorrect. 

For example, setting a value of 0x4 for the amp and driver bias to register 2 returns a value of 0x6. Similarly setting 0x5 returns 0x7, but setting 0x6 and 0x7 correctly returns 0x6 and 0x7 respectively. A similar thing happens on the calibration register. These have been checked by observation of the signals with a cro.

A search of the web and AD site has not revealed any examples for driver code.

Has anyone seen this, or have any example driver code they are willing to share.

Regards, David

Parents
  • Can you provide the data written and the data read in the exact sequence that you are doing? 

    "On a subsequent read operation, I set register 0 to the register address I want to read, and then perform the 2nd read cycle to retrieve the register value. However, on this attempt the register values are incorrect."

    What if you do this twice?  Do the subsequent read/write cycles fetch the correct data?

  • Any success on SPI readback?

  • Apologies for a slow response, this task went on the back burner for a while.

    I believe I've sorted it.

    In addition to the two filters sharing the SPI line, there are others, including a HMC960 variable gain amplifier.

    I isolated the supply to the other devices and managed to consistently get good SPI reads from one HMC1023.

    Here's the write and read code. It's not pretty, and some refactoring needs to be done.

    In our application, commands are being received from an external source, so we don't know what command is coming next, so after every write or read, the SDO line is tri-stated.

    For a write, I send the 24 data bits, followed by the 5 address bits, and the b101  chip address. I then repeat the write but send a chip address of b000. In accordance with the data sheet this turns off the SDO line.

    For reads, in accordance with the data sheet, I write the register address I want to read to register 0, then do the same again to read the data from the SDO line, however this time, I set the chip address to b000 to tri-state the SDO line at the end of the transaction.

    bool FILT1_Write(uint32_t register_address, uint32_t register_value, uint8_t number_of_bits)
    {
        (void)number_of_bits;
        // Setup the mode for HMC1023
        // SEN: active low
        // SCK: SDI is read on rising edge
        // MISO: HMC1023 Data is set on rising edge
        // Format: 24 data bits, 5 register address bits, 3 chip address (b101).
        // MSB first
        uint32_t value = register_value;
        uint32_t address = register_address;
    
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((value & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            value <<= 1;
        }
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // if address[4] high, set MOSI high, else set MOSI low
            if ((address & (1<<4)) == (1<<4))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        value = register_value;
        address = register_address;
        // This write is to turn off the SDO line by sending the wrong chip address
        // Values and register address shouldn't matter
        // Set SEN Low
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            if ((value & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            value <<= 1;
        }
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // if address[4] high, set MOSI high, else set MOSI low
            if ((address & (1<<4)) == (1<<4))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        address = register_address;
        FILT1_Read(0x01, &value, number_of_bits);
        FILT1_Read(address, &value, number_of_bits);
        return true;
    }
    
    bool FILT1_Read(uint32_t register_address, uint32_t *register_value, uint8_t number_of_bits)
    {
        (void)number_of_bits;
        // Setup the mode for HMC1023
        // SEN: active low
        // SCK: SDI is read on rising edge
        // MISO: HMC1023 Data is set on rising edge
        // Format: 24 data bits, 5 register address bits, 3 chip address (b101).
        // MSB first
        uint32_t address = register_address;
        uint32_t value = 0;
    
        // Send the Register value to register address 0x00
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((address & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        }
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
    
        address = register_address;
        value = 0;
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Set CS_FILT1 Low
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((address & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            address <<= 1;
            value <<= 1;
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            // if MISO high, set value[i] high, else set value[i] low
            if (HAL_GPIO_ReadPin(SPI1_MISO_GPIO_Port, SPI1_MISO_Pin) != 0x00u)
            {
                value |= 1;
            }
        }
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        }
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set CS_FILT1 High
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        *register_value = value;
        return true;
    }
    

    And here's a picture.

    We can see the line being tri-stated by the slight dip in the analog trace (pull ups are on).

    I compared the HMC1023 and HMC960 data sheets as their SPI protocols are very similar.

    The HMC1023 datasheet has this small instruction which I had coded for the HMC1023s.

    The HMC960 doesn't, although it does mention the SDO being driven when the chip is active and addressed.

    On a hunch, I did the same thing with HMC960 as both HMC1023s. I send a command with the wrong chip address. Lo and behold, with the limited testing performed so far, all registers are working correctly.

    It would appear a small omission in the datasheet put me on the wrong track for a while.

    Regards,

    David

Reply
  • Apologies for a slow response, this task went on the back burner for a while.

    I believe I've sorted it.

    In addition to the two filters sharing the SPI line, there are others, including a HMC960 variable gain amplifier.

    I isolated the supply to the other devices and managed to consistently get good SPI reads from one HMC1023.

    Here's the write and read code. It's not pretty, and some refactoring needs to be done.

    In our application, commands are being received from an external source, so we don't know what command is coming next, so after every write or read, the SDO line is tri-stated.

    For a write, I send the 24 data bits, followed by the 5 address bits, and the b101  chip address. I then repeat the write but send a chip address of b000. In accordance with the data sheet this turns off the SDO line.

    For reads, in accordance with the data sheet, I write the register address I want to read to register 0, then do the same again to read the data from the SDO line, however this time, I set the chip address to b000 to tri-state the SDO line at the end of the transaction.

    bool FILT1_Write(uint32_t register_address, uint32_t register_value, uint8_t number_of_bits)
    {
        (void)number_of_bits;
        // Setup the mode for HMC1023
        // SEN: active low
        // SCK: SDI is read on rising edge
        // MISO: HMC1023 Data is set on rising edge
        // Format: 24 data bits, 5 register address bits, 3 chip address (b101).
        // MSB first
        uint32_t value = register_value;
        uint32_t address = register_address;
    
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((value & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            value <<= 1;
        }
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // if address[4] high, set MOSI high, else set MOSI low
            if ((address & (1<<4)) == (1<<4))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        value = register_value;
        address = register_address;
        // This write is to turn off the SDO line by sending the wrong chip address
        // Values and register address shouldn't matter
        // Set SEN Low
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            if ((value & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            value <<= 1;
        }
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // if address[4] high, set MOSI high, else set MOSI low
            if ((address & (1<<4)) == (1<<4))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        address = register_address;
        FILT1_Read(0x01, &value, number_of_bits);
        FILT1_Read(address, &value, number_of_bits);
        return true;
    }
    
    bool FILT1_Read(uint32_t register_address, uint32_t *register_value, uint8_t number_of_bits)
    {
        (void)number_of_bits;
        // Setup the mode for HMC1023
        // SEN: active low
        // SCK: SDI is read on rising edge
        // MISO: HMC1023 Data is set on rising edge
        // Format: 24 data bits, 5 register address bits, 3 chip address (b101).
        // MSB first
        uint32_t address = register_address;
        uint32_t value = 0;
    
        // Send the Register value to register address 0x00
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((address & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            address <<= 1;
        }
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        }
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set SEN high
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
    
        address = register_address;
        value = 0;
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // Set CS_FILT1 Low
        GPIO_CLEAR(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        // for 24 value bits, (i: 23 -> 0)
        for (int8_t i = 23; i >= 0; --i)
        {
            // if value[i] high, set MOSI high, else set MOSI low
            if ((address & (1<<23)) == (1<<23))
            {
                GPIO_SET(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            else
            {
                GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
            }
            address <<= 1;
            value <<= 1;
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
            // if MISO high, set value[i] high, else set value[i] low
            if (HAL_GPIO_ReadPin(SPI1_MISO_GPIO_Port, SPI1_MISO_Pin) != 0x00u)
            {
                value |= 1;
            }
        }
        GPIO_CLEAR(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        // for 5 address bits, (i: 4 -> 0)
        for (int8_t i = 4; i >= 0; --i)
        {
            // Pulse SCK
            GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        }
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Pulse SCK
        GPIO_PULSE(SPI1_CLK_GPIO_Port, SPI1_CLK_Pin);
        // Set CS_FILT1 High
        GPIO_SET(CS_FILTER1_GPIO_Port, CS_FILTER1_Pin);
        *register_value = value;
        return true;
    }
    

    And here's a picture.

    We can see the line being tri-stated by the slight dip in the analog trace (pull ups are on).

    I compared the HMC1023 and HMC960 data sheets as their SPI protocols are very similar.

    The HMC1023 datasheet has this small instruction which I had coded for the HMC1023s.

    The HMC960 doesn't, although it does mention the SDO being driven when the chip is active and addressed.

    On a hunch, I did the same thing with HMC960 as both HMC1023s. I send a command with the wrong chip address. Lo and behold, with the limited testing performed so far, all registers are working correctly.

    It would appear a small omission in the datasheet put me on the wrong track for a while.

    Regards,

    David

Children
No Data