cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA continuous sending

JHERI
Senior

hello...

ok so I have managed to get the SPI port on my 446RE nucleo board sending out data to my slave Bus Expander IC via the DMA peripherial.

I am new to the DMA peripheral and not sure if I even need to use it over using interrupt control, but would like to get a little more confident with the SPI DMA use. any help of how it all working and tips on coding would be very much appreciated

I am using the following code, to send 3 bytes to the DMA

                    
uint8_t MCP23017_INIT[]   ={ WR_ADR_0, 0x0A, 0xAA};
//uint8_t MCP23017_INITA[] ={ WR_ADR_0, IODIRA, ALL_INPUT,  0x00, 0xFF, 0xFF, 0xFF, 0x8A, 0x00};
uint8_t MCP23017_INITB[]  ={ WR_ADR_0, 0x10, 0x00};
uint8_t MCP23017_INITB2[] ={ WR_ADR_0, 0x1A, 0xFF};
 
// ALL_OUTPUT, 0x00, 0x00, ALL_PINS, NO_PINS };
 
    HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) MCP23017_INIT, 3 ) ;
 
 
    HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) MCP23017_INITB, 3 ) ;
 
 
    HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) MCP23017_INITB2, 3 ) ;

this is my DMA CONFIG.

/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_tx;
 
/* SPI2 init function */
void MX_SPI2_Init(void)
{
 
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_HARD_OUTPUT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI2)
  {
 
    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();
  
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
 
    /**SPI2 GPIO Configuration    
    PC1     ------> SPI2_MOSI
    PC2     ------> SPI2_MISO
    PB10     ------> SPI2_SCK
    PB12     ------> SPI2_NSS 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_SPI2;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    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_SPI2;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12;
    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_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    /* SPI2 DMA Init */
    /* SPI2_TX Init */
    hdma_spi2_tx.Instance = DMA1_Stream4;
    hdma_spi2_tx.Init.Channel = DMA_CHANNEL_0;
    hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi2_tx.Init.Mode = DMA_NORMAL;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_spi2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(spiHandle, hdmatx, hdma_spi2_tx);
 
    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);
 
  }
}
 
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
 
  if(spiHandle->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspDeInit 0 */
 
  /* USER CODE END SPI2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI2_CLK_DISABLE();
  
    /**SPI2 GPIO Configuration    
    PC1     ------> SPI2_MOSI
    PC2     ------> SPI2_MISO
    PB10     ------> SPI2_SCK
    PB12     ------> SPI2_NSS 
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1|GPIO_PIN_2);
 
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_12);
 
    /* SPI2 DMA DeInit */
    HAL_DMA_DeInit(spiHandle->hdmatx);
 
    /* SPI2 interrupt Deinit */
    HAL_NVIC_DisableIRQ(SPI2_IRQn);
  /* USER CODE BEGIN SPI2_MspDeInit 1 */
 
  /* USER CODE END SPI2_MspDeInit 1 */
  }
} 

I also have two interrupts set up,

1) FIFO Half Full

2) SPI TX Complete

I am not sure what I have to do in sequentially sending SPI data,

HAL_SPI_Transmit_DMA( &hspi2, (uint8_t*) MCP23017_INIT, 3 ) ;

If I want 3 or 10 of the above lines one after the other, i.e. sending SPI TX Data

what do I need to test for to ensure all SPI data gets sent, and the SPI peripheral is free to accept the next packet of SPI data.

do I need to poll,?

do I need to set a software flag in the tx complete interrupt, that then allows me to send the next spi data ???

so I want to do this

HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) MCP23017_INIT, 3 ) ;
HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) DATA1, 30 ) ;
HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) DATA2, 3 ) ;
HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) DATA3, 3 ) ;
HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) DATA4, 3 ) ;
HAL_SPI_Transmit_DMA(  &hspi2, (uint8_t*) DATA5, 3 ) ;

What do I need in between each line above to ensure all SPI DMA data is sequentially sent when ready.

I am assuming I need to wait until the SPI data has been transmitted before trying to send the next packet of data, I know the DMA has a FIFO, but not even sure If i want or need to use this?

DMA direct mode may be better

any help with this DMA spi peripheral will be very much appreciatyed

Regards

1 REPLY 1
Amel NASRI
ST Employee

Hi @JHERI​ ,

You need to wait that SPI becomes ready to perform a new transmission. In the examples provided in Cube packages, it is implemented this way:

 while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
 {
 } 

You can start with a project that you configure by yourself using STM32CubeMX, then add processing code taking as reference an example like STM32Cube_FW_F4_V1.23.0\Projects\STM32F4-Discovery\Examples\SPI\SPI_FullDuplex_ComDMA.

Hope this will bring you some help.

-Amel

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.