SPI master fires uncontrollable DMA TC interrupts without receiving nothing

Hi all,

Introduction

I have two Nucleo L432kc board, I want to make them communicate with the SPI protocol using DMA.

In the following scheme you can see the actual hardware setup:

What works

If I send data from the master to the slave I receive them correctly and when the master is not transmitting the slave does not receive anything.

Master’s code

#include <mbed.h>
uint8_t dma_buffer_tx[4];
uint8_t dma_buffer_rx[4];
uint8_t buff[4];
uint32_t receive_buff_length = 4;

unsigned int c = 0;

Serial pc(USBTX,USBRX,921600);
DigitalOut led(LED3); 

SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi1_tx;

void Error_Handler(){
  led.write(1);
  while(1){}
}

static void HAL_GPIO_Init(void){

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi1.Instance==SPI1)
  {    
      __HAL_RCC_GPIOA_CLK_ENABLE();
      /**SPI1 GPIO Configuration    
      PA1     ------> SPI1_SCK
      PA11     ------> SPI1_MISO
      PA12     ------> SPI1_MOSI 
      */
      GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_11|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_SPI1;
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }

  else
    Error_Handler();
}

static void SPI1_Init(void)
{
  __HAL_RCC_SPI1_CLK_ENABLE();

  /* SPI1 parameter configuration*/
  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_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

  /* SPI1 interrupt Init */
  HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

static void DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* SPI1 DMA Init */
  /* SPI1_RX Init */
    hdma_spi1_rx.Instance = DMA1_Channel2;
    hdma_spi1_rx.Init.Request = DMA_REQUEST_1;
    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;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }

  __HAL_LINKDMA(&hspi1,hdmarx,hdma_spi1_rx);

  /* DMA interrupt init */
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);

    /* SPI1 DMA Init */
  /* SPI1_TX Init */
  hdma_spi1_tx.Instance = DMA1_Channel3;
  hdma_spi1_tx.Init.Request = DMA_REQUEST_1;
  hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_spi1_tx.Init.Mode = DMA_NORMAL;
  hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
  if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
  {
    Error_Handler();
  }

  __HAL_LINKDMA(&hspi1,hdmatx,hdma_spi1_tx);

  /* DMA interrupt init */
  /* DMA1_Channel3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);

}

extern "C"{

  void DMA1_Channel3_IRQHandler(void)
  {
    HAL_NVIC_ClearPendingIRQ(DMA1_Channel3_IRQn);
    HAL_DMA_IRQHandler(&hdma_spi1_tx);
  }

  void DMA1_Channel2_IRQHandler(void)
  {
    HAL_NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn);
    HAL_DMA_IRQHandler(&hdma_spi1_rx);
  }

  void SPI1_IRQHandler(void)
  {
    HAL_SPI_IRQHandler(&hspi1);
  }
}

void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi1){

  for(int i = 0; i < receive_buff_length/2; i++){
    buff[i] = dma_buffer_rx[i];
  }

}

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi1){

  for(int i = receive_buff_length/2; i < receive_buff_length; i++){
    buff[i] = dma_buffer_rx[i];
  }
  
  printf("%u\n",*(unsigned int *)buff); // to understan when I am actually receiving data
  memset(dma_buffer_rx,0,sizeof(dma_buffer_rx));
}

void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi1){
  
  c += 5;
  dma_buffer_tx[0] = c & 0xFF; 
  dma_buffer_tx[1] = (c >> 8) & 0xFF;

}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi1){

  dma_buffer_tx[2] = (c >> 16) & 0xFF;
  dma_buffer_tx[3] = (c >> 24) & 0xFF;
}

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* SPI INIT */
  SPI1_Init();

  /* GPIO USART2 INIT */
  HAL_GPIO_Init();

  /* DMA INIT */
  DMA_Init();

  c = 1000;
  dma_buffer_rx[0] = c & 0xFF; 
  dma_buffer_rx[1] = (c >> 8) & 0xFF;
  dma_buffer_rx[2] = (c >> 16) & 0xFF;
  dma_buffer_rx[3] = (c >> 24) & 0xFF;
  
  
  while(true){
    HAL_SPI_Transmit_DMA(&hspi1,dma_buffer_rx,receive_buff_length); 
    wait(0.001);
  }
}

Slave’s code

#include <mbed.h>
uint8_t dma_buffer_tx[4];
uint8_t dma_buffer_rx[4];
uint8_t buff[4];
uint32_t receive_buff_length = 4;

unsigned int c = 0;

Serial pc(USBTX,USBRX,921600);
DigitalOut led(LED3); 

SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi1_tx;

void Error_Handler(){
  led.write(1);
  while(1){}
}

