cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F429ZI SPI TX Using DMA: SCL Output Timing Wrong, Clock Polarity Generated Wrong

Elliott99
Associate II

I generated code using CubeMX that should permit me to transmit over SPI1 using DMA for RX and TX, as well as CPOL = 1 and CPHA = 1. I very slightly modified the auto generated CubeMX code but see nothing in the SPI CR1 or CR2 registers, nor the DMA2 registers, that would indicate I have made a configuration mistake when modifying the code slightly. My init functions are here:

void STM32F4xx_SPI_Mgr_Init(void) { hspi.Instance = SPI1; hspi.Init.Mode = SPI_MODE_MASTER; hspi.Init.Direction = SPI_DIRECTION_1LINE; hspi.Init.DataSize = SPI_DATASIZE_8BIT; hspi.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi.Init.CLKPhase = SPI_PHASE_2EDGE; hspi.Init.NSS = SPI_NSS_HARD_OUTPUT; hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi.Init.TIMode = SPI_TIMODE_DISABLE; hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi.Init.CRCPolynomial = 10; // enable DMA RX/TX interrupts HAL_NVIC_SetPriority(SPI_TX_DMA_IRQn, 5, 0); HAL_NVIC_EnableIRQ(SPI_TX_DMA_IRQn); HAL_NVIC_SetPriority(SPI_RX_DMA_IRQn, 5, 0); HAL_NVIC_EnableIRQ(SPI_RX_DMA_IRQn); if (HAL_SPI_Init(&hspi) != HAL_OK) { Error_Handler() } }


And the _msp function called within HAL_SPI_Init():

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hspi->Instance == SPI_CTRL_BLOCK) { __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIO_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; HAL_GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct); hdma_spi_rx.Instance = DMA2_Stream0; hdma_spi_rx.Init.Channel = DMA_CHANNEL_3; hdma_spi_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_spi_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_spi_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_spi_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_spi_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_spi_rx.Init.Mode = DMA_NORMAL; hdma_spi_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_spi_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_spi_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(hspi,hdmarx,hdma_spi_rx); /* SPI1_TX Init */ hdma_spi_tx.Instance = DMA2_Stream3; hdma_spi_tx.Init.Channel = DMA_CHANNEL_3; hdma_spi_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_spi_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_spi_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_spi_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_spi_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_spi_tx.Init.Mode = DMA_NORMAL; hdma_spi_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_spi_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_spi_tx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(hspi,hdmatx,hdma_spi_tx); } }
View more




I attempt to transmit two bytes, 0x30 and 0x00 like so:

static uint8_t spiMsgBuffer[MAX_SPI_TRANSMISSION_LEN]; SPI_HandleTypeDef hspi; STM32F4xx_SPI_Mgr_Init(); spiMsgBuffer[0] = 0x30; spiMsgBuffer[1] = 0x00; HAL_SPI_Transmit_DMA(&hspi, (uint8_t *)&spiMsgBuffer, 2);


My transmission is all warped. The SCL is generated wrong (it does not start from a high state, and I have CPOL = 1, and there are not enough pulses for 16 bits) and the NSS pin seems to deassert for some reason:

mangledSPI.png
Any help on what is going here would be appreciated. Thanks!


2 REPLIES 2
TDK
Guru

A few things happening here:

  • SCL will be low before the pin is initialized and the peripheral is enabled. Once initialized/enabled, it goes high. Sending a dummy transfer of 1 byte (with CS high) will enable it, among other ways.
  • Your logic analyzer sampling rate is too slow to see all 16 clocks.
  • Not sure why the NSS pin goes high, perhaps noise, but it's best to control this pin manually. The peripheral will set it low and keep it low--not very useful. Better to make it a GPIO pin, set low before transaction and high afterwards.
If you feel a post has answered your question, please click "Accept as Solution".

Thank you.

I reconfigured the GPIO pin that was previously being used as the NSS pin as a GPIO output, and set the pin LOW when the call to transmit begins. I ensure that the GPIO pin now being used does not go high until the DMA transfer complete callback executes as I have transfer complete interrupts enabled. I mediate this through the use of a binary semaphore which is taken before the call to transmit, and is released once the transfer completes in the ISR like so:

uint8_t STM32F4xx_SPI_Mgr_Write(uint8_t *pTxBuffer, uint16_t dataLen) { uint8_t retVal; xSemaphoreTake(spiTransactionCompleteSemaphore, portMAX_DELAY); DEASSERT_PIN(SPI_GPIO_PORT, SPI_NSS_PIN); retVal = HAL_SPI_Transmit_DMA(&hspi, pTxBuffer, dataLen); return retVal; }

The ISR that signals a new write can occur since transfer completed and CS has been re-asserted:

void DMA2_Stream3_IRQHandler(void) { /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */ /* USER CODE END DMA2_Stream3_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_spi_tx); ASSERT_PIN(SPI_GPIO_PORT, SPI_CS_PIN); xSemaphoreGive(spiTransactionCompleteSemaphore); /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */ /* USER CODE END DMA2_Stream3_IRQn 1 */ }


You can see in the attached capture the general logic with de-asserting/asserting the CS pin works, albeit there is some additional delay between the assert/deassert and the completion of the transfer due to HAL library calls eating up a 10-20 microseconds which messes with the analyzer:

transferPicture.png

However, for some reason now the call to HAL_SPI_Transmit_DMA hangs indefinitely when the SPI handle typedef used to initialize SPI1, the peripheral in use, is unlocked at the end of the function call. I have highlighted here where this occurs. The errorcode is 0x00 (HAL_OK) when debugging:

txDMAStuck.png