PIC32 vs dsPIC vs ARM vs AVR, чи має значення архітектура, коли ми взагалі програмуємо на мові С? [зачинено]


10

Зараз ми використовуємо 32-розрядний мікроконтролер PIC32. Це добре працює для наших потреб, але ми також вивчаємо інші мікроконтролери, які нам краще підходять + у нас є інші проекти, для яких ми вибираємо MCU. Для цього ми вибрали мікроконтроллер SAM DA на основі ARM, який є таким же 32-розрядним, але на основі ARM (більш популярний, ніж PIC32 - галузевий).

Тепер для PIC32 ми використовуємо MPLAB, але для ARM cortex-M0, ми будемо використовувати Atmel Studio. Ми будемо використовувати C-мову в обох платформах. Я хвилюю те, що ми будемо використовувати два 32-бітові мікроконтролери (від однієї компанії), але мати різні архітектури. Це вимагає від нас вивчення двох різних пристроїв і збільшить нашу "криву навчання" + час доставки. Але з іншого боку, я також думаю, оскільки ми використовуватимемо С-мову в обох випадках, крива навчання для ARM не повинна бути такою, що чути, і варто також вивчити цей процесор.

Моє головне питання полягає в тому, наскільки велика різниця робить архітектура, коли ми програмуємо на мові C, оскільки вона забезпечує абстрагування внутрішніх елементів мікроконтролера. І які основні відмінності у MPLAP та Atmel Studio , враховуючи програмування на мові С.


2
Якщо з PIC32 все працює, то в чому сенс перемикання? Навіть якщо код повністю працює (він не буде), все ще є новий ланцюжок інструментів та IDE, до якого звикнути. У чому справа? Перехід з релігійних причин або на "основі зброї" (або на чомусь іншому) - нерозумно. Вам потрібно мати вагомі причини, але ви не показали нам жодної.
Олін Латроп

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

Одне, що я дізнався, що Atmel Studio забезпечує чудові моменти часу, ніж відео на
інженер

Відповіді:


20

Це досить впевнена тема. Я можу говорити сам (AVR, ARM, MSP430).

Різниця 1 (найзначніша) - в периферії. Кожен з MCU має подібні UART, SPI, таймери тощо - просто зареєструйте імена та біти різні. Більшість часу це було головним питанням, з яким мені доводилося стикатися при переміщенні коду між фішками. Рішення. Напишіть драйвери загальним API, щоб ваша програма могла бути портативною.

Різниця 2 - це архітектура пам'яті. Якщо ви хочете розмістити константи спалаху на AVR, вам потрібно використовувати спеціальні атрибути та спеціальні функції для їх читання. У світі ARM ви просто перенаправляєте вказівник, тому що є єдиний адресний простір (я не знаю, наскільки малі PIC-адреси обробляють це, але припускаю, що вони ближче до AVR).

Різниця 3 - це оголошення про переривання та обробку. avr-gccмає ISR()макрос. ARM має лише ім'я функції (наприклад, деякіUART_Handler () - якщо ви використовуєте заголовки CMSIS та код запуску). Вектори переривання ARM можна розмістити в будь-якому місці (включаючи оперативну пам'ять) та змінити під час виконання (дуже зручно, якщо у вас є, наприклад, два різні протоколи UART, які можна перемикати). AVR має лише можливість використовувати вектори або в "основному спалаху", або в "секції завантажувача" (тому, якщо ви хочете керувати перервами по-іншому, вам потрібно використовувати ifоператор).

Різниця 4 - режими сну та управління потужністю. Якщо у вас є потреба в мінімальному енергоспоживанні, вам доведеться збільшити всі функції MCU. Це може сильно відрізнятися між MCU - деякі мають більш грубі режими енергозбереження, деякі можуть вмикати / вимикати окремі периферійні пристрої. Деякі MCU мають регульовані регулятори, тому ви можете запускати їх з меншою напругою на меншій швидкості і т. Д. Я не бачу простий спосіб досягти такої ж ефективності на MCU (скажімо так) з 3 глобальними режимами живлення та іншим із 7 режимами живлення та індивідуальне управління периферійним годинником.

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

