cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U535 GPDMA LLI and SPI what I'm doing wrong

calka
Associate II

Hi,
I have a STM32U535 microcontroller, I would like to send data to the display via SPI using GPDMA with LLI. The Frame buffer is about 150kB, so due to the fact that in a single transfer I can send max. 64kB of data, I want to divide it into three LLI nodes create a queue and send via SPI.
For the test, I created two LLI nodes in CubeMX:

DMA_NodeTypeDef YourNodeName;
DMA_QListTypeDef YourQueueName;
DMA_NodeTypeDef YourNodeName2;

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t ScrAdrPart1[] = {0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,
		0xAD,0xAD,0xAD,0xAD,0xAD,0xAD};
uint16_t DataSizePart1 = 20;

uint8_t ScrAdrPart2[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
		0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
		0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
		0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
		0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
		0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD};
uint16_t DataSizePart2 = 100;

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/**
  * @brief  DMA Linked-list YourQueueName configuration
  * @PAram  None
  * @retval None
  */
HAL_StatusTypeDef MX_YourQueueName_Config(void)
{
  HAL_StatusTypeDef ret = HAL_OK;
  /* DMA node configuration declaration */
  DMA_NodeConfTypeDef pNodeConfig;

  /* Set node configuration ################################################*/
  pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
  pNodeConfig.Init.Request = GPDMA1_REQUEST_SPI2_TX;
  pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
  pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
  pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
  pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
  pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
  pNodeConfig.Init.SrcBurstLength = 1;
  pNodeConfig.Init.DestBurstLength = 1;
  pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  pNodeConfig.Init.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
  pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
  pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
  pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
  pNodeConfig.SrcAddress = (uint32_t) ScrAdrPart1;
  pNodeConfig.DstAddress = (uint32_t) &SPI2->TXDR;
  pNodeConfig.DataSize = DataSizePart1;

  /* Build YourNodeName Node */
  ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &YourNodeName);

  /* Insert YourNodeName to Queue */
  ret |= HAL_DMAEx_List_InsertNode_Tail(&YourQueueName, &YourNodeName);

  /* Set node configuration ################################################*/
  pNodeConfig.SrcAddress = ScrAdrPart2;
  pNodeConfig.DstAddress = &SPI2->TXDR;
  pNodeConfig.DataSize = DataSizePart2;

  /* Build YourNodeName2 Node */
  ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &YourNodeName2);

  /* Insert YourNodeName2 to Queue */
  ret |= HAL_DMAEx_List_InsertNode_Tail(&YourQueueName, &YourNodeName2);

   return ret;
}

I called the MX_YourQueueName_Config(); function in main after initialization to create nodes and a queue.

The GPDMA was configured as follows:

DMA_HandleTypeDef handle_GPDMA1_Channel15;

/* GPDMA1 init function */
void MX_GPDMA1_Init(void)
{

  /* USER CODE BEGIN GPDMA1_Init 0 */

  /* USER CODE END GPDMA1_Init 0 */

  /* Peripheral clock enable */
  __HAL_RCC_GPDMA1_CLK_ENABLE();

  /* GPDMA1 interrupt Init */
    HAL_NVIC_SetPriority(GPDMA1_Channel15_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_Channel15_IRQn);

  /* USER CODE BEGIN GPDMA1_Init 1 */

  /* USER CODE END GPDMA1_Init 1 */
  handle_GPDMA1_Channel15.Instance = GPDMA1_Channel15;
  handle_GPDMA1_Channel15.InitLinkedList.Priority = DMA_HIGH_PRIORITY;
  handle_GPDMA1_Channel15.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
  handle_GPDMA1_Channel15.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
  handle_GPDMA1_Channel15.InitLinkedList.TransferEventMode = DMA_TCEM_EACH_LL_ITEM_TRANSFER;
  handle_GPDMA1_Channel15.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_NORMAL;
  if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel15) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel15, DMA_CHANNEL_PRIV) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN GPDMA1_Init 2 */

  /* USER CODE END GPDMA1_Init 2 */

}

SPI as follows:

void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 0x7;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  hspi2.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  hspi2.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
  if (HAL_SPIEx_SetConfigAutonomousMode(&hspi2, &HAL_SPI_AutonomousMode_Cfg_Struct) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

All of the above generated from CubeMX.

Then in the main I tried to start transmission with such code by observing CLK on SPI2 with a triggered oscilloscope, unfortunately, in this case the transmission is not started interrupts are not generated.

extern DMA_NodeTypeDef YourNodeName;
extern DMA_QListTypeDef YourQueueName;
extern uint8_t ScrAdrPart1[] ;
extern uint16_t DataSizePart1;
extern uint16_t DataSizePart2;

void HAL_DMAEx_List_XferCpltCallback(DMA_HandleTypeDef *hdma)
{
    // Tutaj akcja po zakończeniu transferu całej listy
    // Np. dla LVGL:
    ;//lv_disp_flush_ready(DISPLAY_returnPnt()->lv_display);
    //CS_HIGH();
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_GPDMA1_Init();
  MX_ICACHE_Init();
  MX_SPI2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_OCTOSPI1_Init();
  MX_LPUART1_UART_Init();
  MX_I2C1_Init();
  MX_TIM1_Init();
  MX_TIM8_Init();
  MX_DCACHE1_Init();
  MX_FLASH_Init();
  /* USER CODE BEGIN 2 */
  MX_YourQueueName_Config();

  //HAL_DMAEx_List_ConvertQToDynamic(&YourQueueName);
  HAL_StatusTypeDef status = HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel15, &YourQueueName);
  if (status != HAL_OK)
  	;
      	;
     __HAL_LINKDMA(&hspi2, hdmatx, handle_GPDMA1_Channel15);

     handle_GPDMA1_Channel15.XferCpltCallback = HAL_DMAEx_List_XferCpltCallback;
     status = HAL_DMAEx_List_Start_IT(&handle_GPDMA1_Channel15);
     if (status != HAL_OK)
     	;
     while (1)
     {
       /* USER CODE END WHILE */

       /* USER CODE BEGIN 3 */
     }
     /* USER CODE END 3 */
   }

