2025-09-21 9:11 PM
Hello,
I am trying to use the GPDMA linked list on an STM32U5 to toggle seven chip-select GPIOs before and after SPI transfers.
Setup:
MCU: STM32U575ZITX
I build a linked list of nodes:
Write CSx_Pin to GPIOx->BRR (drive CS low)
Dummy delay node (memory→memory)
Write CSx_Pin to GPIOx->BSRR (drive CS high)
word width, DataSize = 4, SrcInc = FIXED, DstInc = FIXED.
Destination addresses are the GPIO BSRR/BRR registers.
Problem:
Only 3 out of 7 chip-select pins actually toggle when the DMA queue runs. The working pins are:
PE3
PE1
PB6
The non-working ones are on GPIOG (PG9, PG11, PG13) and PB8.
If I write to these pins directly from the CPU with HAL_GPIO_WritePin, they work correctly. Only DMA writes fail.
Questions:
Are all GPIO ports on STM32U5 supposed to be reachable by the GPDMA?
Is there a known limitation where certain GPIO ports (e.g. GPIOG) cannot be written by DMA?
Any clarification on which GPIO ports are DMA-accessible would be appreciated.
Thank you.
Linked List Setup Code :
#define ADC_COUNT 7U
#define CS7_Pin GPIO_PIN_3
#define CS7_GPIO_Port GPIOE
#define CS1_Pin GPIO_PIN_9
#define CS1_GPIO_Port GPIOG
#define CS2_Pin GPIO_PIN_11
#define CS2_GPIO_Port GPIOG
#define CS3_Pin GPIO_PIN_13
#define CS3_GPIO_Port GPIOG
#define CS4_Pin GPIO_PIN_6
#define CS4_GPIO_Port GPIOB
#define CS5_Pin GPIO_PIN_8
#define CS5_GPIO_Port GPIOB
#define CS6_Pin GPIO_PIN_1
#define CS6_GPIO_Port GPIOE
DMA_NodeTypeDef cs_low_node[ADC_COUNT];
DMA_QListTypeDef adc_queue;
DMA_NodeTypeDef adc_delay_node[ADC_COUNT];
DMA_NodeTypeDef adc_get_spi_node[ADC_COUNT];
DMA_NodeTypeDef cs_high_node[ADC_COUNT];
static volatile uint32_t cs_low_dest[ADC_COUNT] = {(uint32_t)&(CS1_GPIO_Port->BRR),
(uint32_t)&(CS2_GPIO_Port->BRR),
(uint32_t)&(CS3_GPIO_Port->BRR),
(uint32_t)&(CS4_GPIO_Port->BRR),
(uint32_t)&(CS5_GPIO_Port->BRR),
(uint32_t)&(CS6_GPIO_Port->BRR),
(uint32_t)&(CS7_GPIO_Port->BRR)};
static volatile uint32_t cs_source[ADC_COUNT] = {
CS1_Pin,
CS2_Pin,
CS3_Pin,
CS4_Pin,
CS5_Pin,
CS6_Pin,
CS7_Pin
};
static volatile uint32_t cs_high_dest[ADC_COUNT] = {(uint32_t)&(CS1_GPIO_Port->BSRR),
(uint32_t)&(CS2_GPIO_Port->BSRR),
(uint32_t)&(CS3_GPIO_Port->BSRR),
(uint32_t)&(CS4_GPIO_Port->BSRR),
(uint32_t)&(CS5_GPIO_Port->BSRR),
(uint32_t)&(CS6_GPIO_Port->BSRR),
(uint32_t)&(CS7_GPIO_Port->BSRR)};
static volatile uint32_t delay_source;
static volatile uint32_t delay_dest;
uint8_t adc1_buffer[5];
uint8_t adc2_buffer[5];
uint8_t adc3_buffer[5];
uint8_t adc4_buffer[5];
uint8_t adc5_buffer[5];
uint8_t adc6_buffer[5];
uint8_t adc7_buffer[5];
static volatile uint32_t adc_data_dst[ADC_COUNT] = {
(uint32_t)&(adc1_buffer[0]),
(uint32_t)&(adc2_buffer[0]),
(uint32_t)&(adc3_buffer[0]),
(uint32_t)&(adc4_buffer[0]),
(uint32_t)&(adc5_buffer[0]),
(uint32_t)&(adc6_buffer[0]),
(uint32_t)&(adc7_buffer[0])
};
HAL_StatusTypeDef adc_data_stream_dma_config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
/* DMA node configuration declaration */
DMA_NodeConfTypeDef pNodeConfig;
uint8_t i;
/* 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_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1U;
pNodeConfig.Init.DestBurstLength = 1U;
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 = 0;
pNodeConfig.DstAddress = 0;
pNodeConfig.DataSize = 0;
for(i = 0;i <ADC_COUNT;i++)
{
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
pNodeConfig.SrcAddress = (uint32_t)&cs_source[i];
pNodeConfig.DstAddress = cs_low_dest[i];
pNodeConfig.DataSize = 4U;
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &cs_low_node[i]);
ret |= HAL_DMAEx_List_InsertNode_Tail(&adc_queue, &cs_low_node[i]);
pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
pNodeConfig.SrcAddress = (uint32_t)&delay_source;
pNodeConfig.DstAddress = (uint32_t)&delay_dest;
pNodeConfig.DataSize = 4U;
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &adc_delay_node[i]);
ret |= HAL_DMAEx_List_InsertNode_Tail(&adc_queue, &adc_delay_node[i]);
pNodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
pNodeConfig.Init.Request = DMA_REQUEST_SW;
pNodeConfig.SrcAddress = (uint32_t)&cs_source[i];
pNodeConfig.DstAddress = cs_high_dest[i];
pNodeConfig.DataSize = 4U;
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &cs_high_node[i]);
ret |= HAL_DMAEx_List_InsertNode_Tail(&adc_queue, &cs_high_node[i]);
}
return ret;
}