Skip to main content
Graduate II
February 28, 2025
Question

STM32G431 SPI2 (16-bit) DMA (DMAMUX) data transmission is incorrect.

  • February 28, 2025
  • 3 replies
  • 1229 views

Here is the code:

 

 

void DMA_Init_SPI2(void *rxBuffer, void *txBuffer, uint16_t rxSIZE, uint16_t txSIZE) {

 RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN;

 // DMA1 Channel 1 для SPI2_RX (Peripheral-to-Memory)
 
 DMA1_Channel1->CCR = DMA_CCR_MINC | // RAM++
 DMA_CCR_TCIE | // IRQ_EN
 DMA_CCR_PSIZE_0 | // 16-bit
 DMA_CCR_MSIZE_0 | // 16-bit
 DMA_CCR_CIRC
 ;
 DMA1_Channel1->CNDTR = rxSIZE; 
 DMA1_Channel1->CPAR = (uint32_t)&(SPI2->DR);
 DMA1_Channel1->CMAR = (uint32_t)rxBuffer; 
 DMAMUX1_Channel0->CCR = 12 ; // SPI2_RX (Table 91 rm440)
 
 // DMA1 Channel 2 для SPI2_RX (Memory to Peripheral)
 
 DMA1_Channel2->CCR = DMA_CCR_MINC 	 | // RAM++
 DMA_CCR_DIR | // RAM -> SPI
 DMA_CCR_PSIZE_0 | // 16-bit
 DMA_CCR_MSIZE_0 // 16-bit
 ;

 DMA1_Channel2->CNDTR = txSIZE; 
 DMA1_Channel2->CPAR = (uint32_t)&(SPI2->DR); 
 DMA1_Channel2->CMAR = (uint32_t)txBuffer; 
 DMAMUX1_Channel1->CCR = 13; // SPI2_TX (Table 91)

 NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

uint8_t dmaComplete;

void DMA1_Channel1_IRQHandler(void) {

 if (DMA1->ISR & DMA_ISR_TCIF1) {

 DMA1->IFCR = DMA_IFCR_CTCIF1;

 //DMA1_Channel1->CCR &= ~DMA_CCR_EN;
 DMA1_Channel2->CCR &= ~DMA_CCR_EN;

 dmaComplete = 1;

 }

// DMA1->IFCR |= DMA_IFCR_CGIF1;
}
#define SPI2_DMA_enable SPI2->CR2 |= 3; // 16 BIT
#define SPI2_DMA_disabled SPI2->CR2 |= ~3;

#define SPI2_CS_on GPIOA->BRR = 1<<10;
#define SPI2_CS_off GPIOA->BSRR = 1<<10;

void IIM42652_ReadSPI2_DMA(uint8_t startReadCommand,uint16_t* ram,uint16_t size) {

	dmaComplete = 0;

 ram[0] = (uint16_t)startReadCommand << 8; //example 0xAD00

 SPI2_CS_on
 
 DMA1_Channel2->CNDTR = size;
 DMA1_Channel2->CPAR = (uint32_t)&(SPI2->DR);
 DMA1_Channel2->CMAR = (uint32_t*)ram;
 DMA1_Channel2->CCR |= DMA_CCR_EN;
 
 	while (!dmaComplete);
 
 SPI2_CS_off

}
void ConfigSPI2(void) {

	RCC->APB1ENR1 |= RCC_APB1ENR1_SPI2EN;
	SPI2->CR1 = (1 << 2) | (2 << 3) | (3 << | 0; // master | clk/8 | SSM=1 SSI =1 
	SPI2->CR2 |= (0b1111 << | 0; // 16 BIT /
	

	//SPI2->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;


	SPI2->CR1 |= (1 << 6);// EN SP2
}
uint16_t spi2_rx_buf[30]={0},spi2rxsize = 12,
	 spi2_tx_buf[30]={0},spi2txsize = 12;

int main(void) {

	// Настройка системного тактирования

	SystemClock_Config();
	GPIO_INIT();
	CAN_Config();

	I2C2_Init();

	ConfigSPI2();
	DMA_Init_SPI2(spi2_rx_buf,spi2_tx_buf,spi2rxsize,spi2txsize);
	init_iim42652(&imu_iim42652);
	
	DMA1_Channel1->CCR |= DMA_CCR_EN; // Rx circle
	SPI2_DMA_enable	
	
 while (1) {
	
	if(readyINT1){readyINT1 = 0; 
		 IIM42652_ReadSPI2_DMA(0xAD,spi2_tx_buf,12);//Read FIFO
		 if((*spi2_rx_buf) &0x04) sendACC = 1;
		 
		 }
	//..........
	
	if (!systick_pause) {
	 	if(sendACC){sendACC =0;
		 CAN_SendMessage(id+1,(uint8_t*) spi2_rx_buf, 8);
		 CAN_SendMessage(id+2,(uint8_t*) spi2_rx_buf+8, 8);
		 };
		systick_pause = 20;
	};
	

 }
}

 

 

 I checked SPI2 the operation using standard methods (in a loop) for sending and receiving data, and everything works as expected; the data is received correctly.

[0C 00 ] 14 00 78 F4 FF EA FF 19 08 02 00 F3 FF FC

 

But as soon as I enable the DMA, everything starts to behave strangely. The first half-word (16 bits) is received correctly, but then there is a change in the data or some garbage.

 

[0C 00] 00 14 DC 78 60 FF 16 00 02 08 F5 00 FD FF

 

Can anyone suggest where I might be missing something or doing it wrong? Unfortunately, the RM440 contains little information on DMA (DMAMUX) and sometimes does not correspond to stm32g431xx.h. I have already asked questions on GROK, DeepSeek, and ChatGPT—none of them could offer anything useful regarding this issue.))

 

 

 

    This topic has been closed for replies.

    3 replies

    Graduate
    February 28, 2025

    To change SPI DMA settings  in SPI CR2, SPI must be disabled in CR1. RX DMA must be enabled before starting TX.

    OleksiiAuthor
    Graduate II
    February 28, 2025
    #define SPI2_DMA_enable SPI2->CR2 |= 3; // 16 BIT
    DMA1_Channel1->CCR |= DMA_CCR_EN; // Rx circle
    	SPI2_DMA_enable	

    Thank you for the response. The datasheet does not specify that SPI needs to be disabled to enable DMA.

    However, I have tried both options, and it works either way. Believe me, I have spent more than one day trying to find the problem.

    Also, please note that the data is received, but it is incorrect; it is similar to what is expected.
    Marker bayt 14 ...78

    [0C 00 ]14 00 78 F4 FF EA FF 19 08 02 00 F3 FF FC   

    [0C 00] 00 14 DC 78 60 FF 16 00 02 08 F5 00 FD FF

    Super User
    February 28, 2025

    How are you determining that the data sent is incorrect? The best source would be to look at a scope or logic analyzer plot.

     

     

    In your RX complete handler, you disable the TX DMA and assume it's done. This is an error unless the TX and RX buffers are exactly the same length, always. The TX DMA stream is automatically disabled when it's done.

     

    Consider writing a minimal example which only performs the SPI functionality and posting it here.

    OleksiiAuthor
    Graduate II
    February 28, 2025

    How are you determining that the data sent is incorrect? The best source would be to look at a scope or logic analyzer plot.

    Thank you for your participation.
    How do I know? Probably because I mentioned in the message what dump should be received )).


    In your RX complete handler, you disable the TX DMA and assume it's done. This is an error unless the TX and RX buffers are exactly the same length, always. The TX DMA stream is automatically disabled when it's done


    I understand what you're talking about, but if you notice in the code, the number of sent and received data is the same, since this is full duplex SPI. Therefore, the RXNE event always occurs later than TX.

     

    Consider writing a minimal example which only performs the SPI functionality and posting it here.

    Знімок екрана з 2025-02-28 20-23-16.png

    In principle, I already understand the reason for this behavior. When bytes are transmitted in 16-bit mode, the first byte transmitted is the most significant byte of the word, followed by the least significant byte. Consequently, the DMA reads the 16-bit number regardless of how the data arrived from the sensor and writes it to memory.

    As a result, when reading the data array from the sensor's buffer, for example:
    11 22 33 44 55 66 77 00
    The DMA ends up writing to memory:
    22 11 44 33 66 55 00 77

    And it seems there is no simple and elegant solution in 16-bit format. Either I haven't found a way for the DMA to swap the most significant and least significant bytes on the fly, or it only allows assembling this array in a loop afterward.

    I might have to revert to the 8-bit format.

     

    OleksiiAuthor
    Graduate II
    October 27, 2025

    Hi,

    If you're really interested :) Here's the link to the project on GitHub: https://github.com/AlekseyMamontov/STM32_example/tree/main/STM32G4/CANFD