STM32F - SPI с DMA "ErrorCallback" и смещенными кадрами

Я передаю 2 uC (дисплей Arduino как MASTER и STM32F429 как подчиненный). Его связь состоит из 10 байтов в полнодуплексном режиме через SPI с использованием DMA, каждые 150 мс.

В течение нескольких минут связь идет очень хорошо, оба ОК правильно отправляют свои 10 байтов. За это время я заметил, что вызывается функция "HAL_SPI_ErrorCallback", потому что я добавил в нее счетчик, и он постепенно увеличивается, но коммикация все равно идет хорошо.

Мой первый вопрос: это нормально, что иногда функция ErrorCallback вызывается случайно? из-за шума или чего-то еще в связи произошла мгновенная ошибка... Я думаю...

Вот захват сигнала MISO в зеленом, CLK в белом и CS в желтом

С другой стороны, через некоторое время (случайным образом 10 мин, 1 ч...) связь нарушается только в сигнале MISO, STM32 отправляет 10-байтовый кадр, но вместо отправки Байт0 Байт1 Байт2 Байт3 Байт4 Байт5 Байт6 Байт7 Байт8 Байт9 Байт10, (Сначала LSB)

он отправляет:

Байт10 Байт0 Байт1 Байт2 Байт3 Байт4 Байт5 Байт6 Байт7 Байт8 Байт9, ПЕРЕМЕЩАЕТСЯ НА ПРАВО 1 байт!?!?

В приложении вы можете увидеть захват "Working.jpg" с byte0 = 0x02, а остальные байты = 0. В другом захвате "NOT_working.jpg" - захват с проблемой. Некоторое время оба uC работали должным образом, и внезапно uC STM32 начал отправлять этот кадр все время (коммуникационный кадр - байт = 0x02, а остальные байты = 0, чтобы легко увидеть эту ошибку).

Working.jpg - это сигнал MISO, отправляющий кадр правильно

NOT_working.jpg - который является сигналом MISO, отправляющим кадр неправильно

Я пробовал связь в: "Init.Mode = DMA_NORMAL" и "DMA_CIRCULAR", и обе конфигурации имеют одинаковое поведение. Я создал 2 переменные, чтобы выяснить проблему:

    DMA_counter_RX = __HAL_DMA_GET_COUNTER(&hdma_spi6_rx);
    DMA_counter_TX = __HAL_DMA_GET_COUNTER(&hdma_spi6_tx); 

И связь идет хорошо, DMA_counter_RX = 10, НО DMA_counter_TX = 9. Это нормальные значения. Но как только возникает ошибка сдвига, оба счетчика DMA = 10.

Также эта проблема всегда возникает в режиме отладки, когда я нажимаю "приостановить" (пауза) и "возобновить" (воспроизведение), как только я нажимаю "возобновить", и процессор продолжает выполнение программы, сигнал MISO смещается навсегда.

Кроме того, я использую TIM1, TIM5, TIM2, TIM3 и TIM4 для других вещей, таких как ШИМ и прерывания, но не связанные с SPI...

Я попытался решить эту проблему, изменив все приоритеты NVIC для всех прерываний и так далее, но проблема усугубляется.

Я использую System Workbench для последней версии STM32.

Любая помощь ценится! Спасибо заранее и наилучшими пожеланиями.

Alejandro