На мою думку, хороша річ щодо ARM - це "монокультура" - наявність багатьох компіляторів (gcc, Keil, IAR ... назвати декілька), безлічі безкоштовних і офіційно підтримуваних IDE (принаймні для NXP, STM32, Silicon Labs, Nordic), багато інструментів налагодження (SEGGER - особливо Ozone, ULINK, OpenOCD ...) та багато постачальників чіпів (я навіть не буду їх називати). PIC32 здебільшого обмежений Microchip (але це має значення лише якщо вам не подобаються їх інструменти.

Якщо мова йде про код C. Це на 99% те саме, ifоператор той самий, цикл працює аналогічно. Однак вам слід подбати про розмір рідного слова. Наприклад, forцикл на AVR є найшвидшим, якщо ви використовуєте uint8_tдля лічильника, тоді як на ARM uint32_t- це найшвидший тип (або int32_t). ARM повинен буде перевіряти наявність 8-бітового переповнення кожного разу, якщо ви використовували менший тип.

Вибір MCU та / або постачальника загалом стосується політики та логістики (якщо ви не маєте чітких чітких технічних обмежень, наприклад: висока температура - використовуйте MSP430 або Vorago). Навіть якщо програма може працювати на будь-чому, і лише 5% коду (драйверів) має бути розроблено та підтримано протягом життя продукту - це все ще є додатковою вартістю для компанії. Усі місця, в яких я працював, мали улюбленого постачальника та лінійку MCU (на кшталт "виберіть будь-яку кінетику, яку ви хочете, якщо немає дуже вагомих причин вибрати щось інше"). Це також допомагає, якщо у вас є інші люди, щоб попросити про допомогу, тому як менеджер я б уникнув відділу розвитку 5 осіб, де кожен використовував абсолютно інший чіп.


3
«AVR є найшвидшим, якщо ви використовуєте uint8_t для лічильника, тоді як на ARM uint32_t - це найшвидший тип (або int32_t). ARM повинен буде перевіряти наявність 8-бітового переповнення кожного разу, якщо ви використовуєте менший тип. " ви можете використовувати uint_fast8_t, якщо вам потрібно лише принаймні 8 біт.
Майкл

@Michael - впевнений, що ви можете використовувати типи _fast, але не можете розраховувати на поведінку переповнення. У моєму stcint gh.h у мене є "typedef unsigned int uint_fast8_t", який в основному є uint32_t :)
filo

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

... не вдалося підтримати таке використання на платформах, які потребувалимуть розповсюдження операцій на сотні мікросекунд. Я не знаю, чому апаратні дизайнери, здається, не дуже намагаються підтримати семантику API «швидкої роботи в будь-який час», але багато хто використовує модель, яка синхронізує окремі операції, а не стан, так що якщо, наприклад, запит було надано увімкніть пристрій і код зрозуміє, що його не потрібно включати, код повинен зачекати, коли пристрій увімкнеться, перш ніж він може надіслати запит на його вимкнення. Безпроблемна робота в API додає великих ускладнень.
supercat

11

Я використав кілька MCU від чотирьох різних виробників. Основна робота щоразу знову - ознайомлення з периферією.

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

GPIO може бути дуже складним. Біт-набір, біт-ясно, біт-перемикання, спеціальні функції включення / відключення, три-стан. Далі ви отримуєте переривання: будь-який край, підйом, падіння, рівень-низький, рівень-високий, самоочищення чи ні.

Потім є I2C, SPI, PWM, таймери та ще два десятки інших типів периферійних пристроїв, кожна з яких має власні годинники, і кожен раз регістри відрізняються новими бітами. Для всіх цих людей потрібно багато годин, читаючи таблицю, як встановити, який біт за яких обставин.

Останній виробник мав багато прикладів коду, який я вважав непридатним. Все було абстраговано. Але коли я простежив це, код пройшов через шість! рівні функціональних викликів для встановлення біта GPIO. Приємно, якщо у вас є процесор 3GHz, але не на MCU 48MHz. Зрештою, мій код був одним рядком:

GPIO->set_output = bit.

Я намагався використовувати більш загальні драйвери, але я відмовився. У MCU ти завжди борешся з простором і циклами годин. Я виявив, що шар абстракції першим вийде у вікно, якщо ви генеруєте певну форму хвилі в процедурі переривання, яка називається на 10 КГц.

Тож зараз у мене все працює, і я планую НЕ перемикатися знову, якщо не дуже, дуже вагома причина.

Все вищезазначене має бути амортизовано на те, скільки товарів ви продаєте та на чому заощаджуєте. Продаж мільйона: заощадження 0,10 для переходу на інший тип означає, що ви можете витратити 100 000 на робочу годину програмного забезпечення. Продаючи 1000, ви можете витратити лише 100.


1
Особисто тому я дотримуюся асемблера. Прекрасний бінарний, ніякої абстракції.
Ян Блен

Препроцесор C може дуже добре працювати з речами, особливо в поєднанні з __builtin_constant вбудованими. Якщо кожен визначає константи для кожного біта вводу / виводу форми (номер порту * 32 + бітове число), можна написати макрос, для OUTPUT_HI(n)якого вийде код, еквівалентний GPIOD->bssr |= 0x400;if n, як константа типу 0x6A, але викликати просту підпрограму, якщо nє не постійний. Як було сказано, більшість постачальників API, які я бачив, мають діапазон між посереднім та жахливим.
supercat

