cancel
Showing results for 
Search instead for 
Did you mean: 

SPI1 + DMA Interrupt setup for RX only from a peripheral sensor (Nucleo-F767ZI)

t888
Associate II

Hello Everyone,

So, I am trying to configure the SPI1 to work with DMA so I can fill in a buffer from my sensor for every occurring interrupt, so when the buffer is full and I can send it to my client server.

I used CubeMX to configure SPI1 and DMA2 Stream2 for SPI1_RX. I enabled both DMA and SPI1 global interrupts.

I am confused in understand how the IRQ handler gets called, because I assume I need to fill my buffer during that routine. However, it never gets called. Below are the initialization and my code.

Thank you !

This is from CubeMX

**

* @brief SPI MSP Initialization

* This function configures the hardware resources used in this example

* @param hspi: SPI handle pointer

* @retval None

*/

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 if(hspi->Instance==SPI1)

 {

 /* USER CODE BEGIN SPI1_MspInit 0 */

 /* USER CODE END SPI1_MspInit 0 */

  /* Peripheral clock enable */

  __HAL_RCC_SPI1_CLK_ENABLE();

  

  __HAL_RCC_GPIOA_CLK_ENABLE();

  __HAL_RCC_GPIOD_CLK_ENABLE();

  /**SPI1 GPIO Configuration   

  PA5   ------> SPI1_SCK

  PA6   ------> SPI1_MISO

  PD7   ------> SPI1_MOSI 

  */

  GPIO_InitStruct.Pin = SPI_SCK_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_PULLUP;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;

  HAL_GPIO_Init(SPI_SCK_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SPI_MISO_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;

  HAL_GPIO_Init(SPI_MISO_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SPI_MOSI_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;

  HAL_GPIO_Init(SPI_MOSI_GPIO_Port, &GPIO_InitStruct);

  /* SPI1 DMA Init */

  /* SPI1_RX Init */

  hdma_spi1_rx.Instance = DMA2_Stream2;

  hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;

  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_HALFWORD;

  hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

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

 /* USER CODE BEGIN SPI1_MspInit 1 */

 /* USER CODE END SPI1_MspInit 1 */

 }

}

MY CODE

void dma_start(void){

/* pull CS low to enable SPI transfer */

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

/* read line from the SPI in 16 bits

* For video packets, the header includes a 2-byte(16bits) ID

* and a 2-byte (16 bits) CRC.

*/

HAL_SPI_TransmitReceive_DMA(&hspi1, &frame_packet[0],&frame_packet[0], FRAME_SIZE/2);

}

/**

 * @brief This function handles DMA2 stream2 global interrupt.

 */

void DMA2_Stream2_IRQHandler(void)

{

/* keep track of the number of lines */

int line_number=0;

static int expected_line_number = 0;

/* keep synchronization*/

static int sync_count = 0;

HAL_DMA_IRQHandler(&hdma_spi1_rx);

/* pull CS HIGH to end SPI transfer */

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

/* Examine the ID field of the packet, identifying a discard packet (xFxx). */

if((frame_packet[0] & 0xf00) != 0xf00)

{

/* store line number */

line_number = frame_packet[0] & 0xff;

/* check input line number and copy lepton buffer data consecutively */

if (line_number < NUMBER_OF_ROWS && line_number == expected_line_number){

memcpy(&lepton_image[line_number], &frame_packet[2], FRAME_PAYLOAD);

/* update line number */

expected_line_number = line_number+1;

/* check when full frame is transferred, 59 packets for raw14 mode */

if (line_number == (NUMBER_OF_ROWS - 1)) {

// data_server_send("s", 1);

// data_server_send((char*)lepton_image, sizeof(lepton_image));

}

}

else {

/* Deassert /CS and idle SCK for at least 5 frame periods (>185 msec).

* This step ensures a timeout of the VoSPI interface,

* which puts the Lepton in the proper state to establish

* (or re-establish) synchronization*/

HAL_Delay(200);

expected_line_number = 0;

sync_count++;

}

}

}

Once I call TramsitReceive_DMA, isnt supposed to jump to the IRQ handler?

In my main, I do dma_start() before the while loop.

6 REPLIES 6
S.Ma
Principal

If using the HAL, the file headers may explain that when DMA is used with a peripheral, it's the peripheral API functions to use (including callbacks) and not DMA's. Can you check this point?

Also I would sugges to get started with 4 wire SPI (and 2 DMA TX/RX), even if MOSI may not be wired to a pin (no half duplex, no Rxonly).

All this assuming HAL is being used.

t888
Associate II

Hi,

Thank you for being responsive.

I am apologize for the confusion. SPI is configured Full Duplex, 2 directions. By RX only, I meant that the DMA is setup like that.

I changed to use the HAL_SPI_RECEIVE_DMA which has the following information : But I do see that the the RX DMA is enabled. Howe

/**

 * @brief Receive an amount of data in non-blocking mode with DMA.

 * @param hspi pointer to a SPI_HandleTypeDef structure that contains

 *        the configuration information for SPI module.

 * @param pData pointer to data buffer

 * @note  When the CRC feature is enabled the pData Length must be Size + 1.

 * @param Size amount of data to be sent

 * @retval HAL status

 */

HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)

