cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F072 circular DMA with SPI bus transfers less than 6 bytes and DMA transfer complete interrupt problem

DSudo.2
Associate III

I'm trying to use circular DMA with a SPI bus peripheral, where the DMA transfer complete interrupt causes something to happen after each block of data is DMA'd to the SPI bus. If my DMA transfer size is greater than 5, everything works well according to my oscilloscope. If the transfer size is 5, the number of transfers between transfer complete interrupts bounces around  in an unstable fashion. If I set the transfer size to less than 5, there are always 6 SPI bus transfers per transfer complete interrupt. I'm using all HAL functions to do this, and when I stop in the debugger and inspect special function registers, everything looks good. Any idea what might be causing this or how to fix it? I need this to work with a circular buffer of only 3 bytes.

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();

  uint32_t timer1 = HAL_GetTick();

  EnableIoPinChannel(IOPORTA, 7);
  SetIoPinChannelWaveform(IOPORTA, 7, 0x1);

  uint8_t buffer[24] = {0};
  for(uint8_t i = 0; i < sizeof(buffer); i++){
    buffer[i] = i;
  }
  // setting transfer sizes:
  // > 5 --> that's how many transfers are sent to the SPI bus per transfer complete interrupt
  // = 5 --> erratic number of transfers are sent to the SPI bus per transfer complete interrupt
  // < 5 --> 6 transfers are sent to the SPI bus per transfer complete interrupt  
  HAL_SPI_Transmit_DMA(&hspi1, buffer, 11);

  while (1)
  {
    if((HAL_GetTick() - timer1) >= 250){
      timer1 += 250;
      HAL_GPIO_TogglePin(LED);
    }
  }
}
void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  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_HARD_OUTPUT;
  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_ENABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

 

void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

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

 

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA15     ------> SPI1_NSS
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = SPI_SSEL_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
    HAL_GPIO_Init(SPI_SSEL_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_CLOCK_Pin|SPI_MISO_Pin|SPI_MOSI_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* SPI1 DMA Init */
    /* SPI1_TX Init */
    hdma_spi1_tx.Instance = DMA1_Channel3;
    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_CIRCULAR;
    hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi1_tx);

  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

 Thanks in advance for any help/suggestions.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

How are you measuring this? I don't see your transfer complete interrupt code anywhere.

Probably, you are running into an issue where the CPU can't complete the interrupt handler before the next one comes in. If the SPI is in circular mode, it is constantly going. TC interrupts will happen constant. You can slow down the clock speed to help with this, or reduce the complexity of your code, or compile with optimizations.

In general, you should limit interrupts to tens of khz, preferably less.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

3 REPLIES 3
TDK
Guru

How are you measuring this? I don't see your transfer complete interrupt code anywhere.

Probably, you are running into an issue where the CPU can't complete the interrupt handler before the next one comes in. If the SPI is in circular mode, it is constantly going. TC interrupts will happen constant. You can slow down the clock speed to help with this, or reduce the complexity of your code, or compile with optimizations.

In general, you should limit interrupts to tens of khz, preferably less.

If you feel a post has answered your question, please click "Accept as Solution".
DSudo.2
Associate III

Your are correct... I did not include my TC interrupt code. I basically copied the provided weak interrupt handler, and pasted in some code to do something completely unrelated to DMA or SPI. It's by design a relatively high frequency interrupt that happens every 3 SPI bus writes. It's the highest priority interrupt in the system, and does very little, but what it does is important. It'll hurt, but I'll slow down the SPI bus clock and see if things then work like I think they should be working now (only slower).

This is happening on a simple I/O expander-like board that has limited functionality.

I'm measuring this by triggering my scope on what the TC ISR does. Part of that is manipulating an I/O pin via a GPIO port BSRR register. I'm probing the SPI bus too, and can see the number of SPI bus writes being wrong when I transfer 5 or less bytes in my circular DMA buffer. 6 or more bytes gives me the desired TC INT behavior, but at 1/2 the frequency I'd like.

DSudo.2
Associate III

I was able to fix the problem by changing the SPI bus initialization code to use a larger prescaler for the SPI bus clock generation. After that, the 3 byte circular buffer DMA write worked reliably. You were correct about the ISR exiting/re-entering problem. Thanks TDK!