2025-04-28 3:07 AM
Hello,
I'm having trouble establishing a working SPI connection between an STM32U585 and a sensor (LIS3DHH). The transaction looks great on an oscilloscope: Clock, CS, MISO, and MOSI are behaving as expected.
This leads me to suspect that the MCU isn't receiving the data correctly. I'm using the low-level driver, but I've reproduced the same behavior with the HAL driver. It's important to note that I want to use DMA mode for performance reasons.
I suspect a bug in my DMA or SPI configuration. After a transaction, the second byte of my rx buffer contains the value 0x00. When reading the WHO-AM-I register, however, it should contain 0x11 (this value is also output via the MISO line). I would greatly appreciate any suggestions.
Here is the GPIO and SPI configuration (DMA below):
LL_GPIO_InitTypeDef gpio_init_sck_miso_mosi = {0};
gpio_init_sck_miso_mosi.Pin = LL_GPIO_PIN_14 | LL_GPIO_PIN_13 | LL_GPIO_PIN_15;
gpio_init_sck_miso_mosi.Mode =LL_GPIO_MODE_ALTERNATE;
gpio_init_sck_miso_mosi.Speed = LL_GPIO_SPEED_FREQ_HIGH;
gpio_init_sck_miso_mosi.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
gpio_init_sck_miso_mosi.Pull = LL_GPIO_PULL_NO;
gpio_init_sck_miso_mosi.Alternate = LL_GPIO_AF_5;
LL_GPIO_InitTypeDef gpio_init_cs = {0};
gpio_init_cs.Pin = LL_GPIO_PIN_12;
gpio_init_cs.Mode = LL_GPIO_MODE_OUTPUT;
gpio_init_cs.OutputType =LL_GPIO_OUTPUT_PUSHPULL;
gpio_init_cs.Pull = LL_GPIO_PULL_NO;
gpio_init_cs.Speed = LL_GPIO_SPEED_FREQ_LOW;
LL_SPI_InitTypeDef spi_init = {0};
spi_init.TransferDirection = LL_SPI_FULL_DUPLEX;
spi_init.Mode = LL_SPI_MODE_MASTER;
spi_init.DataWidth =LL_SPI_DATAWIDTH_8BIT;
spi_init.ClockPolarity = LL_SPI_POLARITY_HIGH;
spi_init.ClockPhase = LL_SPI_PHASE_2EDGE;
spi_init.NSS =LL_SPI_NSS_SOFT;
spi_init.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16; // 2 MHz
spi_init.BitOrder = LL_SPI_MSB_FIRST;
spi_init.CRCCalculation =LL_SPI_CRCCALCULATION_DISABLE;
spi_init.CRCPoly = 0x7;
Here are my initialization and communication functions:
void spi_initialization(LL_SPI_InitTypeDef *spi_init, LL_GPIO_InitTypeDef *gpio_init_sck_miso_mosi, LL_GPIO_InitTypeDef *gpio_init_cs)
{
LL_RCC_SetSPIClockSource(LL_RCC_SPI1_CLKSOURCE_PCLK2);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);
LL_GPIO_Init(port, gpio_init_sck_miso_mosi);
LL_GPIO_Init(port, gpio_init_cs);
LL_GPIO_SetOutputPin(port, gpio_init_cs->Pin);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPDMA1);
LL_DMA_InitTypeDef DMA_InitStruct = {0};
// TX - Channel 1
DMA_InitStruct.SrcAddress = (uint32_t)tx_buffer;
DMA_InitStruct.DestAddress = LL_SPI_DMA_GetTxRegAddr(SPI1);
DMA_InitStruct.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
DMA_InitStruct.BlkHWRequest = LL_DMA_HWREQUEST_SINGLEBURST;
DMA_InitStruct.DataAlignment = LL_DMA_DATA_ALIGN_ZEROPADD;
DMA_InitStruct.SrcBurstLength = 1;
DMA_InitStruct.DestBurstLength = 1;
DMA_InitStruct.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_BYTE;
DMA_InitStruct.DestDataWidth = LL_DMA_DEST_DATAWIDTH_BYTE;
DMA_InitStruct.SrcIncMode = LL_DMA_SRC_INCREMENT;
DMA_InitStruct.DestIncMode = LL_DMA_DEST_FIXED;
DMA_InitStruct.Priority = LL_DMA_LOW_PRIORITY_HIGH_WEIGHT;
DMA_InitStruct.BlkDataLength = SPI_TX_BUFFER_SIZE;
DMA_InitStruct.TriggerMode = LL_DMA_TRIGM_BLK_TRANSFER;
DMA_InitStruct.TriggerPolarity = LL_DMA_TRIG_POLARITY_MASKED;
DMA_InitStruct.TriggerSelection = 0x00000000U;
DMA_InitStruct.Request = LL_GPDMA1_REQUEST_SPI1_TX;
DMA_InitStruct.TransferEventMode = LL_DMA_TCEM_BLK_TRANSFER;
DMA_InitStruct.SrcAllocatedPort = LL_DMA_SRC_ALLOCATED_PORT0;
DMA_InitStruct.DestAllocatedPort = LL_DMA_DEST_ALLOCATED_PORT0;
DMA_InitStruct.LinkAllocatedPort = LL_DMA_LINK_ALLOCATED_PORT1;
DMA_InitStruct.LinkStepMode = LL_DMA_LSM_FULL_EXECUTION;
DMA_InitStruct.LinkedListBaseAddr = 0x00000000U;
DMA_InitStruct.LinkedListAddrOffset = 0x00000000U;
if (LL_DMA_Init(GPDMA1, LL_DMA_CHANNEL_1, &DMA_InitStruct) == ERROR) return false;
LL_DMA_EnableIT_TC(GPDMA1, LL_DMA_CHANNEL_1);
NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
// RX - Channel 0
DMA_InitStruct.SrcAddress = LL_SPI_DMA_GetRxRegAddr(SPI1);
DMA_InitStruct.DestAddress = (uint32_t)rx_buffer;
DMA_InitStruct.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
DMA_InitStruct.BlkDataLength = SPI_RX_BUFFER_SIZE;
DMA_InitStruct.SrcIncMode = LL_DMA_SRC_FIXED;
DMA_InitStruct.DestIncMode = LL_DMA_DEST_INCREMENT;
DMA_InitStruct.Request = LL_GPDMA1_REQUEST_SPI1_RX;
if (LL_DMA_Init(GPDMA1, LL_DMA_CHANNEL_0, &DMA_InitStruct) == ERROR) return false;
LL_DMA_EnableIT_TC(GPDMA1, LL_DMA_CHANNEL_0);
NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);
LL_DMA_SetPeriphRequest(GPDMA1, LL_DMA_CHANNEL_0, LL_GPDMA1_REQUEST_SPI1_RX);
LL_DMA_SetPeriphRequest(GPDMA1, LL_DMA_CHANNEL_0, LL_GPDMA1_REQUEST_SPI1_TX);
LL_SPI_Init(SPI1, spi_init);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_Enable(SPI1);
tx_event_flags_create(&spi_event_flags, "SPI Flags");
}
// IRQ-Handler
void SPI_DMA_ReceiveIRQ_Handler(void)
{
if (LL_DMA_IsActiveFlag_TC(GPDMA1, LL_DMA_CHANNEL_0))
{
LL_DMA_ClearFlag_TC(GPDMA1, LL_DMA_CHANNEL_0);
// set event flag RX_COMPLETE
tx_event_flags_set(&spi_event_flags, SPI_EVENT_RX_COMPLETE, TX_OR);
}
}
void SPI_DMA_TransmitIRQ_Handler(void)
{
if (LL_DMA_IsActiveFlag_TC(GPDMA1, LL_DMA_CHANNEL_1))
{
LL_DMA_ClearFlag_TC(GPDMA1, LL_DMA_CHANNEL_1);
}
}
// Read register function:
uint8_t readSPI(uint8_t register)
{
tx_buffer[0] = register;
tx_buffer[1] = 0x00;
LL_GPIO_ResetOutputPin(GPIOE, LL_GPIO_PIN_12); // CS low
LL_DMA_ConfigAddresses(GPDMA1, LL_DMA_CHANNEL_0, LL_SPI_DMA_GetRxRegAddr(SPI1), (uint32_t)&rx_buffer[0]);
LL_DMA_SetBlkDataLength(GPDMA1, LL_DMA_CHANNEL_0, 2);
LL_DMA_ConfigAddresses(GPDMA1, LL_DMA_CHANNEL_1, (uint32_t)&tx_buffer[0], LL_SPI_DMA_GetTxRegAddr(SPI1));
LL_DMA_SetBlkDataLength(GPDMA1, LL_DMA_CHANNEL_1, 2);
LL_SPI_EnableDMAReq_RX(SPI1);
LL_SPI_EnableDMAReq_TX(SPI1);
// Activate Channel
LL_DMA_EnableChannel(GPDMA1, LL_DMA_CHANNEL_0);
LL_DMA_EnableChannel(GPDMA1, LL_DMA_CHANNEL_1);
// Start SPI
LL_SPI_StartMasterTransfer(SPI1);
// Wait for transmission termination
uint32_t actual_flags;
if (tx_event_flags_get(&spi_event_flags, SPI_EVENT_RX_COMPLETE, TX_OR_CLEAR, &actual_flags, 1000) != TX_SUCCESS)
{
LL_GPIO_SetOutputPin(GPIOE, LL_GPIO_PIN_12);
return 0xFF;
}
LL_DMA_DisableChannel(GPDMA1, LL_DMA_CHANNEL_0);
LL_DMA_DisableChannel(GPDMA1, LL_DMA_CHANNEL_1);
LL_GPIO_SetOutputPin(GPIOE, LL_GPIO_PIN_12);
return rx_buffer[1]; // expected 0x11, got 0x00
}
tx_buffer and rx_buffer are global uint8_t arrays.
Please find a picture of the oscilliscope output attached.