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 регистрирует ОШИБКУ
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 реализован, чтобы отметить это.
Спасибо за помощь и поддержку.
Я надеюсь, что это помогает другим людям.
С наилучшими пожеланиями.
Алехандро.