На самом деле проблема в том, что вот в этом куске кода:
Код
// Освобождаем дескрипторы DMA
// Проверяем, фрейм растянут на несколько дескрипторов или нет
if (DMA_RX_FRAME_infos->Seg_Count > 1)
{
DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc;
}
else
{
DMARxNextDesc = frame.descriptor;
}
// Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA
for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
}
// Очищаем счётчик сегментов
DMA_RX_FRAME_infos->Seg_Count = 0;
// Если стоял флаг о том, что Rx буфер недоступен, то снимаем его и опять включаем приём
if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
{
// Снимаем флаг Receive buffer unavailable
ETH->DMASR = ETH_DMASR_RBUS;
// Включаем приём
ETH->DMARPDR = 0;
}
Может возникнуть такая ситуация, что мы работаем с дескриптором, но внутренняя периферия Ethernet принимает ещё один фрейм в это время. И при переключении на следующий дескриптор там уже нет флага OWN и работать с ним DMA не может. Соответственно и в прерывание мы не попадём. Лечится очень просто. В этом куске кода:
Код
// Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA
for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
}
Добавляем, чтобы стало так:
Код
// Взводим бит OWN в Rx дескрипторах - отдаём буферы назад DMA
for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
// Если после смены дескриптора на следующий он не свободен, то нужно обработать его тоже
// фактически ещё раз попасть в это прерывание
if ((DMARxNextDesc->Status & ETH_DMARxDesc_OWN) == 0) {
again = true;
}
}
А там, где снимаем флаги прерываний (далее по коду) поменять на:
Код
// Снимаем флаги прерываний
if (!again)
{
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
}
else
{
// Сюда можно добавить счётчик и убедиться, что иногда такое бывает
}
Ну и где-нибудь объявить переменную bool again = false;
Мне такой танец с бубном помог. Оставил блок на всю ночь, вот утром проверил - полёт нормальный. А до этого через 2 часа работы зависал стабильно.