cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5 GPDMA only toggles some GPIO pins, others ignored

Silexman
Associate III

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:

    1. Write CSx_Pin to GPIOx->BRR (drive CS low)

    2. Dummy delay node (memory→memory)

    3. 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:

  1. Are all GPIO ports on STM32U5 supposed to be reachable by the GPDMA?

  2. 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;
}

 

1 ACCEPTED SOLUTION

Accepted Solutions

I solved the problem. The issue was caused by how GPDMA writes to the GPIO registers. When I first configured the DMA to write a **32-bit word** into `GPIOx->BSRR` or `GPIOx->BRR`, only some GPIO pins actually toggled (in my case, only PE3, PE1, PB6 worked). 

 

The solution was to configure the DMA for **byte transfers**, and point each DMA node to the exact byte within the GPIO register that corresponds to the pin. For example:

 

#define GPIO_BSRR_OFFSET 0x18
#define GPIO_BRR_OFFSET 0x28
#define GPIO_BSRR_BYTE_ADDR(port_base, pin) ((uint32_t)( (port_base) + GPIO_BSRR_OFFSET + ((pin) >> 3) ))
#define GPIO_BRR_BYTE_ADDR(port_base, pin) ((uint32_t)( (port_base) + GPIO_BRR_OFFSET + ((pin) >> 3) ))

// Example: CS1 = PG9
uint32_t cs1_low = GPIO_BRR_BYTE_ADDR(GPIOG_BASE, 9);
uint32_t cs1_high = GPIO_BSRR_BYTE_ADDR(GPIOG_BASE, 9);

// Mask reduced to a single byte
uint8_t cs1_mask = (1U << (9 & 0x7));

 

With this approach:

 

* DMA transfer width = **BYTE**

* Source = one-byte mask (e.g. `0x02` for PG9)

* Destination = the correct `BSRR` or `BRR` byte address for the pin

 

Now all 7 GPIOs toggle correctly via the GPDMA linked list.


Edited to apply proper source code formatting - please see How to insert source code for future reference.

View solution in original post

2 REPLIES 2
TDK
Super User

All GPIO ports are accessible by the GPDMA per the System Architecture figure in the reference manual. Recheck your assumptions and any security settings you have enabled.

If you feel a post has answered your question, please click "Accept as Solution".

I solved the problem. The issue was caused by how GPDMA writes to the GPIO registers. When I first configured the DMA to write a **32-bit word** into `GPIOx->BSRR` or `GPIOx->BRR`, only some GPIO pins actually toggled (in my case, only PE3, PE1, PB6 worked). 

 

The solution was to configure the DMA for **byte transfers**, and point each DMA node to the exact byte within the GPIO register that corresponds to the pin. For example:

 

#define GPIO_BSRR_OFFSET 0x18
#define GPIO_BRR_OFFSET 0x28
#define GPIO_BSRR_BYTE_ADDR(port_base, pin) ((uint32_t)( (port_base) + GPIO_BSRR_OFFSET + ((pin) >> 3) ))
#define GPIO_BRR_BYTE_ADDR(port_base, pin) ((uint32_t)( (port_base) + GPIO_BRR_OFFSET + ((pin) >> 3) ))

// Example: CS1 = PG9
uint32_t cs1_low = GPIO_BRR_BYTE_ADDR(GPIOG_BASE, 9);
uint32_t cs1_high = GPIO_BSRR_BYTE_ADDR(GPIOG_BASE, 9);

// Mask reduced to a single byte
uint8_t cs1_mask = (1U << (9 & 0x7));

 

With this approach:

 

* DMA transfer width = **BYTE**

* Source = one-byte mask (e.g. `0x02` for PG9)

* Destination = the correct `BSRR` or `BRR` byte address for the pin

 

Now all 7 GPIOs toggle correctly via the GPDMA linked list.


Edited to apply proper source code formatting - please see How to insert source code for future reference.