Почему бы не всегда использовать DMA в пользу прерываний с UART на STM32? [закрыто]


9

В прошлом месяце я трачу много времени на то, чтобы UART (для MIDI) работал с STM (STM32F103C8T6), используя прерывания, но без особого успеха.

Однако в этот вечер с использованием DMA все работало довольно быстро.

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

Я использую STM32CubeMx / HAL.


2
Почему бы и нет? Это либо вопрос мнения, когда кто-то ищет догадки относительно возможной технической причины, либо слишком широкий вопрос, и, следовательно, не вопрос, который здесь относится. Чтобы назвать случайный пример, DMA будет означать большую задержку в запросе данных, тем более что вы не получите никакой реальной выгоды, если вы не позволите ему собирать несколько символов. Часто это может быть хорошо, иногда - нет.
Крис Страттон

6
Если работа над прерываниями заняла недели, это потому, что вы подошли к задаче неправильно; работа DMA может занять больше времени - на самом деле это более сложная задача, поэтому очевидная простота более сложной задачи по сравнению с более простой, вероятно, сводится к ресурсам, которые вы использовали для руководства, а не к самому механизму.
Крис Страттон

5
Никогда не думайте, что dma освобождает процессор, иногда да, процессор продолжает работать, иногда нет, процессор не заморожен, чтобы удерживать шину для механизма dma. Тривиально сделать это с реализацией манипулятора, поэтому не могу просто сказать, что все манипуляторы - это так, и все x86 - так, или что-то в этом роде, это не так просто, вы всегда должны исследовать дизайн системы и, возможно, совершить небольшой взлом. Чип, который у вас есть, вполне может освободить ядро ​​руки, это всего лишь комментарий к dma. Насколько ваш вопрос, не имеет смысла, вы не могли идти в ногу, и dma + int, вероятно, является полным решением, если вы не можете просто опросить.
old_timer

5
Прерывания довольно просты для последовательного порта STM32F. Почему бы вам не опубликовать вопрос со своим кодом, чтобы некоторые из нас могли попытаться определить, где вы идете не так? Никогда не стоит взламывать код, пока он не заработает, не понимая, в чем заключалась основная проблема.
Джон

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

Ответы:


24

Несмотря на то, что DMA освобождает ЦП и, таким образом, может снизить задержку других приложений, управляемых прерываниями, работающих на том же ядре, с этим связаны некоторые расходы:

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

    Например, если у вас массовая передача I2C каждые 5 мс, это кажется лучшим кандидатом на DMA, чем случайная команда отладки, поступающая на UART2.

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

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

  • DMA требует буферов памяти для работы (если вы не делаете периферийное-периферийное DMA), поэтому с ним связаны некоторые затраты памяти.

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

  • DMA создает задержку, потому что процессор получает уведомление только тогда, когда передача завершена / наполовину завершена (см. Другие ответы).

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

    • Это может означать, что необходимо обрабатывать первые символы сообщения, используя прерывания для каждого символа: например, при взаимодействии с XBee сначала нужно прочитать тип и размер пакета, а затем запустить передачу DMA в выделенный буфер.

    • Для других протоколов это может быть вообще невозможно, если они используют только разделители конца сообщения: например, текстовые протоколы, которые используют в '\n'качестве разделителя. (Если только периферийное устройство DMA не поддерживает сопоставление для символа.)

Как видите, здесь есть много компромиссов. Некоторые из них связаны с аппаратными ограничениями (количество каналов, конфликты с другими периферийными устройствами, сопоставление по символам), некоторые основаны на используемом протоколе (разделители, известная длина, буферы памяти).

Чтобы добавить некоторые неподтвержденные доказательства, я столкнулся со всеми этими компромиссами в хобби-проекте, который использовал много различных периферийных устройств с очень разными протоколами. Были некоторые компромиссы, в основном основанные на вопросе «сколько данных я передаю и как часто я собираюсь это делать?». По сути, это дает вам приблизительную оценку влияния простой передачи, управляемой прерываниями, на процессор. Таким образом, я отдавал приоритет вышеупомянутой передаче I2C каждые 5 мсек по сравнению с передачей UART каждые несколько секунд, которые использовали один и тот же канал DMA. Другая передача UART происходит чаще и с большим количеством данных, с другой стороны, имеет приоритет над другой передачей I2C, которая происходит реже. Это все компромиссы.

Конечно, использование DMA также имеет свои преимущества, но это не то, что вы просили.


