2025-11-04 6:37 PM
Hello,
I am using the Nucleo U575ZI-Q board. I need to use GPDMA synchronized with a timer to set multiple GPIO pins synchronously, perform SPI transmission, and configure PWM periods.
I have found a working combination: software request + trigger TRGO. However, TRGO is only available for TIM15 and TIM2, which limits its versatility. Ideally, I would like to use hardware requests (such as REQUEST_TIM15_UP).
Set TIM15 to output TRGO. Configure GPDMA1_CHANNEL7 with LinkedList for DMA_REQUEST_SW and set the trigger to GPDMA1_TRIGGER_TIM15_TRGO. The direction should be set to Memory-to-Memory. Specify the source as memory and the destination as GPIOA->BSRR or SPI2->TXDR. This will allow you to generate the waveform shown in the attached oscilloscope.
uint32_t gpio_node_data[4] = {
0x00000100U, // PA8 = High -> bit8 set (1 << 8)
0x01000000U, // PA8 = Low -> bit24 set (1 << (8 + 16))
0xFFFFFFFF,
0xFFFFFFFF
};
uint32_t spi2_node_data[4] = {
0xAAAAAAAA,
0x55555555,
0x11111111,
0xFFFFFFFF
};
HAL_StatusTypeDef MX_TimingSPI_GPIO_O_I_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 = DMA_REQUEST_SW;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_EACH_LL_ITEM_TRANSFER;
pNodeConfig.TriggerConfig.TriggerMode = DMA_TRIGM_BLOCK_TRANSFER;
pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
pNodeConfig.TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM15_TRGO;
pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
pNodeConfig.DataSize = sizeof(uint32_t);
/* Build GPIO_High_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_High_Node);
/* Insert GPIO_High_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
/* Set node configuration ################################################*/
pNodeConfig.Init.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[1];
/* Build GPIO_Low_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_Low_Node);
/* Insert GPIO_Low_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_Low_Node);
/* Set node configuration ################################################*/
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
pNodeConfig.SrcAddress = (uint32_t)&spi2_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(SPI2->TXDR);
/* Build SPI2_TX_n1 Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_TX_n1);
/* Insert SPI2_TX_n1 to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &SPI2_TX_n1);
/* Set node configuration ################################################*/
pNodeConfig.SrcAddress = (uint32_t)&spi2_node_data[1];
/* Build SPI2_TX_n2 Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_TX_n2);
/* Insert SPI2_TX_n2 to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &SPI2_TX_n2);
/* Set node configuration ################################################*/
pNodeConfig.SrcAddress = (uint32_t)&spi2_node_data[2];
/* Build SPI2_TX_n3 Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_TX_n3);
/* Insert SPI2_TX_n3 to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &SPI2_TX_n3);
ret |= HAL_DMAEx_List_SetCircularModeConfig(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
return ret;
}
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_2);
SPI2->CR1 |= SPI_CR1_SPE;
SPI2->CR1 |= SPI_CR1_CSTART;
MX_TimingSPI_GPIO_O_I_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel7, &TimingSPI_GPIO_O_I);
HAL_DMAEx_List_Start(&handle_GPDMA1_Channel7);
---
ChatGPT advised to do the following, but it does not work (it seems that GPDMA1 is not activated).
// gpio_node_data should be non-const
static uint32_t gpio_node_data[2] = { 0x00000100U, 0x01000000U, 0, 0 };
// Node configuration (only changes are shown)
pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP; // HW request
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH; // Write to PERIPH
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.DataSize = sizeof(uint32_t); // 4
pNodeConfig.TriggerConfig.TriggerMode = DMA_TRIGM_BLOCK_TRANSFER;
pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
// TriggerSelection is unnecessary when using DMAMUX, or align it with DMAMUX settings
// pNodeConfig.TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM15_TRGO; // Usually left to DMAMUX
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
Disable the trigger: pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
Hard request and: pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP;
Direction: Memory to Memory pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
Set the actual destination to memory:
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&gpio_node_data[3];
→ GPDMA operates, and transfer occurs between memory.
Disable the trigger: pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
Hard request and: pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP;
Direction: Memory to Memory pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
Set the actual destination to peripheral:
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
→ Writing to the peripheral does not occur (GPIO does not change).
Disable the trigger: pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
Hard request and: pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP;
Direction: Memory to Peripheral pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
Set the actual destination to peripheral:
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
→ Writing to the peripheral does not occur (GPIO does not change).
Solved! Go to Solution.
2025-11-10 12:51 AM
Hello DHIF Khaled,
The effect of __HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE); was dramatic! I was able to execute the intended process. For synchronizing with TIM15, I used GPDMA1_REQUEST_TIM15_UP only for the first instance, and for the remaining nodes, I used DMA_REQUEST_SW, which allowed the DMA to execute all at once (though I'm not sure if this is the correct usage).
TIM15 executes at twice the frequency compared to other TIMs for PWM waveform generation, and it is used for 96-bit SPI input/output and for switching the PWM duty of other TIMs.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : linked_list.c
* Description : This file provides code for the configuration
* of the LinkedList.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "linked_list.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
DMA_NodeTypeDef GPIO_High_Node;
DMA_QListTypeDef TimingSPI_GPIO_O_I;
DMA_NodeTypeDef GPIO_Low_Node;
DMA_NodeTypeDef SPI2_TX;
DMA_NodeTypeDef SPI2_RX_Node;
DMA_QListTypeDef SPI2_RX_I;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
extern uint32_t gpio_node_data[];
extern uint32_t spi2_node_data[];
extern uint32_t spi2_read_data[];
/* USER CODE END PM */
/**
* @brief DMA Linked-list SPI2_RX_I configuration
* @PAram None
* @retval None
*/
HAL_StatusTypeDef MX_SPI2_RX_I_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_RX;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_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)&(SPI2->RXDR);
pNodeConfig.DstAddress = (uint32_t)&spi2_read_data[0];
pNodeConfig.DataSize = 20*3*sizeof(uint32_t);
/* Build SPI2_RX_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_RX_Node);
/* Insert SPI2_RX_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&SPI2_RX_I, &SPI2_RX_Node);
ret |= HAL_DMAEx_List_SetCircularMode(&SPI2_RX_I);
return ret;
}
/**
* @brief DMA Linked-list TimingSPI_GPIO_O_I configuration
* @PAram None
* @retval None
*/
HAL_StatusTypeDef MX_TimingSPI_GPIO_O_I_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_TIM15_UP;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_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)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
pNodeConfig.DataSize = sizeof(uint32_t);
/* Build GPIO_High_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_High_Node);
/* Insert GPIO_High_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
/* Set node configuration ################################################*/
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[1];
/* Build GPIO_Low_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_Low_Node);
/* Insert GPIO_Low_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_Low_Node);
/* Set node configuration ################################################*/
pNodeConfig.Init.Request = DMA_REQUEST_SW;
pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
pNodeConfig.SrcAddress = (uint32_t)&spi2_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(SPI2->TXDR);
pNodeConfig.DataSize = 3*sizeof(uint32_t);
/* Build SPI2_TX Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_TX);
/* Insert SPI2_TX to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &SPI2_TX);
ret |= HAL_DMAEx_List_SetCircularModeConfig(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
return ret;
}
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_TIM15_Init();
MX_TIM8_Init();
MX_SPI2_Init();
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_4);
SPI2->CR1 |= SPI_CR1_SPE;
SPI2->CR1 |= SPI_CR1_CSTART;
SPI2->CFG1 |= SPI_CFG1_RXDMAEN;
MX_SPI2_RX_I_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel6, &SPI2_RX_I);
HAL_DMAEx_List_Start(&handle_GPDMA1_Channel6);
MX_TimingSPI_GPIO_O_I_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel7, &TimingSPI_GPIO_O_I);
HAL_DMAEx_List_Start(&handle_GPDMA1_Channel7);
__HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE);
2025-11-06 2:17 AM
Hello @mikesmith100 ,
Thank you for sharing your CubeMX configuration.
To correctly enable DMA transfer from memory to the GPIOA BSRR register, please follow this recommended sequence:
1.Timer Configuration:
Keep your timer settings for PWM generation as desired. However, please verify that TIM15 is correctly configured to generate the PWM signal as intended. From your current .ioc configuration, it appears TIM15 is not producing the expected PWM output. Make sure to fix this first to ensure proper timer operation.
2.Disable TIM15 TRGO Generation:
It is not mandatory to use the TIM15 TRGO event as a DMA trigger for this use case. You can disable TRGO generation to simplify the setup.
3.Enable Timer Update DMA Request:
Instead of using TRGO, configure the timer to generate DMA requests on update events. Add the following line in the user code section of your MX_TIM15_Init() function:
/* Enable Update DMA request */
__HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE);4.Update DMA Linked-List Configuration:
Modify your MX_TimingSPI_GPIO_O_I_Config() function to use the timer update event as the DMA request source. For example, configure the node request as follows:
/* Set node configuration ################################################*/
pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
pNodeConfig.Init.Request = GPDMA1_REQUEST_TIM15_UP; //Use TIM15 update event as a request source
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_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_EACH_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)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
pNodeConfig.DataSize = sizeof(uint32_t);
/* Build GPIO_High_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_High_Node);
/* Insert GPIO_High_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
/* Set node configuration ################################################*/
// pNodeConfig.Init.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[1];
/* Build GPIO_Low_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_Low_Node);
/* Insert GPIO_Low_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_Low_Node);
With these changes, your DMA linked list will be triggered correctly by TIM15 update events, and you should observe the PA8 pin toggling as expected via the BSRR register writes.
For more information on generating arbitrary waveforms using the timer DMA burst feature, you may refer to the application note AN4776 for valuable insights and examples.
Kind regards,
DHIF Khaled
2025-11-10 12:51 AM
Hello DHIF Khaled,
The effect of __HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE); was dramatic! I was able to execute the intended process. For synchronizing with TIM15, I used GPDMA1_REQUEST_TIM15_UP only for the first instance, and for the remaining nodes, I used DMA_REQUEST_SW, which allowed the DMA to execute all at once (though I'm not sure if this is the correct usage).
TIM15 executes at twice the frequency compared to other TIMs for PWM waveform generation, and it is used for 96-bit SPI input/output and for switching the PWM duty of other TIMs.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : linked_list.c
* Description : This file provides code for the configuration
* of the LinkedList.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "linked_list.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
DMA_NodeTypeDef GPIO_High_Node;
DMA_QListTypeDef TimingSPI_GPIO_O_I;
DMA_NodeTypeDef GPIO_Low_Node;
DMA_NodeTypeDef SPI2_TX;
DMA_NodeTypeDef SPI2_RX_Node;
DMA_QListTypeDef SPI2_RX_I;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
extern uint32_t gpio_node_data[];
extern uint32_t spi2_node_data[];
extern uint32_t spi2_read_data[];
/* USER CODE END PM */
/**
* @brief DMA Linked-list SPI2_RX_I configuration
* @PAram None
* @retval None
*/
HAL_StatusTypeDef MX_SPI2_RX_I_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_RX;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_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)&(SPI2->RXDR);
pNodeConfig.DstAddress = (uint32_t)&spi2_read_data[0];
pNodeConfig.DataSize = 20*3*sizeof(uint32_t);
/* Build SPI2_RX_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_RX_Node);
/* Insert SPI2_RX_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&SPI2_RX_I, &SPI2_RX_Node);
ret |= HAL_DMAEx_List_SetCircularMode(&SPI2_RX_I);
return ret;
}
/**
* @brief DMA Linked-list TimingSPI_GPIO_O_I configuration
* @PAram None
* @retval None
*/
HAL_StatusTypeDef MX_TimingSPI_GPIO_O_I_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_TIM15_UP;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_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)&gpio_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(GPIOA->BSRR);
pNodeConfig.DataSize = sizeof(uint32_t);
/* Build GPIO_High_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_High_Node);
/* Insert GPIO_High_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
/* Set node configuration ################################################*/
pNodeConfig.SrcAddress = (uint32_t)&gpio_node_data[1];
/* Build GPIO_Low_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &GPIO_Low_Node);
/* Insert GPIO_Low_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &GPIO_Low_Node);
/* Set node configuration ################################################*/
pNodeConfig.Init.Request = DMA_REQUEST_SW;
pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
pNodeConfig.SrcAddress = (uint32_t)&spi2_node_data[0];
pNodeConfig.DstAddress = (uint32_t)&(SPI2->TXDR);
pNodeConfig.DataSize = 3*sizeof(uint32_t);
/* Build SPI2_TX Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SPI2_TX);
/* Insert SPI2_TX to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&TimingSPI_GPIO_O_I, &SPI2_TX);
ret |= HAL_DMAEx_List_SetCircularModeConfig(&TimingSPI_GPIO_O_I, &GPIO_High_Node);
return ret;
}
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_TIM15_Init();
MX_TIM8_Init();
MX_SPI2_Init();
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_4);
SPI2->CR1 |= SPI_CR1_SPE;
SPI2->CR1 |= SPI_CR1_CSTART;
SPI2->CFG1 |= SPI_CFG1_RXDMAEN;
MX_SPI2_RX_I_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel6, &SPI2_RX_I);
HAL_DMAEx_List_Start(&handle_GPDMA1_Channel6);
MX_TimingSPI_GPIO_O_I_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel7, &TimingSPI_GPIO_O_I);
HAL_DMAEx_List_Start(&handle_GPDMA1_Channel7);
__HAL_TIM_ENABLE_DMA(&htim15, TIM_DMA_UPDATE);