static void HAL_GPIO_Init(void){

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi1.Instance==SPI1)
  {    
      __HAL_RCC_GPIOA_CLK_ENABLE();
      /**SPI1 GPIO Configuration    
      PA1     ------> SPI1_SCK
      PA11     ------> SPI1_MISO
      PA12     ------> SPI1_MOSI 
      */
      GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_11|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_SPI1;
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }

  else
    Error_Handler();
}

static void SPI1_Init(void)
{
  __HAL_RCC_SPI1_CLK_ENABLE();

  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_SLAVE;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

  /* SPI1 interrupt Init */
  HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

static void DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* SPI1 DMA Init */
  /* SPI1_RX Init */
    hdma_spi1_rx.Instance = DMA1_Channel2;
    hdma_spi1_rx.Init.Request = DMA_REQUEST_1;
    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;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }

  __HAL_LINKDMA(&hspi1,hdmarx,hdma_spi1_rx);

  /* DMA interrupt init */
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);

    /* SPI1 DMA Init */
  /* SPI1_TX Init */
  hdma_spi1_tx.Instance = DMA1_Channel3;
  hdma_spi1_tx.Init.Request = DMA_REQUEST_1;
  hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_spi1_tx.Init.Mode = DMA_NORMAL;
  hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
  if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
  {
    Error_Handler();
  }

  __HAL_LINKDMA(&hspi1,hdmatx,hdma_spi1_tx);

  /* DMA interrupt init */
  /* DMA1_Channel3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);

}

extern "C"{

  void DMA1_Channel3_IRQHandler(void)
  {
    HAL_NVIC_ClearPendingIRQ(DMA1_Channel3_IRQn);
    HAL_DMA_IRQHandler(&hdma_spi1_tx);
  }

  void DMA1_Channel2_IRQHandler(void)
  {
    HAL_NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn);
    HAL_DMA_IRQHandler(&hdma_spi1_rx);
  }

  void SPI1_IRQHandler(void)
  {
    HAL_SPI_IRQHandler(&hspi1);
  }
}

void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi1){

  for(int i = 0; i < receive_buff_length/2; i++){
    buff[i] = dma_buffer_rx[i];
  }

}

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi1){

  for(int i = receive_buff_length/2; i < receive_buff_length; i++){
    buff[i] = dma_buffer_rx[i];
  }
  
  printf("%u\n",*(unsigned int *)buff); // to understan when I am actually receiving data
  memset(dma_buffer_rx,0,sizeof(dma_buffer_rx));
}

void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi1){
  
  c += 5;
  dma_buffer_tx[0] = c & 0xFF; 
  dma_buffer_tx[1] = (c >> 8) & 0xFF;

}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi1){

  dma_buffer_tx[2] = (c >> 16) & 0xFF;
  dma_buffer_tx[3] = (c >> 24) & 0xFF;
}

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* SPI INIT */
  SPI1_Init();

  /* GPIO USART2 INIT */
  HAL_GPIO_Init();

  /* DMA INIT */
  DMA_Init();

  c = 1000;
  dma_buffer_rx[0] = c & 0xFF; 
  dma_buffer_rx[1] = (c >> 8) & 0xFF;
  dma_buffer_rx[2] = (c >> 16) & 0xFF;
  dma_buffer_rx[3] = (c >> 24) & 0xFF;
  
  
  while(true){
    HAL_SPI_Receive_DMA(&hspi1,dma_buffer_rx,receive_buff_length); 
  }
}

What does not work

If I change the role in the communication, so I send the data from the salve to the master swapping the following lines

[...]
HAL_SPI_Transmit_DMA(&hspi1,dma_buffer_rx,receive_buff_length); 
wait(0.001);
[...]

with:

[...]
HAL_SPI_Receive_DMA(&hspi1,dma_buffer_rx,receive_buff_length); 
[...]

Even if the slave is not powered up the master continusly receives interrupts and invokes the HAL_SPI_RxCpltCallback. If we power up the slave the master prints random numbers.

What I have already tried without success

  1. I’ve tested the master code on a Nucleo F446 board.
  2. I’ve set pull-down and pull-up resistors on the MISO pin.
  3. I’ve tried different cable for the hw connection.

Considerations

Walking throught the HAL_SPI_Receive_DMA source I’ve noticed that in the SPI_MODE_MASTER the HAL_SPI_TransmitReceive_DMA fuction is actually called.
I think that the same buffer is used for transmission and reception, but I don’t know how to prove that.
I’ve also printed the hdma_spi1_rx.Instance->CNDTR and I’ve noticed that the value increments to 4 whitch is actually the number of bytes that we are going to receive.