2025-10-07 11:56 AM
Hi everyone,
I'm using STM32H573I-DK, since I wanted to experiment with it a little bit. I want to do some audio processing, so I wanted to configure I2S (not planning on using the on board audio codec) to use DMA for circular buffering of RX (using GPDMA1_Channel4 for it) and TX (GPDMA1_Channel5 for it). I didn't know that the DMA changed so much in configuring it, but oh well, I started learning the linked-list mode since it uses it anyway for circular mode. I looked up at different tutorials and posts here, but I still have a problem. I also followed the procedure for setting up the I2S transfer using DMA from 52.9.16 section, Programing Examples.
Anyway, I tried to set it up using the node configuration in CubeMX and using HAL functions, but I just could't get it to work. Whenever I looked at the Linked List registers in GPDMA_XXXX (as in for example GPDMA_C4SAR, GPDMA_C4CCR) they were filled with zeros. So I started to use LL so that I can be sure that I write the correct configuration.
I achieved moderate success, the registers are now written into correctly. The configuration seems correct, only maybe CLBAR and CLLLR-LA maybe incorrect. I say maybe because I stumble across 1 last problem. I noticed when debugging and looking at register values, right after enabling DMA channel, in the next step, the CXDAR/CXSAR (for RX/TX) updates into not correct value. It it correct before enabling, but right after enabling it goes to value C4DAR = 8 and value C5SAR = 12. And at the same time DTFE (Data Transfer Error) flag pops up.
I really can't wrap my head around it, tried different combinations for CLBAR and CLLR-LA, but still C4DAR/C5SAR updates incorrectly and right after DTFE pops up. Does anyone know what to do or maybe also struggles with this? Any help appreciated
Snippets of the code:
buffers (they are in a different file)
uint32_t data32_rx_buffer[BUFFER_SIZE] = {0};
uint32_t data32_tx_buffer[BUFFER_SIZE] = {0};
uint32_t data32_aligned_rx_buffer __attribute__((aligned(32))) = (uint32_t) data32_rx_buffer;
uint32_t data32_aligned_tx_buffer __attribute__((aligned(32))) = (uint32_t) data32_tx_buffer;main.c
int main(void)
{
/* USER CODE BEGIN 1 */
fill_buffers_with_test_pattern(); // just so i can see if something changed with buffers
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_I2S2_Init();
/* USER CODE BEGIN 2 */
WM8960_Init(); // external audio codec, configured via I2C, no problem there (not using DMA)
DMA_Transfer_Init(); // I get stuck here
LL_I2S_StartTransfer(SPI2);I2S configuration (i2s.c):
void MX_I2S2_Init(void)
{
/* USER CODE BEGIN I2S2_Init 0 */
/* USER CODE END I2S2_Init 0 */
/* USER CODE BEGIN I2S2_Init 1 */
/* USER CODE END I2S2_Init 1 */
hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_FULLDUPLEX;
hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_48K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.FirstBit = I2S_FIRSTBIT_MSB;
hi2s2.Init.WSInversion = I2S_WS_INVERSION_DISABLE;
hi2s2.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT;
hi2s2.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
if (HAL_I2S_Init(&hi2s2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2S2_Init 2 */
i2s_init(&hi2s2);
/* USER CODE END I2S2_Init 2 */
}
static void i2s_init(I2S_HandleTypeDef *hi2s) {
/* Adjust the FIFO threshold */
LL_SPI_SetFIFOThreshold(hi2s->Instance, LL_SPI_FIFO_TH_16DATA);
LL_SPI_EnableDMAReq_RX(hi2s->Instance); // SPI_CFG1_RXDMAEN
LL_SPI_EnableDMAReq_TX(hi2s->Instance); // SPI_CFG1_TXDMAEN
/* Clear all interrupt enable fields located in the SPI_IER register. */
SPI2->IER &= ~(0x000003FF);
/* Clear all status flags, by setting the appropriate bits in the SPI_SR register */
SPI2->IFCR |= 0x00000BF8;
/* Set the SPE bit */
SET_BIT(SPI2->CR1, SPI_CR1_SPE);
}DMA Configuration (for ch5 really similar structure of functions):
LL_DMA_LinkNodeTypeDef I2S_DMA_Rx_Node;
LL_DMA_LinkNodeTypeDef I2S_DMA_Tx_Node;
void DMA_Transfer_Init(void)
{
__HAL_RCC_GPDMA1_CLK_ENABLE();
assert_param(((uint32_t)&data32_aligned_rx_buffer & 0x1F) == 0); // 32-byte aligned check
assert_param(((uint32_t)&data32_aligned_tx_buffer & 0x1F) == 0); // 32-byte aligned check
/* configuring linked-list parameters for rx and tx */
Configure_I2S_DMA_Rx_Node(&I2S_DMA_Rx_Node);
Configure_I2S_DMA_Tx_Node(&I2S_DMA_Tx_Node);
/* just to make sure, maybe redundant */
LL_DMA_DisableChannel(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_DisableChannel(GPDMA1, LL_DMA_CHANNEL_5);
Configure_CCR_Ch4();
Configure_CCR_Ch5();
Configure_CLBAR_Ch4();
Configure_CLBAR_Ch5();
init_LLI0_registers(GPDMA1, LL_DMA_CHANNEL_4, &I2S_DMA_Rx_Node);
init_LLI0_registers(GPDMA1, LL_DMA_CHANNEL_5, &I2S_DMA_Tx_Node);
LL_DMA_EnableChannel(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableChannel(GPDMA1, LL_DMA_CHANNEL_5);
NVIC_SetPriority(GPDMA1_Channel4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_SetPriority(GPDMA1_Channel5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(GPDMA1_Channel4_IRQn);
NVIC_EnableIRQ(GPDMA1_Channel5_IRQn);
}
static void Configure_CCR_Ch4(void){
LL_DMA_SetChannelPriorityLevel(GPDMA1, LL_DMA_CHANNEL_4, LL_DMA_HIGH_PRIORITY);
LL_DMA_SetLinkAllocatedPort(GPDMA1, LL_DMA_CHANNEL_4, LL_DMA_LINK_ALLOCATED_PORT0);
LL_DMA_SetLinkStepMode(GPDMA1, LL_DMA_CHANNEL_4, LL_DMA_LSM_FULL_EXECUTION);
LL_DMA_EnableIT_TO(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_SUSP(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_USE(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_ULE(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_DTE(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_HT(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_TC(GPDMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableIT_ULE(GPDMA1, LL_DMA_CHANNEL_4);
}
static void Configure_CLBAR_Ch4(void){
/* It already uses mask so no need for & 0xFFFF0000 */
LL_DMA_SetLinkedListBaseAddr(GPDMA1, LL_DMA_CHANNEL_4, (uint32_t)(data32_aligned_rx_buffer));
}
static void Configure_I2S_DMA_Rx_Node(LL_DMA_LinkNodeTypeDef *node){
LL_DMA_LinkNodeTypeDef *LLI0 = node;
/* Destination increment memory and transmitting Words */
LLI0->LinkRegisters[CTR1] = DMA_CTR1_DINC | DMA_CTR1_SDW_LOG2_1 | DMA_CTR1_DDW_LOG2_1;
/* I2S Rx request */
LLI0->LinkRegisters[CTR2] = (8UL << DMA_CTR2_REQSEL_Pos);
/* One Word burst */
LLI0->LinkRegisters[CBR1] = (BLOCK_DATA_LENGTH << DMA_CBR1_BNDT_Pos);
/* Source is I2S Rx data register (SPI share registers with I2S)*/
LLI0->LinkRegisters[CSAR] = (uint32_t)&(SPI2->RXDR);
/* Destination is our rx buffer */
LLI0->LinkRegisters[CDAR] = (uint32_t)data32_rx_buffer; // it written into correctly (before enabling channel)
/* Used for 2d addressing, when I erased it still didnt work */
LLI0->LinkRegisters[CTR3] = 0;
LLI0->LinkRegisters[CBR2] = 0;
/* Circular mode, after transaction update destination address (its supposed to be data32_rx_buffer) */
LLI0->LinkRegisters[CLLR] = (DMA_CLLR_UDA);
}
2025-10-09 6:51 AM
Hello @Prof_UwU ,
You are currently using the data buffer address (data32_aligned_rx_buffer) as the DMA linked-list base address (CLBAR). However, CLBAR must reference the linked-list descriptor nodes rather than the data buffers themselves. Could you please share your node descriptor configuration for further review?
Kind regards,
DHIF Khaled
2025-10-10 12:15 PM
Hi,
Thanks for answering, so I just initialized the LLI0 as so:
static void Configure_I2S_DMA_Rx_Node(LL_DMA_LinkNodeTypeDef *node){
LL_DMA_LinkNodeTypeDef *LLI0 = node;
/* Destination increment memory and transmitting Words */
LLI0->LinkRegisters[CTR1] = DMA_CTR1_DINC | DMA_CTR1_SDW_LOG2_1 | DMA_CTR1_DDW_LOG2_1;
/* I2S Rx request */
LLI0->LinkRegisters[CTR2] = (8UL << DMA_CTR2_REQSEL_Pos);
/* One Word burst */
LLI0->LinkRegisters[CBR1] = (BLOCK_DATA_LENGTH << DMA_CBR1_BNDT_Pos);
/* Source is I2S Rx data register (SPI share registers with I2S)*/
LLI0->LinkRegisters[CSAR] = (uint32_t)&(SPI2->RXDR);
/* Destination is our rx buffer */
LLI0->LinkRegisters[CDAR] = (uint32_t)data32_rx_buffer; // it written into correctly (before enabling channel)
/* Used for 2d addressing, when I erased it still didnt work */
LLI0->LinkRegisters[CTR3] = 0;
LLI0->LinkRegisters[CBR2] = 0;
/* Circular mode, after transaction update destination address (its supposed to be data32_rx_buffer) */
LLI0->LinkRegisters[CLLR] = (DMA_CLLR_UDA);
}I had a version with LLI1 also, but it also didn't work correctly (but maybe something else was the problem?). Please do tell, if to try something again, it's just I went through so many trial & errors, I may have missed some combinations. There's also a possibility that I may have misunderstood parts of Reference Manual. Anyway, this is the part of configuring node descriptors, later I use
init_LLI0_registers(GPDMA1, LL_DMA_CHANNEL_4, &I2S_DMA_Rx_Node);
to fill the relevant registers in memory.
2025-10-16 8:55 AM
Hello @Prof_UwU ,
I understand that you are aiming to implement a circular queue where the GPDMA_CxDAR (destination address register) is updated at each node. To achieve this, you can create multiple linked-list items (LLIs), each responsible for updating only the destination address as needed. These LLIs should be linked sequentially—each node pointing to the next one in the list.
To form the circular behavior you want, the last LLI in the sequence should link back to the very first LLI, effectively creating a circular linked list that the DMA engine cycles through continuously.
For example, for the first LLI, you would configure the CLLR register like this:
LLI0->LinkRegisters[CLLR] = ((uint32_t)LLI1 & DMA_CLLR_LA) | DMA_CLLR_UDA | DMA_CLLR_ULL;Here, LLI1 is the next node in the list, DMA_CLLR_UDA enables updating the destination address, and DMA_CLLR_ULL ensures the linked address is updated accordingly.
You can refer to the logic implemented in the STM32 GPDMA linked-list circular mode examples, as the approach is very similar.
Additionally, could you please share a register dump showing the contents of the DMA linked-list node registers? This will help verify that the registers are correctly programmed as expected.