Что может быть причиной исключительно большой задержки для прерывания приема UART?

Принимаю данные по UART, использую 8-битный атмега, обычно около 5 байт подключено, потом долгая пауза. Общее время для одного байта (со стартовыми и стоповыми битами, я не использую четность) составляет 160 мкс. Однако прерывание приема срабатывает через 60–100 мкс после стопового бита и почти в половине случаев вообще не срабатывает! (проверено прицелом)

Было несколько довольно длинных прерываний, поэтому я их обвинил, но после отключения всех прерываний, кроме UART, ситуация остается прежней. Прерывания UART все время заканчиваются менее чем за 10 мкс (обычно 7 мкс). Сила сигнала в порядке, 5В, как и напряжение питания.

Сначала, поняв, что потеряно много байтов, я подумал, что причина в частоте сигнала, но перепроверил: скорость в бодах идеальная, качество сигнала хорошее, погрешность скорости передачи близка к нулю. Если бы это было проблемой, я бы получил несколько потерянных прерываний (потому что некоторые биты были потеряны), но остальное должно произойти в нужное время, не так ли? В моем случае прерывание приходит очень поздно, если вообще приходит, и даже когда приходит, иногда содержит мусор. Сигнал на контакте в порядке, я могу правильно прочитать и оценить его на прицеле.

Я искал типичную задержку прерывания UART, но ничего не нашел. Я сильно подозреваю, что дикий разброс между 60 и 100 мкс не должен быть нормальным.

Можете ли вы опубликовать свой код?
@OliGlaser: я сомневаюсь, что публикация 3000 строк кода может чем-то помочь. Если вас интересуют настройки, часы 10МГц, UCSR0A=0x01; UCSR0B=0xDC; UCSR0C=0x06; UBRR0H=0x00; UBRR0L=0x09;и я читаю UDR0в прерывании.
Да, пожалуйста, не публикуйте код! :-)
Я не думал обо всем коде, только о прерывании и любых других важных битах. Проблема, скорее всего, в коде, а если и так, то без просмотра его не найти. Упрощение всего этого (или создание тестовой программы) поможет найти причину.
Это какая-то оценочная доска? У вас есть транслятор логического уровня между вашим atmega и ПК? На какой строке кода вы устанавливаете точку останова, разумно ли ее оптимизирует компилятор? Также может быть проблема со скоростью передачи данных. Попробуйте пойти обратным путем и отправить символы с атмеги на ПК и посмотреть, правильно ли они принимаются.
Можете ли вы вместо этого вставить свой код и предоставить нам ссылку? ( pastebin.com )
Не знаком с этой частью, но должен ли UART давать вам прерывание для каждого символа или у него есть fifo? весь смысл использования fifo заключается в уменьшении частоты прерываний, но для этого uart должен отложить выполнение прерывания, используя некоторую стратегию. Один из способов — подождать, пока не будет достигнута «высокая отметка». Другим было бы ждать, пока не будет обнаружен подходящий промежуток между полученными байтами. Вроде пахнет как последняя ситуация. Любопытно, как «от 60 до 100» имеет среднее значение, близкое к половине байта, не так ли? Случайно ли разрывы между вашими группами из 5 человек близки к этому или меньше?
Вам нужно сократить свой код до основного цикла, который ничего не делает, и ISR. Если у вас все еще есть проблема, вы можете получить некоторую помощь. В противном случае (и это мое предположение) у вас происходит что-то еще, что вы еще не учитываете.

Ответы (4)

Я предполагаю, что вы используете тот же процесс отладки, который я бы сделал в этом случае - одна из первых инструкций в процедуре прерывания включает светодиод, а одна из последних инструкций в процедуре прерывания выключает этот светодиод.

Затем вы использовали осциллограф с двумя трассами, один щуп прикрепил к соответствующему контакту для наблюдения за байтами, поступающими в UART, а другой щуп прикрепил к контакту, управляющему светодиодом.

Я предполагаю, что ваша процедура прерывания обработчика UART заканчивается инструкцией возврата из прерывания (вместо использования инструкции возврата из подпрограммы, используемой обычными инструкциями).

