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

  }

}




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