cancel
Showing results for 
Search instead for 
Did you mean: 

Wrong value in rx buffer after using SPI in DMA mode

KoCT
Associate

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 }
View more

tx_buffer and rx_buffer are global uint8_t arrays.

 

Please find a picture of the oscilliscope output attached.

1 REPLY 1
KORKAD
ST Employee

Hello @KoCT,

Please refer to the LIS3DSH BSP driver available on STM32CubeF4. For one-byte transfer, we recommend using polling or IT transfer.

With Regards,

Anis