cancel
Showing results for 
Search instead for 
Did you mean: 

G474 SPI DMA sync occasionally missed?

JamesAguilar
Associate

I have a program where I have TIM2 CH1 set up in output compare mode. Approximately 0.5 ms after the timer is reset, CH1's compare value is reached. This triggers the CH1 DMA, which is a dummy DMA on DMA1 channel 1. The SPI TX DMA is synced to the TIM2_CH1 DMA (with a request count of 16, to allow 16 bytes through the SPI), and therefore if it is armed should automatically start at that moment. The purpose of this is to eventually provide an interrupt-free delay for an external SPI slave that requires a 350ns delay between CSn active and SCLK active.

I have this all mostly working, but my problem is that about 5% of the time, the SPI DMA does not complete. I can't figure out what in my code is allowing this to happen. The basic structure is:

  • Arm SPI DMAs.
  • Reset TIM2 counter.
  • Wait until the DMA completes. (This should be triggered by TIM2 reaching the compare value.

However, for reasons I cannot understand, about 5-10% of my transfers never appear to start -- I wait for a long period of time after resetting the timer, and even after that, nothing happens. I've tried 1000ms, but the code I'll show below uses 10ms. The results are much the same either way.

Resetting the timer again solves the problem. I would assume that this indicates that the timer compare event was missed somehow, but the delay of 500usec should make this impossible, and anyway, the SPI DMAs are armed before the timer is even reset.

Another funny symtom I've noticed, that I can't explain. I twiddle PA4 in the process of this code to help trigger my oscilloscope. Usually, I can see the SPI clock associated with PA4 going high, and PA4 goes low sometime later (caused by resetting the pin in the TxRxCpltCallback). But sometimes, I see the SPI SCLK ticking without an associated high state of the PA4 GPIO.

Here's the main body of the program:

/* USER CODE BEGIN 4 */
osSemaphoreId_t tx_cmplt_event;

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
    LL_GPIO_SetOutputPin(GPIOA, GPIO_PIN_4);
  }
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
  if (hspi->Instance == SPI1) {
    spi_dma_in_flight = 0;
    spi_dma_success_count++;
    LL_GPIO_ResetOutputPin(GPIOA, GPIO_PIN_4);
    osSemaphoreRelease(tx_cmplt_event);
  }
}

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
  if (hspi->Instance == SPI1) {
    spi_dma_in_flight = 0;
    spi_last_error_code = hspi->ErrorCode;
    spi_dma_error_count++;
    osSemaphoreRelease(tx_cmplt_event);
  }
}

void Tim2Ch1DmaComplete(DMA_HandleTypeDef* hdma) {
}
/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
 * @brief  Function implementing the defaultTask thread.
 * @PAram  argument: Not used
 * @retval None
 */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  osSemaphoreAttr_t semaphore_def = {
    .name = "tx_cmplt_event",
  };
  tx_cmplt_event = osSemaphoreNew(1, 0, &semaphore_def);
  if (HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1) != HAL_OK) {
    Error_Handler();
  }
  LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2);

  int successful_transfers = 0;
  int timeouts = 0;
  for (;;) {
    static uint8_t spi_data[16];
    static uint8_t spi_rx_data[16];
    spi_data[0] = 0x55;
    spi_rx_data[0] = 0xF0;
    if (HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY ) {
      HAL_StatusTypeDef status = HAL_SPI_TransmitReceive_DMA(&hspi1, spi_data, spi_rx_data, sizeof(spi_data));
      if (status != HAL_OK) {
        spi_dma_error_count++;
      }
    }
    osDelay(1);
    LL_TIM_SetCounter(TIM2, 0);
    LL_GPIO_ResetOutputPin(GPIOA, GPIO_PIN_4);
    
    // Set up the SPI to send 0x55 over DMA.
    if (osSemaphoreAcquire(tx_cmplt_event, 10) == osErrorTimeout) {
      // If we time out waiting for the SPI transfer to complete, that's a problem.
      timeouts += 1;
    } else {
      successful_transfers += 1;
    }
  }
  /* USER CODE END 5 */
}

The entire project can be cloned from here. Particularly you might be interested in lookign at the whole main.c or stm32g4xx_hal_msp.c. Let me know if you spot what's wrong or have ideas where I can look.

1 ACCEPTED SOLUTION

Accepted Solutions
JamesAguilar
Associate

Setting

    pSyncConfig.RequestNumber = 17;  // Increased from 16.

appears to have completely resolved the issue. Any larger number than 17 also appears to work. From this I take it to be the case that the number of DMA requests from the SPI peripheral to shift 16 bytes is sometimes 17, but substantially never greater than that? I have to admit I find it extremely puzzling. But it doesn't seem to do any harm to set this number higher than required so I guess I'll go with it.

View solution in original post

1 REPLY 1
JamesAguilar
Associate

Setting

    pSyncConfig.RequestNumber = 17;  // Increased from 16.

appears to have completely resolved the issue. Any larger number than 17 also appears to work. From this I take it to be the case that the number of DMA requests from the SPI peripheral to shift 16 bytes is sometimes 17, but substantially never greater than that? I have to admit I find it extremely puzzling. But it doesn't seem to do any harm to set this number higher than required so I guess I'll go with it.