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);
}