Настройте SPI Slave для обработки данных, поступающих в неподходящее время.

Я экспериментирую с протоколом SPI (высокие скорости, при которых 5 байт данных передаются чуть менее 500 мкс) на STM32F410, сконфигурированном как полнодуплексное ведомое устройство SPI. Ведущее устройство отправляет байт команды на STM32F4, а STM32F4 отвечает 4 байтами подтверждения, за которыми следует процедура, которая выполняется примерно за 1,3 мс (измеряется по подсчету тактовых циклов DWT). Ожидается, что эта 5-байтовая передача данных будет происходить от 25 до 27 раз в секунду.

Я заметил, что иногда ведущее устройство может отправлять одни и те же данные последовательно в диапазоне 90 мкс (ведомое устройство STM32F4 должно ожидать получения чередующихся данных через SPI с интервалом не менее 4 мс между ними). Я прикрепил скриншоты логического анализатора, показывающие это явление:

Следует ожидать первого полученного байта 0x42, и ведомое устройство STM32F4 ответило соответствующими байтами (обратите внимание, что второго 0x42 после этого быть не должно):Первая часть необычного явления

Получение второго байта 0x42 не должно было происходить (в этом сценарии ведущее устройство не может быть настроено/запрограммировано):Вторая порция необычного явления

После описанного выше случая ведомое устройство STM32F4 больше никогда не сможет правильно ответить ведущему устройству (байты подтверждения, отправляемые обратно ведущему устройству, всегда имеют значение 0x99 — это байт, настроенный в буфере отправки прерываний SPI):Неправильный ответ отправляется обратно мастеру все время после ошибки

В этой ситуации я попытался удалить вызов функции __AppliSendBuff(), который дает команду RF-трансиверу на передачу данных, и когда происходит необычное событие, ведомое устройство STM32F4 все еще может правильно реагировать на ведущее устройство.

Мой вопрос : как я могу игнорировать данные SPI, поступающие от мастера в неожиданное время? Почему одна неудачная SPI-транзакция приводит к сбою всех последующих SPI-транзакций? Какие меры предосторожности следует принимать при написании кода для ведомого устройства STM32F4?

Что я пробовал : отключение прерываний SPI_CS GPIO всякий раз, когда микросхема CS отключается, чтобы в середине всего процесса транзакция SPI не происходила до тех пор, пока я снова не включу прерывание SPI_CS GPIO. До этого изменения во второй полученной команде 0x42 ведомое устройство STM32F4 просто отвечало 0x99 на 0x42, а 0xFF следовали за 0x42. Несмотря на это, все остальные транзакции SPI по-прежнему терпят неудачу, и ведомое устройство отвечает кодом 0x99.

Мой код STM32F4 :
сегмент прерывания:

/*** STM32F4 INTERRUPT HANDLERS ***/

/* GPIO Interrupt Handler */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
   if(GPIO_Pin == SPI2_CS_PIN) {
      /* Temporarily disable SPI2 CS interrupt */
      HAL_NVIC_DisableIRQ(EXTI1_IRQn);

      /* Generate interrupt flags when master device sends SPI data */
      /* dummybyte is a uint8_t array of size 1, with the element being 0x99 */
      HAL_SPI_TransmitReceive_IT(&hspi2, (uint8_t*)dummybyte, (uint8_t*)pSpi2RxBuff, cSpi2RxLen);
   }
   else if (...) {
      ...
   }
}

/* SPI Interrupt Handler */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
   if(hspi->Instance == SPI2) {
      if (...) {
         ...
      }
      else if(pSpi2RxBuff[0] == 0x41) {
         /* Activate 0x41 flag for further processing in main loop if 0x41 was received */
         xFlag_41 = SET;
      }
      else if(pSpi2RxBuff[0] == 0x42) {
         /* Activate 0x42 flag for further processing in main loop if 0x42 was received */
         xFlag_42 = SET;
      }
      else {
         /* Discard data by doing nothing */
      }
   }
}

Основной цикл:

while (1) {
   if(xFlag_41 == SET) {
      /* Respond back to system with acknowledge bytes */
      HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)pSend41Data, (uint8_t*)pTempRxBuff, cSpi2TxLen, SPI2_TIMEOUT);
      
      /* Commands RF Transceiver to transmit data respective to received command from master */
      __AppliSendBuff(&command41);

      xFlag_41 = RESET;

      /* Re-enable SPI2_CS interrupts. Note that priority was set in MX_GPIO_Init() */
      HAL_NVIC_EnableIRQ(EXTI1_IRQn);
   }
   else if(xFlag_42 == SET) {
      /* Respond back to system with acknowledge bytes */
      HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)pSend42Data, (uint8_t*)pTempRxBuff, cSpi2TxLen, SPI2_TIMEOUT);
          
      /* Commands RF Transceiver to transmit data respective to received command from master */
      __AppliSendBuff(&command42);

      xFlag_42 = RESET;

      /* Re-enable SPI2_CS interrupts. Note that priority was set in MX_GPIO_Init() */
      HAL_NVIC_EnableIRQ(EXTI1_IRQn);
   }
}