8

Це скоріше думка / коментар, ніж відповідь.

Ви не хочете і не повинні програмувати на C. C ++, якщо правильно користуватися , набагато перевершує. (Гаразд, я мушу визнати, що при неправильному використанні це набагато гірше, ніж C.) Це обмежує вас чіпами, які мають (сучасний) компілятор C ++, що є майже всім, що підтримується GCC, включаючи AVR (з деякі обмеження, філо згадує проблеми нерівномірного адресного простору), але виключаючи майже всі ПІК (PIC32 може бути підтриманий, але я ще не бачив пристойного порту).

Коли ви програмуєте алгоритми на C / C ++, різниця між варіантами, про які ви згадуєте, невелика (за винятком того, що 8 або 16 бітовий мікросхема буде дуже серйозним недоліком, коли ви робите велику арифметику біт 16, 32 або вище). Коли вам потрібна остання унція продуктивності, вам, ймовірно, потрібно буде використовувати ассемблер (власний або код, наданий постачальником або третьою стороною). У такому випадку ви можете повторно розглянути обраний вами чіп.

Коли ви кодуєте обладнання, ви можете або використовувати якийсь шар абстракції (часто надається виробником) або написати свій власний (на основі коду даних та / або прикладу коду). ІМЕ існуючих абстракцій C (mbed, cmsis, ...) часто функціонально (майже) коректні, але жахливо спрацьовують (перевіряйте, що старі деталі займають близько 6 шарів непрямості для операції встановлення штифтів), зручність використання та портативність. Вони хочуть розкрити для вас всю функціональність певного чіпа, що майже у всіх випадках вам не знадобиться, і, швидше за все, не піклується, і він блокує ваш код саме до цього постачальника (і, можливо, саме цього чіпа).

Це було, якщо C ++ може зробити набагато краще: при правильному виконанні набір контактів може пройти через 6 або більше шарів абстракції (адже це робить кращий (портативний!) Інтерфейс і можливий коротший код), але при цьому надавати інтерфейс, не залежний від цілі для простих випадків , і все одно призводить до того ж машинного коду, який ви писали в асемблері .

Фрагмент стилю кодування, який я використовую, може або зробити вас ентузіастичним, або відвернутись від жаху:

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

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

target::led::init();
target::led::set( 1 );

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

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

Ось як я написав би це в асемблері - якби я зрозумів, що регістри PIO можна використовувати з компенсаціями із загальної бази. У цьому випадку я, мабуть, хотів би, але компілятор набагато краще оптимізує такі речі, ніж я.

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

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

(редагувати - відповідь на "Отже, ефективність роботи, C або C ++ була б на одному рівні?")

Для одних і тих же конструкцій C і C ++ однакові. C ++ має набагато більше конструкцій для абстрагування (лише декілька: класи, шаблони, конспектор), які можна, як і будь-який інструмент, використовувати для добра або для поганого. Щоб зробити дискусії цікавішими: не всі згодні з тим, що добре чи погано ...


Отже, ефективність роботи, C чи C ++, була б на одному рівні? Я думаю, що C ++ матиме більше перевантаження. Однозначно ви вказали мені в правильному напрямку, C ++ - це шлях, а не С.
інженер

Шаблони C ++ змушують поліморфізм часу компіляції, який може становити нульову (або навіть негативну) вартість з точки зору продуктивності, оскільки код складається для кожного конкретного випадку використання. Однак, як правило, найкраще піддаються швидкості націлювання (O3 для GCC). Поліморфізм під час виконання, як і віртуальні функції, може зазнавати набагато більшого покарання, хоча суперечливо легше підтримувати, а в деяких випадках і досить добре.
Ганс

1
Ви стверджуєте, що C ++ кращий, але тоді ви переходите і використовуєте касти в стилі C. За сором.
JAB

@JAB Я ніколи не відчував особливого значення для кастингу в новому стилі, але спробую їх. Але мій поточний пріоритет - інші частини цієї бібліотеки. Справжня проблема, звичайно, в тому, що я не міг передати покажчики як параметри шаблону.
Wouter van Ooijen

Стиль @Hans my cto (Compile Time Objects) має досить вузький випадок використання (близький до апаратного, відома ситуація під час компіляції), це скоріше C-убивця, ніж заміна для традиційного використання OO на основі віртуальних даних. Корисний привід полягає в тому, що відсутність непрямості дає можливість обчислити розмір стека.
Wouter van Ooijen

4

Якщо я правильно розумію, ви хочете знати, які особливості архітектури платформи "спливають" у вашому мовному середовищі C, роблячи складніше писати постійний, портативний код на обох платформах.