Есть 4 вещи, которые могут вызвать большую задержку между концом последнего байта сообщения и запуском обработчика UART:

  • Какой-то предыдущий байт в сообщении запускает обработчик UART, и каким-то образом требуется много времени, прежде чем прерывания снова разрешатся. Некоторые люди строят свои процедуры прерывания так, что после того, как обработчик UART заканчивает сохранять байт в соответствующем буфере, он проверяет кучу других вещей перед выполнением инструкции возврата из прерывания — это увеличивает джиттер и задержку, но иногда такие люди делают это. это в любом случае, потому что это улучшает пропускную способность.

  • Некоторое другое прерывание требует много времени для выполнения, прежде чем оно снова разрешает прерывания, выполняя команду возврата из прерывания. (Если вы можете заставить каждое прерывание включать и выключать какой-либо другой светодиод, довольно легко увидеть на о'скопе, является ли это проблемой, или исключить это).

  • Некоторый не прерывающий код "временно" отключает прерывания. (Это увеличивает дрожание и задержку, но люди все равно делают это, потому что часто это самый простой способ предотвратить повреждение данных, когда и некоторое прерывание, и какая-то фоновая задача основного цикла работают с одним и тем же фрагментом данных). (Если вы можете заставить каждый бит кода, который делает это, включать и выключать какой-либо другой светодиод, довольно легко увидеть на o'scope, если это проблема, или исключить это).

  • Инструкции, выполнение которых занимает много времени.

Традиционный способ выяснить, что именно вызывает проблему, — это сохранить текущую версию вашего кода (вы используете TortoiseHg или какую-то другую систему контроля версий, верно?), а затем преднамеренно взломать временную копию вашего кода. код, заглушая и полностью удаляя код по несколько подпрограмм за раз, повторно тестируя после каждого раунда удаления, пока у вас не будет крошечной, но технически «полной» и работоспособной, программы, которая демонстрирует ту же проблему.

Слишком часто люди показывают нам фрагменты полной программы — части, которые, по мнению этих людей , важны, — и мы не можем им помочь, потому что одна из частей, которые они пропустили, вызывает проблему.

Процесс сведения программы к небольшому тестовому сценарию — очень полезный навык, потому что часто, проходя через этот процесс, вы быстро обнаруживаете, в чем заключается реальная проблема.

Если у вас есть такая крошечная, но работоспособная программа, пожалуйста, опубликуйте ее здесь. Если вы выясните, в чем проблема во время этого процесса, пожалуйста, сообщите нам об этом, чтобы остальные из нас могли избежать этой проблемы.

Как правило, UART с кодом не в реальном времени ненадежны. Это означает, что если у вас есть повторяющиеся подпрограммы и неопределенная длина выполнения кода, можете ли вы гарантировать, что прошивка будет вовремя реагировать на немаскируемое прерывание? Но вы отключили их все, так соответствует ли продолжительность IRQ требованиям минимального времени для наихудшего случая?

Если это становится сложной проблемой, вам может потребоваться использовать данные буфера с UART с глубиной 16 уровней и обнаружением переполнения и недостаточного выполнения буфера, а также использовать стратегию опроса для данных, чтобы определить, получены ли данные в буфере. Моя первая конструкция UART в 1976 году сначала имела эту проблему. Затем я перешел к дизайну DMA с ответом на опрос меньше, чем длина буфера, с почти полным FIFO, допускающим прерывание.

Вы говорите, что скорость передачи правильная, но я вычисляю ее как 62500 бит/с (160 мкс / 10 бит = 16 мкс на бит). Это кажется немного странным для последовательного соединения, и неправильная скорость передачи вызовет проблемы, аналогичные тем, которые вы видите. То есть UART вполне может прерывать то, что он считает стоповым битом, но который на самом деле является частью следующего октета. Естественно, данные тоже будут повреждены :)

Когда вы получаете данные, можете ли вы также считать флаги ошибок с UART? Это скажет вам, почему UART не любит данные.

Я не знаком с ATMega, но некоторые контроллеры используют прерывания по фронту, а не по уровню; на таких контроллерах подпрограмма обслуживания прерывания должна перед выходом проверять, все ли связанные с ней причины «удовлетворены»; если таковые отсутствуют, подпрограмма должна вернуться назад и обработать их, а не выйти. Если программа обработки прерывания завершается, не удовлетворив одновременно все свои причины, прерывание может оказаться отключенным до тех пор, пока какой-либо другой код не приведет к удовлетворению всех причин прерывания.

Например, если контроллер использует одну и ту же процедуру обслуживания прерываний для обработки входящих и исходящих данных UART, и процедура обслуживания начинается с обработки входящих данных, а затем обработки исходящих данных. Если байт входящих данных поступает непосредственно перед тем, как процедура обслуживания прерывания передает байт исходящих данных в UART, возможно, что контроллер прерываний увидит устойчивый уровень сигнала прерывания от UART. Прежде чем исходящий байт будет загружен, он хотел бы прерваться, потому что ему нужны исходящие данные. К тому времени, когда он будет загружен, UART захочет прерваться, потому что у него есть входящие данные. Однако с точки зрения контроллера прерываний может показаться, что процедура обслуживания прерываний UART просто неэффективна при разрешении условия прерывания.