2025-02-28 12:52 AM - last edited on 2025-02-28 1:01 AM by Andrew Neil
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.))
2025-02-28 1:17 AM
To change SPI DMA settings in SPI CR2, SPI must be disabled in CR1. RX DMA must be enabled before starting TX.
2025-02-28 1:46 AM
#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
2025-02-28 6:14 AM - edited 2025-02-28 6:15 AM
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.
2025-02-28 10:44 AM
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.
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.
2025-02-28 10:55 AM
I forgot that in my usual code for SPI, I swapped the most significant and least significant bytes when writing to memory.
/////////////////////////// specific IMU 42xxx /////////////////////////////////////
uint8_t SPI2_read_reg_to_array8_check(uint16_t reg, uint8_t *data, uint16_t len) {
if(!len) return 0;
uint32_t timeout = 0;
SPI2_CS_on
//////// registr
while (!(SPI2->SR & SPI_SR_TXE)){
if (++timeout > SPI_TIMEOUT){SPI2_CS_off;return 1;};
}
SPI2->DR = reg;
timeout = 0;
//////// data
while (!(SPI2->SR & SPI_SR_RXNE)){
if (++timeout > SPI_TIMEOUT){SPI2_CS_off;return 2;};
}
*data++ = SPI2->DR & 0xFF;
len--;
while (len--){
timeout = 0;
while (!(SPI2->SR & SPI_SR_TXE)){
if (++timeout > SPI_TIMEOUT){SPI2_CS_off;return 1;};
}
SPI2->DR = 0;
timeout = 0;
while (!(SPI2->SR & SPI_SR_RXNE)){
if (++timeout > SPI_TIMEOUT){SPI2_CS_off;return 2;};
}
timeout = SPI2->DR;
*data++ = timeout >> 8 & 0xFF;
*data++ = timeout & 0xFF;
};
SPI2_CS_off
return 0;
};