STM32F429ZI SPI TX Using DMA: SCL Output Timing Wrong, Clock Polarity Generated Wrong
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-08-22 02:08 PM
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:
Any help on what is going here would be appreciated. Thanks!
- Labels:
-
SPI
-
STM32F4 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-08-22 02:12 PM - edited ‎2024-08-22 02:12 PM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-08-23 01:32 PM
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:
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: