Як зробити TDD на вбудованих пристроях?


17

Я не новачок у програмуванні, і я навіть працював з деякими низькими рівнями С та ASM над AVR, але я дійсно не можу обернутись навколо більш масштабного вбудованого проекту С.

Будучи виродженою філософією Рубі TDD / BDD, я не можу зрозуміти, як люди пишуть і перевіряють код, як це. Я не кажу, що це поганий код, я просто не розумію, як це може працювати.

Мені хотілося більше зайнятися програмуванням на низькому рівні, але я дійсно не маю уявлення, як до цього підійти, оскільки це схоже на зовсім інший менталітет, до якого я звик. У мене немає проблем з розумінням арифметики вказівника або як працює розподіл пам'яті, але коли я бачу, як складний код C / C ++ виглядає в порівнянні з Ruby, це здається неможливо важким.

Оскільки я вже замовив собі дошку Arduino, я хотів би більше зайнятись деяким низьким рівнем С і дійсно розуміти, як робити все правильно, але, схоже, жодне з правил мов високого рівня не застосовується.

Чи можливо навіть робити TDD на вбудованих пристроях або при розробці драйверів або таких речей, як користувальницький завантажувач тощо?


3
Привіт, Дарт, ми справді не можемо допомогти вам подолати свій страх перед C, але питання щодо TDD на вбудованих пристроях тут є темою: я переглянув ваше запитання, щоб замість цього ознайомитись.

Відповіді:


18

По-перше, ви повинні знати, що намагатися зрозуміти код, який ви не написали, в 5 разів важче, ніж писати його самостійно. Ви можете вивчити C, прочитавши виробничий код, але це займе набагато більше часу, ніж навчання за допомогою занять.

Будучи виродженою філософією Рубі TDD / BDD, я не можу зрозуміти, як люди пишуть і перевіряють код, як це. Я не кажу, що це поганий код, я просто не розумію, як це може працювати.

Це вміння; вам краще в цьому. Більшість програмістів на C не розуміють, як люди використовують Ruby, але це не означає, що вони не можуть.

Чи можливо навіть робити TDD на вбудованих пристроях або при розробці драйверів або таких речей, як користувальницький завантажувач тощо?

Ну, є книги на цю тему:

введіть тут опис зображення Якщо джміль може це зробити, ви теж можете!

Майте на увазі, що застосування практики з інших мов зазвичай не працює. TDD досить універсальний, хоча.


2
Кожен TDD, який я бачив у своїх вбудованих системах, знаходив лише помилки в системах, які легко вирішували помилки, які я легко знайшов би самостійно. Вони ніколи не знайшли б мені потрібну допомогу, залежну від часу взаємодію з іншими фішками та переривання взаємодії.
Кортук

3
Це залежить від того, над якою системою ви працюєте. Я виявив, що використання TDD для тестування програмного забезпечення в поєднанні з гарною апаратною абстракцією насправді дозволяє мені набагато легше висмикувати ці взаємодії, залежні від часу. Інша перевага, яку люди часто переглядають, полягає в тому, що тести, будучи автоматизованими, можна запускати в будь-який час і не вимагати, щоб хтось сидів на пристрої за допомогою логічного аналізатора, щоб переконатися, що програмне забезпечення працює. TDD врятувало мені тижні налагодження лише в моєму поточному проекті. Часто помилки, які, на нашу думку, легко виявити, викликають помилки, яких ми не очікували.
Нік Паскуччі

Плюс це дозволяє розробляти і тестувати поза ціль.
cp.engr

Чи можу я слідувати цією книгою, щоб зрозуміти TDD для невбудованого C? Для будь-якого програмування простору користувача C?
переобмін

15

Тут велика різноманітність відповідей ... в основному це питання вирішується різними способами.

Я вже більше 25 років пишу вбудоване програмне забезпечення та програмне забезпечення низького рівня на різних мовах - переважно на C (але з переходами на Ada, Occam2, PL / M та безліч асемблерів).

Після тривалого роздуму, спроб і помилок я застосував метод, який отримує результати досить швидко і досить легко створити тестові обгортки та джгути (де вони додають значення!)