Спасибо за ваш подробный ответ. MIDI будет наиболее важной частью, поэтому я думаю, что DMA подходит для этого (хотя скорость низкая: 31250 бод). У меня достаточно каналов DMA, позже я собираюсь использовать другой STM32 при использовании 4 USART. Мне не нужно приостанавливать процессор, так как он будет иметь питание 5 В USB, и мне нужно выполнять обработку между сообщениями (для обработки сообщений в основном цикле). У меня 256-байтовое чтение и 256-байтовый буфер передачи. Я могу увеличить его позже, если это необходимо. STM32f103c8t6 имеет 20 КБ ОЗУ, возможный STM, который я буду использовать, имеет 192 КБ.
Мишель Кейзерс

И вы даете мне очень хорошую идею, как улучшить. До сих пор я всегда читаю 1 байт и постоянно проверяю, получено ли полное (MIDI) сообщение. Но я могу прочитать первый байт, и в зависимости от этого, в основном, размер известен и может попросить остальное. Это стоило мне еще одного небольшого буфера, но это нормально.
Мишель Кейзерс

Чтение отдельных байтов с помощью DMA очень неэффективно. Для более низкой задержки и более высокой эффективности использование прерываний для каждого символа до тех пор, пока вы не узнаете размер, а затем переключение на DMA будет целесообразным.
Йонас Шефер

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

6
Это, вероятно, ошибка - вы должны исправить свой простой код прерывания без DMA.
Крис Страттон

10

Использование DMA обычно означает, что вы больше не прерываете каждый символ, а только после того, как «полный буфер» символов был получен (или передан). Это увеличивает задержку обработки этих символов - первый символ не обрабатывается до тех пор, пока не будет получен последний символ в буфере.

Эта задержка может быть плохой, особенно в чувствительном к задержке приложении, таком как MIDI, где несколько мс здесь и там могут привести к серьезным проблемам с воспроизведением для живых выступлений.


Я получаю 1 байт за раз (т.е. буфер «DMA» в 1 байт) и после каждого обратного вызова DMA этого байта сохраняю его в кольцевом буфере, который я обрабатываю вручную. В моем основном цикле я собираюсь проверить полные MIDI-сообщения и обработать их.
Мишель Кейзерс

3
DMA обычно используется для получения нескольких байтов и прерывания только тогда, когда они все были получены. Прерывание после одного байта является нормальным, когда не используется DMA, поэтому меня удивляет: какой смысл в дополнительном усложнении использования DMA для этого?
Стив Мельникофф

5
@MichelKeijzers Тогда то, что вы делаете, - это почти то же самое, что вы делаете в чистых реализациях, управляемых прерываниями. Следовательно, в этом случае нет пользы от использования DMA, и ваша первоначальная проблема, вероятно, решается не DMA, а переписыванием кода (ISR, setup).
JimmyB

@JimmyB ... спасибо ... однако из-за ответа Джонаса ниже, я сделаю улучшение, чтобы прочитать столько байтов, сколько сообщение длинное. Я знаю это после получения первого байта (в большинстве случаев). Чем больше будет выгоднее использовать DMA по прерываниям.
Мишель Кейзерс

8

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


Правда, может быть, только на STM32 механизм прерывания (чистый не DMA) немного неуклюжий по сравнению с прямым DMA.
Мишель Кейзерс

2
@duskwuff Не совсем; вы можете опрашивать, чтобы увидеть, когда DMA готов, и вы, возможно, захотите, потому что одной из ключевых причин использования DMA является то, что вам не нужно беспокоиться о последовательном порте, пока ваша программа не будет в состоянии, в котором она может воздействовать на полученные данные. Или для исходящего DMA, вы можете просто опросить, чтобы увидеть, возможно ли добавить больше в буфер отправки.
Крис Страттон

1
@MichelKeijzers: IDK для конкретного чипа, но обычно альтернатива DMA буквально не прерывается, это запрограммированный ввод-вывод (где вы используете инструкции ЦП для чтения / записи данных из / в регистр ввода-вывода). В обработчике прерываний вы обычно выполняете одно чтение, а затем, возможно, другое в случае, если во время чтения первого появился символ, особенно если это не вызовет другое прерывание. Или читайте, пока внутренний буфер не станет пустым, если такой буфер существует. Очевидно, вам нужно больше прерываний для PIO и настроить их по-другому.
Питер Кордес

@ChrisStratton Хороший вопрос ... пока я не проверял, можно ли передавать, я просто передаю что-то, не проверяя, все ли в порядке. Возможно, если нет, я попробую еще раз позже.
Мишель Кейзерс

