cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 SPI DMA not working

Gabriel T
Senior

Hello,

When I use DMA with SPI it is not working.

0693W000007BGY9QAO.pngWhen I get a falling edge on DRDY this function gets called :

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == DRDY_ADC_Pin)
	{
		HAL_SPI_Receive_DMA(&hspi1, SPI1_RxBuffer, READBACK_LENGTH);
	}
}

SPI1 RX Buffer is located in a DMA accessible memory, RAM D2 (it works I checked the .map) :

uint8_t __attribute__(( section(".ramd2section"))) SPI1_RxBuffer[READBACK_LENGTH]; // SPI Tx

My question is do I need to use a setup function outside of what HAL_SPI_MspInit() already does. For example HAL_MDMA_Start_IT() ?

I've red few posts with the same problem but the solution is never explicated or done not using only HAL function.

What I use :

SPI1 Master full-duplex

DMA1 Stream 2 SPI1_Rx to SPI1_RxBuffer

MDMA Stream 0 SPI1_RxBuffer to ads_buf located in DTCMRAM

11 REPLIES 11

Read out and check/post SPI and relevant DMA registers (including status) content.

JW

Gabriel T
Senior

Tried adding DMAMUX init but it didnt change anything

dmamux_ReqGenParams.SignalID  = 37U; 						
dmamux_ReqGenParams.Polarity  = HAL_DMAMUX_REQ_GEN_RISING;    
dmamux_ReqGenParams.RequestNumber = 1;                      
 
HAL_DMAEx_ConfigMuxRequestGenerator(&hdma_spi1_rx,&dmamux_ReqGenParams);
 
/* NVIC configuration for DMAMUX request generator overrun errors*/
HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);
 
HAL_DMAEx_EnableMuxRequestGenerator (&hdma_spi1_rx);

Gabriel T
Senior

The SPI is always in "HAL_SPI_STATE_BUSY_RX"

I got this with debugging tool for the DMA Handler

0693W000007BHHsQAO.png

I mean the SPI and DMA peripherals (and, once you've mentioned, relevant DMAMUX) registers.

JW

Gabriel T
Senior

Here is my DMA init

hdma_spi1_rx.Instance = DMA1_Stream2;
    hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;
    hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi1_rx.Init.Mode = DMA_NORMAL;
    hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
 
    /* SPI1 interrupt Init */
    HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);

And my SPI init

hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 0x0;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

I'm not really interested in source code, I don't use Cube and don't quite understand it.

Read out the registers' content.

JW

Gabriel T
Senior

Sorry, I get it now. I looked register value with debugger tool at the end of the init function:

SP1 :

CR1 = 0x 0000 1000
CR2 = 0
CFG1 = 0x 7000 0007
CFG2 = 0x 0540 0000
IER = 0
SR = 0x 0000 1002
IFCR = 0
TXDR = 0
RXDR = 0
UDRDR = 0
I2SCFGR = 0

I got it from this :

0693W000007BPJmQAO.pngHere is what i got for DMA :

// DMA
CR = 0x 0000 0400
FCR = 0x 0000 0020
 
// DMAMUX
CR = 0x 0000 0025

Do you think I should stop using Cube and just go with the datasheet ?

Thank you,

Gabriel

Gabriel T
Senior

I added my own "Bare Metal" register values :

#define DMAMUX_SPI1_RX_ID 37U
void SPI1_DMA1_DMAMUX_MDMA_init (void)
{
	//----Shutdown
	SPI1->CR1 = 0;
	DMAMUX1_Channel2->CCR = 0;
	DMA1_Stream2->CR = 0;
	MDMA_Channel2->CCR = 0;
 
	//------DMA1 Stream 2----
 
	// Configuration Register
	DMA1_Stream2->CR = DMA_SxCR_CIRC | DMA_SxCR_PL_0 | DMA_SxCR_PL_1 | DMA_SxCR_MINC | DMA_SxCR_TCIE;
	// Fifo control register
	DMA1_Stream2->FCR = 0;
	// Peripheral address register
	DMA1_Stream2->PAR = (uint32_t)&(SPI1->RXDR);
	// Memory address
	DMA1_Stream2->M0AR = (uint32_t)&(SPI1_RxBuffer[0]);
	// Second buffer in case of double buffer
	DMA1_Stream2->M1AR = 0;
	// Number of data items to transfer NDTR*PSIZE
	DMA1_Stream2->NDTR = READBACK_LENGTH;
 
	// Clear interrupt registers
	DMA1->LISR = 0x0F7D;
	DMA1->HISR = 0x0F7D;
	DMA1->LIFCR = 0x0F7D;
	DMA1->HIFCR = 0x0F7D;
 
	//------DMAMUX1 Channel 2----
 
	// Configuration Register
	DMAMUX1_Channel2->CCR = DMAMUX_CxCR_EGE | DMAMUX_SPI1_RX_ID;
	// Request Generator Configuration Register
	DMAMUX1_RequestGenerator2->RGCR = DMAMUX_RGxCR_GE | DMAMUX_RGxCR_GPOL_0 | 2U;
 
	// Clear interrupt registers
	DMAMUX1_ChannelStatus->CFR = 0x0000FFFF;
	DMAMUX1_RequestGenStatus->RGSR = 0x000000FF;
	DMAMUX1_RequestGenStatus->RGCFR = 0x000000FF;
 
	//------MDMA Channel 2----
 
	// Config Registers
	MDMA_Channel2->CCR = MDMA_CCR_PL_1 | MDMA_CCR_PL_0 | MDMA_CCR_TCIE | MDMA_CCR_CTCIE;
	MDMA_Channel2->CTCR = ((READBACK_LENGTH-1)CSAR = (uint32_t)&(SPI1_RxBuffer[0]);
	MDMA_Channel2->CDAR = (uint32_t)&(ads_buf[0]);
	MDMA_Channel2->CTBR = MDMA_CTBR_SBUS | MDMA_CTBR_SBUS | 2;
	// Clear interrupt
	MDMA_Channel2->CIFCR = 0x0000001F;
 
	// Configuration Register
 
 
	// Clear interrupt registers
	DMAMUX1_ChannelStatus->CFR = 0x0000FFFF;
	DMAMUX1_RequestGenStatus->RGSR = 0x000000FF;
	DMAMUX1_RequestGenStatus->RGCFR = 0x000000FF;
 
	//------SP1------
 
	// Configuration Registers
	SPI1->CR2 = (1<<16U) | READBACK_LENGTH;
	SPI1->CFG1 = (0x01110000) | SPI_CFG1_RXDMAEN;
	SPI1->CFG2 = SPI_CFG2_AFCNTR | SPI_CFG2_SSOE | SPI_CFG2_CPHA | SPI_CFG2_MASTER;
	SPI1->I2SCFGR = 0;
	// Clear interrupt registers
	SPI1->IFCR = 0x00000FF4;
 
	//Start Peripherals
	MDMA_Channel2->CCR |= MDMA_CCR_EN;
	DMAMUX1_Channel2->CCR |= DMAMUX_CxCR_EGE;
	DMA1_Stream2->CR |= DMA_SxCR_EN;
	SPI1->CR1 = SPI_CR1_SPE;
}

But the chronogram is still the exact same I posted before

This appears to be much more complicated than I thought. I don't use the H7 and I realized that the SPI in there is much more different from other STM32 than I thought, and I don't understand how Rx-only on this SPI is supposed to work. It appears to me that there's no such mode in this SPI.

In other STM32, once you set Rx-only mode and set SPE, it automatically starts to generate clocks. Here, it appears, that you are supposed to Tx to be able to generate clocks.

JW