Метод виглядає приблизно так:

  1. Напишіть код коду абстракції драйверів або апаратних засобів для кожної основної периферійної пристрої, яку ви хочете використовувати. Також напишіть один, щоб ініціалізувати процесор і налаштувати все (це створює сприятливе середовище). Зазвичай на невеликих вбудованих процесорах - наприклад, ваш AVR - може бути 10 - 20 таких блоків, і все це мало. Це можуть бути одиниці для ініціалізації, перетворення A / D в нерозміщені буфери пам’яті, побітовий вихід, кнопковий вхід (відключення не тільки відбирається), драйвери модуляції ширини імпульсу, UART / прості драйвери послідовного використання, переривання використання та невеликі буфери вводу / виводу. Можливо, ще кілька - наприклад, драйвери I2C або SPI для EEPROM, EPROM або інших пристроїв I2C / SPI.

  2. Після цього для кожного блоку апаратної абстракції (HAL) / драйвера я пишу тестову програму. Це покладається на послідовний порт (UART) та процесор init - тому перша програма тестування використовує лише ці 2 одиниці і лише виконує деякі базові введення та виведення. Це дозволяє мені перевірити, що я можу запустити процесор, і що у мене є основна послідовна підтримка налагодження вводу / виводу. Після того, як це працює (і тільки тоді), я тоді розробляю інші програми тестування HAL, будуючи їх на основі відомих хороших підрозділів UART та INIT. Тож у мене можуть бути тестові програми для зчитування побітових входів і відображення їх у хорошій формі (шістнадцятковий, десятковий, будь-який інший) на моєму серійному терміналі налагодження. Потім я можу перейти до великих і складніших речей, таких як тестові програми EEPROM або EPROM - я змушую більшість цих меню керувати, щоб я міг вибрати тест для його запуску, запустити його і побачити результат. Я не можу це написати, але зазвичай я не

  3. Після того, як у мене все HAL працює, я знаходжу спосіб отримати звичайний галочку таймера. Зазвичай це швидкість десь від 4 до 20 мс. Це повинно бути регулярним, генеруватися в перерві. Як це можна зробити, перекидання / переповнення лічильників - це зазвичай. Потім обробник переривання ВКАЗУЄ розмір байту "семафор". У цей момент ви також можете поспілкуватися з управлінням живленням, якщо вам потрібно. Ідея семафору полягає в тому, що якщо його значення> 0, потрібно запустити "основний цикл".

  4. Виконавець виконує основний цикл. Він майже просто чекає, коли цей семафор стане не-0 (я абстрагую цю деталь подалі). На цьому етапі ви можете пограти з лічильниками для підрахунку цих кліщів (тому що ви знаєте кількість галочок), і таким чином ви можете встановити прапори, що показують, чи є поточний виконавчий галочок на інтервал 1 секунду, 1 хвилину та інші загальні інтервали можливо, захоче використовувати. Як тільки керівник дізнається, що семафор> 0, він виконує один прохід через кожну функцію "додаток" обробляє "оновлення".

  5. Процеси застосування ефективно сидять поруч і регулярно запускаються галочкою "оновлення". Це лише функція, яку викликає виконавча влада. Це фактично багатозадачні завдання з дуже простим домашнім RTOS, який покладається на всі програми, що надходять, роблячи невелику роботу та виїжджаючи. Програми повинні підтримувати власні змінні стану і не можуть робити тривалі обчислення, оскільки не існує преференційної операційної системи, яка б надавала справедливості. ОБОВ'ЯЗКОВО час запуску програм (сукупно) повинен бути меншим, ніж основний період галочки.

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

Цей підхід працює майже для будь-якої системи комунікацій, яку ви хочете назвати - вона може (і вже була зроблена) працює для багатьох фірмових систем, відкритих стандартних систем comms, вона працює навіть для стеків TCP / IP.

Він також має перевагу, будучи модульними елементами з чітко визначеними інтерфейсами. Ви можете витягувати шматки в будь-який час, замінюючи різні частини. У кожну точку шляху можна додати тестовий джгут або оброблювачі, які будуються на відомих хороших деталях нижнього шару (деталі нижче). Я виявив, що приблизно 30% до 50% дизайну можуть отримати користь від додавання спеціально написаних одиничних тестів, які зазвичай доволі легко додаються.

