Я экспериментирую с протоколом 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 сразу после второй неправильной последовательности выше:
0x42 сразу после 0x41 выше (неверные байты отправлены обратно мастеру):
И все байты, которыми затем обмениваются, как показано ниже (независимо от 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);
У меня небольшая проблема с тем, что произошло до того, как вы добавили прерывание GPIO, и после. Если я правильно понимаю, то, что происходило до того, как вы добавили это, было:
После первой (правильной) последовательности вы выходите из ISR с установленным флагом xFlag_42 и, таким образом, в конечном итоге попадаете в __AppliSendBuff(&command42).
На полпути через __AppliSendBuff прерывание подтверждается из-за неправильной последовательности.
ISR устанавливает буфер для отправки 0x99 и возвращается к точке, в которой он выполнялся до получения прерывания.
Следующая строка сбрасывает xFlag_42, поэтому после этого отправляется 0x99.
Все это имеет смысл. Судя по вашему описанию, последующие транзакции также получат ответ 0x99, что для меня не имеет смысла, поскольку с этого момента все должно начаться сначала.
Все становится значительно сложнее, когда вы добавляете прерывание для отключения GPIO. Вы не упоминаете, каков приоритет прерывания, но в целом у вас есть два ISR, которые возятся с буфером SPI, один за другим, и это, вероятно, не то, что вам нужно.
Вы можете попробовать переместить этот xFlag_42 = RESET; быть первой строкой else if. Это приведет к тому, что все это будет выполняться дважды. Если этот результат нежелателен, вы можете добавить флаг, который говорит, что вы обрабатываете первый трафик, и написать свой ISR, чтобы он возвращался немедленно, ничего не делая, если этот флаг установлен.
Симори
HAL_SPI_TransmitReceive_IT()
выполняется несколько раз, потому что 0x99 находится только в этой функции.Симори
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
в то время как сам приоритет прерывания SPI:HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1);
Энни
Симори
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
выполняется до перехода к моей функции обратного вызова прерывания GPIO. Я не уверен в очистке прерываний SPI. Разве функция__HAL_SPI_DISABLE_IT()
внутри не должнаHAL_SPI_IRQHandler()
сбрасывать все флаги прерываний и постоянно отключать прерывания SPI до тех пор, пока они не будут активированы снова?Симори
HAL_SPI_DeInit()
и снова инициализировать периферийное устройство SPI, но это не помогло.Энни
Энни
Симори
Симори
HAL_SPI_TransmitReceive_IT()
запуск, если какой-либо из флагов цикла while равенSET
, и связь SPI работает нормально. Спасибо за предложение и за помощь в понимании проблемы!Энни