Извините за длинный вопрос...:(

Ниже вы можете увидеть мою конфигурацию для SPI и DMA, если она может помочь вам:

void MX_DMA_Init(void)
{
  __HAL_RCC_DMA2_CLK_ENABLE();



  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

}

void MX_SPI6_Init(void)
{

  hspi6.Instance = SPI6;
  hspi6.Init.Mode = SPI_MODE_SLAVE;
  hspi6.Init.Direction = SPI_DIRECTION_2LINES;
  hspi6.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi6.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi6.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi6.Init.NSS = SPI_NSS_HARD_INPUT;
  hspi6.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi6.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi6.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi6) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{

      GPIO_InitTypeDef GPIO_InitStruct;
      if(hspi->Instance==SPI6)
      {
        __HAL_RCC_SPI6_CLK_ENABLE();



        /**SPI6 GPIO Configuration
        PG8     ------> SPI6_NSS
        PG12     ------> SPI6_MISO
        PG13     ------> SPI6_SCK
        PG14     ------> SPI6_MOSI
        */
        GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI6;
        HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);



        hdma_spi6_rx.Instance = DMA2_Stream6;
        hdma_spi6_rx.Init.Channel = DMA_CHANNEL_1;
        hdma_spi6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_spi6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_spi6_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_spi6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_spi6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_spi6_rx.Init.Mode = DMA_NORMAL;
        hdma_spi6_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_spi6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_spi6_rx) != HAL_OK)
        {
          Error_Handler();
        }



        __HAL_LINKDMA(hspi,hdmarx,hdma_spi6_rx);



        hdma_spi6_tx.Instance = DMA2_Stream5;
        hdma_spi6_tx.Init.Channel = DMA_CHANNEL_1;
        hdma_spi6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_spi6_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_spi6_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_spi6_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_spi6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_spi6_tx.Init.Mode = DMA_NORMAL;
        hdma_spi6_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_spi6_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_spi6_tx) != HAL_OK)
        {
          Error_Handler();
        }



        __HAL_LINKDMA(hspi,hdmatx,hdma_spi6_tx);



        /* Peripheral interrupt init */
        HAL_NVIC_SetPriority(SPI6_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(SPI6_IRQn);
      }
}

Во время кода инициализации я настраиваю SPI6 и DMA, как описано выше, сразу после этого я включаю связь, используя:

HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data, 10);

Также были добавлены следующие 2 функции, связанные с коммуникацией SPI:

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi -> Instance == SPI6)
    {
        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data, 10);
    }
}





void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi -> Instance == SPI6)
    {

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data,10);
    }
}

STM куб mx автоматически создается:

void DMA2_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream5_IRQn 0 */



  /* USER CODE END DMA2_Stream5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi6_tx);
  /* USER CODE BEGIN DMA2_Stream5_IRQn 1 */



  /* USER CODE END DMA2_Stream5_IRQn 1 */
}



/**
* @brief This function handles DMA2 stream6 global interrupt.
*/
void DMA2_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */



  /* USER CODE END DMA2_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi6_rx);
  /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */



  /* USER CODE END DMA2_Stream6_IRQn 1 */
}



void SPI6_IRQHandler(void)
{
  /* USER CODE BEGIN SPI6_IRQn 0 */



  /* USER CODE END SPI6_IRQn 0 */
  HAL_SPI_IRQHandler(&hspi6);
  /* USER CODE BEGIN SPI6_IRQn 1 */



  /* USER CODE END SPI6_IRQn 1 */
}

------------------------------ EDITED ------------------- --------- Я добавляю 2 захвата регистра SPI

SPI регистрирует РАБОТУ

SPI регистрирует ОШИБКУ

1 ответ

Я наконец получил решение, я нашел, в чем проблема!

Обычно сигнал CS переходит от 1 до 0, затем MISO и MOSI обмениваются данными, и после завершения связи сигнал CS переходит от 0 до 1, и STM32F429 продолжает выполнение остальных задач...

Это происходило каждые 150 мс, это период времени, когда оба ОК общаются. Но STM32 uC имеет другие задачи с более высоким приоритетом, чем связь SPI.

Когда один из этих более высоких приоритетов начинается во время связи SPI, и как только этот более высокий приоритет выполняется, тогда UC продолжает выполнение задачи (это был SPI), очевидно, что этот кадр теряется, и выполняется "HAL_SPI_ErrorCallback", а затем SPI перезапускается. Если SPI перезапускается, когда сигнал CS равен 1 (spidle), то проблем нет, SPI перезапускается правильно, и следующий кадр будет принят без проблем. НО, если SPI перезапускается, когда сигнал CS равен 0 (SPM STM32 выбран и готов к передаче), тогда STM32 ожидает отправки и получения количества байтов, но будет принимать меньше, поэтому несоответствие байтов связи является ключом эта проблема.

Я решил эту проблему, просто добавив:

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi -> Instance == SPI6)
    {

        while(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) != GPIO_PIN_SET) // CS signal
        {
        }

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data,10);
    }
}

Я должен изменить "WHILE", чтобы не останавливать процессор, но это первое приближение.

Теперь связь работает все время, но иногда кадр теряется (и вызывается "HAL_SPI_ErrorCallback") из-за задачи с более высоким приоритетом. Но это нормально, CRC реализован, чтобы отметить это.

Спасибо за помощь и поддержку.

Я надеюсь, что это помогает другим людям.

С наилучшими пожеланиями.

Алехандро.

Другие вопросы по тегам