cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H5x and SPI DMA Transfer issue

margce
Associate III

Hi,

I'm trying to use SPI Tx DMA transfer on a Nucleo STM32H563ZI board connected to a LCD module that works on 3wire spi, so just 1Tx line. I know the SPI configuration and code for the LCD driver is OK because everything works if I run in interrupt mode but when trying to send data over SPI DMA it fails.

My SPI and DMA configuration looks like this.

  	__HAL_RCC_GPDMA1_CLK_ENABLE();
	/* SPI1 GPDMA Init */
	target_hgpdma1_spi1_tx.Instance = GPDMA1_Channel0;
    target_hgpdma1_spi1_tx.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    target_hgpdma1_spi1_tx.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    target_hgpdma1_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    target_hgpdma1_spi1_tx.Init.SrcInc = DMA_SINC_INCREMENTED;
    target_hgpdma1_spi1_tx.Init.DestInc = DMA_DINC_FIXED;
    target_hgpdma1_spi1_tx.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    target_hgpdma1_spi1_tx.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    target_hgpdma1_spi1_tx.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
    target_hgpdma1_spi1_tx.Init.SrcBurstLength = 1;
    target_hgpdma1_spi1_tx.Init.DestBurstLength = 1;
    target_hgpdma1_spi1_tx.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_SRC_ALLOCATED_PORT1;
    target_hgpdma1_spi1_tx.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    target_hgpdma1_spi1_tx.Init.Mode = DMA_NORMAL;

    if( HAL_DMA_Init(&target_hgpdma1_spi1_tx) == HAL_OK ){
		__HAL_LINKDMA( &target_hspi1, hdmatx, target_hgpdma1_spi1_tx);

		/* DMA interrupt init */
		HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
		HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

		/*!< SPI configuration */
		target_hspi1.Instance = LCD_SPI;
		target_hspi1.Init.Direction = SPI_DIRECTION_1LINE;
		target_hspi1.Init.Mode = SPI_MODE_MASTER;
		target_hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
		target_hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
		target_hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
		target_hspi1.Init.NSS = SPI_NSS_SOFT;
		target_hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
		target_hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
		target_hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
		target_hspi1.Init.CRCPolynomial = 7;
		target_hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
		target_hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
		HAL_SPI_Init( &target_hspi1 );

		HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
		HAL_NVIC_EnableIRQ(SPI1_IRQn);
	}


The problem I see is that when I call HAL_SPI_Transmit_DMA(&target_hspi1, data, dataSize);  the DMA transfer complete callback gets executed and the EOT interrupt gets enabled but there is no interrupt generated after that and the HAL_SPI_IRQHandler never marks the transmition as completed and the hspi1 state remains as TX_BUSY.  

static void SPI_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
  SPI_HandleTypeDef *hspi = (SPI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

  if (hspi->State != HAL_SPI_STATE_ABORT)
  {
    if (hspi->hdmatx->Mode == DMA_LINKEDLIST_CIRCULAR)
    {
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
      hspi->TxCpltCallback(hspi);
#else
      HAL_SPI_TxCpltCallback(hspi);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
    }
    else
    {
      /* Enable EOT interrupt */
      __HAL_SPI_ENABLE_IT(hspi, SPI_IT_EOT);
    }
  }
}

 

I'm new to the STM32H5x series and I noticed the DMA has new features compared to previous STM32 devices that I've used in the past so I'm probably missing something.

The DMA transmission is 8192bytes.

All IO pins and clock sources have been enabled.

I have written a callback for the TxCpltCallback to set a flag to be able to transmit the next frame, this works for the interrupt mode, but for DMA transfer it never gets called even though the DMA Transfer complete was called

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) 
{
	if (hspi->Instance==DISPL_SPI) {
		Displ_SpiAvailable=1;
	}
}

 

Any hints?

 

Thanks,

Martin

1 ACCEPTED SOLUTION

Accepted Solutions
margce
Associate III

I changed the DMA configuration to

    target_hgpdma1_spi1_tx.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    target_hgpdma1_spi1_tx.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;

 

Instead of HALFWORD and now it works !

Is there anything else I could have got wrong with the new GPDMA?

View solution in original post

2 REPLIES 2
margce
Associate III

I changed the DMA configuration to

    target_hgpdma1_spi1_tx.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    target_hgpdma1_spi1_tx.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;

 

Instead of HALFWORD and now it works !

Is there anything else I could have got wrong with the new GPDMA?

PFlor.2
Senior

I'm having a similar issue on the STM32H563 with the hdmatx->state always staying busy and will not complete.

I have both the src and dest width set as bytes

/* SPI3 init function */
void MX_SPI3_Init(void)
{

  /* USER CODE BEGIN SPI3_Init 0 */

  /* USER CODE END SPI3_Init 0 */

  /* USER CODE BEGIN SPI3_Init 1 */

  /* USER CODE END SPI3_Init 1 */
  hspi3.Instance = SPI3;
  hspi3.Init.Mode = SPI_MODE_MASTER;
  hspi3.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
  hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi3.Init.NSS = SPI_NSS_SOFT;
  hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi3.Init.CRCPolynomial = 0x7;
  hspi3.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  hspi3.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi3.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi3.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi3.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi3.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi3.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  hspi3.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  hspi3.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
  if (HAL_SPI_Init(&hspi3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI3_Init 2 */

  /* USER CODE END SPI3_Init 2 */

}


    /* SPI3 DMA Init */
    /* GPDMA1_REQUEST_SPI3_TX Init */
    handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
    handle_GPDMA1_Channel0.Init.Request = GPDMA1_REQUEST_SPI3_TX;
    handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    handle_GPDMA1_Channel0.Init.Direction = DMA_MEMORY_TO_PERIPH;
    handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_INCREMENTED;
    handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_FIXED;
    handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
    handle_GPDMA1_Channel0.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
    handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
    handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
    handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;
    if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle, hdmatx, handle_GPDMA1_Channel0);

    if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
    {
      Error_Handler();
    }

    /* SPI3 interrupt Init */
    HAL_NVIC_SetPriority(SPI3_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(SPI3_IRQn);
  /* USER CODE BEGIN SPI3_MspInit 1 */

  /* USER CODE END SPI3_MspInit 1 */
  }

 Here's the code calling the SPI with DMA...any ideas?

void ST7789v_Fill_Color(uint16_t color)
{
  uint16_t i;

  ST7789v_SetWindow(0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);

  CS_L();

  uint32_t size = ST7789_WIDTH * ST7789_HEIGHT * 2;
  uint16_t chunk_size;
  uint16_t buffer[size/2];
  uint8_t *buf;

  memset(buffer, color, sizeof(buffer));

  if (size > 0) {
    DC_H();

    LCD_WR_REG(ST7789_CMD_RAMWR);

    while (size > 0) {
      buf = (uint8_t *)buffer;
      chunk_size = size > 8192 ? 8192 : size;

      if (DMA_MIN_SIZE <= size) {
        if (HAL_SPI_Transmit_DMA(&hspi3, buf, chunk_size ) != HAL_OK) {
          Error_Handler();
        }
        while (hspi3.hdmatx->State != HAL_DMA_STATE_READY);
      }
      else {
        if (HAL_SPI_Transmit(&hspi3, buf, chunk_size, HAL_MAX_DELAY) != HAL_OK) {
          Error_Handler();
        }
      }
      buf += chunk_size;
      size -= chunk_size;
    }
  }
  CS_H();
}