В прошлом месяце я потратил много времени на то, чтобы UART (для MIDI) работал с STM (STM32F103C8T6) с использованием прерываний, но без особого успеха.
Однако в этот вечер при использовании DMA все работало довольно быстро.
Поскольку, насколько я читал, DMA быстрее и разгружает процессор, почему бы не всегда использовать DMA в пользу прерываний? Тем более, что на STM32, похоже, довольно много проблем.
Я использую STM32CubeMx/HAL.
Хотя DMA разгружает ЦП и, таким образом, может уменьшить задержку других приложений, управляемых прерываниями, работающих на том же ядре, с этим связаны затраты:
Существует лишь ограниченное количество каналов прямого доступа к памяти, и существуют ограничения на то, как эти каналы могут взаимодействовать с различными периферийными устройствами. Другое периферийное устройство на том же канале может больше подходить для использования DMA.
Например, если у вас есть массовая передача I2C каждые 5 мс, это кажется лучшим кандидатом для DMA, чем случайная команда отладки, поступающая на UART2.
Настройка и поддержка прямого доступа к памяти сама по себе требует затрат . (Обычно настройка DMA считается более сложной, чем настройка обычной посимвольной передачи, управляемой прерываниями, из-за управления памятью, задействования большего количества периферийных устройств, DMA с использованием самих прерываний и возможности того, что вам нужно проанализировать первые несколько символов вне DMA. в любом случае, см. ниже.)
DMA может использовать дополнительную мощность , поскольку это еще один домен ядра, который необходимо синхронизировать. С другой стороны, вы можете приостановить работу процессора во время передачи DMA, если ядро поддерживает это.
Для работы DMA требуются буферы памяти (если только вы не выполняете DMA от периферии к периферии), поэтому с ним связаны некоторые затраты памяти.
(Стоимость памяти также может быть при использовании посимвольных прерываний, но она также может быть намного меньше или вообще исчезнуть, если сообщения интерпретируются сразу внутри прерывания.)
DMA создает задержку , потому что ЦП получает уведомление только тогда, когда передача завершена / наполовину завершена (см. Другие ответы).
За исключением потоковой передачи данных в/из кольцевого буфера, вам необходимо заранее знать, сколько данных вы будете получать/отправлять.
Это может означать, что необходимо обрабатывать первые символы сообщения с использованием посимвольных прерываний: например, при взаимодействии с XBee вы должны сначала прочитать тип и размер пакета, а затем инициировать передачу DMA в выделенный буфер.
Для других протоколов это может быть вообще невозможно, если они используют только разделители конца сообщения: например, текстовые протоколы, которые используют '\n'
в качестве разделителя. (Если только периферийное устройство прямого доступа к памяти не поддерживает сопоставление символов.)
Как видите, здесь нужно учитывать множество компромиссов. Некоторые связаны с аппаратными ограничениями (количество каналов, конфликты с другими периферийными устройствами, сопоставление символов), некоторые основаны на используемом протоколе (разделители, известная длина, буферы памяти).
Чтобы добавить некоторые неофициальные данные, я столкнулся со всеми этими компромиссами в хобби-проекте, в котором использовалось много разных периферийных устройств с очень разными протоколами. Приходилось идти на некоторые компромиссы, в основном из-за вопроса «сколько данных я передаю и как часто я собираюсь это делать?». По сути, это дает вам приблизительную оценку влияния простой передачи, управляемой прерываниями, на ЦП. Таким образом, я отдал приоритет вышеупомянутой передаче I2C каждые 5 мс по сравнению с передачей UART каждые несколько секунд, которая использовала тот же канал DMA. Другая передача UART, происходящая чаще и с большим количеством данных, с другой стороны, имеет приоритет над другой передачей I2C, которая происходит реже. Это все компромиссы.
Конечно, использование DMA тоже имеет свои преимущества, но это не то, о чем вы просили.
Использование DMA обычно означает, что вы больше не выполняете прерывание по каждому символу, а только после того, как «буфер заполнен» символами был получен (или передан). Это увеличивает задержку обработки этих символов — первый символ не обрабатывается до тех пор, пока не будет получен последний символ в буфере.
Эта задержка может быть плохой вещью, особенно в чувствительном к задержке приложении, таком как MIDI, где несколько мс здесь и там могут привести к серьезным проблемам с воспроизведением для живых выступлений.
DMA не заменяет прерывания — обычно они используются вместе! Например, если вы используете DMA для отправки данных через UART, вам все равно нужно прерывание, чтобы сообщить вам, когда отправка завершена.
Использование прямого доступа к памяти ставит некоторые интересные вопросы и проблемы, выходящие за рамки всех других соображений использования периферийных устройств UART. Я приведу несколько примеров: Предположим, что ваш UC подключен к шине RS485 (или любой другой) с другими устройствами. В шине много сообщений, некоторые предназначены для вашего УК, некоторые нет. Кроме того, предположим, что все эти соседи по шине говорят по разным протоколам данных, что означает, что длина сообщений различна.
Некоторые вопросы, которые возникают только при использовании DMA:
В любом случае, просто пища для размышлений.
На принимающей стороне (насколько я помню) DMA завершается либо при совпадении символа, либо при подсчете терминала. Некоторые протоколы и многие интерактивные приложения не вписываются в эту модель, и вам действительно нужно обрабатывать вещи посимвольно. Технологии прямого доступа к памяти также могут быть ненадежными, если канал связи ненадежен: потеря одного символа в потоке может легко вывести из строя конечный автомат прямого доступа к памяти.
Я использовал STM32CubeMx/HAL в нескольких проектах и обнаружил, что программное обеспечение для обработки UART, которое он генерирует, имеет определенные недостатки на приемной стороне.
При передаче вы обычно хотите отправить блок данных или строку текста. В этом случае вы заранее знаете, как долго идет передача данных, поэтому использование прямого доступа к памяти является очевидным решением. Вы получаете прерывание после завершения передачи и можете использовать функцию обратного вызова UART TX complete, чтобы указать основному коду, что передача завершена и вы можете отправить еще один блок данных.
Когда дело доходит до приема данных, все функции, предоставляемые ST, предполагают, что вы знаете, сколько символов передаст вам отправляющее устройство, прежде чем оно начнет отправлять. Обычно это неизвестно. Функциональность прерывания помещает полученные данные в буфер и указывает, что данные доступны только тогда, когда получено предопределенное количество символов. Если вы попытаетесь использовать DMA или функции прерывания для получения данных, настроив последовательную передачу отдельных символов, то время настройки для каждого из них будет означать, что вы будете терять символы при любых других скоростях передачи данных, кроме самых медленных (скорость передачи в бодах, которую вы начать терять данные будет зависеть от тактовой частоты вашего процессора) и будет чрезмерно нагружать процессор, не оставляя циклов инструкций для любой другой обработки
Чтобы обойти это, я написал свою собственную функцию обработки прерывания, которая сохраняет данные в небольшом локальном круговом буфере и устанавливает счетчик, который считывается основным кодом (семафором подсчета ОСРВ), чтобы указать, что полученные данные готовы. Затем основной код может собирать данные из этого буфера на досуге, не имеет значения, есть ли какая-то задержка в сборе данных, при условии, что локальный буфер не переполняется до того, как данные будут собраны.
Гарри Свенссон
Мишель Кейзерс
Крис Стрэттон
Крис Стрэттон
АК
Старожил
Старожил
Старожил
Мишель Кейзерс
Джон
Мишель Кейзерс
ДиБоско
Мишель Кейзерс
Мишель Кейзерс