{

 HAL_StatusTypeDef errorcode = HAL_OK;

 if ((hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->Init.Mode == SPI_MODE_MASTER))

 {

  hspi->State = HAL_SPI_STATE_BUSY_RX;

  /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */

  return HAL_SPI_TransmitReceive_DMA(hspi, pData, pData, Size);

 }

 /* Process Locked */

 __HAL_LOCK(hspi);

 if (hspi->State != HAL_SPI_STATE_READY)

 {

  errorcode = HAL_BUSY;

  goto error;

 }

 if ((pData == NULL) || (Size == 0U))

 {

  errorcode = HAL_ERROR;

  goto error;

 }

 /* Set the transaction information */

 hspi->State    = HAL_SPI_STATE_BUSY_RX;

 hspi->ErrorCode  = HAL_SPI_ERROR_NONE;

 hspi->pRxBuffPtr = (uint8_t *)pData;

 hspi->RxXferSize = Size;

 hspi->RxXferCount = Size;

 /*Init field not used in handle to zero */

 hspi->RxISR    = NULL;

 hspi->TxISR    = NULL;

 hspi->TxXferSize = 0U;

 hspi->TxXferCount = 0U;

 /* Configure communication direction : 1Line */

 if (hspi->Init.Direction == SPI_DIRECTION_1LINE)

 {

  SPI_1LINE_RX(hspi);

 }

#if (USE_SPI_CRC != 0U)

 /* Reset CRC Calculation */

 if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)

 {

  SPI_RESET_CRC(hspi);

 }

#endif /* USE_SPI_CRC */

 /* packing mode management is enabled by the DMA settings */

 if ((hspi->Init.DataSize <= SPI_DATASIZE_8BIT) && (hspi->hdmarx->Init.MemDataAlignment == DMA_MDATAALIGN_HALFWORD))

 {

  /* Restriction the DMA data received is not allowed in this mode */

  errorcode = HAL_ERROR;

  goto error;

 }

 CLEAR_BIT(hspi->Instance->CR2, SPI_CR2_LDMARX);

 if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)

 {

  /* set fiforxthreshold according the reception data length: 16bit */

  CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);

 }

 else

 {

  /* set fiforxthreshold according the reception data length: 8bit */

  SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);

 }

 /* Set the SPI RxDMA Half transfer complete callback */

 hspi->hdmarx->XferHalfCpltCallback = SPI_DMAHalfReceiveCplt;

 /* Set the SPI Rx DMA transfer complete callback */

 hspi->hdmarx->XferCpltCallback = SPI_DMAReceiveCplt;

 /* Set the DMA error callback */

 hspi->hdmarx->XferErrorCallback = SPI_DMAError;

 /* Set the DMA AbortCpltCallback */

 hspi->hdmarx->XferAbortCallback = NULL;

 /* Enable the Rx DMA Stream/Channel */

 HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->DR, (uint32_t)hspi->pRxBuffPtr, hspi->RxXferCount);

 /* Check if the SPI is already enabled */

 if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)

 {

  /* Enable SPI peripheral */

  __HAL_SPI_ENABLE(hspi);

 }

 /* Enable the SPI Error Interrupt Bit */

 __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

 /* Enable Rx DMA Request */

 SET_BIT(hspi->Instance->CR2, SPI_CR2_RXDMAEN);

error:

 /* Process Unlocked */

 __HAL_UNLOCK(hspi);

 return errorcode;

}

hspi1.Instance = SPI1;

hspi1.Init.Mode = SPI_MODE_MASTER;

hspi1.Init.Direction = SPI_DIRECTION_2LINES;

hspi1.Init.DataSize = SPI_DATASIZE_16BIT;

hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;

hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;

hspi1.Init.NSS = SPI_NSS_SOFT;

hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;

hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

hspi1.Init.TIMode = SPI_TIMODE_DISABLE;

hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi1.Init.CRCPolynomial = 7;

hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;

hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

And this is my SPI setup :

t888
Associate II

Also, why would you want to configure 2 DMA TX/RX? Do i need to TX something over SPI in order to have the RX channel working?

S.Ma
Principal

If you want to be SPI master and control how many SCK bit clocks to move data, yes you do.

t888
Associate II

Okay, I just configured 2DM TX/RX.

Then I should do my buffer filling in HAL_SPI_RxCpltCallback ?

I am currently looking at a UART+DMA example, and trying to setup the same: https://www.youtube.com/watch?v=urHGtvLYctk

Is it the right approach?

Thank you for your response.

S.Ma
Principal

Find a Nucleo STM32F7 Cube SPI example code with DMA. Some extra work will be needed.

USART and SPI behaviour is very different.

Remember that TX is to generate the right amount of clocks.

RX event (callback) will be the event that the transfer is completed.

Have a look at the HAL driver SPI files top comment lines to get the right hints on doing things.