Skip to main content
Visitor II
August 22, 2024
Question

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

  • August 22, 2024
  • 1 reply
  • 709 views

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);

 }

}




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!


    This topic has been closed for replies.

    1 reply

    Super User
    August 22, 2024

    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.
    Elliott99Author
    Visitor II
    August 23, 2024

    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