Должен ли я также настраивать ведомое устройство SPI для передачи 4 фиктивных байтов (возможно, для очистки регистров данных SPI) в случае, если я получаю ненужный/незарегистрированный командный байт?

Спасибо!


Обновление: я пропустил отключение отключения прерывания линии CS (которое является отключением прерывания GPIO) и переместил действия сброса флага на первую строку в блоках else if, и в логическом анализаторе были показаны некоторые изменения (первая правильная последовательность была все еще такой же):

Вторая неверная последовательность:Вторая неправильная последовательность

0x41 сразу после второй неправильной последовательности выше:Неправильно 0x41

0x42 сразу после 0x41 выше (неверные байты отправлены обратно мастеру):Неправильно 0x42

И все байты, которыми затем обмениваются, как показано ниже (независимо от 0x41 или 0x42):Неправильные последовательности после

Я забыл включить приоритеты прерываний в исходный пост выше:

/* SPI CS line has the highest interrupt priority */
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
/* SPI Peripheral connected to the master device */
HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1);
HAL_NVIC_EnableIRQ(SPI2_IRQn);

Ответы (1)

У меня небольшая проблема с тем, что произошло до того, как вы добавили прерывание GPIO, и после. Если я правильно понимаю, то, что происходило до того, как вы добавили это, было:

  1. После первой (правильной) последовательности вы выходите из ISR с установленным флагом xFlag_42 и, таким образом, в конечном итоге попадаете в __AppliSendBuff(&command42).

  2. На полпути через __AppliSendBuff прерывание подтверждается из-за неправильной последовательности.

  3. ISR устанавливает буфер для отправки 0x99 и возвращается к точке, в которой он выполнялся до получения прерывания.

  4. Следующая строка сбрасывает xFlag_42, поэтому после этого отправляется 0x99.

Все это имеет смысл. Судя по вашему описанию, последующие транзакции также получат ответ 0x99, что для меня не имеет смысла, поскольку с этого момента все должно начаться сначала.

Все становится значительно сложнее, когда вы добавляете прерывание для отключения GPIO. Вы не упоминаете, каков приоритет прерывания, но в целом у вас есть два ISR, которые возятся с буфером SPI, один за другим, и это, вероятно, не то, что вам нужно.

Вы можете попробовать переместить этот xFlag_42 = RESET; быть первой строкой else if. Это приведет к тому, что все это будет выполняться дважды. Если этот результат нежелателен, вы можете добавить флаг, который говорит, что вы обрабатываете первый трафик, и написать свой ISR, чтобы он возвращался немедленно, ничего не делая, если этот флаг установлен.

Спасибо за ответ! Да, именно это и происходило. При второй неправильной последовательности ведомое устройство отвечает мастеру пятью 0x99, хотя это был 0x42. Следует ожидать первого 0x99 в неправильной последовательности, но я до сих пор не понимаю, почему на другие байты ответили 0x99, как будто он HAL_SPI_TransmitReceive_IT()выполняется несколько раз, потому что 0x99 находится только в этой функции.
Я также не понимаю, почему на последующие транзакции SPI отвечали 0x99. Как вы сказали, это не имеет смысла, потому что регистр данных SPI должен был быть заменен новыми буферами. Я попытался отключить прерывание линии CS и переместить флаги на первую строку else if, но все еще были ошибки, хотя были некоторые изменения. Я обновлю сообщение о приоритетах прерываний, прерывание SPI2_CS GPIO: HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);в то время как сам приоритет прерывания SPI:HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1);
@Cimory, ты очищаешь прерывания, верно?
Прерывания GPIO очищаются автоматически (я полагаю) через библиотеку HAL. Функция __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);выполняется до перехода к моей функции обратного вызова прерывания GPIO. Я не уверен в очистке прерываний SPI. Разве функция __HAL_SPI_DISABLE_IT()внутри не должна HAL_SPI_IRQHandler()сбрасывать все флаги прерываний и постоянно отключать прерывания SPI до тех пор, пока они не будут активированы снова?
Есть ли флаги, которые мне нужно очистить перед инициированием новой транзакции SPI? Я также пытался деинициализировать периферийное устройство SPI всякий раз, когда происходит сбой транзакции SPI, HAL_SPI_DeInit()и снова инициализировать периферийное устройство SPI, но это не помогло.
Извините, я не уверен в очистке прерываний.
Выключение и включение периферийного устройства звучит довольно рискованно.
Я посмотрю на очистку прерывания SPI - это может быть причиной того, что я отправляю 0x99 после появления какой-либо ошибки. Спасибо за предложение. Есть ли какие-либо другие шаги/процедуры, которые я мог бы пропустить с точки зрения настройки STM32 в качестве ведомого устройства?
Я добавил проверку активных флагов в блоке прерывания SPI_CS и предотвратил HAL_SPI_TransmitReceive_IT()запуск, если какой-либо из флагов цикла while равен SET, и связь SPI работает нормально. Спасибо за предложение и за помощь в понимании проблемы!
Большой! Я рад, что смог помочь.