cancel
Showing results for 
Search instead for 
Did you mean: 

LIS3MDL not responding over SPI on custom PCB

VictorK
Associate

Hi,

I'm designing a flight controller pcb using several ST mems sensors, (LSM6DSO, LIS3MDL, LPS22HH) and interfacing them to an STM32H743IIT6 microcontroller over a shared SPI bus. I'm on the second iteration of this board, all sensors worked on the first version except the LIS3MDL, this was consistent across multiple boards, which leads me to believe its either a hardware design or a software issue in how I was trying to communicate with it. The behavior I experience with the LIS3MDL is no response at all when any communication is attempted, this is confirmed by scoping the SPI lines.

Below are screenshots from my oscilloscope of an SPI read of register 0x0F. Excuse the poor aliasing on the SCK lines, this was right at the limits of my cheap scope lol. Its not shown here but I also confirmed the CS line goes low before the clock starts and return to high after the read is complete.

VictorK_7-1725716130081.bmp
Yellow: MOSI, Blue: MISO

VictorK_8-1725716140311.bmp

Yellow: MOSI, Blue SCK

Software Version 1

I used CubeMX to configure the project, as well as the MEMS1 libraries for communicating with all ST sensors. The SPI bus configuration is attached below (I have also tried reducing the speed of the bus as low as 312.5KBits/s with no change in behavior).

VictorK_6-1725714206425.png

 

Below is the relevant code for communicating with the LIS3MDL. The same SPI read/write functions are also used with the other mems sensors and behave as expected, additionally this is running on FreeRTOS, which is why the SPI bus includes mutexes (I have also tested the LIS3MDL on its own without any RTOS running with no change in behavior). 
#define READ_MASK 0x80
#define WRITE_MASK 0x7F

