Чому б не завжди використовувати 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. Мені не потрібно призупиняти процесор, оскільки він буде мати напругу USB 5В, і мені потрібно робити обробку між повідомленнями (для обробки повідомлень в основному циклі). У мене є 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, установки) коду.
JimmyB

@JimmyB ... спасибі ... однак завдяки відповіді Йонаса нижче, я вдосконалюю, щоб прочитати, що багато байтів, оскільки повідомлення є довгим. Я знаю це після отримання першого байта (у більшості випадків). Тоді більше користі буде використовувати DMA через переривання.
Мішель Кейзерс

8

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


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

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

1
@MichelKeijzers: IDK специфічний чіп, але зазвичай альтернатива DMA не буквально переривається, вона запрограмована-IO (де ви використовуєте інструкції процесора для читання / запису даних з / до реєстру вводу-виводу). У обробнику переривань ти зазвичай робиш одне читання, а потім, можливо, інше, якщо персонаж з'явився під час читання першого, особливо якщо це не спровокує іншого переривання. Або читайте, поки внутрішній буфер не порожній, якщо такий буфер є. Очевидно, вам потрібно більше переривань для PIO, і налаштовуйте їх по-іншому.
Пітер Кордес

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

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

5

Використання DMA вводить цікаві питання та виклики, що перевищують усі інші аспекти периферійного використання UART. Наведу кілька прикладів: Припустимо, що ваш комп'ютер сидить на шині RS485 (або будь-якій іншій) з іншими пристроями. У автобусі є багато повідомлень, деякі призначені для вашого UC, деякі - ні. Крім того, припустимо, що всі ці сусіди шини говорять про різні протоколи даних, що означає, що довжина повідомлень різна.

Деякі питання, які виникають лише при використанні DMA:

  • коли я перебиваю?
    • Перешкоджати перерві DMA дуже хочеться лише тоді, коли вони перенесли заданий обсяг даних.
    • Що ви робите, якщо ніколи не отримуєте достатньо даних, щоб викликати перерву DMA?
  • Що робити, якщо ви отримуєте лише часткове повідомлення, коли DMA переривається?
  • Як виглядають ваші буфери RX? Вони лінійні чи кругові?
    • DMA може бути непорушним учасником кругового буфера в тому сенсі, що він підпорядковується лише кордону адреси, але не має проблем пробиватися повз інші вказівники в системі кругового буфера.

У всякому разі, просто їжа для роздумів.


Дякую за ці міркування. В даний час я завжди отримую 1 байт і зберігаю його в буфері дзвінка, оскільки дійсно мої повідомлення (MIDI) можуть мати різну довжину, і я не знаю, що я отримаю далі. У своєму головному циклі я перевіряю, чи немає повних повідомлень для їх обробки (і якщо вони завершені, я видаляю їх з буфера дзвінка). Тому я завжди отримуватиму достатню кількість даних (якщо я не пропущу байти, я повинен це перевірити). Мій буфер RX - це лише 1 байт, але я копіюю його в кільце / круговий буфер. Я не робив перевірок, чи він повний (потрібно додати його).
Мішель Кейджерс

Гей, не хвилюйся. Я впевнений, що ваша програма буде добре запрограмована. Як згадували інші, DMA - це чудово, але все це не безкоштовно. Це вносить додаткові міркування в систему, які не існують, якщо ви можете піти, не використовуючи її.
pgvoorhees

добре, сподіваюся, я все ще новачок.
Мішель Кейзерс

3

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


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

1

Я використовував STM32CubeMx / HAL в декількох проектах зараз і виявив, що програмне забезпечення для обробки UART, яке він створює, має певні недоліки на стороні прийому.

Під час передачі, як правило, ви хочете надіслати блок даних або рядок тексту. У цьому випадку ви наперед знаєте, скільки триває передача даних, і тому використання DMA - очевидне рішення. Ви отримуєте переривання, коли передача завершена, і можете скористатися функцією зворотного виклику UART TX завершити, щоб вказати вашому головному коду, що передача завершена, і ви можете надіслати ще один блок даних.

Що стосується прийому даних, то функції, передбачені ST, передбачають, що ви знаєте, скільки символів надасть вам пристрій, що надсилає, перш ніж він почне надсилати. Зазвичай це не відомо. Функціональність переривання розміщує отримані дані в буфер і вказує лише на те, що є дані, коли отримано заздалегідь задану кількість символів. Якщо ви спробуєте використовувати функцію DMA або перервати функціональність для отримання даних, встановивши послідовні передачі одного символу, то час настройки для кожного з них означатиме, що ви втратите символи за будь-яким іншим, ніж найповільніші швидкості передачі даних (швидкість передачі даних, яку ви будете мати Початок втрати даних буде залежати від тактової частоти процесора) і надмірно завантажить процесор, не залишаючи циклів інструкцій для будь-якої іншої обробки

Щоб обійти це питання, я написав власну функцію обробника переривань, яка зберігає дані в невеликому локальному круговому буфері і встановлює кількість, яка читається основним кодом (RTM-рахунковий семафор), щоб вказати, що отримані дані готові. Основний код може потім збирати дані з цього буфера у вільний час, неважливо, чи є певна затримка в зборі даних, якщо локальний буфер не переповнюється до збору даних.


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

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

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

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