STM32F733xx SPI DMA transfer issue.
I've built a custom system using the STM32F733xx and an external ADC (TI ADS8588S) connected over SPI. I have verified that the system works consistently using the STM32 HAL L0_V1.2.8 when transferring data via the blocking/polling method. However, when I configure the SPI to run non-blocking with a linked DMA I can only get one full transfer to complete. Successive transfers fail to produce SCLK as shown below:
This is the SPI and DMA configuration code:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // Needed for Saleae match
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|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(GPIOA, &GPIO_InitStruct);
/* SPI1_RX DMA Init */
__HAL_RCC_DMA2_CLK_ENABLE();
hdma_spi1_rx.Instance = DMA2_Stream2;
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
Error_Handler();
}
HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0); // SPI1 RX - Chan 3
HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx); /*link DMA2 to SPI1 Rx*/
/*DMA 1 / channel 0 / stream 5 sends data to SPI (no GPIO pin existent, only needed for clocking in right number of bytes)*/
__HAL_RCC_DMA2_CLK_ENABLE(); /*DMA clock*/
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; /*from memory to SPI*/
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE; /*no increment on SPI side*/
hdma_spi1_tx.Init.MemInc = DMA_MINC_DISABLE; /*no increment sample memory address*/
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hspi, hdmatx, hdma_spi1_tx); /*link DMA2 to SPI1 Tx*/
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}I am calling for the DMA transfer on 2 lines (TX/RX) so I setup the TX line only to send out dummy data and generate SCLK:
static uint16_t ulDummyWord[16]={0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF};
static uint16_t Adc1RxBuff[16];
int main(void)
{
/* USER CODE BEGIN 1 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(10);
HAL_GPIO_WritePin(ADDevice1.AD_pinMapping->CS_gpioPort, ADDevice1.AD_pinMapping->CS_gpioPin, 0); // select ADC1 for SPI transfer
HAL_SPI_TransmitReceive_DMA(hspi1,(uint8_t *)ulDummyWord,(uint8_t *)Adc1RxBuff, 16); // this only works once
}
}
void DMA2_Stream2_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream2_IRQn 0 */
__HAL_DMA_CLEAR_FLAG(&hdma_spi1_rx,DMA_FLAG_TCIF2_6);
__HAL_DMA_CLEAR_FLAG(&hdma_spi1_rx,DMA_FLAG_HTIF2_6);
__HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_TCIF3_7);
__HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_HTIF3_7);
__HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_FEIF3_7);
HAL_GPIO_WritePin(ADDevice1.AD_pinMapping->CS_gpioPort, ADDevice1.AD_pinMapping->CS_gpioPin, 1);
}The issue is that HAL_SPI_TransmitReceive_DMA call errors out on the 2nd call with HAL_BUSY because hspi->State is HAL_SPI_STATE_BUSY_TX_RX. I thought the root cause was related to SPI Tx sending dummy data and asserting the DMA_FLAG_FEIF3_7 flag but clearing this didn't help.
I cannot see how to reset the SPI1 DMA so that successive transfers can happen. I have suspicions on the status flags (TXE, RXNE) but I cannot see what is wrong.
Thank you in advance.