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

This discussion is locked. Please start a new topic to ask your question.
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.