HAL_StatusTypeDef SPI_Read_Register(SPI_HandleTypeDef *hspi, osMutexId_t *mspi, GPIO_TypeDef *csPort, uint16_t csPin, uint8_t regAddr, uint8_t *pData, uint16_t size)
{
// Acquire the SPI mutex
osMutexAcquire(*mspi, osWaitForever);

uint8_t buffer[size + 1];

// The register address needs to be OR-ed with the READ_MASK to set the MSB (read command)
buffer[0] = regAddr | READ_MASK;

// Initiate the SPI transmission
HAL_GPIO_WritePin(csPort, csPin, GPIO_PIN_RESET);
uint8_t status = HAL_SPI_TransmitReceive(hspi, buffer, buffer, size + 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(csPort, csPin, GPIO_PIN_SET);

// Check if the transmission was successful
if (status != HAL_OK)
{
// Release the SPI mutex
osMutexRelease(*mspi);

return HAL_ERROR;
}

// Store the received data in pData
for (int i = 0; i < size; i++)
{
pData[i] = buffer[i + 1]; // Skip the first byte (dummy byte)
}

// Release the SPI mutex
osMutexRelease(*mspi);

return HAL_OK;
}

HAL_StatusTypeDef SPI_Write_Register(SPI_HandleTypeDef *hspi, osMutexId_t *mspi, GPIO_TypeDef *csPort, uint16_t csPin, uint8_t regAddr, const uint8_t *pData, uint16_t size)
{
// Acquire the SPI mutex
osMutexAcquire(*mspi, osWaitForever);

uint8_t buffer[size + 1];

// The register address needs to be OR-ed with the WRITE_MASK to indicate a write operation
buffer[0] = regAddr & WRITE_MASK;

// Copy the data to be written into the txBuffer
for (int i = 0; i < size; i++)
{
buffer[i + 1] = pData[i];
}

// Initiate the SPI transmission
HAL_GPIO_WritePin(csPort, csPin, GPIO_PIN_RESET);
uint8_t status = HAL_SPI_Transmit(hspi, buffer, size + 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(csPort, csPin, GPIO_PIN_SET);

// Release the SPI mutex
osMutexRelease(*mspi);

// Check if the transmission was successful
if (status != HAL_OK)
{
return HAL_ERROR;
}

return HAL_OK;
}
MagData mag_data;

LIS3MDL_Object_t lis3mdl;

int32_t Write_LIS3MDL(void *handle, uint8_t reg, uint8_t *data, uint16_t len)
{
    reg |= 0x40; // Set the multi-write bit (0bx1xxxxxx)
    return SPI_Write_Register(&hspi1, &spi1MutexHandle, MAG_CS_GPIO_Port, MAG_CS_Pin, reg, data, len);
}

int32_t Read_LIS3MDL(void *handle, uint8_t reg, uint8_t *data, uint16_t len)
{
    reg |= 0x40; // Set the multi-read bit (0bx1xxxxxx)
    return SPI_Read_Register(&hspi1, &spi1MutexHandle, MAG_CS_GPIO_Port, MAG_CS_Pin, reg, data, len);
}

void StartMagTask(void *argument)
{
    mag_data.active = false;

    lis3mdl.Ctx.handle = &hspi1;
    lis3mdl.Ctx.write_reg = Write_LIS3MDL;
    lis3mdl.Ctx.read_reg = Read_LIS3MDL;
    lis3mdl.IO.BusType = LIS3MDL_SPI_4WIRES_BUS;

    uint8_t id = 0, rst = 0;

    LIS3MDL_ReadID(&lis3mdl, &id);
    lis3mdl_reset_set(&lis3mdl.Ctx, PROPERTY_ENABLE);

    do
    {
        lis3mdl_reset_get(&lis3mdl.Ctx, &rst);
    } while (rst);

    LIS3MDL_Init(&lis3mdl);

    // Magnetometer configuration
    LIS3MDL_MAG_Enable(&lis3mdl);

    // Set the data rate to 155 Hz in ultra-high-performance mode
    lis3mdl_data_rate_set(&lis3mdl.Ctx, LIS3MDL_UHP_155Hz);

    // Set the full-scale to 4 Gauss
    LIS3MDL_MAG_SetFullScale(&lis3mdl, LIS3MDL_4_GAUSS);

    osDelay(10);

    LIS3MDL_Axes_t mag;
    bool status = false;

    while (true)
    {
        Read_LIS3MDL(&lis3mdl, 0x0F, &id, 1);
        status = LIS3MDL_MAG_GetAxes(&lis3mdl, &mag) == LIS3MDL_OK;

        // Check if the magnetometer is active
        if (!status || id != LIS3MDL_ID)
        {
            mag_data.active = false;
            osDelay(100);
            continue;
        }

        mag_data.magnetic_field = Vector{(float)mag.x, (float)mag.y, (float)mag.z};
        mag_data.active = true;

        // printf("ID: 0x%02X Magnetic Field: %.2f %.2f %.2f\n", id, *mag_data.magnetic_field.x, *mag_data.magnetic_field.y, *mag_data.magnetic_field.z);

        osDelay(10);
    }
}


Hardware Version 1

The schematic and pcb layout for the first iteration are below. The schematic for the magnetometer was taken directly from the datasheet with the 1uF capacitor substituted for a 2.2uF capacitor to reduce the BOM. The pcb is a 4 layer board with a signal-gnd-pwr-signal stack up. The SPI bus is shared between 3 sensors and an SD card (which was never in use, and has since been moved to its own SDMMC bus). The trace lengths for each SPI line is around 6cm.

VictorK_2-1725713533727.png

VictorK_3-1725713626479.png

 

Hardware Version 2
Below is the schematic and pcb layout for the new board (I have also attached the full schematic in PDF format). The main changes from the first iteration is the change from a 2.2uF bulk decoupling capacitor to 1uF to better match the datasheet, reduced SPI bus trace lengths (<2cm each), and I tried to space out the SPI lines better to avoid crosstalk, especially with the SCK line.

VictorK_1-1725713031377.png

VictorK_0-1725712961793.png

I've spent several weeks debugging and researching this issue, and my main goal right now is to confirm whether this is a hardware or software issue before ordering the the version 2 boards.

Any help is appreciated, let me know if you require any more info.

Best Regards,
Victor

2 REPLIES 2
TDK
Guru

> The main changes from the first iteration is the change from a 2.2uF bulk decoupling capacitor to 1uF to better match the datasheet, reduced SPI bus trace lengths (<2cm each), and I tried to space out the SPI lines better to avoid crosstalk, especially with the SCK line.

None of those changes are going to cause the chip not to work. Problem lies elsewhere. You seem to have a good grasp on how the SPI works based on the rev1 board working, and your scope pictures. If CS/MOSI/SCK lines show a signal, and the chip is powered, it should respond. This is even more likely as other chips on the same SPI bus are responding. I assume you verified that the chip is powered by measuring voltage across caps? Voltage on C1 isn't specified but you should see something and be able to compare it to the rev1 version.

So if those aren't the problem, what is left? Primarily bad soldering on the board. Do you have other boards you can test? Can you verify the soldering connections? Hard to do on these sorts of chips where the pad is only on the bottom. Do you have the tools or skills to reflow the chip?

If you feel a post has answered your question, please click "Accept as Solution".

Thanks for the reply,

I've measured the voltage on the VDD and VDD_IO caps and confirmed they are 3.3V, also checked C1 its very close to 0V (~0.021V, probably inaccuracy in measurement). I have a second identical board that has the same issues as the first one that I have also tested. I don't have the tools to properly check the soldering connections on the pads, as for reflowing the chip, the closest thing I have is a hot air gun, and for such a small chip I think I would probably do a worse job than the PCB fab :grinning_face_with_sweat:.