Я зробив це все на крок далі (ідею, яку я отримав від когось іншого, хто це зробив) і замінив шар HAL на еквівалент ПК. Наприклад, ви можете використовувати C / C ++ і winforms або подібне на ПК, і, записуючи код, Дбайливо ви можете емулювати кожен інтерфейс (наприклад, EEPROM = файл диска, прочитаний у пам'яті ПК), а потім запустити всю вбудовану програму на ПК. Можливість використовувати сприятливе середовище налагодження може заощадити величезну кількість часу та зусиль. Тільки справді великі проекти зазвичай можуть виправдати цю кількість зусиль.

Наведений вище опис - це щось не властиве тому, як я роблю речі на вбудованих платформах - я натрапив на численні комерційні організації, які роблять подібні. Спосіб його виконання, як правило, сильно відрізняється у здійсненні, але принципи часто набагато однакові.

Я сподіваюсь, що вищесказане додасть трохи смаку ... такий підхід працює для невеликих вбудованих систем, що працюють в кілька кБ, з агресивним керуванням батареями до монстрів 100 Кб і більше джерел ліній, які працюють постійно. Якщо ви запускаєте "вбудований" на великій ОС, на зразок Windows CE тощо, тоді все вищезазначене абсолютно не має значення. Але це все-таки НЕ РЕАЛЬНЕ вбудоване програмування.


2
Більшість апаратних периферійних пристроїв ви не можете протестувати через UART, оскільки досить часто вас цікавлять в основному характеристики часу. Якщо ви хочете перевірити частоту вибірки АЦП, робочий цикл ШІМ, поведінку деяких інших серійних периферійних пристроїв (SPI, CAN тощо) або просто час виконання якоїсь частини вашої програми, ви не можете це зробити через УАРТ. Будь-яке серйозне вбудоване тестування програмного забезпечення включає осцилограф - ви не можете програмувати вбудовані системи без нього.

1
О так, абсолютно. Я просто забув згадати про це. Але після того, як ваш UART буде запущений і запущений, його дуже легко створити тестові або тестові справи (саме про це йшлося в питанні), стимулюйте речі, дозволяйте вводити користувачеві, отримувати результати та дружно показувати. Цей + ваш CRO робить життя дуже легким.
quick_now

2

Код, який має давню історію поступового розвитку та оптимізації для декількох платформ, наприклад, вибрані вами приклади, зазвичай читати важче.

Справа в C полягає в тому, що він насправді здатний охоплювати платформи в широкому діапазоні багатства API та продуктивності обладнання (та його відсутність). MacVim реагував на машинах з більш ніж 1000X меншою продуктивністю пам’яті та процесором, ніж звичайний сьогодні смартфон. Може ваш код Ruby? Це одна з причин, що може виглядати простіше, ніж зрілі приклади C, які ви вибрали.


2

Я перебуваю в зворотному становищі, коли більшу частину останніх 9 років провів як програміст C, а останнім часом працюю над деякими передніми країнами Ruby on Rails.

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

Я можу сказати, перш за все, ми не робимо жодної TDD. Я неодноразово пробував вводити одиничні тести, але в C це більше проблем, ніж це варто - принаймні при розробці спеціального програмного забезпечення. Але я б сказав, що TDD набагато менше потрібен C, ніж Рубі. В основному, це лише тому, що компілюється C, і якщо він компілюється без попереджень, ви вже зробили досить аналогічну кількість тестування на тести автоматизованого сформованого риштування rspec в Rails. Рубін без одиничних тестів неможливий.

Але я б сказав, що C не повинен бути настільки важким, як це роблять деякі люди. Значна частина стандартної бібліотеки С - це безлад незрозумілих імен функцій, і багато програм C дотримуються цієї конвенції. Я радий сказати, що у нас немає, а насправді є багато обгортків для стандартної функціональності бібліотеки (ST_Copy замість strncpy, ST_PatternMatch замість regcomp / regexec, CHARSET_Convert замість iconv_open / iconv / iconv_close тощо). Наш внутрішній код C читає мені краще, ніж більшість інших речей, які я читав.

Але коли ви кажете, що правила з інших мов вищого рівня, схоже, не застосовуються, я б не погоджувався. Багато хорошого коду С "відчуває" об'єктно-орієнтовану. Ви часто бачите шаблон ініціалізації ручки до ресурсу, називаючи деякі функції, що передають ручку в якості аргументу, і врешті-решт випускаєте ресурс. Дійсно, принципи проектування об'єктно-орієнтованого програмування значною мірою виходили з того, що люди робили процедурними мовами.

Часи, коли C стає дійсно складним, часто бувають такі речі, як драйвери пристроїв та ядра ОС, які в принципі дуже низькі. Коли ви пишете систему вищого рівня, ви також можете використовувати функції вищого рівня C та уникати низької складності.

Одна дуже цікава річ, на яку ви можете поглянути, - це вихідний код C для Ruby. У документах Ruby API (http://www.ruby-doc.org/core-1.9.3/) ви можете натиснути та переглянути вихідний код для різних методів. Цікаво, що цей код виглядає досить приємно і елегантно - він не виглядає так складно, як ви могли б собі уявити.


" ... ви також можете використовувати функції вищого рівня C ... ", як це є? ;-)
алк

Я маю на увазі вищий рівень, ніж обробка бітів і вказівник на майстер вказівника, який ви, як правило, бачите в коді типу драйвера пристрою! І якщо вас не турбує накладні витрати на пару функціональних викликів, ви можете зробити код C, який дійсно виглядає досить високим рівнем.
asc99c

" ... ви можете зробити код C, який дійсно виглядає досить високим рівнем. ", абсолютно, я цілком згоден на це. Але хоч " ... особливості вищого рівня ... " не з C, а в голові, чи не так?
алк

2

Що я зробив, це відокремити залежний від пристрою код від незалежного коду пристрою, а потім перевірити незалежний код пристрою. З хорошою модульністю та дисциплінованістю ви зможете заграти в основному добре перевірену кодову базу.


2

Немає причини, чому ти не можеш. Проблема полягає в тому, що можуть не бути приємні рамки тестування "поза штатним" блоком, як у вас в інших типах розробок. Гаразд. Це просто означає, що до тестування вам доведеться скористатися "власноруч".

Наприклад, можливо, вам доведеться запрограмувати прилади для виготовлення "підроблених входів" для A / D-перетворювачів, або, можливо, вам доведеться генерувати потік "підроблених даних", щоб ваш вбудований пристрій відповів на це.

Якщо ви зіткнулися з опором до використання слова "TDD", назвіть його "DVT" (тест перевірки дизайну), що зробить EE більш комфортним з цією ідеєю.


0

Чи можливо навіть робити TDD на вбудованих пристроях або при розробці драйверів або таких речей, як користувальницький завантажувач тощо?

Деякий час тому мені потрібно було написати завантажувач першого рівня для процесора ARM. Насправді є один із хлопців, які продають цей процесор. І ми використали схему, коли їх завантажувач завантажує наш завантажувач. Але це було повільно, тому що нам потрібно було спалахнути два файли у спалах NOR замість одного, нам потрібно було збільшити розмір нашого завантажувача в перший завантажувач і перестроювати його щоразу, коли ми змінювали завантажувач тощо.

Тому я вирішив інтегрувати функції їх завантажувача в нашу. Оскільки це був комерційний код, я повинен був переконатися, що все працює як слід. Тож я змінив QEMU для імітації IP-блоків цього процесора (не всіх, лише тих, які торкаються завантажувача), і додав код до QEMU, щоб "printf" всіх читав / записував до реєстрів, які керують елементами, такими як PLL, UART, SRAM-контролер та так далі. Потім я оновив наш завантажувач, щоб підтримати цей процесор, і після цього порівняв вихід, який дає наш завантажувач та їх на емуляторі, це допомагає мені зловити кілька помилок. Частково це було написано в асемблері ARM, частково в C. Також після цього модифікований QEMU допоміг мені зловити одну помилку, яку я не міг зловити за допомогою JTAG та реального процесора ARM.

Тож навіть з C і асемблером можна використовувати тести.


-2

Так, можна зробити TDD на вбудованому програмному забезпеченні. Люди, які говорять про це неможливо, не мають відношення чи не застосовуються, не коректні. Існує серйозна цінність, яку слід отримати від TDD у вбудованому вигляді, як і будь-яке програмне забезпечення.

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

Коли ви робите TDD, ви будете створювати і проводити безліч тестів. Вам потрібно програмне забезпечення, яке допоможе вам це зробити. Ви хочете, щоб тестові рамки дозволяли зробити це швидко і просто, з автоматичним виявленням тестів і створенням макетів.

Найкращий варіант для C зараз - це Ceedling. Ось допис про те, що я написав про це:

http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling

І вона побудована в Рубі! Вам не потрібно знати жодного Ruby, щоб користуватися ним.


очікується, що відповіді відповідатимуть самостійно. Змушують читачів потрапляти на зовнішній ресурс, щоб дізнатися, що речовина нахмурилося на Stack Exchange ("прочитайте статтю або ознайомтеся з" Седлінг "). Розгляньте редагування, щоб воно відповідало нормам якості сайту
gnat

Чи є у Ceedling якісь механізми для підтримки асинхронних подій? Одним із найбільш складних аспектів вбудованих додатків у реальному часі є те, що вони мають справу з отриманням входів із дуже складних систем, які самі по собі складно моделювати ...
Джей Елстон,

@Jay У нього немає нічого конкретного, щоб це підтримати. Однак у мене було успішно тестувати таку штуку з глузування та створивши архітектуру для її підтримки. Наприклад, я нещодавно працював над проектом, коли події, що керуються переривами, ставляться в чергу і потім споживаються в державній машині "обробник подій". По суті це була лише функція, яку викликали щоразу, коли відбулася подія. Під час тестування цієї функції я міг висміяти виклик функції, який витягував події з черги, і так міг імітувати будь-які події, що відбуваються в системі. Тест-водіння допомагає і тут.
черно
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.