@PeterCordes Кажется, что STM32 имеет достаточно прерываний для прямого доступа к памяти, и я читаю каждый раз только 1 байт. Даже самый простой STM32 (F103c8t6) имеет достаточно доступных портов / прерываний DMA.
Мишель Кейзерс

5

Использование DMA ставит некоторые интересные вопросы и проблемы, выходящие за рамки всех других аспектов использования периферийных устройств UART. Я приведу несколько примеров: Предположим, что ваш УК находится на шине RS485 (или любой другой) с другими устройствами. В шине много сообщений, некоторые предназначены для вашего ОК, некоторые нет. Дополнительно предположим, что все эти соседи по шине говорят по другому протоколу данных, что означает, что длины сообщений различны.

Некоторые вопросы, которые возникают только при использовании DMA:

  • когда я прерываю?
    • DMA действительно любят прерывать, только когда они передали заданный объем данных.
    • Что вы делаете, если вы никогда не получаете достаточно данных, чтобы вызвать прерывание DMA?
  • Что делать, если вы получаете только частичное сообщение, когда DMA прерывает?
  • Как выглядят ваши RX буферы? Они линейные или круглые?
    • DMA может быть недисциплинированным участником кольцевого буфера в том смысле, что он подчиняется только границе адреса, но без проблем проходит мимо других указателей в системе циклического буфера.

Во всяком случае, просто пища для размышлений.


Спасибо за эти соображения. В настоящее время я всегда получаю 1 байт и сохраняю его в кольцевом буфере, поскольку мои сообщения (MIDI) действительно могут иметь разную длину, и я не знаю, что получу дальше. В моем основном цикле я проверяю полные сообщения для их обработки (и, если они завершены, я удаляю их из кольцевого буфера). Поэтому я всегда получаю достаточно данных (если я не пропущу байты, я должен проверить это). Мой буфер RX составляет всего 1 байт, но я копирую его в кольцевой / кольцевой буфер. Я не делал проверки, если он полон (нужно добавить его).
Мишель Кейзерс

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

ну, я надеюсь, я все еще начинающий.
Мишель Кейзерс

3

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


Я действительно получил побайтово и скопировал его вручную в кольцевой буфер, чтобы обработать его позже.
Мишель Кейзерс

1

Я уже использовал STM32CubeMx / HAL в нескольких проектах и ​​обнаружил, что генерируемая им программа обработки UART имеет определенные недостатки на стороне приема.

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

Когда дело доходит до получения данных, все функции, предоставляемые ST, предполагают, что вы знаете, сколько символов будет выдано отправляющим устройством, прежде чем оно начнет отправлять. Обычно это не известно. Функция прерывания помещает полученные данные в буфер и указывает только на то, что данные доступны, когда получено заранее определенное количество символов. Если вы попытаетесь использовать DMA или функцию прерывания для получения данных путем настройки последовательных односимвольных передач, то время установки для каждого из них будет означать, что вы потеряете символы с любой скоростью, кроме самой низкой скорости передачи данных (скорость передачи данных, которую вы будете потеря данных будет зависеть от тактовой частоты вашего процессора) и будет чрезмерно загружать процессор, не оставляя циклов команд для какой-либо другой обработки

Чтобы обойти это, я написал свою собственную функцию обработчика прерываний, которая хранит данные в небольшом локальном циклическом буфере и устанавливает счетчик, который читается основным кодом (семафор подсчета RTOS), чтобы указать, что полученные данные готовы. Затем основной код может собирать данные из этого буфера в свободное время, при этом не имеет значения, имеется ли некоторая задержка в сборе данных, при условии, что локальный буфер не переполняется до того, как данные собраны.


Я делаю точно так же (я думаю). Я читаю 1 байт за раз и сохраняю его в циклическом буфере, и я собираюсь проверить в основном цикле полные сообщения. Может быть немного улучшено, хотя.
Мишель Кейзерс

Как вы думаете, я мог столкнуться с проблемой, что настройка DMA каждый раз будет перегружать мой процессор / пропущенные символы со скоростью 31 250 бод?
Мишель Кейзерс

1
Пока вы устанавливаете DMA для передачи количества символов за раз, это не будет проблемой. У меня есть 4 UART, работающие с 115200 и выше, и I2C, использующий DMA без проблем. Все передачи UART составляют ~ 20 байтов или более. Проблема заключалась в использовании DMA для приема на UART (процессор L4 на 80 МГц, 9600 бод).
uɐɪ

В настоящее время я устанавливаю его на 1 байт за раз, но я могу улучшить его (выполнив первый байт, а затем проверьте, сколько еще нужно байтов).
Мишель Кейзерс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.