If, on the other hand, it uses the function not HAL_DMAEx_List_Start_IT but HAL_SPI_Transmit_DMA(&hspi2, ScrAdrPart1, DataSizePart1) to send;
SPI only sends data from the first node and does not automatically switch to the second.

How, then, should GPDMA SPI and ILL be configured, or how should the transmission be started to ensure that the data configured in all nodes of the list is transmitted without software intervention.

Greetings, calka.

 

 

 

5 REPLIES 5
calka
Associate II

I also added SPI configuration elements with DMA to the main function, also without any effect, no node is sent via SPI:

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern DMA_NodeTypeDef YourNodeName;
extern DMA_QListTypeDef YourQueueName;
extern uint8_t ScrAdrPart1[] ;
extern uint16_t DataSizePart1;
extern uint16_t DataSizePart2;

void HAL_DMAEx_List_XferCpltCallback(DMA_HandleTypeDef *hdma)
{
    // Tutaj akcja po zakończeniu transferu całej listy
    // Np. dla LVGL:
    ;//lv_disp_flush_ready(DISPLAY_returnPnt()->lv_display);
    //CS_HIGH();
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_GPDMA1_Init();
  MX_ICACHE_Init();
  MX_SPI2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_OCTOSPI1_Init();
  MX_LPUART1_UART_Init();
  MX_I2C1_Init();
  MX_TIM1_Init();
  MX_TIM8_Init();
  MX_DCACHE1_Init();
  MX_FLASH_Init();
  /* USER CODE BEGIN 2 */
  MX_YourQueueName_Config();

  //HAL_DMAEx_List_ConvertQToDynamic(&YourQueueName);
  HAL_StatusTypeDef status = HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel15, &YourQueueName);
  if (status != HAL_OK)
  	;

     __HAL_LINKDMA(&hspi2, hdmatx, handle_GPDMA1_Channel15);
/////////////////////
     SET_BIT(hspi2.Instance->CFG1, SPI_CFG1_TXDMAEN);
     __HAL_SPI_ENABLE(&hspi2);
//////////////////////
     handle_GPDMA1_Channel15.XferCpltCallback = HAL_DMAEx_List_XferCpltCallback;
     status = HAL_DMAEx_List_Start_IT(&handle_GPDMA1_Channel15);
     //HAL_SPI_Transmit_DMA(&hspi2, ScrAdrPart1, DataSizePart1);
     if (status != HAL_OK)
     {
     	;
     }

     while (1)
     {
       /* USER CODE END WHILE */

       /* USER CODE BEGIN 3 */
     }
     /* USER CODE END 3 */
   }
Saket_Om
ST Employee

Hello @calka 

Please refer to the examples below: 

STM32CubeU5/Projects/NUCLEO-U575ZI-Q/Examples/SPI/SPI_FullDuplex_ComDMA_Autonomous_Master at main · STMicroelectronics/STM32CubeU5 · GitHub

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
calka
Associate II

Good Morning,
I'm familiar with these examples. They use only one linked list node in a single queue, and the HAL_SPI_TransmitReceive_DMA function is used to initiate transmission. However, I need at least four list nodes in a single queue.

I reworked a similar solution, initiating transmission using the HAL_SPI_Transmit_DMA(...) function, but then only one node is sent, up to the data length specified as the third argument of the function; the remaining nodes are not transmitted.

Hello @calka 

You can refer to the example Projects/NUCLEO-U575ZI-Q/Examples/UART/UART_TwoBoards_ComDMAlinkedlist and get inspired from it. It uses 3 nodes. 

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om

I analyzed the configuration of the individual nodes in the example and compared it with my configuration. I analyzed the GPDMA settings and compared them with my own; there are no differences in the configuration. Unfortunately, the example refers to UART, so the SPI configuration remains the same as in the example: STM32CubeU5/Projects/NUCLEO-U575ZI-Q/Examples/SPI/SPI_FullDuplex_ComDMA_Autonomous_Master at main · STMicroelectronics/STM32CubeU5 · GitHub.
I have two nodes, and only the first one transmits via SPI DMA; the second 100-byte SPI is silent.
I initiate transmission as in the example.

  HAL_StatusTypeDef status = HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel15, &YourQueueName);
  __HAL_LINKDMA(&hspi2, hdmatx, handle_GPDMA1_Channel15);
     HAL_SPI_Transmit_DMA(&hspi2, ScrAdrPart1, DataSizePart1);

     while (1)
     {
       /* USER CODE END WHILE */

       /* USER CODE BEGIN 3 */

     }