C вже досить гнучка тим, що це "портативний асемблер". У всіх обраних вами платформах є GCC / комерційні компілятори, які підтримують мовні стандарти C89 та C99, тобто ви можете запускати подібний код на всіх платформах.

Є кілька міркувань:

  • Деякі архітектури - фон Нойман (ARM, MIPS), інші - Гарвард. Основні обмеження виникають, коли програмі С потрібно читати дані з ПЗУ, наприклад, для друку рядків, дані, визначені як "const" або подібні.

Деякі платформи / компілятори можуть приховати це "обмеження" краще, ніж інші. Наприклад, на AVR вам потрібно використовувати певні макроси для читання даних ROM. На PIC24 / dsPIC також доступні спеціальні вставки tblrd. Однак на додаток, деякі частини також мають функцію "видимість програмного простору" (PSVPAG), яка дозволяє відображати сторінку FLASH в оперативну пам'ять, роблячи негайну адресацію даних без tblrd. Компілятор може зробити це досить ефективно.

ARM і MIPS є Von Neumann, завдяки чому на 1 шині упаковані області пам'яті для ROM, RAM та периферійних пристроїв. Ви не помітите різниці між читанням даних з оперативної пам’яті чи “ROM”.

  • Якщо ви пірнаєте нижче C і подивитесь створені інструкції щодо певних операцій, ви знайдете великі відмінності навколо вводу-виводу. ARM і MIPS - це архітектура реєстру для завантаження RISC . Це означає, що доступ до даних на шині пам'яті повинен проходити через інструкції MOV. Це також означає, що будь-яка модифікація периферійного значення призведе до операції зчитування-зміни-запису (RMW). Є деякі частини ARM, які підтримують біт-бандінг, що набір карт / бітів clr в периферійному просторі вводу / виводу. Однак вам потрібно кодувати цей доступ самостійно.

З іншого боку, PIC24 дозволяє операціям ALU зчитувати та записувати дані безпосередньо за допомогою непрямої адресації (навіть із змінами вказівника ..). Це має деякі характеристики архітектури CISC, наприклад, 1 інструкція може зробити більше роботи. Така конструкція може призвести до складніших процесорних ядер, менших тактових частот, більшого енергоспоживання і т. Д. На щастя, для вас деталь вже розроблена. ;-)

Ці відмінності можуть означати, що PIC24 може бути "більш пунктирним" операціями вводу / виводу Wrt, ніж аналогічно замиканий чип ARM або MIPS. Однак ви можете отримати набагато більш високу деталь ARM / MIPS-шафи для однакових цінових / пакетних / дизайнерських обмежень. Я думаю, що в практичному плані я думаю, що багато "вивчення платформи" - це зрозуміти, що архітектура може, а що не може зробити, наскільки швидко буде кілька операцій тощо.

  • Периферія, управління годинником тощо відрізняються від групи деталей. Строго кажучи, це також зміниться в екосистемі ARM між постачальниками, за винятком декількох периферійних пристроїв Cortex m, таких як NVIC та SysTick.

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

Крім того, якщо ви залишаєте екосистеми Microchip / колишнього Atmel, ви можете виявити, що деталі ARM потребують більшої настройки, щоб примусити їх працювати. Я маю на увазі з точки зору; включення годинника до периферійних пристроїв, потім налаштування периферійних пристроїв та "включення" їх, налаштування NVIC окремо тощо. Це лише частина кривої навчання. Як тільки ви пам’ятаєте робити всі ці речі, у правильному порядку записування драйверів пристроїв для всіх цих мікроконтролерів в якийсь момент відчує себе досить схожим.

  • Також спробуйте використовувати такі бібліотеки, як stdint.h, stdbool.h тощо, якщо ви ще цього не зробили. Ці цілі типи роблять ширини явними, що робить поведінку коду найбільш передбачуваним між платформами. Це може означати використання 32-бітових цілих чисел на 8-бітному AVR; але якщо ваш код потрібен, так і буде.

3

Так і ні. З точки зору програмістів, ви ідеально приховуєте деталі набору інструкцій. Але це певною мірою вже не стосується периферійних пристроїв, які є сутністю в написанні програми, не є частиною набору інструкцій. Тепер у той же час ви не можете просто порівняти деталі спалаху 4096Byte у цих наборах інструкцій, особливо якщо за допомогою C кількість споживання флеш-пам’яті сильно визначається набором інструкцій та компілятором, деякі ніколи не повинні бачити компілятор (кашель PIC кашель) через те, скільки відходів цих ресурсів споживається при складанні. Інші споживання спалаху - це менші витрати. Продуктивність також є проблемою при використанні мови та продуктивності на високому рівні в додатках MCU, тому вона може змінити витрату 3 доларів за плату за mcu або 1 долар.

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

Підсумок наборів інструкцій - це найменша стурбованість, переходите до реальних питань.

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